summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /src/modules
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/addcontact/addcontact.cpp271
-rw-r--r--src/modules/autoaway/autoaway.cpp74
-rw-r--r--src/modules/button/button.cpp614
-rw-r--r--src/modules/clist/Docking.cpp392
-rw-r--r--src/modules/clist/clc.cpp1350
-rw-r--r--src/modules/clist/clc.h248
-rw-r--r--src/modules/clist/clcfiledrop.cpp278
-rw-r--r--src/modules/clist/clcidents.cpp201
-rw-r--r--src/modules/clist/clcitems.cpp707
-rw-r--r--src/modules/clist/clcmsgs.cpp472
-rw-r--r--src/modules/clist/clcutils.cpp879
-rw-r--r--src/modules/clist/clistcore.cpp224
-rw-r--r--src/modules/clist/clistevents.cpp441
-rw-r--r--src/modules/clist/clistmenus.cpp1443
-rw-r--r--src/modules/clist/clistmod.cpp569
-rw-r--r--src/modules/clist/clistsettings.cpp331
-rw-r--r--src/modules/clist/clisttray.cpp980
-rw-r--r--src/modules/clist/clui.cpp1136
-rw-r--r--src/modules/clist/cluiservices.cpp221
-rw-r--r--src/modules/clist/contact.cpp193
-rw-r--r--src/modules/clist/genmenu.cpp1294
-rw-r--r--src/modules/clist/genmenu.h146
-rw-r--r--src/modules/clist/genmenuopt.cpp870
-rw-r--r--src/modules/clist/groups.cpp577
-rw-r--r--src/modules/clist/keyboard.cpp173
-rw-r--r--src/modules/clist/movetogroup.cpp160
-rw-r--r--src/modules/clist/protocolorder.cpp351
-rw-r--r--src/modules/contacts/contacts.cpp513
-rw-r--r--src/modules/database/database.cpp563
-rw-r--r--src/modules/database/dbini.cpp490
-rw-r--r--src/modules/database/dblists.cpp282
-rw-r--r--src/modules/database/dblists.h39
-rw-r--r--src/modules/database/dbutils.cpp365
-rw-r--r--src/modules/database/profilemanager.cpp850
-rw-r--r--src/modules/database/profilemanager.h43
-rw-r--r--src/modules/findadd/findadd.cpp1033
-rw-r--r--src/modules/findadd/findadd.h59
-rw-r--r--src/modules/findadd/searchresults.cpp398
-rw-r--r--src/modules/fonts/FontOptions.cpp1523
-rw-r--r--src/modules/fonts/FontService.cpp126
-rw-r--r--src/modules/fonts/FontService.h112
-rw-r--r--src/modules/fonts/services.cpp534
-rw-r--r--src/modules/help/about.cpp148
-rw-r--r--src/modules/help/help.cpp117
-rw-r--r--src/modules/history/history.cpp434
-rw-r--r--src/modules/icolib/IcoLib.h83
-rw-r--r--src/modules/icolib/extracticon.cpp280
-rw-r--r--src/modules/icolib/skin2icons.cpp1992
-rw-r--r--src/modules/idle/idle.cpp520
-rw-r--r--src/modules/ignore/ignore.cpp481
-rw-r--r--src/modules/keybindings/keybindings.cpp616
-rw-r--r--src/modules/keybindings/keybindings.h43
-rw-r--r--src/modules/langpack/langpack.cpp569
-rw-r--r--src/modules/langpack/lpservices.cpp168
-rw-r--r--src/modules/netlib/netlib.cpp640
-rw-r--r--src/modules/netlib/netlib.h209
-rw-r--r--src/modules/netlib/netlibautoproxy.cpp460
-rw-r--r--src/modules/netlib/netlibbind.cpp287
-rw-r--r--src/modules/netlib/netlibhttp.cpp1331
-rw-r--r--src/modules/netlib/netlibhttpproxy.cpp522
-rw-r--r--src/modules/netlib/netliblog.cpp637
-rw-r--r--src/modules/netlib/netlibopenconn.cpp944
-rw-r--r--src/modules/netlib/netlibopts.cpp556
-rw-r--r--src/modules/netlib/netlibpktrecver.cpp85
-rw-r--r--src/modules/netlib/netlibsecurity.cpp570
-rw-r--r--src/modules/netlib/netlibsock.cpp203
-rw-r--r--src/modules/netlib/netlibssl.cpp981
-rw-r--r--src/modules/netlib/netlibupnp.cpp885
-rw-r--r--src/modules/options/descbutton.cpp332
-rw-r--r--src/modules/options/filter.cpp217
-rw-r--r--src/modules/options/filter.h108
-rw-r--r--src/modules/options/headerbar.cpp372
-rw-r--r--src/modules/options/iconheader.cpp545
-rw-r--r--src/modules/options/options.cpp1521
-rw-r--r--src/modules/plugins/newplugins.cpp1204
-rw-r--r--src/modules/protocols/protoaccs.cpp638
-rw-r--r--src/modules/protocols/protochains.cpp274
-rw-r--r--src/modules/protocols/protocols.cpp844
-rw-r--r--src/modules/protocols/protodir.cpp248
-rw-r--r--src/modules/protocols/protoint.cpp271
-rw-r--r--src/modules/protocols/protoopts.cpp1084
-rw-r--r--src/modules/skin/hotkeys.cpp1465
-rw-r--r--src/modules/skin/skinicons.cpp489
-rw-r--r--src/modules/skin/sounds.cpp469
-rw-r--r--src/modules/srauth/auth.cpp129
-rw-r--r--src/modules/srauth/authdialogs.cpp312
-rw-r--r--src/modules/srawaymsg/awaymsg.cpp194
-rw-r--r--src/modules/srawaymsg/sendmsg.cpp637
-rw-r--r--src/modules/sremail/email.cpp89
-rw-r--r--src/modules/srfile/file.cpp392
-rw-r--r--src/modules/srfile/file.h115
-rw-r--r--src/modules/srfile/fileexistsdlg.cpp354
-rw-r--r--src/modules/srfile/fileopts.cpp247
-rw-r--r--src/modules/srfile/filerecvdlg.cpp446
-rw-r--r--src/modules/srfile/filesenddlg.cpp363
-rw-r--r--src/modules/srfile/filexferdlg.cpp799
-rw-r--r--src/modules/srfile/ftmanager.cpp592
-rw-r--r--src/modules/srurl/url.cpp182
-rw-r--r--src/modules/srurl/url.h41
-rw-r--r--src/modules/srurl/urldialogs.cpp664
-rw-r--r--src/modules/updatenotify/updatenotify.cpp680
-rw-r--r--src/modules/userinfo/contactinfo.cpp515
-rw-r--r--src/modules/userinfo/stdinfo.cpp613
-rw-r--r--src/modules/userinfo/userinfo.cpp636
-rw-r--r--src/modules/useronline/useronline.cpp117
-rw-r--r--src/modules/utils/bmpfilter.cpp242
-rw-r--r--src/modules/utils/colourpicker.cpp107
-rw-r--r--src/modules/utils/hyperlink.cpp274
-rw-r--r--src/modules/utils/imgconv.cpp152
-rw-r--r--src/modules/utils/md5.cpp374
-rw-r--r--src/modules/utils/openurl.cpp228
-rw-r--r--src/modules/utils/path.cpp600
-rw-r--r--src/modules/utils/resizer.cpp152
-rw-r--r--src/modules/utils/sha1.cpp175
-rw-r--r--src/modules/utils/timeutils.cpp277
-rw-r--r--src/modules/utils/timezones.cpp662
-rw-r--r--src/modules/utils/utf.cpp413
-rw-r--r--src/modules/utils/utils.cpp587
-rw-r--r--src/modules/utils/windowlist.cpp101
-rw-r--r--src/modules/visibility/visibility.cpp297
-rw-r--r--src/modules/xml/xmlApi.cpp446
-rw-r--r--src/modules/xml/xmlParser.cpp3061
-rw-r--r--src/modules/xml/xmlParser.h746
123 files changed, 62378 insertions, 0 deletions
diff --git a/src/modules/addcontact/addcontact.cpp b/src/modules/addcontact/addcontact.cpp
new file mode 100644
index 0000000000..0be08b93ba
--- /dev/null
+++ b/src/modules/addcontact/addcontact.cpp
@@ -0,0 +1,271 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+INT_PTR CALLBACK AddContactDlgProc(HWND hdlg,UINT msg,WPARAM wparam,LPARAM lparam)
+{
+ ADDCONTACTSTRUCT *acs;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ {
+ char szUin[10];
+ acs=(ADDCONTACTSTRUCT *)lparam;
+ SetWindowLongPtr(hdlg,GWLP_USERDATA,(LONG_PTR)acs);
+
+ TranslateDialogDefault(hdlg);
+ Window_SetIcon_IcoLib(hdlg, SKINICON_OTHER_ADDCONTACT);
+ if ( acs->handleType == HANDLE_EVENT ) {
+ DWORD dwUin;
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=sizeof(DWORD);
+ dbei.pBlob=(PBYTE)&dwUin;
+ CallService(MS_DB_EVENT_GET,(WPARAM)acs->handle,(LPARAM)&dbei);
+ _ltoa(dwUin,szUin,10);
+ acs->szProto = dbei.szModule;
+ }
+ {
+ TCHAR *szName = NULL, *tmpStr = NULL;
+ if ( acs->handleType == HANDLE_CONTACT )
+ szName = cli.pfnGetContactDisplayName( acs->handle, GCDNF_TCHAR );
+ else {
+ int isSet = 0;
+
+ if (acs->handleType == HANDLE_EVENT) {
+ DBEVENTINFO dbei;
+ HANDLE hcontact;
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)acs->handle,0);
+ dbei.pBlob=(PBYTE)mir_alloc(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET,(WPARAM)acs->handle,(LPARAM)&dbei);
+ hcontact=*((PHANDLE)(dbei.pBlob+sizeof(DWORD)));
+ mir_free(dbei.pBlob);
+ if (hcontact!=INVALID_HANDLE_VALUE) {
+ szName = cli.pfnGetContactDisplayName( hcontact, 0 );
+ isSet = 1;
+ }
+ }
+ if (!isSet) {
+ szName = (acs->handleType == HANDLE_EVENT) ? (tmpStr = mir_a2t(szUin)) :
+ (acs->psr->id ? acs->psr->id : acs->psr->nick);
+ } }
+
+ if ( szName && szName[0] ) {
+ TCHAR szTitle[128];
+ mir_sntprintf( szTitle, SIZEOF(szTitle), TranslateT("Add %s"), szName );
+ SetWindowText( hdlg, szTitle );
+ }
+ else SetWindowText( hdlg, TranslateT("Add Contact"));
+ mir_free(tmpStr);
+ } }
+
+ if ( acs->handleType == HANDLE_CONTACT && acs->handle )
+ if ( acs->szProto == NULL || (acs->szProto != NULL && *acs->szProto == 0 ))
+ acs->szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)acs->handle,0);
+
+ {
+ int groupId;
+ for ( groupId = 0; groupId < 999; groupId++ ) {
+ DBVARIANT dbv;
+ char idstr[4];
+ int id;
+ _itoa(groupId,idstr,10);
+ if(DBGetContactSettingTString(NULL,"CListGroups",idstr,&dbv)) break;
+ id = SendDlgItemMessage(hdlg,IDC_GROUP,CB_ADDSTRING,0,(LPARAM)(dbv.ptszVal+1));
+ SendDlgItemMessage(hdlg,IDC_GROUP,CB_SETITEMDATA ,id,groupId+1);
+ DBFreeVariant(&dbv);
+ } }
+
+ SendDlgItemMessage(hdlg,IDC_GROUP,CB_INSERTSTRING,0,(LPARAM)TranslateT("None"));
+ SendDlgItemMessage(hdlg,IDC_GROUP,CB_SETCURSEL,0,0);
+ /* acs->szProto may be NULL don't expect it */
+ {
+ // By default check both checkboxes
+ CheckDlgButton(hdlg,IDC_ADDED,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_AUTH,BST_CHECKED);
+
+ DWORD flags = (acs->szProto) ? CallProtoService(acs->szProto,PS_GETCAPS,PFLAGNUM_4,0) : 0;
+ if (flags&PF4_FORCEADDED) { // force you were added requests for this protocol
+ EnableWindow(GetDlgItem(hdlg,IDC_ADDED),FALSE);
+ }
+ if (flags&PF4_FORCEAUTH) { // force auth requests for this protocol
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTH),FALSE);
+ }
+ if (flags&PF4_NOCUSTOMAUTH) {
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHREQ),FALSE);
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHGB),FALSE);
+ }
+ else {
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHREQ),IsDlgButtonChecked(hdlg,IDC_AUTH));
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHGB),IsDlgButtonChecked(hdlg,IDC_AUTH));
+ SetDlgItemText(hdlg,IDC_AUTHREQ,TranslateT("Please authorize my request and add me to your contact list."));
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ acs = (ADDCONTACTSTRUCT *)GetWindowLongPtr(hdlg, GWLP_USERDATA);
+
+ switch (LOWORD(wparam))
+ {
+ case IDC_AUTH:
+ {
+ DWORD flags = CallProtoService(acs->szProto,PS_GETCAPS,PFLAGNUM_4,0);
+ if (flags & PF4_NOCUSTOMAUTH) {
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHREQ),FALSE);
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHGB),FALSE);
+ }
+ else {
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHREQ),IsDlgButtonChecked(hdlg,IDC_AUTH));
+ EnableWindow(GetDlgItem(hdlg,IDC_AUTHGB),IsDlgButtonChecked(hdlg,IDC_AUTH));
+ }
+ }
+ break;
+ case IDOK:
+ {
+ HANDLE hContact = INVALID_HANDLE_VALUE;
+ switch (acs->handleType)
+ {
+ case HANDLE_EVENT:
+ {
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM)acs->handle, (LPARAM)&dbei);
+ hContact = (HANDLE)CallProtoService(dbei.szModule, PS_ADDTOLISTBYEVENT, 0, (LPARAM)acs->handle);
+ }
+ break;
+
+ case HANDLE_SEARCHRESULT:
+ hContact = (HANDLE)CallProtoService(acs->szProto, PS_ADDTOLIST, 0, (LPARAM)acs->psr);
+ break;
+
+ case HANDLE_CONTACT:
+ hContact = acs->handle;
+ break;
+ }
+
+ if (hContact == NULL)
+ break;
+
+ TCHAR szHandle[256];
+ if (GetDlgItemText(hdlg, IDC_MYHANDLE, szHandle, SIZEOF(szHandle)))
+ DBWriteContactSettingTString(hContact, "CList", "MyHandle", szHandle);
+
+ int item = SendDlgItemMessage(hdlg, IDC_GROUP, CB_GETCURSEL, 0, 0);
+ if (item > 0)
+ {
+ item = SendDlgItemMessage(hdlg, IDC_GROUP, CB_GETITEMDATA, item, 0);
+ CallService(MS_CLIST_CONTACTCHANGEGROUP, (WPARAM)hContact, item);
+ }
+
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+
+ if (IsDlgButtonChecked(hdlg, IDC_ADDED))
+ CallContactService(hContact, PSS_ADDED, 0, 0);
+
+ if (IsDlgButtonChecked(hdlg, IDC_AUTH))
+ {
+ DWORD flags = CallProtoService(acs->szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+ if (flags & PF4_NOCUSTOMAUTH)
+ CallContactService(hContact, PSS_AUTHREQUESTT, 0, 0);
+ else
+ {
+ TCHAR szReason[512];
+ GetDlgItemText(hdlg, IDC_AUTHREQ, szReason, SIZEOF(szReason));
+ CallContactService(hContact, PSS_AUTHREQUESTT, 0, (LPARAM)szReason);
+ }
+ }
+ }
+ // fall through
+ case IDCANCEL:
+ if ( GetParent( hdlg ) == NULL)
+ DestroyWindow( hdlg );
+ else
+ EndDialog( hdlg, 0 );
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ /* if there is no parent for the dialog, its a modeless dialog and can't be killed using EndDialog() */
+ if ( GetParent( hdlg ) == NULL )
+ DestroyWindow(hdlg);
+ else
+ EndDialog( hdlg, 0 );
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hdlg);
+ acs = ( ADDCONTACTSTRUCT* )GetWindowLongPtr(hdlg,GWLP_USERDATA);
+ if (acs) {
+ if (acs->psr) {
+ mir_free(acs->psr->nick);
+ mir_free(acs->psr->firstName);
+ mir_free(acs->psr->lastName);
+ mir_free(acs->psr->email);
+ mir_free(acs->psr);
+ }
+ mir_free(acs);
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+INT_PTR AddContactDialog(WPARAM wParam,LPARAM lParam)
+{
+ if (lParam) {
+ ADDCONTACTSTRUCT* acs = ( ADDCONTACTSTRUCT* )mir_alloc(sizeof(ADDCONTACTSTRUCT));
+ memmove( acs, ( ADDCONTACTSTRUCT* )lParam, sizeof( ADDCONTACTSTRUCT ));
+ if ( acs->psr ) {
+ PROTOSEARCHRESULT *psr;
+ /* bad! structures that are bigger than psr will cause crashes if they define pointers within unreachable structural space */
+ psr = (PROTOSEARCHRESULT *)mir_alloc(acs->psr->cbSize);
+ memmove(psr,acs->psr,acs->psr->cbSize);
+ psr->nick = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->nick) : mir_a2t((char*)psr->nick);
+ psr->firstName = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->firstName) : mir_a2t((char*)psr->firstName);
+ psr->lastName = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->lastName) : mir_a2t((char*)psr->lastName);
+ psr->email = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->email) : mir_a2t((char*)psr->email);
+ psr->flags = psr->flags & ~PSR_UNICODE | PSR_TCHAR;
+ acs->psr = psr;
+ /* copied the passed acs structure, the psr structure with, the pointers within that */
+ }
+
+ if ( wParam )
+ DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(IDD_ADDCONTACT),(HWND)wParam,AddContactDlgProc,(LPARAM)acs);
+ else
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_ADDCONTACT),(HWND)wParam,AddContactDlgProc,(LPARAM)acs);
+ return 0;
+ }
+ return 1;
+}
+
+int LoadAddContactModule(void)
+{
+ CreateServiceFunction(MS_ADDCONTACT_SHOW,AddContactDialog);
+ return 0;
+}
diff --git a/src/modules/autoaway/autoaway.cpp b/src/modules/autoaway/autoaway.cpp
new file mode 100644
index 0000000000..9805535173
--- /dev/null
+++ b/src/modules/autoaway/autoaway.cpp
@@ -0,0 +1,74 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define AA_MODULE "AutoAway"
+
+void Proto_SetStatus(const char* szProto, unsigned status);
+
+static int AutoAwayEvent(WPARAM, LPARAM lParam)
+{
+ int i;
+
+ MIRANDA_IDLE_INFO mii;
+ mii.cbSize = sizeof( mii );
+ CallService( MS_IDLE_GETIDLEINFO, 0, (LPARAM)&mii );
+ if ( mii.aaStatus == 0 )
+ return 0;
+
+ for ( i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+
+ if (!Proto_IsAccountEnabled( pa ) || Proto_IsAccountLocked( pa )) continue;
+
+ int statusbits = CallProtoService( pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0 );
+ int currentstatus = CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0 );
+ int status = mii.aaStatus;
+ if ( !(statusbits & Proto_Status2Flag(status)) ) {
+ // the protocol doesnt support the given status
+ if ( statusbits & Proto_Status2Flag( ID_STATUS_AWAY ))
+ status = ID_STATUS_AWAY;
+ // the proto doesnt support user mode or even away, bail.
+ else
+ continue;
+ }
+ if ( currentstatus >= ID_STATUS_ONLINE && currentstatus != ID_STATUS_INVISIBLE ) {
+ if ( (lParam&IDF_ISIDLE) && ( currentstatus == ID_STATUS_ONLINE || currentstatus == ID_STATUS_FREECHAT )) {
+ DBWriteContactSettingByte( NULL, AA_MODULE, pa->szModuleName, 1 );
+ Proto_SetStatus( pa->szModuleName, status );
+ }
+ else if ( !(lParam & IDF_ISIDLE) && DBGetContactSettingByte( NULL, AA_MODULE, pa->szModuleName, 0 )) {
+ // returning from idle and this proto was set away, set it back
+ DBWriteContactSettingByte( NULL, AA_MODULE, pa->szModuleName, 0 );
+ if ( !mii.aaLock )
+ Proto_SetStatus( pa->szModuleName, ID_STATUS_ONLINE);
+ } } }
+
+ return 0;
+}
+
+int LoadAutoAwayModule(void)
+{
+ HookEvent(ME_IDLE_CHANGED, AutoAwayEvent);
+ return 0;
+}
diff --git a/src/modules/button/button.cpp b/src/modules/button/button.cpp
new file mode 100644
index 0000000000..b6265dfa7a
--- /dev/null
+++ b/src/modules/button/button.cpp
@@ -0,0 +1,614 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <initguid.h>
+#include <oleacc.h>
+
+// TODO:
+// - Support for bitmap buttons (simple call to DrawIconEx())
+
+static LRESULT CALLBACK MButtonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+typedef struct {
+ HWND hwnd;
+ int stateId; // button state
+ int focus; // has focus (1 or 0)
+ HFONT hFont; // font
+ HICON arrow; // uses down arrow
+ int defbutton; // default button
+ HICON hIcon;
+ HBITMAP hBitmap;
+ int pushBtn;
+ int pbState;
+ HTHEME hThemeButton;
+ HTHEME hThemeToolbar;
+ char cHot;
+ int flatBtn;
+ HWND hwndToolTips;
+ IAccPropServices* pAccPropServices;
+} MButtonCtrl;
+
+
+static CRITICAL_SECTION csTips;
+static SortedList lToolTips;
+static BOOL bModuleInitialized = FALSE;
+
+typedef struct
+{
+ DWORD ThreadId;
+ HWND hwnd;
+} TTooltips;
+
+int LoadButtonModule(void)
+{
+ WNDCLASSEX wc = {0};
+
+ if ( bModuleInitialized ) return 0;
+ bModuleInitialized = TRUE;
+
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = MIRANDABUTTONCLASS;
+ wc.lpfnWndProc = MButtonWndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(MButtonCtrl*);
+ wc.hbrBackground = 0;
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+
+ InitializeCriticalSection(&csTips);
+ lToolTips.increment = 1;
+ lToolTips.sortFunc = NumericKeySort;
+ return 0;
+}
+
+void UnloadButtonModule()
+{
+ if ( !bModuleInitialized ) return;
+ EnterCriticalSection(&csTips);
+ List_Destroy(&lToolTips);
+ LeaveCriticalSection(&csTips);
+ DeleteCriticalSection(&csTips);
+}
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLID 100
+#define BUTTON_POLLDELAY 50
+
+static void DestroyTheme(MButtonCtrl *ctl) {
+ if (closeThemeData) {
+ if (ctl->hThemeButton) {
+ closeThemeData(ctl->hThemeButton);
+ ctl->hThemeButton = NULL;
+ }
+ if (ctl->hThemeToolbar) {
+ closeThemeData(ctl->hThemeToolbar);
+ ctl->hThemeToolbar = NULL;
+ }
+ }
+}
+
+static void LoadTheme(MButtonCtrl *ctl)
+{
+ if (openThemeData) {
+ DestroyTheme(ctl);
+ ctl->hThemeButton = openThemeData(ctl->hwnd, L"BUTTON");
+ ctl->hThemeToolbar = openThemeData(ctl->hwnd, L"TOOLBAR");
+ }
+}
+
+static void SetHwndPropInt(MButtonCtrl* bct, DWORD idObject, DWORD idChild, MSAAPROPID idProp, int val)
+{
+ if (bct->pAccPropServices == NULL) return;
+ VARIANT var;
+ var.vt = VT_I4;
+ var.lVal = val;
+ bct->pAccPropServices->SetHwndProp(bct->hwnd, idObject, idChild, idProp, var);
+}
+static int TBStateConvert2Flat(int state)
+{
+ switch(state) {
+ case PBS_NORMAL: return TS_NORMAL;
+ case PBS_HOT: return TS_HOT;
+ case PBS_PRESSED: return TS_PRESSED;
+ case PBS_DISABLED: return TS_DISABLED;
+ case PBS_DEFAULTED: return TS_NORMAL;
+ }
+ return TS_NORMAL;
+}
+
+#ifndef DFCS_HOT
+#define DFCS_HOT 0x1000
+#endif
+
+static void PaintWorker(MButtonCtrl *ctl, HDC hdcPaint)
+{
+ if (hdcPaint) {
+ HDC hdcMem;
+ HBITMAP hbmMem;
+ HDC hOld;
+ RECT rcClient;
+
+ GetClientRect(ctl->hwnd, &rcClient);
+ hdcMem = CreateCompatibleDC(hdcPaint);
+ hbmMem = CreateCompatibleBitmap(hdcPaint, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
+ hOld = ( HDC )SelectObject(hdcMem, hbmMem);
+
+ // If its a push button, check to see if it should stay pressed
+ if (ctl->pushBtn && ctl->pbState) ctl->stateId = PBS_PRESSED;
+
+ // Draw the flat button
+ if (ctl->flatBtn) {
+ if (ctl->hThemeToolbar) {
+ int state = IsWindowEnabled(ctl->hwnd)?(ctl->stateId==PBS_NORMAL&&ctl->defbutton?PBS_DEFAULTED:ctl->stateId):PBS_DISABLED;
+ if (isThemeBackgroundPartiallyTransparent(ctl->hThemeToolbar, TP_BUTTON, TBStateConvert2Flat(state))) {
+ drawThemeParentBackground(ctl->hwnd, hdcMem, &rcClient);
+ }
+ drawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), &rcClient, &rcClient);
+ }
+ else {
+ HBRUSH hbr;
+
+ if (ctl->stateId==PBS_PRESSED||ctl->stateId==PBS_HOT)
+ hbr = GetSysColorBrush(COLOR_3DLIGHT);
+ else {
+ HWND hwndParent = GetParent(ctl->hwnd);
+ HDC dc = GetDC(hwndParent);
+ HBRUSH oldBrush = (HBRUSH)GetCurrentObject( dc, OBJ_BRUSH );
+ hbr = (HBRUSH)SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM)dc, (LPARAM)hwndParent);
+ SelectObject(dc,oldBrush);
+ ReleaseDC(hwndParent,dc);
+ }
+ if (hbr) {
+ FillRect(hdcMem, &rcClient, hbr);
+ DeleteObject(hbr);
+ }
+ if (ctl->stateId==PBS_HOT||ctl->focus) {
+ if (ctl->pbState)
+ DrawEdge(hdcMem,&rcClient, EDGE_ETCHED,BF_RECT|BF_SOFT);
+ else DrawEdge(hdcMem,&rcClient, BDR_RAISEDOUTER,BF_RECT|BF_SOFT|BF_FLAT);
+ }
+ else if (ctl->stateId==PBS_PRESSED)
+ DrawEdge(hdcMem, &rcClient, BDR_SUNKENOUTER,BF_RECT|BF_SOFT);
+ }
+ }
+ else {
+ // Draw background/border
+ if (ctl->hThemeButton) {
+ int state = IsWindowEnabled(ctl->hwnd)?(ctl->stateId==PBS_NORMAL&&ctl->defbutton?PBS_DEFAULTED:ctl->stateId):PBS_DISABLED;
+ if (isThemeBackgroundPartiallyTransparent(ctl->hThemeButton, BP_PUSHBUTTON, state)) {
+ drawThemeParentBackground(ctl->hwnd, hdcMem, &rcClient);
+ }
+ drawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, &rcClient, &rcClient);
+ }
+ else {
+ UINT uState = DFCS_BUTTONPUSH|((ctl->stateId==PBS_HOT)?DFCS_HOT:0)|((ctl->stateId == PBS_PRESSED)?DFCS_PUSHED:0);
+ if (ctl->defbutton&&ctl->stateId==PBS_NORMAL) uState |= DLGC_DEFPUSHBUTTON;
+ DrawFrameControl(hdcMem, &rcClient, DFC_BUTTON, uState);
+ }
+
+ // Draw focus rectangle if button has focus
+ if (ctl->focus) {
+ RECT focusRect = rcClient;
+ InflateRect(&focusRect, -3, -3);
+ DrawFocusRect(hdcMem, &focusRect);
+ }
+ }
+
+ // If we have an icon or a bitmap, ignore text and only draw the image on the button
+ if (ctl->hIcon) {
+ int ix = (rcClient.right-rcClient.left)/2 - (GetSystemMetrics(SM_CXSMICON)/2);
+ int iy = (rcClient.bottom-rcClient.top)/2 - (GetSystemMetrics(SM_CYSMICON)/2);
+ if (ctl->stateId == PBS_PRESSED) {
+ ix++;
+ iy++;
+ }
+ {
+ HIMAGELIST hImageList;
+ HICON hIconNew;
+
+ hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON), IsWinVerXPPlus()? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, 1, 0);
+ ImageList_AddIcon(hImageList, ctl->hIcon);
+ hIconNew = ImageList_GetIcon(hImageList, 0, ILD_NORMAL);
+ DrawState(hdcMem,NULL,NULL,(LPARAM)hIconNew,0,ix,iy,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),IsWindowEnabled(ctl->hwnd)?DST_ICON|DSS_NORMAL:DST_ICON|DSS_DISABLED);
+ ImageList_RemoveAll(hImageList);
+ ImageList_Destroy(hImageList);
+ DestroyIcon(hIconNew);
+ }
+ }
+ else if (ctl->hBitmap) {
+ BITMAP bminfo;
+ int ix,iy;
+
+ GetObject(ctl->hBitmap, sizeof(bminfo), &bminfo);
+ ix = (rcClient.right-rcClient.left)/2 - (bminfo.bmWidth/2);
+ iy = (rcClient.bottom-rcClient.top)/2 - (bminfo.bmHeight/2);
+ if (ctl->stateId == PBS_PRESSED) {
+ ix++;
+ iy++;
+ }
+ DrawState(hdcMem,NULL,NULL,(LPARAM)ctl->hBitmap,0,ix,iy,bminfo.bmWidth,bminfo.bmHeight,IsWindowEnabled(ctl->hwnd)?DST_BITMAP:DST_BITMAP|DSS_DISABLED);
+ }
+ else if (GetWindowTextLength(ctl->hwnd)) {
+ // Draw the text and optinally the arrow
+ TCHAR szText[MAX_PATH];
+ SIZE sz;
+ RECT rcText;
+ HFONT hOldFont;
+
+ CopyRect(&rcText, &rcClient);
+ GetWindowText(ctl->hwnd, szText, SIZEOF(szText));
+ SetBkMode(hdcMem, TRANSPARENT);
+ hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+ // XP w/themes doesn't used the glossy disabled text. Is it always using COLOR_GRAYTEXT? Seems so.
+ SetTextColor(hdcMem, IsWindowEnabled(ctl->hwnd)||!ctl->hThemeButton?GetSysColor(COLOR_BTNTEXT):GetSysColor(COLOR_GRAYTEXT));
+ GetTextExtentPoint32(hdcMem, szText, lstrlen(szText), &sz);
+ if (ctl->cHot) {
+ SIZE szHot;
+
+ GetTextExtentPoint32 (hdcMem, _T("&"), 1, &szHot);
+ sz.cx -= szHot.cx;
+ }
+ if (ctl->arrow) {
+ DrawState(hdcMem,NULL,NULL,(LPARAM)ctl->arrow,0,rcClient.right-rcClient.left-5-GetSystemMetrics(SM_CXSMICON)+(!ctl->hThemeButton&&ctl->stateId==PBS_PRESSED?1:0),(rcClient.bottom-rcClient.top)/2-GetSystemMetrics(SM_CYSMICON)/2+(!ctl->hThemeButton&&ctl->stateId==PBS_PRESSED?1:0),GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),IsWindowEnabled(ctl->hwnd)?DST_ICON:DST_ICON|DSS_DISABLED);
+ }
+ SelectObject(hdcMem, ctl->hFont);
+ DrawState(hdcMem,NULL,NULL,(LPARAM)szText,0,(rcText.right-rcText.left-sz.cx)/2+(!ctl->hThemeButton&&ctl->stateId==PBS_PRESSED?1:0),ctl->hThemeButton?(rcText.bottom-rcText.top-sz.cy)/2:(rcText.bottom-rcText.top-sz.cy)/2-(ctl->stateId==PBS_PRESSED?0:1),sz.cx,sz.cy,IsWindowEnabled(ctl->hwnd)||ctl->hThemeButton?DST_PREFIXTEXT|DSS_NORMAL:DST_PREFIXTEXT|DSS_DISABLED);
+ SelectObject(hdcMem, hOldFont);
+ }
+ BitBlt(hdcPaint, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+
+ }
+}
+
+static LRESULT CALLBACK MButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MButtonCtrl* bct = (MButtonCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch(msg) {
+ case WM_NCCREATE:
+ SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
+ bct = ( MButtonCtrl* )mir_calloc(sizeof(MButtonCtrl));
+ if (bct==NULL) return FALSE;
+ bct->hwnd = hwndDlg;
+ bct->stateId = PBS_NORMAL;
+ bct->hFont = ( HFONT )GetStockObject(DEFAULT_GUI_FONT);
+ LoadTheme(bct);
+ if (SUCCEEDED(CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
+ IID_IAccPropServices, (void**)&bct->pAccPropServices)))
+ {
+ // Annotating the Role of this object to be PushButton
+ SetHwndPropInt(bct, OBJID_CLIENT, CHILDID_SELF, PROPID_ACC_ROLE, ROLE_SYSTEM_PUSHBUTTON);
+ }
+ else
+ bct->pAccPropServices = NULL;
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)bct);
+ if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
+ return TRUE;
+
+ case WM_DESTROY:
+ if (bct) {
+ if (bct->pAccPropServices) {
+ bct->pAccPropServices->Release();
+ bct->pAccPropServices = NULL;
+ }
+ if (bct->hwndToolTips) {
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(bct->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(bct->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ if ( SendMessage(bct->hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0 ) {
+ int idx;
+ TTooltips tt;
+ tt.ThreadId = GetCurrentThreadId();
+
+ EnterCriticalSection(&csTips);
+ if ( List_GetIndex( &lToolTips, &tt, &idx ) ) {
+ mir_free( lToolTips.items[idx] );
+ List_Remove( &lToolTips, idx );
+ DestroyWindow( bct->hwndToolTips );
+ }
+ LeaveCriticalSection(&csTips);
+
+ bct->hwndToolTips = NULL;
+ }
+ }
+ if (bct->arrow) IconLib_ReleaseIcon(bct->arrow, 0);
+ DestroyTheme(bct);
+ }
+ break; // DONT! fall thru
+
+ case WM_NCDESTROY:
+ mir_free(bct);
+ break;
+
+ case WM_SETTEXT:
+ bct->cHot = 0;
+ if ( lParam != 0 ) {
+ TCHAR *tmp = ( TCHAR* )lParam;
+ while (*tmp) {
+ if (*tmp=='&' && *(tmp+1)) {
+ bct->cHot = _tolower(*(tmp+1));
+ break;
+ }
+ tmp++;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+
+ case WM_KEYUP:
+ if (bct->stateId!=PBS_DISABLED && wParam == VK_SPACE) {
+ if (bct->pushBtn) {
+ if (bct->pbState) {
+ bct->pbState = 0;
+ bct->stateId = PBS_NORMAL;
+ }
+ else {
+ bct->pbState = 1;
+ bct->stateId = PBS_PRESSED;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+
+ case WM_SYSKEYUP:
+ if (bct->stateId!=PBS_DISABLED && bct->cHot && bct->cHot == tolower((int)wParam)) {
+ if (bct->pushBtn) {
+ if (bct->pbState) {
+ bct->pbState = 0;
+ bct->stateId = PBS_NORMAL;
+ }
+ else {
+ bct->pbState = 1;
+ bct->stateId = PBS_PRESSED;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+
+ case WM_THEMECHANGED:
+ // themed changed, reload theme object
+ LoadTheme(bct);
+ InvalidateRect(bct->hwnd, NULL, TRUE); // repaint it
+ break;
+
+ case WM_SETFONT: // remember the font so we can use it later
+ bct->hFont = (HFONT)wParam; // maybe we should redraw?
+ break;
+
+ case WM_NCPAINT:
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdcPaint;
+
+ hdcPaint = BeginPaint(hwndDlg, &ps);
+ if (hdcPaint) {
+ PaintWorker(bct, hdcPaint);
+ EndPaint(hwndDlg, &ps);
+ }
+ break;
+ }
+ case BM_SETIMAGE:
+ {
+ HGDIOBJ hnd = NULL;
+ if (bct->hIcon) hnd = bct->hIcon;
+ else if (bct->hBitmap) hnd = bct->hBitmap;
+
+ if (wParam == IMAGE_ICON) {
+ bct->hIcon = (HICON)lParam;
+ bct->hBitmap = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ else if (wParam == IMAGE_BITMAP) {
+ bct->hBitmap = (HBITMAP)lParam;
+ bct->hIcon = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ return (LRESULT)hnd;
+ }
+ case BM_GETIMAGE:
+ if (bct->hIcon) return (LRESULT)bct->hIcon;
+ else if (bct->hBitmap) return (LRESULT)bct->hBitmap;
+ else return 0;
+ case BM_SETCHECK:
+ if (!bct->pushBtn) break;
+ if (wParam == BST_CHECKED) {
+ bct->pbState = 1;
+ bct->stateId = PBS_PRESSED;
+ }
+ else if (wParam == BST_UNCHECKED) {
+ bct->pbState = 0;
+ bct->stateId = PBS_NORMAL;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BM_GETCHECK:
+ if (bct->pushBtn) {
+ return bct->pbState?BST_CHECKED:BST_UNCHECKED;
+ }
+ return 0;
+ case BUTTONSETARROW: // turn arrow on/off
+ if (wParam) {
+ if (!bct->arrow) {
+ bct->arrow = LoadSkinIcon(SKINICON_OTHER_DOWNARROW);
+ SetHwndPropInt(bct, OBJID_CLIENT, CHILDID_SELF, PROPID_ACC_ROLE, ROLE_SYSTEM_BUTTONDROPDOWN);
+ }
+ }
+ else {
+ if (bct->arrow) {
+ IconLib_ReleaseIcon(bct->arrow, 0);
+ bct->arrow = NULL;
+ SetHwndPropInt(bct, OBJID_CLIENT, CHILDID_SELF, PROPID_ACC_ROLE, ROLE_SYSTEM_PUSHBUTTON);
+ }
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETDEFAULT:
+ bct->defbutton = wParam?1:0;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETASPUSHBTN:
+ bct->pushBtn = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETASFLATBTN:
+ bct->flatBtn = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONADDTOOLTIP:
+ if ( wParam ) {
+ TOOLINFO ti = {0};
+ if ( !bct->hwndToolTips ) {
+ int idx;
+ TTooltips tt;
+ tt.ThreadId = GetCurrentThreadId();
+
+ EnterCriticalSection(&csTips);
+ if ( List_GetIndex( &lToolTips, &tt, &idx )) {
+ bct->hwndToolTips = ((TTooltips*)lToolTips.items[idx])->hwnd;
+ } else {
+ TTooltips *ptt = ( TTooltips* )mir_alloc( sizeof(TTooltips) );
+ ptt->ThreadId = tt.ThreadId;
+ ptt->hwnd = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, _T(""), TTS_ALWAYSTIP, 0, 0, 0, 0, NULL, NULL, hMirandaInst, NULL);
+ List_Insert( &lToolTips, ptt, idx );
+ bct->hwndToolTips = ptt->hwnd;
+ }
+ LeaveCriticalSection(&csTips);
+ }
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(bct->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
+ SendMessage(bct->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ #if defined( _UNICODE )
+ if ( lParam & BATF_UNICODE )
+ ti.lpszText = mir_wstrdup( TranslateW(( WCHAR* )wParam ));
+ else
+ ti.lpszText = LangPackPcharToTchar(( char* )wParam );
+ #else
+ ti.lpszText = Translate(( char* )wParam );
+ #endif
+ if (bct->pAccPropServices) {
+ wchar_t *tmpstr = mir_t2u(ti.lpszText);
+ bct->pAccPropServices->SetHwndPropStr(bct->hwnd, OBJID_CLIENT,
+ CHILDID_SELF, PROPID_ACC_DESCRIPTION, tmpstr);
+ mir_free(tmpstr);
+ }
+ SendMessage( bct->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ #if defined( _UNICODE )
+ mir_free( ti.lpszText );
+ #endif
+ }
+ break;
+ case WM_SETFOCUS: // set keybord focus and redraw
+ bct->focus = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+
+ case WM_KILLFOCUS: // kill focus and redraw
+ bct->focus = 0;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+
+ case WM_ENABLE: // windows tells us to enable/disable
+ bct->stateId = wParam?PBS_NORMAL:PBS_DISABLED;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+
+ case WM_MOUSELEAVE: // faked by the WM_TIMER
+ if (bct->stateId!=PBS_DISABLED) { // don't change states if disabled
+ bct->stateId = PBS_NORMAL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ if (bct->stateId!=PBS_DISABLED) { // don't change states if disabled
+ bct->stateId = PBS_PRESSED;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ int showClick = 0;
+ if (bct->pushBtn) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ }
+ if (bct->stateId!=PBS_DISABLED) { // don't change states if disabled
+ if (bct->stateId==PBS_PRESSED)
+ showClick = 1;
+ if (msg==WM_LBUTTONUP) bct->stateId = PBS_HOT;
+ else bct->stateId = PBS_NORMAL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ if (showClick) // Tell your daddy you got clicked.
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ break;
+ }
+ case WM_MOUSEMOVE:
+ if (bct->stateId == PBS_NORMAL) {
+ bct->stateId = PBS_HOT;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ // Call timer, used to start cheesy TrackMouseEvent faker
+ SetTimer(hwndDlg,BUTTON_POLLID,BUTTON_POLLDELAY,NULL);
+ break;
+ case WM_TIMER: // use a timer to check if they have did a mouseout
+ if (wParam == BUTTON_POLLID) {
+ RECT rc;
+ POINT pt;
+ GetWindowRect(hwndDlg,&rc);
+ GetCursorPos(&pt);
+ if(!PtInRect(&rc,pt)) { // mouse must be gone, trigger mouse leave
+ PostMessage(hwndDlg,WM_MOUSELEAVE,0,0L);
+ KillTimer(hwndDlg,BUTTON_POLLID);
+ } }
+ break;
+
+ case WM_ERASEBKGND:
+ return 1;
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
diff --git a/src/modules/clist/Docking.cpp b/src/modules/clist/Docking.cpp
new file mode 100644
index 0000000000..8dd61cdc37
--- /dev/null
+++ b/src/modules/clist/Docking.cpp
@@ -0,0 +1,392 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+#define WM_DOCKCALLBACK (WM_USER+121)
+#define EDGESENSITIVITY 3
+
+#define DOCKED_NONE 0
+#define DOCKED_LEFT 1
+#define DOCKED_RIGHT 2
+
+static char docked;
+static POINT dockPos;
+
+static void Docking_GetMonitorRectFromPoint(LPPOINT pt, LPRECT rc)
+{
+ if (MyMonitorFromPoint)
+ {
+ MONITORINFO monitorInfo;
+ HMONITOR hMonitor = MyMonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); // always returns a valid value
+ monitorInfo.cbSize = sizeof(monitorInfo);
+
+ if (MyGetMonitorInfo(hMonitor, &monitorInfo))
+ {
+ *rc = monitorInfo.rcMonitor;
+ return;
+ }
+ }
+
+ // "generic" win95/NT support, also serves as failsafe
+ rc->left = 0;
+ rc->top = 0;
+ rc->bottom = GetSystemMetrics(SM_CYSCREEN);
+ rc->right = GetSystemMetrics(SM_CXSCREEN);
+}
+
+static void Docking_RectToDock(LPRECT rc)
+{
+ rc->right += dockPos.x - rc->left;
+ rc->left = dockPos.x;
+ rc->bottom += dockPos.y - rc->top;
+ rc->top = dockPos.y;
+}
+
+static void Docking_PosCommand(HWND hwnd, LPRECT rc, bool query)
+{
+ APPBARDATA abd = {0};
+
+ abd.cbSize = sizeof(abd);
+ abd.hWnd = hwnd;
+ abd.uEdge = docked == DOCKED_LEFT ? ABE_LEFT : ABE_RIGHT;
+ abd.rc = *rc;
+ SHAppBarMessage(query ? ABM_QUERYPOS : ABM_SETPOS, &abd);
+ *rc = abd.rc;
+}
+
+static UINT_PTR Docking_Command(HWND hwnd, int cmd)
+{
+ APPBARDATA abd = {0};
+
+ abd.cbSize = sizeof(abd);
+ abd.hWnd = hwnd;
+ abd.uCallbackMessage = WM_DOCKCALLBACK;
+ return SHAppBarMessage(cmd, &abd);
+}
+
+static void Docking_AdjustPosition(HWND hwnd, LPRECT rcDisplay, LPRECT rc, bool query, bool move)
+{
+ int cx = rc->right - rc->left;
+
+ rc->top = rcDisplay->top;
+ rc->bottom = rcDisplay->bottom;
+ if (docked == DOCKED_LEFT)
+ {
+ rc->right = rcDisplay->left + (rc->right - rc->left);
+ rc->left = rcDisplay->left;
+ }
+ else
+ {
+ rc->left = rcDisplay->right - (rc->right - rc->left);
+ rc->right = rcDisplay->right;
+ }
+ Docking_PosCommand(hwnd, rc, true);
+
+ if (docked == DOCKED_LEFT)
+ rc->right = rc->left + cx;
+ else
+ rc->left = rc->right - cx;
+
+ if (!query)
+ {
+ Docking_PosCommand(hwnd, rc, false);
+ dockPos = *(LPPOINT)rc;
+ }
+
+ if (move)
+ {
+ MoveWindow(hwnd, rc->left, rc->top, rc->right - rc->left,
+ rc->bottom - rc->top, TRUE);
+ }
+}
+
+static void Docking_SetSize(HWND hwnd, LPRECT rc, bool query, bool move)
+{
+ RECT rcMonitor;
+ Docking_GetMonitorRectFromPoint(
+ docked == DOCKED_LEFT && !query ? (LPPOINT)&rc->right : (LPPOINT)rc, &rcMonitor);
+ Docking_AdjustPosition(hwnd, &rcMonitor, rc, query, move);
+}
+
+static bool Docking_IsWindowVisible(HWND hwnd)
+{
+ LONG style = GetWindowLong(hwnd, GWL_STYLE);
+ return style & WS_VISIBLE && !(style & WS_MINIMIZE);
+}
+
+INT_PTR Docking_IsDocked(WPARAM, LPARAM)
+{
+ return docked;
+}
+
+int fnDocking_ProcessWindowMessage(WPARAM wParam, LPARAM lParam)
+{
+ static int draggingTitle;
+ MSG *msg = (MSG *) wParam;
+
+ if (msg->message == WM_DESTROY)
+ {
+ if (docked)
+ {
+ DBWriteContactSettingByte(NULL, "CList", "Docked", (BYTE) docked);
+ DBWriteContactSettingDword(NULL, "CList", "DockX", (DWORD) dockPos.x);
+ DBWriteContactSettingDword(NULL, "CList", "DockY", (DWORD) dockPos.y);
+ }
+ else
+ {
+ DBDeleteContactSetting(NULL, "CList", "Docked");
+ DBDeleteContactSetting(NULL, "CList", "DockX");
+ DBDeleteContactSetting(NULL, "CList", "DockY");
+ }
+ }
+
+ if (!docked && msg->message != WM_CREATE && msg->message != WM_MOVING)
+ return 0;
+
+ switch (msg->message)
+ {
+ case WM_CREATE:
+ draggingTitle = 0;
+ docked = DBGetContactSettingByte(NULL, "CLUI", "DockToSides", 1) ?
+ (char) DBGetContactSettingByte(NULL, "CList", "Docked", 0) : 0;
+ dockPos.x = (int) DBGetContactSettingDword(NULL, "CList", "DockX", 0);
+ dockPos.y = (int) DBGetContactSettingDword(NULL, "CList", "DockY", 0);
+ break;
+
+ case WM_ACTIVATE:
+ Docking_Command(msg->hwnd, ABM_ACTIVATE);
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ {
+ LPWINDOWPOS wp = (LPWINDOWPOS)msg->lParam;
+
+ bool vis = Docking_IsWindowVisible(msg->hwnd);
+ if (wp->flags & SWP_SHOWWINDOW)
+ vis = !IsIconic(msg->hwnd);
+ if (wp->flags & SWP_HIDEWINDOW)
+ vis = false;
+
+ if (vis)
+ {
+ if (!(wp->flags & (SWP_NOMOVE | SWP_NOSIZE)))
+ {
+ bool addbar = Docking_Command(msg->hwnd, ABM_NEW) != 0;
+
+ RECT rc = {0};
+ GetWindowRect(msg->hwnd, &rc);
+
+ int cx = rc.right - rc.left;
+ if (!(wp->flags & SWP_NOMOVE)) { rc.left = wp->x; rc.top = wp->y; }
+
+ if (addbar)
+ Docking_RectToDock(&rc);
+
+ if (!(wp->flags & SWP_NOSIZE))
+ {
+ rc.right = rc.left + wp->cx;
+ rc.bottom = rc.top + wp->cy;
+ addbar |= (cx != wp->cx);
+ }
+
+ Docking_SetSize(msg->hwnd, &rc, !addbar, false);
+
+ if (!(wp->flags & SWP_NOMOVE)) { wp->x = rc.left; wp->y = rc.top; }
+ if (!(wp->flags & SWP_NOSIZE)) wp->cy = rc.bottom - rc.top;
+
+ *((LRESULT *) lParam) = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ if ((wp->flags & SWP_SHOWWINDOW) && Docking_Command(msg->hwnd, ABM_NEW))
+ {
+ RECT rc = {0};
+ GetWindowRect(msg->hwnd, &rc);
+ Docking_RectToDock(&rc);
+
+ Docking_SetSize(msg->hwnd, &rc, false, false);
+
+ wp->x = rc.left;
+ wp->y = rc.top;
+ wp->cy = rc.bottom - rc.top;
+ wp->cx = rc.right - rc.left;
+ wp->flags &= ~(SWP_NOSIZE | SWP_NOMOVE);
+ }
+ }
+ }
+ break;
+ }
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ LPWINDOWPOS wp = (LPWINDOWPOS)msg->lParam;
+ bool vis = Docking_IsWindowVisible(msg->hwnd);
+ if (wp->flags & SWP_SHOWWINDOW)
+ vis = !IsIconic(msg->hwnd);
+ if (wp->flags & SWP_HIDEWINDOW)
+ vis = false;
+
+ if (!vis)
+ Docking_Command(msg->hwnd, ABM_REMOVE);
+ else
+ Docking_Command(msg->hwnd, ABM_WINDOWPOSCHANGED);
+ break;
+ }
+
+ case WM_DISPLAYCHANGE:
+ if (Docking_IsWindowVisible(msg->hwnd))
+ {
+ RECT rc = {0};
+ GetWindowRect(msg->hwnd, &rc);
+ Docking_RectToDock(&rc);
+ Docking_SetSize(msg->hwnd, &rc, false, true);
+ }
+ break;
+
+ case WM_MOVING:
+ if (!docked)
+ {
+ RECT rcMonitor;
+ POINT ptCursor;
+
+ // stop early
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ return 0;
+
+ // GetMessagePos() is no good, position is always unsigned
+// GetCursorPos(&ptCursor);
+ DWORD pos = GetMessagePos();
+ ptCursor.x = GET_X_LPARAM(pos);
+ ptCursor.y = GET_Y_LPARAM(pos);
+ Docking_GetMonitorRectFromPoint(&ptCursor, &rcMonitor);
+
+ if (((ptCursor.x < rcMonitor.left + EDGESENSITIVITY) ||
+ (ptCursor.x >= rcMonitor.right - EDGESENSITIVITY)) &&
+ DBGetContactSettingByte(NULL, "CLUI", "DockToSides", 1))
+ {
+ docked = (ptCursor.x < rcMonitor.left + EDGESENSITIVITY) ? DOCKED_LEFT : DOCKED_RIGHT;
+ PostMessage(msg->hwnd, WM_LBUTTONUP, 0, MAKELPARAM(ptCursor.x, ptCursor.y));
+
+ Docking_Command(msg->hwnd, ABM_NEW);
+ Docking_AdjustPosition(msg->hwnd, &rcMonitor, (LPRECT)msg->lParam, false, true);
+
+ *((LRESULT *) lParam) = TRUE;
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_NCHITTEST:
+ switch (DefWindowProc(msg->hwnd, WM_NCHITTEST, msg->wParam, msg->lParam))
+ {
+ case HTSIZE: case HTTOP: case HTTOPLEFT: case HTTOPRIGHT:
+ case HTBOTTOM: case HTBOTTOMRIGHT: case HTBOTTOMLEFT:
+ *((LRESULT *) lParam) = HTCLIENT;
+ return TRUE;
+
+ case HTLEFT:
+ if (docked == DOCKED_LEFT)
+ {
+ *((LRESULT *) lParam) = HTCLIENT;
+ return TRUE;
+ }
+ break;
+
+ case HTRIGHT:
+ if (docked == DOCKED_RIGHT)
+ {
+ *((LRESULT *) lParam) = HTCLIENT;
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_SYSCOMMAND:
+ if ((msg->wParam & 0xFFF0) != SC_MOVE)
+ return 0;
+
+ SetActiveWindow(msg->hwnd);
+ SetCapture(msg->hwnd);
+ draggingTitle = 1;
+ *((LRESULT *) lParam) = 0;
+ return 1;
+
+ case WM_MOUSEMOVE:
+ if (draggingTitle)
+ {
+ RECT rc;
+ POINT pt;
+ GetClientRect(msg->hwnd, &rc);
+ if ((docked == DOCKED_LEFT && (short) LOWORD(msg->lParam) > rc.right) ||
+ (docked == DOCKED_RIGHT && (short) LOWORD(msg->lParam) < 0))
+ {
+ ReleaseCapture();
+ draggingTitle = 0;
+ docked = 0;
+ GetCursorPos(&pt);
+ PostMessage(msg->hwnd, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ SetWindowPos(msg->hwnd, 0, pt.x - rc.right / 2,
+ pt.y - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYSMCAPTION) / 2,
+ DBGetContactSettingDword(NULL, "CList", "Width", 0),
+ DBGetContactSettingDword(NULL, "CList", "Height", 0),
+ SWP_NOZORDER);
+ Docking_Command(msg->hwnd, ABM_REMOVE);
+ }
+ return 1;
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (draggingTitle)
+ {
+ ReleaseCapture();
+ draggingTitle = 0;
+ }
+ break;
+
+ case WM_DOCKCALLBACK:
+ switch (msg->wParam)
+ {
+ case ABN_WINDOWARRANGE:
+ ShowWindow(msg->hwnd, msg->lParam ? SW_HIDE : SW_SHOW);
+ break;
+
+ case ABN_POSCHANGED:
+ {
+ RECT rc = {0};
+ GetWindowRect(msg->hwnd, &rc);
+ Docking_SetSize(msg->hwnd, &rc, false, true);
+ }
+ break;
+ }
+ return 1;
+
+ case WM_DESTROY:
+ Docking_Command(msg->hwnd, ABM_REMOVE);
+ break;
+ }
+ return 0;
+}
diff --git a/src/modules/clist/clc.cpp b/src/modules/clist/clc.cpp
new file mode 100644
index 0000000000..1994706992
--- /dev/null
+++ b/src/modules/clist/clc.cpp
@@ -0,0 +1,1350 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+int InitGenMenu( void );
+int UnitGenMenu( void );
+
+void InitCustomMenus( void );
+void UninitCustomMenus( void );
+
+void MTG_OnmodulesLoad( void );
+
+static BOOL bModuleInitialized = FALSE;
+static HANDLE hClcWindowList;
+static HANDLE hShowInfoTipEvent;
+HANDLE hHideInfoTipEvent;
+static HANDLE hAckHook;
+static HANDLE hClcSettingsChanged;
+
+int g_IconWidth, g_IconHeight;
+
+void FreeDisplayNameCache(void);
+
+void fnClcBroadcast( int msg, WPARAM wParam, LPARAM lParam )
+{
+ WindowList_Broadcast(hClcWindowList, msg, wParam, lParam);
+}
+
+void fnClcOptionsChanged(void)
+{
+ cli.pfnClcBroadcast( INTM_RELOADOPTIONS, 0, 0);
+}
+
+HMENU fnBuildGroupPopupMenu( struct ClcGroup* group )
+{
+ HMENU hMenu = LoadMenu(cli.hInst, MAKEINTRESOURCE(IDR_CONTEXT));
+ HMENU hGroupMenu = GetSubMenu(hMenu, 2);
+ RemoveMenu(hMenu, 2, MF_BYPOSITION);
+ DestroyMenu(hMenu);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hGroupMenu, 0);
+
+ CheckMenuItem(hGroupMenu, POPUP_GROUPHIDEOFFLINE, group->hideOffline ? MF_CHECKED : MF_UNCHECKED);
+ return hGroupMenu;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// standard CLC services
+
+static int ClcSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ char *szProto;
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
+ if ( (HANDLE)wParam != NULL && !strcmp(cws->szModule, "CList")) {
+ if (!strcmp(cws->szSetting, "MyHandle")) {
+ cli.pfnInvalidateDisplayNameCacheEntry((HANDLE) wParam);
+ cli.pfnClcBroadcast( INTM_NAMECHANGED, wParam, lParam);
+ }
+ else if (!strcmp(cws->szSetting, "Group"))
+ cli.pfnClcBroadcast( INTM_GROUPCHANGED, wParam, lParam);
+ else if (!strcmp(cws->szSetting, "Hidden"))
+ cli.pfnClcBroadcast( INTM_HIDDENCHANGED, wParam, lParam);
+ else if (!strcmp(cws->szSetting, "NotOnList"))
+ cli.pfnClcBroadcast( INTM_NOTONLISTCHANGED, wParam, lParam);
+ else if (!strcmp(cws->szSetting, "Status"))
+ cli.pfnClcBroadcast( INTM_INVALIDATE, 0, 0);
+ else if (!strcmp(cws->szSetting, "NameOrder"))
+ cli.pfnClcBroadcast( INTM_NAMEORDERCHANGED, 0, 0);
+ }
+ else if (!strcmp(cws->szModule, "CListGroups")) {
+ cli.pfnClcBroadcast( INTM_GROUPSCHANGED, wParam, lParam);
+ }
+ else {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto != NULL && (HANDLE) wParam != NULL) {
+ char *id = NULL;
+ if (!strcmp(cws->szModule, "Protocol") && !strcmp(cws->szSetting, "p")) {
+ cli.pfnClcBroadcast( INTM_PROTOCHANGED, wParam, lParam);
+ }
+ // something is being written to a protocol module
+ if (!strcmp(szProto, cws->szModule)) {
+ // was a unique setting key written?
+ id = (char *) CallProtoService(szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ if ((INT_PTR) id != CALLSERVICE_NOTFOUND && id != NULL && !strcmp(id, cws->szSetting)) {
+ cli.pfnClcBroadcast( INTM_PROTOCHANGED, wParam, lParam);
+ }
+ }
+ }
+ if (szProto == NULL || strcmp(szProto, cws->szModule))
+ return 0;
+ if (!strcmp(cws->szSetting, "Nick") || !strcmp(cws->szSetting, "FirstName") || !strcmp(cws->szSetting, "e-mail")
+ || !strcmp(cws->szSetting, "LastName") || !strcmp(cws->szSetting, "UIN"))
+ cli.pfnClcBroadcast( INTM_NAMECHANGED, wParam, lParam);
+ else if (!strcmp(cws->szSetting, "ApparentMode"))
+ cli.pfnClcBroadcast( INTM_APPARENTMODECHANGED, wParam, lParam);
+ else if (!strcmp(cws->szSetting, "IdleTS"))
+ cli.pfnClcBroadcast( INTM_IDLECHANGED, wParam, lParam);
+ }
+ return 0;
+}
+
+static int ClcAccountsChanged(WPARAM, LPARAM)
+{
+ int i, cnt;
+ for (i = 0, cnt = 0; i < accounts.getCount(); ++i)
+ if (Proto_IsAccountEnabled(accounts[i])) ++cnt;
+
+ cli.hClcProtoCount = cnt;
+ cli.clcProto = (ClcProtoStatus *) mir_realloc(cli.clcProto, sizeof(ClcProtoStatus) * cli.hClcProtoCount);
+
+ for (i = 0, cnt = 0; i < accounts.getCount(); ++i) {
+ if (Proto_IsAccountEnabled(accounts[i])) {
+ cli.clcProto[cnt].szProto = accounts[i]->szModuleName;
+ cli.clcProto[cnt].dwStatus = CallProtoService(accounts[i]->szModuleName, PS_GETSTATUS, 0, 0);
+ ++cnt;
+ }
+ }
+ return 0;
+}
+
+static int ClcModulesLoaded(WPARAM, LPARAM)
+{
+ ClcAccountsChanged(0, 0);
+ MTG_OnmodulesLoad();
+ return 0;
+}
+
+static int ClcProtoAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA *) lParam;
+ int i;
+
+ if (ack->type == ACKTYPE_STATUS) {
+ WindowList_BroadcastAsync(hClcWindowList,INTM_INVALIDATE,0,0);
+ if (ack->result == ACKRESULT_SUCCESS) {
+ for (i = 0; i < cli.hClcProtoCount; i++) {
+ if (!lstrcmpA(cli.clcProto[i].szProto, ack->szModule)) {
+ cli.clcProto[i].dwStatus = (WORD) ack->lParam;
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ClcContactAdded(WPARAM wParam, LPARAM lParam)
+{
+ WindowList_BroadcastAsync(hClcWindowList,INTM_CONTACTADDED,wParam,lParam);
+ return 0;
+}
+
+static int ClcContactDeleted(WPARAM wParam, LPARAM lParam)
+{
+ WindowList_BroadcastAsync(hClcWindowList,INTM_CONTACTDELETED,wParam,lParam);
+ return 0;
+}
+
+static int ClcContactIconChanged(WPARAM wParam, LPARAM lParam)
+{
+ WindowList_BroadcastAsync(hClcWindowList,INTM_ICONCHANGED,wParam,lParam);
+ return 0;
+}
+
+static int ClcIconsChanged(WPARAM, LPARAM)
+{
+ WindowList_BroadcastAsync(hClcWindowList,INTM_INVALIDATE,0,0);
+ return 0;
+}
+
+static INT_PTR SetInfoTipHoverTime(WPARAM wParam, LPARAM)
+{
+ DBWriteContactSettingWord(NULL, "CLC", "InfoTipHoverTime", (WORD) wParam);
+ cli.pfnClcBroadcast( INTM_SETINFOTIPHOVERTIME, wParam, 0);
+ return 0;
+}
+
+static INT_PTR GetInfoTipHoverTime(WPARAM, LPARAM)
+{
+ return DBGetContactSettingWord(NULL, "CLC", "InfoTipHoverTime", 750);
+}
+
+static void SortClcByTimer( HWND hwnd )
+{
+ KillTimer( hwnd, TIMERID_DELAYEDRESORTCLC );
+ SetTimer( hwnd, TIMERID_DELAYEDRESORTCLC, 200, NULL );
+}
+
+int LoadCLCModule(void)
+{
+ bModuleInitialized = TRUE;
+
+ g_IconWidth = GetSystemMetrics(SM_CXSMICON);
+ g_IconHeight = GetSystemMetrics(SM_CYSMICON);
+
+ hClcWindowList = (HANDLE) CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+ hShowInfoTipEvent = CreateHookableEvent(ME_CLC_SHOWINFOTIP);
+ hHideInfoTipEvent = CreateHookableEvent(ME_CLC_HIDEINFOTIP);
+ CreateServiceFunction(MS_CLC_SETINFOTIPHOVERTIME, SetInfoTipHoverTime);
+ CreateServiceFunction(MS_CLC_GETINFOTIPHOVERTIME, GetInfoTipHoverTime);
+
+ InitFileDropping();
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, ClcModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, ClcAccountsChanged);
+ hClcSettingsChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ClcSettingChanged);
+ HookEvent(ME_DB_CONTACT_ADDED, ClcContactAdded);
+ HookEvent(ME_DB_CONTACT_DELETED, ClcContactDeleted);
+ HookEvent(ME_CLIST_CONTACTICONCHANGED, ClcContactIconChanged);
+ HookEvent(ME_SKIN_ICONSCHANGED, ClcIconsChanged);
+ hAckHook = (HANDLE) HookEvent(ME_PROTO_ACK, ClcProtoAck);
+
+ InitCustomMenus();
+ return 0;
+}
+
+void UnloadClcModule()
+{
+ if ( !bModuleInitialized ) return;
+
+ UnhookEvent(hAckHook);
+ UnhookEvent(hClcSettingsChanged);
+
+ mir_free(cli.clcProto);
+
+ FreeDisplayNameCache();
+
+ UninitCustomMenus();
+ UnitGenMenu();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// default contact list control window procedure
+
+LRESULT CALLBACK fnContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct ClcData *dat;
+
+ dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0);
+ if (msg >= CLM_FIRST && msg < CLM_LAST)
+ return cli.pfnProcessExternalMessages(hwnd, dat, msg, wParam, lParam);
+
+ switch (msg) {
+ case WM_CREATE:
+ WindowList_Add(hClcWindowList, hwnd, NULL);
+ cli.pfnRegisterFileDropping(hwnd);
+ if ( dat == NULL ) {
+ dat = (struct ClcData *) mir_calloc(sizeof(struct ClcData));
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR) dat);
+ }
+ {
+ int i;
+ for (i = 0; i <= FONTID_MAX; i++)
+ dat->fontInfo[i].changed = 1;
+ }
+ dat->selection = -1;
+ dat->iconXSpace = 20;
+ dat->checkboxSize = 13;
+ dat->dragAutoScrollHeight = 30;
+ dat->iDragItem = -1;
+ dat->iInsertionMark = -1;
+ dat->insertionMarkHitHeight = 5;
+ dat->iHotTrack = -1;
+ dat->infoTipTimeout = DBGetContactSettingWord(NULL, "CLC", "InfoTipHoverTime", 750);
+ dat->extraColumnSpacing = 20;
+ dat->list.cl.increment = 30;
+ dat->needsResort = 1;
+ cli.pfnLoadClcOptions(hwnd, dat);
+ if (!IsWindowVisible(hwnd))
+ SetTimer(hwnd,TIMERID_REBUILDAFTER,10,NULL);
+ else
+ {
+ cli.pfnRebuildEntireList(hwnd,dat);
+ NMCLISTCONTROL nm;
+ nm.hdr.code = CLN_LISTREBUILT;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ }
+ break;
+ case INTM_SCROLLBARCHANGED:
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CONTACTLIST) {
+ if (dat->noVScrollbar)
+ ShowScrollBar(hwnd, SB_VERT, FALSE);
+ else
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ }
+ break;
+
+ case INTM_RELOADOPTIONS:
+ cli.pfnLoadClcOptions(hwnd, dat);
+ cli.pfnSaveStateAndRebuildList(hwnd, dat);
+ break;
+ case WM_THEMECHANGED:
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ case WM_SIZE:
+ cli.pfnEndRename(hwnd, dat, 1);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ { // creating imagelist containing blue line for highlight
+ HBITMAP hBmp, hBmpMask, hoBmp, hoMaskBmp;
+ HDC hdc,hdcMem;
+ RECT rc;
+ int depth;
+ HBRUSH hBrush;
+
+ GetClientRect(hwnd, &rc);
+ if (rc.right == 0)
+ break;
+ rc.bottom = dat->rowHeight;
+ hdc = GetDC(hwnd);
+ depth = GetDeviceCaps(hdc, BITSPIXEL);
+ if (depth < 16)
+ depth = 16;
+ hBmp = CreateBitmap(rc.right, rc.bottom, 1, depth, NULL);
+ hBmpMask = CreateBitmap(rc.right, rc.bottom, 1, 1, NULL);
+ hdcMem = CreateCompatibleDC(hdc);
+ hoBmp = (HBITMAP) SelectObject(hdcMem, hBmp);
+ hBrush = CreateSolidBrush(dat->useWindowsColours ? GetSysColor(COLOR_HIGHLIGHT) : dat->selBkColour);
+ FillRect(hdcMem, &rc, hBrush);
+ DeleteObject(hBrush);
+
+ hoMaskBmp = ( HBITMAP )SelectObject(hdcMem, hBmpMask);
+ FillRect(hdcMem, &rc, ( HBRUSH )GetStockObject(BLACK_BRUSH));
+ SelectObject(hdcMem, hoMaskBmp);
+ SelectObject(hdcMem, hoBmp);
+ DeleteDC(hdcMem);
+ ReleaseDC(hwnd, hdc);
+ if (dat->himlHighlight)
+ ImageList_Destroy(dat->himlHighlight);
+ dat->himlHighlight = ImageList_Create(rc.right, rc.bottom, (IsWinVerXPPlus()? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 1, 1);
+ ImageList_Add(dat->himlHighlight, hBmp, hBmpMask);
+ DeleteObject(hBmpMask);
+ DeleteObject(hBmp);
+ }
+ break;
+
+ case WM_SYSCOLORCHANGE:
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ break;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *msg = (MSG *) lParam;
+ if (msg->message == WM_KEYDOWN) {
+ if (msg->wParam == VK_TAB)
+ return 0;
+ if (msg->wParam == VK_ESCAPE && dat->hwndRenameEdit == NULL && dat->szQuickSearch[0] == 0)
+ return 0;
+ }
+ if (msg->message == WM_CHAR) {
+ if (msg->wParam == '\t')
+ return 0;
+ if (msg->wParam == 27 && dat->hwndRenameEdit == NULL && dat->szQuickSearch[0] == 0)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KILLFOCUS:
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ case WM_SETFOCUS:
+ case WM_ENABLE:
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case WM_GETFONT:
+ return (LRESULT) dat->fontInfo[FONTID_CONTACTS].hFont;
+
+ case INTM_GROUPSCHANGED:
+ {
+ DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *) lParam;
+ if (dbcws->value.type == DBVT_ASCIIZ || dbcws->value.type == DBVT_UTF8) {
+ int groupId = atoi(dbcws->szSetting) + 1;
+ struct ClcContact *contact;
+ struct ClcGroup *group;
+ TCHAR szFullName[512];
+ int i, nameLen, eq;
+ //check name of group and ignore message if just being expanded/collapsed
+ if (cli.pfnFindItem(hwnd, dat, (HANDLE) (groupId | HCONTACT_ISGROUP), &contact, &group, NULL)) {
+ lstrcpy(szFullName, contact->szText);
+ while (group->parent) {
+ for (i = 0; i < group->parent->cl.count; i++)
+ if (group->parent->cl.items[i]->group == group)
+ break;
+ if (i == group->parent->cl.count) {
+ szFullName[0] = '\0';
+ break;
+ }
+ group = group->parent;
+ nameLen = lstrlen(group->cl.items[i]->szText);
+ if (lstrlen(szFullName) + 1 + nameLen > SIZEOF(szFullName)) {
+ szFullName[0] = '\0';
+ break;
+ }
+ memmove(szFullName + 1 + nameLen, szFullName, sizeof( TCHAR )*( lstrlen(szFullName) + 1));
+ memcpy(szFullName, group->cl.items[i]->szText, sizeof( TCHAR )*nameLen);
+ szFullName[nameLen] = '\\';
+ }
+
+ if ( dbcws->value.type == DBVT_ASCIIZ ) {
+ #if defined( UNICODE )
+ WCHAR* wszGrpName = mir_a2u(dbcws->value.pszVal+1);
+ eq = !lstrcmp( szFullName, wszGrpName );
+ mir_free( wszGrpName );
+ #else
+ eq = !lstrcmp( szFullName, dbcws->value.pszVal+1 );
+ #endif
+ }
+ else {
+ char* szGrpName = NEWSTR_ALLOCA(dbcws->value.pszVal+1);
+ #if defined( UNICODE )
+ WCHAR* wszGrpName;
+ Utf8Decode(szGrpName, &wszGrpName );
+ eq = !lstrcmp( szFullName, wszGrpName );
+ mir_free( wszGrpName );
+ #else
+ Utf8Decode(szGrpName, NULL);
+ eq = !lstrcmp( szFullName, szGrpName );
+ #endif
+ }
+ if ( eq && (contact->group->hideOffline != 0) == ((dbcws->value.pszVal[0] & GROUPF_HIDEOFFLINE) != 0))
+ break; //only expanded has changed: no action reqd
+ }
+ }
+ cli.pfnSaveStateAndRebuildList(hwnd, dat);
+ break;
+ }
+ case INTM_NAMEORDERCHANGED:
+ PostMessage(hwnd, CLM_AUTOREBUILD, 0, 0);
+ break;
+
+ case INTM_CONTACTADDED:
+ cli.pfnAddContactToTree(hwnd, dat, (HANDLE) wParam, 1, 1);
+ cli.pfnNotifyNewContact(hwnd, (HANDLE) wParam);
+ SortClcByTimer(hwnd);
+ break;
+
+ case INTM_CONTACTDELETED:
+ cli.pfnDeleteItemFromTree(hwnd, (HANDLE) wParam);
+ SortClcByTimer(hwnd);
+ break;
+
+ case INTM_HIDDENCHANGED:
+ {
+ DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *) lParam;
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN)
+ break;
+ if (dbcws->value.type == DBVT_DELETED || dbcws->value.bVal == 0) {
+ if (cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, NULL, NULL, NULL))
+ break;
+ cli.pfnAddContactToTree(hwnd, dat, (HANDLE) wParam, 1, 1);
+ cli.pfnNotifyNewContact(hwnd, (HANDLE) wParam);
+ }
+ else cli.pfnDeleteItemFromTree(hwnd, (HANDLE) wParam);
+
+ dat->needsResort = 1;
+ SortClcByTimer(hwnd);
+ break;
+ }
+ case INTM_GROUPCHANGED:
+ {
+ struct ClcContact *contact;
+ BYTE iExtraImage[MAXEXTRACOLUMNS];
+ BYTE flags = 0;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ memset(iExtraImage, 0xFF, SIZEOF(iExtraImage));
+ else {
+ CopyMemory(iExtraImage, contact->iExtraImage, SIZEOF(iExtraImage));
+ flags = contact->flags;
+ }
+ cli.pfnDeleteItemFromTree(hwnd, (HANDLE) wParam);
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !DBGetContactSettingByte((HANDLE) wParam, "CList", "Hidden", 0)) {
+ NMCLISTCONTROL nm;
+ cli.pfnAddContactToTree(hwnd, dat, (HANDLE) wParam, 1, 1);
+ if (cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) {
+ CopyMemory(contact->iExtraImage, iExtraImage, SIZEOF(iExtraImage));
+ if(flags & CONTACTF_CHECKED)
+ contact->flags |= CONTACTF_CHECKED;
+ }
+ nm.hdr.code = CLN_CONTACTMOVED;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = (HANDLE) wParam;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ dat->needsResort = 1;
+ }
+ SetTimer(hwnd,TIMERID_REBUILDAFTER,1,NULL);
+ break;
+ }
+ case INTM_ICONCHANGED:
+ {
+ struct ClcContact *contact = NULL;
+ struct ClcGroup *group = NULL;
+ int recalcScrollBar = 0, shouldShow;
+ WORD status;
+ char *szProto;
+ HANDLE hSelItem = NULL;
+ struct ClcContact *selcontact = NULL;
+
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto == NULL)
+ status = ID_STATUS_OFFLINE;
+ else
+ status = DBGetContactSettingWord((HANDLE) wParam, szProto, "Status", ID_STATUS_OFFLINE);
+
+ DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ shouldShow = (style & CLS_SHOWHIDDEN || !DBGetContactSettingByte((HANDLE) wParam, "CList", "Hidden", 0))
+ && (!cli.pfnIsHiddenMode(dat, status)
+ || CallService(MS_CLIST_GETCONTACTICON, wParam, 0) != lParam); // this means an offline msg is flashing, so the contact should be shown
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, &group, NULL)) {
+ if (shouldShow && CallService(MS_DB_CONTACT_IS, wParam, 0)) {
+ if (dat->selection >= 0 && cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1)
+ hSelItem = cli.pfnContactToHItem(selcontact);
+ cli.pfnAddContactToTree(hwnd, dat, (HANDLE) wParam, (style & CLS_CONTACTLIST) == 0, 0);
+ recalcScrollBar = 1;
+ cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL);
+ if (contact) {
+ contact->iImage = (WORD) lParam;
+ cli.pfnNotifyNewContact(hwnd, (HANDLE) wParam);
+ dat->needsResort = 1;
+ } }
+ }
+ else { // item in list already
+ if (contact->iImage == (WORD) lParam)
+ break;
+ if (!shouldShow && !(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ if (dat->selection >= 0 && cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1)
+ hSelItem = cli.pfnContactToHItem(selcontact);
+ cli.pfnRemoveItemFromGroup(hwnd, group, contact, (style & CLS_CONTACTLIST) == 0);
+ recalcScrollBar = 1;
+ }
+ else {
+ contact->iImage = (WORD) lParam;
+ if (!cli.pfnIsHiddenMode(dat, status))
+ contact->flags |= CONTACTF_ONLINE;
+ else
+ contact->flags &= ~CONTACTF_ONLINE;
+ }
+ dat->needsResort = 1;
+ }
+ if (hSelItem) {
+ struct ClcGroup *selgroup;
+ if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL))
+ dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf(( SortedList* )&selgroup->cl, selcontact));
+ else
+ dat->selection = -1;
+ }
+ SortClcByTimer(hwnd);
+ break;
+ }
+ case INTM_NAMECHANGED:
+ {
+ struct ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+
+ lstrcpyn(contact->szText, cli.pfnGetContactDisplayName((HANDLE)wParam,0), SIZEOF(contact->szText));
+ dat->needsResort = 1;
+ SortClcByTimer(hwnd);
+ break;
+ }
+ case INTM_PROTOCHANGED:
+ {
+ struct ClcContact *contact = NULL;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ contact->proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ cli.pfnInvalidateDisplayNameCacheEntry((HANDLE)wParam);
+ lstrcpyn(contact->szText, cli.pfnGetContactDisplayName((HANDLE)wParam,0), SIZEOF(contact->szText));
+ SortClcByTimer(hwnd);
+ break;
+ }
+ case INTM_NOTONLISTCHANGED:
+ {
+ DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *) lParam;
+ struct ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ if (contact->type != CLCIT_CONTACT)
+ break;
+ if (dbcws->value.type == DBVT_DELETED || dbcws->value.bVal == 0)
+ contact->flags &= ~CONTACTF_NOTONLIST;
+ else
+ contact->flags |= CONTACTF_NOTONLIST;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+ case INTM_INVALIDATE:
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case INTM_APPARENTMODECHANGED:
+ {
+ WORD apparentMode;
+ char *szProto;
+ struct ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto == NULL)
+ break;
+ apparentMode = DBGetContactSettingWord((HANDLE) wParam, szProto, "ApparentMode", 0);
+ contact->flags &= ~(CONTACTF_INVISTO | CONTACTF_VISTO);
+ if (apparentMode == ID_STATUS_OFFLINE)
+ contact->flags |= CONTACTF_INVISTO;
+ else if (apparentMode == ID_STATUS_ONLINE)
+ contact->flags |= CONTACTF_VISTO;
+ else if (apparentMode)
+ contact->flags |= CONTACTF_VISTO | CONTACTF_INVISTO;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+ case INTM_SETINFOTIPHOVERTIME:
+ dat->infoTipTimeout = wParam;
+ break;
+
+ case INTM_IDLECHANGED:
+ {
+ char *szProto;
+ struct ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto == NULL)
+ break;
+ contact->flags &= ~CONTACTF_IDLE;
+ if (DBGetContactSettingDword((HANDLE) wParam, szProto, "IdleTS", 0)) {
+ contact->flags |= CONTACTF_IDLE;
+ }
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+ case WM_PRINTCLIENT:
+ cli.pfnPaintClc(hwnd, dat, (HDC) wParam, NULL);
+ break;
+
+ case WM_NCPAINT:
+ if (wParam == 1)
+ break;
+ {
+ POINT ptTopLeft = { 0, 0 };
+ HRGN hClientRgn;
+ ClientToScreen(hwnd, &ptTopLeft);
+ hClientRgn = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(hClientRgn, (HRGN) wParam, NULL, RGN_COPY);
+ OffsetRgn(hClientRgn, -ptTopLeft.x, -ptTopLeft.y);
+ InvalidateRgn(hwnd, hClientRgn, FALSE);
+ DeleteObject(hClientRgn);
+ UpdateWindow(hwnd);
+ }
+ break;
+
+ case WM_PAINT:
+ {
+ HDC hdc;
+ PAINTSTRUCT ps;
+ hdc = BeginPaint(hwnd, &ps);
+ /* we get so many cli.pfnInvalidateRect()'s that there is no point painting,
+ Windows in theory shouldn't queue up WM_PAINTs in this case but it does so
+ we'll just ignore them */
+ if (IsWindowVisible(hwnd))
+ cli.pfnPaintClc(hwnd, dat, hdc, &ps.rcPaint);
+ EndPaint(hwnd, &ps);
+ break;
+ }
+ case WM_VSCROLL:
+ {
+ int desty;
+ RECT clRect;
+ int noSmooth = 0;
+
+ cli.pfnEndRename(hwnd, dat, 1);
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ desty = dat->yScroll;
+ GetClientRect(hwnd, &clRect);
+ switch (LOWORD(wParam)) {
+ case SB_LINEUP: desty -= dat->rowHeight; break;
+ case SB_LINEDOWN: desty += dat->rowHeight; break;
+ case SB_PAGEUP: desty -= clRect.bottom - dat->rowHeight; break;
+ case SB_PAGEDOWN: desty += clRect.bottom - dat->rowHeight; break;
+ case SB_BOTTOM: desty = 0x7FFFFFFF; break;
+ case SB_TOP: desty = 0; break;
+ case SB_THUMBTRACK: desty = HIWORD(wParam); noSmooth = 1; break; //noone has more than 4000 contacts, right?
+ default: return 0;
+ }
+ cli.pfnScrollTo(hwnd, dat, desty, noSmooth);
+ break;
+ }
+ case WM_MOUSEWHEEL:
+ {
+ UINT scrollLines;
+ cli.pfnEndRename(hwnd, dat, 1);
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, FALSE))
+ scrollLines = 3;
+ cli.pfnScrollTo(hwnd, dat, dat->yScroll - (short) HIWORD(wParam) * dat->rowHeight * (signed) scrollLines / WHEEL_DELTA, 0);
+ return 0;
+ }
+ case WM_KEYDOWN:
+ {
+ int selMoved = 0;
+ int changeGroupExpand = 0;
+ int pageSize;
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ if (CallService(MS_CLIST_MENUPROCESSHOTKEY, wParam, MPCF_CONTACTMENU))
+ break;
+ {
+ RECT clRect;
+ GetClientRect(hwnd, &clRect);
+ pageSize = clRect.bottom / dat->rowHeight;
+ }
+ switch (wParam) {
+ case VK_DOWN: dat->selection++; selMoved = 1; break;
+ case VK_UP: dat->selection--; selMoved = 1; break;
+ case VK_PRIOR: dat->selection -= pageSize; selMoved = 1; break;
+ case VK_NEXT: dat->selection += pageSize; selMoved = 1; break;
+ case VK_HOME: dat->selection = 0; selMoved = 1; break;
+ case VK_END: dat->selection = cli.pfnGetGroupContentsCount(&dat->list, 1) - 1; selMoved = 1; break;
+ case VK_LEFT: changeGroupExpand = 1; break;
+ case VK_RIGHT: changeGroupExpand = 2; break;
+ case VK_RETURN: cli.pfnDoSelectionDefaultAction(hwnd, dat); return 0;
+ case VK_F2: cli.pfnBeginRenameSelection(hwnd, dat); return 0;
+ case VK_DELETE: cli.pfnDeleteFromContactList(hwnd, dat); return 0;
+ default:
+ {
+ NMKEY nmkey;
+ nmkey.hdr.hwndFrom = hwnd;
+ nmkey.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nmkey.hdr.code = NM_KEYDOWN;
+ nmkey.nVKey = wParam;
+ nmkey.uFlags = HIWORD(lParam);
+ if (SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nmkey))
+ return 0;
+ }
+ }
+ if (changeGroupExpand) {
+ int hit;
+ struct ClcContact *contact;
+ struct ClcGroup *group;
+ dat->szQuickSearch[0] = 0;
+ hit = cli.pfnGetRowByIndex(dat, dat->selection, &contact, &group);
+ if (hit != -1) {
+ if (changeGroupExpand == 1 && contact->type == CLCIT_CONTACT) {
+ if (group == &dat->list)
+ return 0;
+ dat->selection = cli.pfnGetRowsPriorTo(&dat->list, group, -1);
+ selMoved = 1;
+ }
+ else {
+ if (contact->type == CLCIT_GROUP)
+ cli.pfnSetGroupExpand(hwnd, dat, contact->group, changeGroupExpand == 2);
+ return 0;
+ }
+ }
+ else
+ return 0;
+ }
+ if (selMoved) {
+ dat->szQuickSearch[0] = 0;
+ if (dat->selection >= cli.pfnGetGroupContentsCount(&dat->list, 1))
+ dat->selection = cli.pfnGetGroupContentsCount(&dat->list, 1) - 1;
+ if (dat->selection < 0)
+ dat->selection = 0;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ UpdateWindow(hwnd);
+ return 0;
+ }
+ break;
+ }
+ case WM_CHAR:
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ if (wParam == 27) //escape
+ dat->szQuickSearch[0] = 0;
+ else if (wParam == '\b' && dat->szQuickSearch[0])
+ dat->szQuickSearch[lstrlen(dat->szQuickSearch) - 1] = '\0';
+ else if (wParam < ' ')
+ break;
+ else if (wParam == ' ' && dat->szQuickSearch[0] == '\0' && GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CHECKBOXES) {
+ struct ClcContact *contact;
+ NMCLISTCONTROL nm;
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) == -1)
+ break;
+ if (contact->type != CLCIT_CONTACT)
+ break;
+ contact->flags ^= CONTACTF_CHECKED;
+ if (contact->type == CLCIT_GROUP)
+ cli.pfnSetGroupChildCheckboxes(contact->group, contact->flags & CONTACTF_CHECKED);
+ cli.pfnRecalculateGroupCheckboxes(hwnd, dat);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ nm.hdr.code = CLN_CHECKCHANGED;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ }
+ else {
+ TCHAR szNew[2];
+ szNew[0] = (TCHAR) wParam;
+ szNew[1] = '\0';
+ if (lstrlen(dat->szQuickSearch) >= SIZEOF(dat->szQuickSearch) - 1) {
+ MessageBeep(MB_OK);
+ break;
+ }
+ _tcscat(dat->szQuickSearch, szNew);
+ }
+ if (dat->szQuickSearch[0]) {
+ int index;
+ index = cli.pfnFindRowByText(hwnd, dat, dat->szQuickSearch, 1);
+ if (index != -1)
+ dat->selection = index;
+ else {
+ MessageBeep(MB_OK);
+ dat->szQuickSearch[ lstrlen(dat->szQuickSearch) - 1] = '\0';
+ }
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ }
+ else
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case WM_SYSKEYDOWN:
+ cli.pfnEndRename(hwnd, dat, 1);
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ dat->iHotTrack = -1;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ ReleaseCapture();
+ if (wParam == VK_F10 && GetKeyState(VK_SHIFT) & 0x8000)
+ break;
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ return 0;
+
+ case WM_TIMER:
+ switch( wParam ) {
+ case TIMERID_RENAME:
+ cli.pfnBeginRenameSelection(hwnd, dat);
+ break;
+ case TIMERID_DRAGAUTOSCROLL:
+ cli.pfnScrollTo(hwnd, dat, dat->yScroll + dat->dragAutoScrolling * dat->rowHeight * 2, 0);
+ break;
+ case TIMERID_INFOTIP:
+ { CLCINFOTIP it;
+ struct ClcContact *contact;
+ int hit;
+ RECT clRect;
+ POINT ptClientOffset = { 0 };
+
+ KillTimer(hwnd, wParam);
+ GetCursorPos(&it.ptCursor);
+ ScreenToClient(hwnd, &it.ptCursor);
+ if (it.ptCursor.x != dat->ptInfoTip.x || it.ptCursor.y != dat->ptInfoTip.y)
+ break;
+ GetClientRect(hwnd, &clRect);
+ it.rcItem.left = 0;
+ it.rcItem.right = clRect.right;
+ hit = cli.pfnHitTest(hwnd, dat, it.ptCursor.x, it.ptCursor.y, &contact, NULL, NULL);
+ if (hit == -1)
+ break;
+ if (contact->type != CLCIT_GROUP && contact->type != CLCIT_CONTACT)
+ break;
+ ClientToScreen(hwnd, &it.ptCursor);
+ ClientToScreen(hwnd, &ptClientOffset);
+ it.isTreeFocused = GetFocus() == hwnd;
+ it.rcItem.top = cli.pfnGetRowTopY(dat, hit) - dat->yScroll;
+ it.rcItem.bottom = it.rcItem.top + cli.pfnGetRowHeight(dat, hit);
+ OffsetRect(&it.rcItem, ptClientOffset.x, ptClientOffset.y);
+ it.isGroup = contact->type == CLCIT_GROUP;
+ it.hItem = contact->type == CLCIT_GROUP ? (HANDLE) contact->groupId : contact->hContact;
+ it.cbSize = sizeof(it);
+ dat->hInfoTipItem = cli.pfnContactToHItem(contact);
+ NotifyEventHooks(hShowInfoTipEvent, 0, (LPARAM) & it);
+ break;
+ }
+ case TIMERID_REBUILDAFTER:
+ KillTimer(hwnd,TIMERID_REBUILDAFTER);
+ cli.pfnInvalidateRect(hwnd,NULL,FALSE);
+ cli.pfnSaveStateAndRebuildList(hwnd,dat);
+ break;
+
+ case TIMERID_DELAYEDRESORTCLC:
+ KillTimer(hwnd,TIMERID_DELAYEDRESORTCLC);
+ cli.pfnInvalidateRect(hwnd,NULL,FALSE);
+ cli.pfnSortCLC(hwnd,dat,1);
+ cli.pfnRecalcScrollBar(hwnd,dat);
+ break;
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ {
+ struct ClcContact *contact;
+ struct ClcGroup *group;
+ int hit;
+ DWORD hitFlags;
+
+ if (GetFocus() != hwnd)
+ SetFocus(hwnd);
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ cli.pfnEndRename(hwnd, dat, 1);
+ dat->ptDragStart.x = (short) LOWORD(lParam);
+ dat->ptDragStart.y = (short) HIWORD(lParam);
+ dat->szQuickSearch[0] = 0;
+ hit = cli.pfnHitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), &contact, &group, &hitFlags);
+ if (hit != -1) {
+ if (hit == dat->selection && hitFlags & CLCHT_ONITEMLABEL && dat->exStyle & CLS_EX_EDITLABELS) {
+ SetCapture(hwnd);
+ dat->iDragItem = dat->selection;
+ dat->dragStage = DRAGSTAGE_NOTMOVED | DRAGSTAGEF_MAYBERENAME;
+ dat->dragAutoScrolling = 0;
+ break;
+ } }
+
+ if (hit != -1 && contact->type == CLCIT_GROUP)
+ if (hitFlags & CLCHT_ONITEMICON) {
+ struct ClcGroup *selgroup;
+ struct ClcContact *selcontact;
+ dat->selection = cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, &selgroup);
+ cli.pfnSetGroupExpand(hwnd, dat, contact->group, -1);
+ if (dat->selection != -1) {
+ dat->selection =
+ cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl,selcontact));
+ if (dat->selection == -1)
+ dat->selection = cli.pfnGetRowsPriorTo(&dat->list, contact->group, -1);
+ }
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ UpdateWindow(hwnd);
+ break;
+ }
+ if (hit != -1 && hitFlags & CLCHT_ONITEMCHECK) {
+ NMCLISTCONTROL nm;
+ contact->flags ^= CONTACTF_CHECKED;
+ if (contact->type == CLCIT_GROUP)
+ cli.pfnSetGroupChildCheckboxes(contact->group, contact->flags & CONTACTF_CHECKED);
+ cli.pfnRecalculateGroupCheckboxes(hwnd, dat);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ nm.hdr.code = CLN_CHECKCHANGED;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ }
+ if (!(hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMLABEL | CLCHT_ONITEMCHECK))) {
+ NMCLISTCONTROL nm;
+ nm.hdr.code = NM_CLICK;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ if (hit == -1)
+ nm.hItem = NULL;
+ else
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ nm.iColumn = hitFlags & CLCHT_ONITEMEXTRA ? HIBYTE(HIWORD(hitFlags)) : -1;
+ nm.pt = dat->ptDragStart;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ }
+ if (hitFlags & (CLCHT_ONITEMCHECK | CLCHT_ONITEMEXTRA))
+ break;
+ dat->selection = hit;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ if (dat->selection != -1)
+ cli.pfnEnsureVisible(hwnd, dat, hit, 0);
+ UpdateWindow(hwnd);
+ if (dat->selection != -1 && (contact->type == CLCIT_CONTACT || contact->type == CLCIT_GROUP)
+ && !(hitFlags & (CLCHT_ONITEMEXTRA | CLCHT_ONITEMCHECK))) {
+ SetCapture(hwnd);
+ dat->iDragItem = dat->selection;
+ dat->dragStage = DRAGSTAGE_NOTMOVED;
+ dat->dragAutoScrolling = 0;
+ }
+ break;
+ }
+ case WM_MOUSEMOVE:
+ if (dat->iDragItem == -1) {
+ int iOldHotTrack = dat->iHotTrack;
+ if (dat->hwndRenameEdit != NULL)
+ break;
+ if (GetKeyState(VK_MENU) & 0x8000 || GetKeyState(VK_F10) & 0x8000)
+ break;
+ dat->iHotTrack = cli.pfnHitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), NULL, NULL, NULL);
+ if (iOldHotTrack != dat->iHotTrack) {
+ if (iOldHotTrack == -1)
+ SetCapture(hwnd);
+ else if (dat->iHotTrack == -1)
+ ReleaseCapture();
+ if (dat->exStyle & CLS_EX_TRACKSELECT) {
+ cli.pfnInvalidateItem(hwnd, dat, iOldHotTrack);
+ cli.pfnInvalidateItem(hwnd, dat, dat->iHotTrack);
+ }
+ cli.pfnHideInfoTip(hwnd, dat);
+ }
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ if (wParam == 0 && dat->hInfoTipItem == NULL) {
+ dat->ptInfoTip.x = (short) LOWORD(lParam);
+ dat->ptInfoTip.y = (short) HIWORD(lParam);
+ SetTimer(hwnd, TIMERID_INFOTIP, dat->infoTipTimeout, NULL);
+ }
+ break;
+ }
+ if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_NOTMOVED && !(dat->exStyle & CLS_EX_DISABLEDRAGDROP)) {
+ if (abs((short) LOWORD(lParam) - dat->ptDragStart.x) >= GetSystemMetrics(SM_CXDRAG)
+ || abs((short) HIWORD(lParam) - dat->ptDragStart.y) >= GetSystemMetrics(SM_CYDRAG))
+ dat->dragStage = (dat->dragStage & ~DRAGSTAGEM_STAGE) | DRAGSTAGE_ACTIVE;
+ }
+ if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_ACTIVE) {
+ HCURSOR hNewCursor;
+ RECT clRect;
+ POINT pt;
+ int target;
+
+ GetClientRect(hwnd, &clRect);
+ pt.x = (short) LOWORD(lParam);
+ pt.y = (short) HIWORD(lParam);
+ hNewCursor = LoadCursor(NULL, IDC_NO);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ if (dat->dragAutoScrolling) {
+ KillTimer(hwnd, TIMERID_DRAGAUTOSCROLL);
+ dat->dragAutoScrolling = 0;
+ }
+ target = cli.pfnGetDropTargetInformation(hwnd, dat, pt);
+ if (dat->dragStage & DRAGSTAGEF_OUTSIDE && target != DROPTARGET_OUTSIDE) {
+ NMCLISTCONTROL nm;
+ struct ClcContact *contact;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL);
+ nm.hdr.code = CLN_DRAGSTOP;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ dat->dragStage &= ~DRAGSTAGEF_OUTSIDE;
+ }
+ switch (target) {
+ case DROPTARGET_ONSELF:
+ case DROPTARGET_ONCONTACT:
+ break;
+ case DROPTARGET_ONGROUP:
+ hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER));
+ break;
+ case DROPTARGET_INSERTION:
+ hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER));
+ break;
+ case DROPTARGET_OUTSIDE:
+ {
+ NMCLISTCONTROL nm;
+ struct ClcContact *contact;
+
+ if (pt.x >= 0 && pt.x < clRect.right
+ && ((pt.y < 0 && pt.y > -dat->dragAutoScrollHeight)
+ || (pt.y >= clRect.bottom && pt.y < clRect.bottom + dat->dragAutoScrollHeight))) {
+ if (!dat->dragAutoScrolling) {
+ if (pt.y < 0)
+ dat->dragAutoScrolling = -1;
+ else
+ dat->dragAutoScrolling = 1;
+ SetTimer(hwnd, TIMERID_DRAGAUTOSCROLL, dat->scrollTime, NULL);
+ }
+ SendMessage(hwnd, WM_TIMER, TIMERID_DRAGAUTOSCROLL, 0);
+ }
+
+ dat->dragStage |= DRAGSTAGEF_OUTSIDE;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL);
+ nm.hdr.code = CLN_DRAGGING;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ nm.pt = pt;
+ if (SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm))
+ return 0;
+ break;
+ }
+ default:
+ {
+ struct ClcGroup *group;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, NULL, &group);
+ if (group->parent)
+ hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER));
+ break;
+ }
+ }
+ SetCursor(hNewCursor);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (dat->iDragItem == -1)
+ break;
+ SetCursor((HCURSOR) GetClassLongPtr(hwnd, GCLP_HCURSOR));
+ if (dat->exStyle & CLS_EX_TRACKSELECT) {
+ dat->iHotTrack = cli.pfnHitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), NULL, NULL, NULL);
+ if (dat->iHotTrack == -1)
+ ReleaseCapture();
+ }
+ else ReleaseCapture();
+ KillTimer(hwnd, TIMERID_DRAGAUTOSCROLL);
+ if (dat->dragStage == (DRAGSTAGE_NOTMOVED | DRAGSTAGEF_MAYBERENAME))
+ SetTimer(hwnd, TIMERID_RENAME, GetDoubleClickTime(), NULL);
+ else if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_ACTIVE) {
+ POINT pt;
+ int target;
+
+ pt.x = (short) LOWORD(lParam);
+ pt.y = (short) HIWORD(lParam);
+ target = cli.pfnGetDropTargetInformation(hwnd, dat, pt);
+ switch (target) {
+ case DROPTARGET_ONSELF:
+ break;
+ case DROPTARGET_ONCONTACT:
+ break;
+ case DROPTARGET_ONGROUP:
+ {
+ struct ClcContact *contactn, *contacto;
+ cli.pfnGetRowByIndex(dat, dat->selection, &contactn, NULL);
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contacto, NULL);
+ if (contacto->type == CLCIT_CONTACT) //dropee is a contact
+ CallService(MS_CLIST_CONTACTCHANGEGROUP, (WPARAM)contacto->hContact, contactn->groupId);
+ else if (contacto->type == CLCIT_GROUP) { //dropee is a group
+ TCHAR szNewName[120];
+ TCHAR* szGroup = cli.pfnGetGroupName(contactn->groupId, NULL);
+ mir_sntprintf(szNewName, SIZEOF(szNewName), _T("%s\\%s"), szGroup, contacto->szText);
+ cli.pfnRenameGroup( contacto->groupId, szNewName );
+ }
+ break;
+ }
+ case DROPTARGET_INSERTION:
+ {
+ struct ClcContact *contact, *destcontact;
+ struct ClcGroup *destgroup;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL);
+ if (cli.pfnGetRowByIndex(dat, dat->iInsertionMark, &destcontact, &destgroup) == -1 || destgroup != contact->group->parent)
+ CallService(MS_CLIST_GROUPMOVEBEFORE, contact->groupId, 0);
+ else {
+ if (destcontact->type == CLCIT_GROUP)
+ destgroup = destcontact->group;
+ else
+ destgroup = destgroup;
+ CallService(MS_CLIST_GROUPMOVEBEFORE, contact->groupId, destgroup->groupId);
+ }
+ break;
+ }
+ case DROPTARGET_OUTSIDE:
+ {
+ NMCLISTCONTROL nm;
+ struct ClcContact *contact;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL);
+ nm.hdr.code = CLN_DROPPED;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags);
+ nm.pt = pt;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+ break;
+ }
+ default:
+ {
+ struct ClcGroup *group;
+ struct ClcContact *contact;
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, &group);
+ if (group->parent) { //move to root
+ if (contact->type == CLCIT_CONTACT) //dropee is a contact
+ CallService(MS_CLIST_CONTACTCHANGEGROUP, (WPARAM)contact->hContact, 0);
+ else if (contact->type == CLCIT_GROUP) { //dropee is a group
+ TCHAR szNewName[120];
+ lstrcpyn(szNewName, contact->szText, SIZEOF(szNewName));
+ cli.pfnRenameGroup( contact->groupId, szNewName );
+ } } } } }
+
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ dat->iDragItem = -1;
+ dat->iInsertionMark = -1;
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ {
+ struct ClcContact *contact;
+ DWORD hitFlags;
+ ReleaseCapture();
+ dat->iHotTrack = -1;
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_RENAME);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ dat->szQuickSearch[0] = 0;
+ dat->selection = cli.pfnHitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), &contact, NULL, &hitFlags);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ if (dat->selection != -1)
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ if (!(hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMLABEL)))
+ break;
+ UpdateWindow(hwnd);
+ cli.pfnDoSelectionDefaultAction(hwnd, dat);
+ break;
+ }
+ case WM_CONTEXTMENU:
+ {
+ struct ClcContact *contact;
+ HMENU hMenu = NULL;
+ POINT pt;
+ DWORD hitFlags;
+
+ cli.pfnEndRename(hwnd, dat, 1);
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_RENAME);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ if (GetFocus() != hwnd)
+ SetFocus(hwnd);
+ dat->iHotTrack = -1;
+ dat->szQuickSearch[0] = 0;
+ pt.x = (short) LOWORD(lParam);
+ pt.y = (short) HIWORD(lParam);
+ if (pt.x == -1 && pt.y == -1) {
+ dat->selection = cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL);
+ if (dat->selection != -1)
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ pt.x = dat->iconXSpace + 15;
+ pt.y = cli.pfnGetRowTopY(dat, dat->selection) - dat->yScroll + (int)(cli.pfnGetRowHeight(dat, dat->selection) * .7);
+ hitFlags = dat->selection == -1 ? CLCHT_NOWHERE : CLCHT_ONITEMLABEL;
+ }
+ else {
+ ScreenToClient(hwnd, &pt);
+ dat->selection = cli.pfnHitTest(hwnd, dat, pt.x, pt.y, &contact, NULL, &hitFlags);
+ }
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ if (dat->selection != -1)
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ UpdateWindow(hwnd);
+
+ if (dat->selection != -1 && hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMCHECK | CLCHT_ONITEMLABEL)) {
+ if (contact->type == CLCIT_GROUP) {
+ hMenu = cli.pfnBuildGroupPopupMenu(contact->group);
+ ClientToScreen(hwnd, &pt);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ else if (contact->type == CLCIT_CONTACT)
+ hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) contact->hContact, 0);
+ }
+ else {
+ //call parent for new group/hide offline menu
+ SendMessage(GetParent(hwnd), WM_CONTEXTMENU, wParam, lParam);
+ }
+ if (hMenu != NULL) {
+ ClientToScreen(hwnd, &pt);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ }
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+
+ case WM_DRAWITEM:
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+ case WM_COMMAND:
+ {
+ struct ClcContact *contact;
+ int hit = cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL);
+ if (hit == -1)
+ break;
+ if (contact->type == CLCIT_CONTACT)
+ if (CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM) contact->hContact))
+ break;
+ switch (LOWORD(wParam)) {
+ case POPUP_NEWSUBGROUP:
+ if (contact->type != CLCIT_GROUP)
+ break;
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~CLS_HIDEEMPTYGROUPS);
+ CallService(MS_CLIST_GROUPCREATE, contact->groupId, 0);
+ break;
+ case POPUP_RENAMEGROUP:
+ cli.pfnBeginRenameSelection(hwnd, dat);
+ break;
+ case POPUP_DELETEGROUP:
+ if (contact->type != CLCIT_GROUP)
+ break;
+ CallService(MS_CLIST_GROUPDELETE, contact->groupId, 0);
+ break;
+ case POPUP_GROUPHIDEOFFLINE:
+ if (contact->type != CLCIT_GROUP)
+ break;
+ CallService(MS_CLIST_GROUPSETFLAGS, contact->groupId,
+ MAKELPARAM(contact->group->hideOffline ? 0 : GROUPF_HIDEOFFLINE, GROUPF_HIDEOFFLINE));
+ break;
+ }
+ break;
+ }
+ case WM_DESTROY:
+ cli.pfnHideInfoTip(hwnd, dat);
+ {
+ int i;
+ for (i = 0; i <= FONTID_MAX; i++)
+ if (!dat->fontInfo[i].changed)
+ DeleteObject(dat->fontInfo[i].hFont);
+ }
+ if (dat->himlHighlight)
+ ImageList_Destroy(dat->himlHighlight);
+ if (dat->hwndRenameEdit)
+ DestroyWindow(dat->hwndRenameEdit);
+ if (!dat->bkChanged && dat->hBmpBackground)
+ DeleteObject(dat->hBmpBackground);
+ cli.pfnFreeGroup(&dat->list);
+ mir_free(dat);
+ cli.pfnUnregisterFileDropping(hwnd);
+ WindowList_Remove(hClcWindowList, hwnd);
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
diff --git a/src/modules/clist/clc.h b/src/modules/clist/clc.h
new file mode 100644
index 0000000000..0bbf197b44
--- /dev/null
+++ b/src/modules/clist/clc.h
@@ -0,0 +1,248 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+struct ClcContact {
+ BYTE type;
+ BYTE flags;
+ union {
+ struct {
+ WORD iImage;
+ HANDLE hContact;
+ };
+ struct {
+ WORD groupId;
+ struct ClcGroup *group;
+ };
+ };
+ BYTE iExtraImage[MAXEXTRACOLUMNS];
+ TCHAR szText[120-MAXEXTRACOLUMNS];
+ char * proto; // MS_PROTO_GETBASEPROTO
+};
+
+struct ClcData {
+ struct ClcGroup list;
+ int rowHeight;
+ int yScroll;
+ int selection;
+ struct ClcFontInfo fontInfo[FONTID_MAX + 1];
+ int scrollTime;
+ HIMAGELIST himlHighlight;
+ int groupIndent;
+ TCHAR szQuickSearch[128];
+ int iconXSpace;
+ HWND hwndRenameEdit;
+ COLORREF bkColour, selBkColour, selTextColour, hotTextColour, quickSearchColour;
+ int iDragItem, iInsertionMark;
+ int dragStage;
+ POINT ptDragStart;
+ int dragAutoScrolling;
+ int dragAutoScrollHeight;
+ int leftMargin;
+ int insertionMarkHitHeight;
+ HBITMAP hBmpBackground;
+ int backgroundBmpUse, bkChanged;
+ int iHotTrack;
+ int gammaCorrection;
+ DWORD greyoutFlags; //see m_clc.h
+ DWORD offlineModes;
+ DWORD exStyle;
+ POINT ptInfoTip;
+ int infoTipTimeout;
+ HANDLE hInfoTipItem;
+ HIMAGELIST himlExtraColumns;
+ int extraColumnsCount;
+ int extraColumnSpacing;
+ int checkboxSize;
+ int showSelAlways;
+ int showIdle;
+ int noVScrollbar;
+ int useWindowsColours;
+ int needsResort;
+};
+
+/* clc.c */
+extern int g_IconWidth, g_IconHeight;
+
+void fnClcOptionsChanged( void );
+void fnClcBroadcast( int msg, WPARAM wParam, LPARAM lParam );
+HMENU fnBuildGroupPopupMenu( struct ClcGroup* group );
+
+LRESULT CALLBACK fnContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+/* clcidents.c */
+int fnGetRowsPriorTo( struct ClcGroup *group, struct ClcGroup *subgroup, int contactIndex );
+int fnFindItem( HWND hwnd, struct ClcData *dat, HANDLE hItem, struct ClcContact **contact, struct ClcGroup **subgroup, int *isVisible );
+int fnGetRowByIndex( struct ClcData *dat, int testindex, struct ClcContact **contact, struct ClcGroup **subgroup );
+HANDLE fnContactToHItem( struct ClcContact* contact );
+HANDLE fnContactToItemHandle( struct ClcContact * contact, DWORD * nmFlags );
+
+/* clcitems.c */
+struct ClcGroup* fnAddGroup( HWND hwnd, struct ClcData *dat, const TCHAR *szName, DWORD flags, int groupId, int calcTotalMembers );
+struct ClcGroup* fnRemoveItemFromGroup(HWND hwnd, struct ClcGroup *group, struct ClcContact *contact, int updateTotalCount);
+
+void fnFreeContact( struct ClcContact *p );
+void fnFreeGroup( struct ClcGroup *group );
+int fnAddInfoItemToGroup(struct ClcGroup *group, int flags, const TCHAR *pszText);
+int fnAddItemToGroup( struct ClcGroup *group,int iAboveItem );
+void fnAddContactToTree( HWND hwnd, struct ClcData *dat, HANDLE hContact, int updateTotalCount, int checkHideOffline);
+int fnAddContactToGroup( struct ClcData *dat, struct ClcGroup *group, HANDLE hContact);
+void fnDeleteItemFromTree( HWND hwnd, HANDLE hItem );
+void fnRebuildEntireList( HWND hwnd, struct ClcData *dat );
+int fnGetGroupContentsCount( struct ClcGroup *group, int visibleOnly );
+void fnSortCLC( HWND hwnd, struct ClcData *dat, int useInsertionSort );
+void fnSaveStateAndRebuildList(HWND hwnd, struct ClcData *dat);
+
+/* clcmsgs.c */
+LRESULT fnProcessExternalMessages(HWND hwnd, struct ClcData *dat, UINT msg, WPARAM wParam, LPARAM lParam );
+
+/* clcutils.c */
+char* fnGetGroupCountsText(struct ClcData *dat, struct ClcContact *contact );
+int fnHitTest( HWND hwnd, struct ClcData *dat, int testx, int testy, struct ClcContact **contact, struct ClcGroup **group, DWORD * flags );
+void fnScrollTo( HWND hwnd, struct ClcData *dat, int desty, int noSmooth );
+void fnEnsureVisible(HWND hwnd, struct ClcData *dat, int iItem, int partialOk );
+void fnRecalcScrollBar( HWND hwnd, struct ClcData *dat );
+void fnSetGroupExpand( HWND hwnd, struct ClcData *dat, struct ClcGroup *group, int newState );
+void fnDoSelectionDefaultAction( HWND hwnd, struct ClcData *dat );
+int fnFindRowByText(HWND hwnd, struct ClcData *dat, const TCHAR *text, int prefixOk );
+void fnEndRename(HWND hwnd, struct ClcData *dat, int save );
+void fnDeleteFromContactList( HWND hwnd, struct ClcData *dat );
+void fnBeginRenameSelection( HWND hwnd, struct ClcData *dat );
+void fnCalcEipPosition( struct ClcData *dat, struct ClcContact *contact, struct ClcGroup *group, POINT *result);
+int fnGetDropTargetInformation( HWND hwnd, struct ClcData *dat, POINT pt );
+int fnClcStatusToPf2( int status );
+int fnIsHiddenMode( struct ClcData *dat, int status );
+void fnHideInfoTip( HWND hwnd, struct ClcData *dat );
+void fnNotifyNewContact( HWND hwnd, HANDLE hContact );
+DWORD fnGetDefaultExStyle( void );
+void fnGetSetting( int i, LOGFONT* lf, COLORREF* colour );
+void fnGetDefaultFontSetting(int i, LOGFONT* lf, COLORREF* colour);
+void fnGetFontSetting( int i, LOGFONT* lf, COLORREF* colour );
+void fnLoadClcOptions( HWND hwnd, struct ClcData *dat );
+void fnRecalculateGroupCheckboxes( HWND hwnd, struct ClcData *dat );
+void fnSetGroupChildCheckboxes( struct ClcGroup *group, int checked );
+void fnInvalidateItem( HWND hwnd, struct ClcData *dat, int iItem );
+
+int fnGetRowBottomY(struct ClcData *dat, int item);
+int fnGetRowHeight(struct ClcData *dat, int item);
+int fnGetRowTopY(struct ClcData *dat, int item);
+int fnGetRowTotalHeight(struct ClcData *dat);
+int fnRowHitTest(struct ClcData *dat, int y);
+
+/* clcopts.c */
+int ClcOptInit(WPARAM wParam,LPARAM lParam);
+DWORD GetDefaultExStyle(void);
+void GetFontSetting(int i,LOGFONTA *lf,COLORREF *colour);
+
+/* clistmenus.c */
+HGENMENU fnGetProtocolMenu( const char* );
+int fnGetProtocolVisibility( const char* accName );
+
+int fnGetAccountIndexByPos(int Pos);
+int fnGetProtoIndexByPos(PROTOCOLDESCRIPTOR ** proto, int protoCnt, int Pos);
+void RebuildMenuOrder( void );
+
+INT_PTR MenuProcessCommand(WPARAM wParam, LPARAM lParam);
+
+/* clistsettings.c */
+TCHAR* fnGetContactDisplayName( HANDLE hContact, int mode );
+void fnGetDefaultFontSetting( int i, LOGFONT* lf, COLORREF * colour);
+void fnInvalidateDisplayNameCacheEntry( HANDLE hContact );
+
+ClcCacheEntryBase* fnGetCacheEntry( HANDLE hContact );
+ClcCacheEntryBase* fnCreateCacheItem ( HANDLE hContact );
+void fnCheckCacheItem( ClcCacheEntryBase* p );
+void fnFreeCacheItem( ClcCacheEntryBase* p );
+
+/* clcfiledrop.c */
+void InitFileDropping(void);
+
+void fnRegisterFileDropping ( HWND hwnd );
+void fnUnregisterFileDropping ( HWND hwnd );
+
+/* clistevents.c */
+struct CListEvent* fnAddEvent( CLISTEVENT *cle );
+CLISTEVENT* fnGetEvent( HANDLE hContact, int idx );
+
+struct CListEvent* fnCreateEvent( void );
+void fnFreeEvent( struct CListEvent* p );
+
+int fnEventsProcessContactDoubleClick( HANDLE hContact );
+int fnEventsProcessTrayDoubleClick( int );
+int fnGetImlIconIndex(HICON hIcon);
+int fnRemoveEvent( HANDLE hContact, HANDLE dbEvent );
+
+/* clistmod.c */
+int fnIconFromStatusMode(const char *szProto, int status, HANDLE hContact);
+int fnShowHide( WPARAM wParam, LPARAM lParam );
+HICON fnGetIconFromStatusMode( HANDLE hContact, const char *szProto, int status );
+TCHAR* fnGetStatusModeDescription( int wParam, int lParam);
+int fnGetWindowVisibleState(HWND hWnd, int iStepX, int iStepY);
+
+/* clisttray.c */
+void fnInitTray( void );
+void fnUninitTray( void );
+void fnLockTray( void );
+void fnUnlockTray( void );
+int fnCListTrayNotify(MIRANDASYSTRAYNOTIFY *msn);
+int fnTrayIconAdd(HWND hwnd, const char *szProto, const char *szIconProto, int status);
+int fnTrayIconDestroy( HWND hwnd );
+void fnTrayIconIconsChanged ( void );
+int fnTrayIconInit( HWND hwnd );
+TCHAR* fnTrayIconMakeTooltip( const TCHAR *szPrefix, const char *szProto );
+int fnTrayIconPauseAutoHide ( WPARAM wParam, LPARAM lParam );
+INT_PTR fnTrayIconProcessMessage ( WPARAM wParam, LPARAM lParam );
+void fnTrayIconRemove(HWND hwnd, const char *szProto);
+int fnTrayIconSetBaseInfo(HICON hIcon, const char *szPreferredProto);
+void fnTrayIconSetToBase ( char *szPreferredProto );
+void fnTrayIconTaskbarCreated( HWND hwnd );
+int fnTrayIconUpdate( HICON hNewIcon, const TCHAR *szNewTip, const char *szPreferredProto, int isBase );
+void fnTrayIconUpdateBase ( const char *szChangedProto );
+void fnTrayIconUpdateWithImageList ( int iImage, const TCHAR *szNewTip, char *szPreferredProto );
+
+VOID CALLBACK fnTrayCycleTimerProc(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime);
+
+/* clui.c */
+LRESULT CALLBACK fnContactListWndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
+void fnLoadCluiGlobalOpts( void );
+void fnCluiProtocolStatusChanged(int,const char*);
+void fnDrawMenuItem(DRAWITEMSTRUCT *dis, HICON hIcon, HICON eventIcon);
+
+/* contact.c */
+void fnChangeContactIcon ( HANDLE hContact, int iIcon, int add );
+void fnLoadContactTree ( void );
+int fnCompareContacts ( const struct ClcContact *contact1, const struct ClcContact *contact2);
+void fnSortContacts ( void );
+int fnSetHideOffline ( WPARAM wParam, LPARAM lParam );
+
+/* docking.c */
+int fnDocking_ProcessWindowMessage ( WPARAM wParam, LPARAM lParam );
+
+/* group.c */
+TCHAR* fnGetGroupName ( int idx, DWORD* pdwFlags );
+int fnRenameGroup ( int groupID, TCHAR* newName );
+
+/* keyboard.c */
+int fnHotKeysRegister ( HWND hwnd );
+void fnHotKeysUnregister ( HWND hwnd );
+int fnHotKeysProcess ( HWND hwnd, WPARAM wParam, LPARAM lParam );
+int fnHotkeysProcessMessage ( WPARAM wParam, LPARAM lParam );
diff --git a/src/modules/clist/clcfiledrop.cpp b/src/modules/clist/clcfiledrop.cpp
new file mode 100644
index 0000000000..aae299d7b8
--- /dev/null
+++ b/src/modules/clist/clcfiledrop.cpp
@@ -0,0 +1,278 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+#include <shlobj.h>
+
+struct CDropTarget : IDropTarget
+{
+ LONG refCount;
+ IDropTargetHelper *pDropTargetHelper;
+
+ ULONG STDMETHODCALLTYPE AddRef(void);
+ ULONG STDMETHODCALLTYPE Release(void);
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);
+
+ HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ HRESULT STDMETHODCALLTYPE DragLeave(void);
+ HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+}
+static dropTarget;
+
+static HWND hwndCurrentDrag = NULL;
+static int originalSelection;
+
+HRESULT CDropTarget::QueryInterface(REFIID riid, LPVOID * ppvObj)
+{
+ if (riid == IID_IDropTarget) {
+ *ppvObj = this;
+ AddRef();
+ return S_OK;
+ }
+ *ppvObj = NULL;
+ return E_NOINTERFACE;
+}
+
+ULONG CDropTarget::AddRef(void)
+{
+ return InterlockedIncrement(&refCount);
+}
+
+ULONG CDropTarget::Release(void)
+{
+ if (refCount == 1) {
+ if (pDropTargetHelper)
+ pDropTargetHelper->Release();
+ }
+ return InterlockedDecrement(&refCount);
+}
+
+static HANDLE HContactFromPoint(HWND hwnd, struct ClcData *dat, int x, int y, int *hitLine)
+{
+ int hit;
+ struct ClcContact *contact;
+ DWORD hitFlags;
+ char *szProto;
+ DWORD protoCaps;
+
+ hit = cli.pfnHitTest(hwnd, dat, x, y, &contact, NULL, &hitFlags);
+ if (hit == -1 || !(hitFlags & (CLCHT_ONITEMLABEL | CLCHT_ONITEMICON)) || contact->type != CLCIT_CONTACT)
+ return NULL;
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) contact->hContact, 0);
+ if (szProto == NULL)
+ return NULL;
+ protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (!(protoCaps & PF1_FILESEND))
+ return NULL;
+ if (ID_STATUS_OFFLINE == DBGetContactSettingWord(contact->hContact, szProto, "Status", ID_STATUS_OFFLINE))
+ return NULL;
+ if (hitLine)
+ *hitLine = hit;
+ return contact->hContact;
+}
+
+HRESULT CDropTarget::DragOver(DWORD /*grfKeyState*/, POINTL pt, DWORD * pdwEffect)
+{
+ POINT shortPt;
+ struct ClcData *dat;
+ RECT clRect;
+ int hit;
+ HANDLE hContact;
+
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->DragOver((POINT*)&pt, *pdwEffect);
+
+ *pdwEffect = 0;
+ if (hwndCurrentDrag == NULL) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+ CallService(MS_CLIST_PAUSEAUTOHIDE, 0, 0);
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ ScreenToClient(hwndCurrentDrag, &shortPt);
+ GetClientRect(hwndCurrentDrag, &clRect);
+
+ if (shortPt.y < dat->dragAutoScrollHeight || shortPt.y >= clRect.bottom - dat->dragAutoScrollHeight) {
+ *pdwEffect |= DROPEFFECT_SCROLL;
+ cli.pfnScrollTo(hwndCurrentDrag, dat, dat->yScroll + (shortPt.y < dat->dragAutoScrollHeight ? -1 : 1) * dat->rowHeight * 2, 0);
+ }
+ hContact = HContactFromPoint(hwndCurrentDrag, dat, shortPt.x, shortPt.y, &hit);
+ if (hContact == NULL) {
+ hit = -1;
+ *pdwEffect |= DROPEFFECT_NONE;
+ }
+ else
+ *pdwEffect |= DROPEFFECT_COPY;
+
+ if (dat->selection != hit) {
+ dat->selection = hit;
+ cli.pfnInvalidateRect(hwndCurrentDrag, NULL, FALSE);
+ if (pDropTargetHelper) pDropTargetHelper->Show(FALSE);
+ UpdateWindow(hwndCurrentDrag);
+ if (pDropTargetHelper) pDropTargetHelper->Show(TRUE);
+ }
+
+ return S_OK;
+}
+
+HRESULT CDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ HWND hwnd;
+ TCHAR szWindowClass[64];
+ POINT shortPt;
+
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ hwnd = WindowFromPoint(shortPt);
+ GetClassName(hwnd, szWindowClass, SIZEOF(szWindowClass));
+ if (!lstrcmp(szWindowClass, CLISTCONTROL_CLASS)) {
+ struct ClcData *dat;
+ hwndCurrentDrag = hwnd;
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ originalSelection = dat->selection;
+ dat->showSelAlways = 1;
+ }
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->DragEnter(hwndCurrentDrag, pDataObj, (POINT*)&pt, *pdwEffect);
+ return DragOver(grfKeyState, pt, pdwEffect);
+}
+
+HRESULT CDropTarget::DragLeave(void)
+{
+ if (hwndCurrentDrag) {
+ struct ClcData *dat;
+ if (pDropTargetHelper)
+ pDropTargetHelper->DragLeave();
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ dat->showSelAlways = 0;
+ dat->selection = originalSelection;
+ cli.pfnInvalidateRect(hwndCurrentDrag, NULL, FALSE);
+ }
+ hwndCurrentDrag = NULL;
+ return S_OK;
+}
+
+static void AddToFileList(TCHAR ***pppFiles, int *totalCount, const TCHAR *szFilename)
+{
+ *pppFiles = (TCHAR **) mir_realloc(*pppFiles, (++*totalCount + 1) * sizeof(TCHAR *));
+ (*pppFiles)[*totalCount] = NULL;
+ (*pppFiles)[*totalCount - 1] = mir_tstrdup(szFilename);
+ if (GetFileAttributes(szFilename) & FILE_ATTRIBUTE_DIRECTORY) {
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+ TCHAR szPath[MAX_PATH];
+ lstrcpy(szPath, szFilename);
+ lstrcat(szPath, _T("\\*"));
+ if (hFind = FindFirstFile(szPath, &fd)) {
+ do {
+ if (!lstrcmp(fd.cFileName, _T(".")) || !lstrcmp(fd.cFileName, _T("..")))
+ continue;
+ lstrcpy(szPath, szFilename);
+ lstrcat(szPath, _T("\\"));
+ lstrcat(szPath, fd.cFileName);
+ AddToFileList(pppFiles, totalCount, szPath);
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ }
+ }
+}
+
+HRESULT CDropTarget::Drop(IDataObject * pDataObj, DWORD /*fKeyState*/, POINTL pt, DWORD * pdwEffect)
+{
+ FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HDROP hDrop;
+ POINT shortPt;
+ struct ClcData *dat;
+ HANDLE hContact;
+
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->Drop(pDataObj, (POINT*)&pt, *pdwEffect);
+
+ *pdwEffect = DROPEFFECT_NONE;
+ if (hwndCurrentDrag == NULL || S_OK != pDataObj->GetData(&fe, &stg))
+ return S_OK;
+ hDrop = (HDROP) stg.hGlobal;
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ ScreenToClient(hwndCurrentDrag, &shortPt);
+ hContact = HContactFromPoint(hwndCurrentDrag, dat, shortPt.x, shortPt.y, NULL);
+ if (hContact != NULL) {
+ TCHAR **ppFiles = NULL;
+ TCHAR szFilename[MAX_PATH];
+ int fileCount, totalCount = 0, i;
+
+ fileCount = DragQueryFile(hDrop, -1, NULL, 0);
+ ppFiles = NULL;
+ for (i = 0; i < fileCount; i++) {
+ DragQueryFile(hDrop, i, szFilename, SIZEOF(szFilename));
+ AddToFileList(&ppFiles, &totalCount, szFilename);
+ }
+
+ if (!CallService(MS_FILE_SENDSPECIFICFILEST, (WPARAM) hContact, (LPARAM) ppFiles))
+ *pdwEffect = DROPEFFECT_COPY;
+
+ for (i = 0; ppFiles[i]; i++)
+ mir_free(ppFiles[i]);
+ mir_free(ppFiles);
+ }
+
+ if (stg.pUnkForRelease)
+ stg.pUnkForRelease->Release();
+ else
+ GlobalFree(stg.hGlobal);
+
+ DragLeave();
+ return S_OK;
+}
+
+static VOID CALLBACK CreateDropTargetHelperTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD)
+{
+ KillTimer(hwnd, idEvent);
+ //This is a ludicrously slow function (~200ms) so we delay load it a bit.
+ if (S_OK != CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
+ IID_IDropTargetHelper, (LPVOID*)&dropTarget.pDropTargetHelper))
+ dropTarget.pDropTargetHelper = NULL;
+}
+
+void InitFileDropping(void)
+{
+ // Disabled as this function loads tons of dlls for no apparenet reason
+ // we will se what the reaction will be
+// SetTimer(NULL, 1, 1000, CreateDropTargetHelperTimerProc);
+}
+
+void fnRegisterFileDropping(HWND hwnd)
+{
+ RegisterDragDrop(hwnd, (IDropTarget *) & dropTarget);
+}
+
+void fnUnregisterFileDropping(HWND hwnd)
+{
+ RevokeDragDrop(hwnd);
+}
diff --git a/src/modules/clist/clcidents.cpp b/src/modules/clist/clcidents.cpp
new file mode 100644
index 0000000000..7cb18760fe
--- /dev/null
+++ b/src/modules/clist/clcidents.cpp
@@ -0,0 +1,201 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+/* the CLC uses 3 different ways to identify elements in its list, this file
+contains routines to convert between them.
+
+1) struct ClcContact/struct ClcGroup pair. Only ever used within the duration
+ of a single operation, but used at some point in nearly everything
+2) index integer. The 0-based number of the item from the top. Only visible
+ items are counted (ie not closed groups). Used for saving selection and drag
+ highlight
+3) hItem handle. Either the hContact or (hGroup|HCONTACT_ISGROUP). Used
+ exclusively externally
+
+1->2: GetRowsPriorTo()
+1->3: ContactToHItem()
+3->1: FindItem()
+2->1: GetRowByIndex()
+*/
+
+int fnGetRowsPriorTo(struct ClcGroup *group, struct ClcGroup *subgroup, int contactIndex)
+{
+ int count = 0;
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+ if (group == subgroup && contactIndex == group->scanIndex)
+ return count;
+ count++;
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ if (group->cl.items[group->scanIndex]->group == subgroup && contactIndex == -1)
+ return count - 1;
+ if (group->cl.items[group->scanIndex]->group->expanded) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ continue;
+ }
+ }
+ group->scanIndex++;
+ }
+ return -1;
+}
+
+int fnFindItem(HWND hwnd, struct ClcData *dat, HANDLE hItem, struct ClcContact **contact, struct ClcGroup **subgroup, int *isVisible)
+{
+ int index = 0;
+ int nowVisible = 1;
+ struct ClcGroup *group = &dat->list;
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ struct ClcGroup *tgroup;
+ group = group->parent;
+ if (group == NULL)
+ break;
+ nowVisible = 1;
+ for (tgroup = group; tgroup; tgroup = tgroup->parent)
+ if (!group->expanded) {
+ nowVisible = 0;
+ break;
+ }
+ group->scanIndex++;
+ continue;
+ }
+ if (nowVisible)
+ index++;
+ if ((IsHContactGroup(hItem) && group->cl.items[group->scanIndex]->type == CLCIT_GROUP
+ && ((unsigned) hItem & ~HCONTACT_ISGROUP) == group->cl.items[group->scanIndex]->groupId) || (IsHContactContact(hItem)
+ && group->cl.items[group->scanIndex]->type == CLCIT_CONTACT
+ && group->cl.items[group->scanIndex]->hContact == hItem) || (IsHContactInfo(hItem)
+ && group->cl.items[group->scanIndex]->type == CLCIT_INFO
+ && group->cl.items[group->scanIndex]->hContact == (HANDLE) ((UINT_PTR)hItem & ~HCONTACT_ISINFO)))
+ {
+ if (isVisible) {
+ if (!nowVisible)
+ *isVisible = 0;
+ else {
+ int posY = cli.pfnGetRowTopY(dat, index+1);
+ if (posY < dat->yScroll)
+ *isVisible = 0;
+ else {
+ RECT clRect;
+ GetClientRect(hwnd, &clRect);
+ if (posY >= dat->yScroll + clRect.bottom)
+ *isVisible = 0;
+ else
+ *isVisible = 1;
+ }
+ }
+ }
+ if (contact)
+ *contact = group->cl.items[group->scanIndex];
+ if (subgroup)
+ *subgroup = group;
+ return 1;
+ }
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ nowVisible &= group->expanded;
+ continue;
+ }
+ group->scanIndex++;
+ }
+ return 0;
+}
+
+int fnGetRowByIndex(struct ClcData *dat, int testindex, struct ClcContact **contact, struct ClcGroup **subgroup)
+{
+ int index = 0;
+ struct ClcGroup *group = &dat->list;
+
+ if (testindex<0)
+ return (-1);
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+ if (testindex == index) {
+ if (contact)
+ *contact = group->cl.items[group->scanIndex];
+ if (subgroup)
+ *subgroup = group;
+ return index;
+ }
+ index++;
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && group->cl.items[group->scanIndex]->group->expanded) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ continue;
+ }
+ group->scanIndex++;
+ }
+ return -1;
+}
+
+HANDLE fnContactToHItem(struct ClcContact * contact)
+{
+ switch (contact->type) {
+ case CLCIT_CONTACT:
+ return contact->hContact;
+ case CLCIT_GROUP:
+ return (HANDLE) (contact->groupId | HCONTACT_ISGROUP);
+ case CLCIT_INFO:
+ return (HANDLE) ((UINT_PTR) contact->hContact | HCONTACT_ISINFO);
+ }
+ return NULL;
+}
+
+HANDLE fnContactToItemHandle(struct ClcContact * contact, DWORD * nmFlags)
+{
+ switch (contact->type) {
+ case CLCIT_CONTACT:
+ return contact->hContact;
+ case CLCIT_GROUP:
+ if (nmFlags)
+ *nmFlags |= CLNF_ISGROUP;
+ return (HANDLE) contact->groupId;
+ case CLCIT_INFO:
+ if (nmFlags)
+ *nmFlags |= CLNF_ISINFO;
+ return (HANDLE) ((UINT_PTR) contact->hContact | HCONTACT_ISINFO);
+ }
+ return NULL;
+}
diff --git a/src/modules/clist/clcitems.cpp b/src/modules/clist/clcitems.cpp
new file mode 100644
index 0000000000..cc338d5ec9
--- /dev/null
+++ b/src/modules/clist/clcitems.cpp
@@ -0,0 +1,707 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+//routines for managing adding/removal of items in the list, including sorting
+
+int fnAddItemToGroup(struct ClcGroup *group, int iAboveItem)
+{
+ struct ClcContact* newItem = cli.pfnCreateClcContact();
+ newItem->type = CLCIT_DIVIDER;
+ newItem->flags = 0;
+ newItem->szText[0] = '\0';
+ memset( newItem->iExtraImage, 0xFF, SIZEOF(newItem->iExtraImage));
+
+ List_Insert(( SortedList* )&group->cl, newItem, iAboveItem );
+ return iAboveItem;
+}
+
+struct ClcGroup* fnAddGroup(HWND hwnd, struct ClcData *dat, const TCHAR *szName, DWORD flags, int groupId, int calcTotalMembers)
+{
+ TCHAR *pBackslash, *pNextField, szThisField[ SIZEOF(dat->list.cl.items[0]->szText) ];
+ struct ClcGroup *group = &dat->list;
+ int i, compareResult;
+
+ dat->needsResort = 1;
+ if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS))
+ return &dat->list;
+
+ pNextField = ( TCHAR* )szName;
+ do {
+ pBackslash = _tcschr(pNextField, '\\');
+ if (pBackslash == NULL) {
+ lstrcpyn(szThisField, pNextField, SIZEOF(szThisField));
+ pNextField = NULL;
+ }
+ else {
+ lstrcpyn(szThisField, pNextField, min( SIZEOF(szThisField), pBackslash - pNextField + 1 ));
+ pNextField = pBackslash + 1;
+ }
+ compareResult = 1;
+ for (i = 0; i < group->cl.count; i++) {
+ if (group->cl.items[i]->type == CLCIT_CONTACT)
+ break;
+ if (group->cl.items[i]->type != CLCIT_GROUP)
+ continue;
+ compareResult = lstrcmp(szThisField, group->cl.items[i]->szText);
+ if (compareResult == 0) {
+ if (pNextField == NULL && flags != (DWORD) - 1) {
+ group->cl.items[i]->groupId = (WORD) groupId;
+ group = group->cl.items[i]->group;
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ group->groupId = groupId;
+ }
+ else
+ group = group->cl.items[i]->group;
+ break;
+ }
+ if (pNextField == NULL && group->cl.items[i]->groupId == 0)
+ break;
+ if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && group->cl.items[i]->groupId > groupId)
+ break;
+ }
+ if (compareResult) {
+ if (groupId == 0)
+ return NULL;
+ i = cli.pfnAddItemToGroup(group, i);
+ group->cl.items[i]->type = CLCIT_GROUP;
+ lstrcpyn(group->cl.items[i]->szText, szThisField, SIZEOF( group->cl.items[i]->szText ));
+ group->cl.items[i]->groupId = (WORD) (pNextField ? 0 : groupId);
+ group->cl.items[i]->group = (struct ClcGroup *) mir_alloc(sizeof(struct ClcGroup));
+ group->cl.items[i]->group->parent = group;
+ group = group->cl.items[i]->group;
+ memset( &group->cl, 0, sizeof( group->cl ));
+ group->cl.increment = 10;
+ if (flags == (DWORD) - 1 || pNextField != NULL) {
+ group->expanded = 0;
+ group->hideOffline = 0;
+ }
+ else {
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ }
+ group->groupId = pNextField ? 0 : groupId;
+ group->totalMembers = 0;
+ if (flags != (DWORD) - 1 && pNextField == NULL && calcTotalMembers) {
+ DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact) {
+ ClcCacheEntryBase* cache = cli.pfnGetCacheEntry( hContact );
+ if ( !lstrcmp( cache->group, szName) && (style & CLS_SHOWHIDDEN || !cache->isHidden ))
+ group->totalMembers++;
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ }
+ }
+ } while (pNextField);
+ return group;
+}
+
+void fnFreeContact(struct ClcContact* p)
+{
+ if (p->type == CLCIT_GROUP) {
+ cli.pfnFreeGroup(p->group);
+ mir_free(p->group);
+} }
+
+void fnFreeGroup(struct ClcGroup *group)
+{
+ int i;
+ for (i = 0; i < group->cl.count; i++) {
+ cli.pfnFreeContact(group->cl.items[i]);
+ mir_free(group->cl.items[i]);
+ }
+ if (group->cl.items)
+ mir_free(group->cl.items);
+ group->cl.limit = group->cl.count = 0;
+ group->cl.items = NULL;
+}
+
+static int iInfoItemUniqueHandle = 0;
+int fnAddInfoItemToGroup(struct ClcGroup *group, int flags, const TCHAR *pszText)
+{
+ int i = 0;
+
+ if (flags & CLCIIF_BELOWCONTACTS)
+ i = group->cl.count;
+ else if (flags & CLCIIF_BELOWGROUPS) {
+ for (; i < group->cl.count; i++)
+ if (group->cl.items[i]->type == CLCIT_CONTACT)
+ break;
+ }
+ else
+ for (; i < group->cl.count; i++)
+ if (group->cl.items[i]->type != CLCIT_INFO)
+ break;
+ i = cli.pfnAddItemToGroup(group, i);
+ iInfoItemUniqueHandle = (iInfoItemUniqueHandle + 1) & 0xFFFF;
+ if (iInfoItemUniqueHandle == 0)
+ ++iInfoItemUniqueHandle;
+ group->cl.items[i]->type = CLCIT_INFO;
+ group->cl.items[i]->flags = (BYTE) flags;
+ group->cl.items[i]->hContact = (HANDLE)++ iInfoItemUniqueHandle;
+ lstrcpyn(group->cl.items[i]->szText, pszText, SIZEOF( group->cl.items[i]->szText ));
+ return i;
+}
+
+int fnAddContactToGroup(struct ClcData *dat, struct ClcGroup *group, HANDLE hContact)
+{
+ char *szProto;
+ WORD apparentMode;
+ DWORD idleMode;
+
+ int i, index = -1;
+
+ dat->needsResort = 1;
+ for (i = group->cl.count - 1; i >= 0; i--) {
+ if (group->cl.items[i]->hContact == hContact )
+ return i;
+
+ if ( index == -1 )
+ if (group->cl.items[i]->type != CLCIT_INFO || !(group->cl.items[i]->flags & CLCIIF_BELOWCONTACTS))
+ index = i;
+ }
+
+ i = cli.pfnAddItemToGroup(group, index + 1);
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ group->cl.items[i]->type = CLCIT_CONTACT;
+ group->cl.items[i]->iImage = CallService(MS_CLIST_GETCONTACTICON, (WPARAM) hContact, 0);
+ group->cl.items[i]->hContact = hContact;
+ group->cl.items[i]->proto = szProto;
+ if (szProto != NULL && !cli.pfnIsHiddenMode(dat, DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE)))
+ group->cl.items[i]->flags |= CONTACTF_ONLINE;
+ apparentMode = szProto != NULL ? DBGetContactSettingWord(hContact, szProto, "ApparentMode", 0) : 0;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ group->cl.items[i]->flags |= CONTACTF_INVISTO;
+ else if (apparentMode == ID_STATUS_ONLINE)
+ group->cl.items[i]->flags |= CONTACTF_VISTO;
+ else if (apparentMode)
+ group->cl.items[i]->flags |= CONTACTF_VISTO | CONTACTF_INVISTO;
+ if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ group->cl.items[i]->flags |= CONTACTF_NOTONLIST;
+ idleMode = szProto != NULL ? DBGetContactSettingDword(hContact, szProto, "IdleTS", 0) : 0;
+ if (idleMode)
+ group->cl.items[i]->flags |= CONTACTF_IDLE;
+ lstrcpyn(group->cl.items[i]->szText, cli.pfnGetContactDisplayName(hContact,0), SIZEOF(group->cl.items[i]->szText));
+
+ { ClcCacheEntryBase* p = cli.pfnGetCacheEntry(hContact);
+ if ( p != NULL ) {
+ if ( p->group ) mir_free( p->group );
+ p->group = NULL;
+ } }
+
+ return i;
+}
+
+void fnAddContactToTree(HWND hwnd, struct ClcData *dat, HANDLE hContact, int updateTotalCount, int checkHideOffline)
+{
+ struct ClcGroup *group;
+ DBVARIANT dbv;
+ DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ WORD status = ID_STATUS_OFFLINE;
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ dat->needsResort = 1;
+ if (style & CLS_NOHIDEOFFLINE)
+ checkHideOffline = 0;
+ if (checkHideOffline)
+ if (szProto != NULL)
+ status = DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE);
+
+ if ( DBGetContactSettingTString(hContact, "CList", "Group", &dbv))
+ group = &dat->list;
+ else {
+ group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0);
+ if (group == NULL) {
+ int i, len;
+ DWORD groupFlags;
+ TCHAR *szGroupName;
+ if (!(style & CLS_HIDEEMPTYGROUPS)) {
+ mir_free(dbv.ptszVal);
+ return;
+ }
+ if (checkHideOffline && cli.pfnIsHiddenMode(dat, status)) {
+ for (i = 1;; i++) {
+ szGroupName = cli.pfnGetGroupName(i, &groupFlags);
+ if (szGroupName == NULL) {
+ mir_free(dbv.ptszVal);
+ return;
+ } //never happens
+ if (!lstrcmp(szGroupName, dbv.ptszVal))
+ break;
+ }
+ if (groupFlags & GROUPF_HIDEOFFLINE) {
+ mir_free(dbv.ptszVal);
+ return;
+ }
+ }
+ for (i = 1;; i++) {
+ szGroupName = cli.pfnGetGroupName(i, &groupFlags);
+ if (szGroupName == NULL) {
+ mir_free(dbv.ptszVal);
+ return;
+ } //never happens
+ if (!lstrcmp(szGroupName, dbv.ptszVal))
+ break;
+ len = lstrlen(szGroupName);
+ if (!_tcsncmp(szGroupName, dbv.ptszVal, len) && dbv.ptszVal[len] == '\\')
+ cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1);
+ }
+ group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, groupFlags, i, 1);
+ }
+ mir_free(dbv.ptszVal);
+ }
+ if (checkHideOffline) {
+ if (cli.pfnIsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ if (updateTotalCount)
+ group->totalMembers++;
+ return;
+ }
+ }
+ cli.pfnAddContactToGroup(dat, group, hContact);
+ if (updateTotalCount)
+ group->totalMembers++;
+}
+
+struct ClcGroup* fnRemoveItemFromGroup(HWND hwnd, struct ClcGroup *group, struct ClcContact *contact, int updateTotalCount)
+{
+ int iContact;
+ if (( iContact = List_IndexOf(( SortedList* )&group->cl, contact )) == -1 )
+ return group;
+
+ if (updateTotalCount && contact->type == CLCIT_CONTACT)
+ group->totalMembers--;
+
+ { ClcCacheEntryBase* p = cli.pfnGetCacheEntry(contact->hContact);
+ if ( p != NULL ) {
+ if ( p->group ) mir_free( p->group );
+ p->group = NULL;
+ } }
+
+ cli.pfnFreeContact( group->cl.items[iContact] );
+ mir_free( group->cl.items[iContact] );
+ List_Remove(( SortedList* )&group->cl, iContact );
+
+ if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.count == 0) {
+ int i;
+ if (group->parent == NULL)
+ return group;
+ for (i = 0; i < group->parent->cl.count; i++)
+ if (group->parent->cl.items[i]->type == CLCIT_GROUP && group->parent->cl.items[i]->groupId == group->groupId)
+ break;
+ if (i == group->parent->cl.count)
+ return group; //never happens
+ return cli.pfnRemoveItemFromGroup(hwnd, group->parent, group->parent->cl.items[i], 0);
+ }
+ return group;
+}
+
+void fnDeleteItemFromTree(HWND hwnd, HANDLE hItem)
+{
+ struct ClcContact *contact;
+ struct ClcGroup *group;
+ struct ClcData *dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0);
+
+ dat->needsResort = 1;
+ if (!cli.pfnFindItem(hwnd, dat, hItem, &contact, &group, NULL)) {
+ DBVARIANT dbv;
+ int i, nameOffset;
+ if (!IsHContactContact(hItem))
+ return;
+ if (DBGetContactSettingTString(hItem, "CList", "Group", &dbv))
+ return;
+
+ //decrease member counts of all parent groups too
+ group = &dat->list;
+ nameOffset = 0;
+ for (i = 0;; i++) {
+ if (group->scanIndex == group->cl.count)
+ break;
+ if (group->cl.items[i]->type == CLCIT_GROUP) {
+ int len = lstrlen(group->cl.items[i]->szText);
+ if (!_tcsncmp(group->cl.items[i]->szText, dbv.ptszVal + nameOffset, len) &&
+ (dbv.ptszVal[nameOffset + len] == '\\' || dbv.ptszVal[nameOffset + len] == '\0')) {
+ group->totalMembers--;
+ if (dbv.ptszVal[nameOffset + len] == '\0')
+ break;
+ }
+ }
+ }
+ mir_free(dbv.ptszVal);
+ }
+ else
+ cli.pfnRemoveItemFromGroup(hwnd, group, contact, 1);
+}
+
+void fnRebuildEntireList(HWND hwnd, struct ClcData *dat)
+{
+ char *szProto;
+ DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ HANDLE hContact;
+ struct ClcGroup *group;
+ DBVARIANT dbv;
+
+ dat->list.expanded = 1;
+ dat->list.hideOffline = DBGetContactSettingByte(NULL, "CLC", "HideOfflineRoot", 0) && style&CLS_USEGROUPS;
+ dat->list.cl.count = dat->list.cl.limit = 0;
+ dat->selection = -1;
+ {
+ int i;
+ TCHAR *szGroupName;
+ DWORD groupFlags;
+
+ for (i = 1;; i++) {
+ szGroupName = cli.pfnGetGroupName(i, &groupFlags);
+ if (szGroupName == NULL)
+ break;
+ cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0);
+ }
+ }
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact) {
+ if (style & CLS_SHOWHIDDEN || !DBGetContactSettingByte(hContact, "CList", "Hidden", 0)) {
+ if (DBGetContactSettingTString(hContact, "CList", "Group", &dbv))
+ group = &dat->list;
+ else {
+ group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0);
+ if (group == NULL && style & CLS_SHOWHIDDEN) group = &dat->list;
+ mir_free(dbv.ptszVal);
+ }
+
+ if (group != NULL) {
+ group->totalMembers++;
+ if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto == NULL) {
+ if (!cli.pfnIsHiddenMode(dat, ID_STATUS_OFFLINE))
+ cli.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else if (!cli.pfnIsHiddenMode(dat, DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE)))
+ cli.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else cli.pfnAddContactToGroup(dat, group, hContact);
+ }
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+ if (style & CLS_HIDEEMPTYGROUPS) {
+ group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ if (group->cl.items[group->scanIndex]->group->cl.count == 0) {
+ group = cli.pfnRemoveItemFromGroup(hwnd, group, group->cl.items[group->scanIndex], 0);
+ }
+ else {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ }
+ continue;
+ }
+ group->scanIndex++;
+ }
+ }
+
+ cli.pfnSortCLC(hwnd, dat, 0);
+}
+
+int fnGetGroupContentsCount(struct ClcGroup *group, int visibleOnly)
+{
+ int count = group->cl.count;
+ struct ClcGroup *topgroup = group;
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ if (group == topgroup)
+ break;
+ group = group->parent;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && (!visibleOnly || group->cl.items[group->scanIndex]->group->expanded)) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ count += group->cl.count;
+ continue;
+ }
+ group->scanIndex++;
+ }
+ return count;
+}
+
+static int __cdecl GroupSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = ( ClcContact** )p1, **contact2 = ( ClcContact** )p2;
+
+ return lstrcmpi(contact1[0]->szText, contact2[0]->szText);
+}
+
+static int __cdecl ContactSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = ( ClcContact** )p1, **contact2 = ( ClcContact** )p2;
+
+ int result = cli.pfnCompareContacts( contact1[0], contact2[0] );
+ if (result)
+ return result;
+ //nothing to distinguish them, so make sure they stay in the same order
+ return (int)((INT_PTR) contact2[0]->hContact - (INT_PTR) contact1[0]->hContact);
+}
+
+static void InsertionSort(struct ClcContact **pContactArray, int nArray, int (*CompareProc) (const void *, const void *))
+{
+ int i, j;
+ struct ClcContact* testElement;
+
+ for (i = 1; i < nArray; i++) {
+ if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) {
+ testElement = pContactArray[i];
+ for (j = i - 2; j >= 0; j--)
+ if (CompareProc(&pContactArray[j], &testElement) <= 0)
+ break;
+ j++;
+ memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j));
+ pContactArray[j] = testElement;
+} } }
+
+static void SortGroup(struct ClcData *dat, struct ClcGroup *group, int useInsertionSort)
+{
+ int i, sortCount;
+
+ for (i = group->cl.count - 1; i >= 0; i--) {
+ if (group->cl.items[i]->type == CLCIT_DIVIDER) {
+ mir_free( group->cl.items[i] );
+ List_Remove(( SortedList* )&group->cl, i );
+ } }
+
+ for (i = 0; i < group->cl.count; i++)
+ if (group->cl.items[i]->type != CLCIT_INFO)
+ break;
+ if (i > group->cl.count - 2)
+ return;
+ if (group->cl.items[i]->type == CLCIT_GROUP) {
+ if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) {
+ for (sortCount = 0; i + sortCount < group->cl.count; sortCount++)
+ if (group->cl.items[i + sortCount]->type != CLCIT_GROUP)
+ break;
+ qsort(group->cl.items + i, sortCount, sizeof(void*), GroupSortProc);
+ i = i + sortCount;
+ }
+ for (; i < group->cl.count; i++)
+ if (group->cl.items[i]->type == CLCIT_CONTACT)
+ break;
+ if (group->cl.count - i < 2)
+ return;
+ }
+ for (sortCount = 0; i + sortCount < group->cl.count; sortCount++)
+ if (group->cl.items[i + sortCount]->type != CLCIT_CONTACT)
+ break;
+ if (useInsertionSort)
+ InsertionSort(group->cl.items + i, sortCount, ContactSortProc);
+ else
+ qsort(group->cl.items + i, sortCount, sizeof(void*), ContactSortProc);
+ if (dat->exStyle & CLS_EX_DIVIDERONOFF) {
+ int prevContactOnline = 0;
+ for (i = 0; i < group->cl.count; i++) {
+ if (group->cl.items[i]->type != CLCIT_CONTACT)
+ continue;
+ if (group->cl.items[i]->flags & CONTACTF_ONLINE)
+ prevContactOnline = 1;
+ else {
+ if (prevContactOnline) {
+ i = cli.pfnAddItemToGroup(group, i);
+ group->cl.items[i]->type = CLCIT_DIVIDER;
+ lstrcpy(group->cl.items[i]->szText, TranslateT("Offline"));
+ }
+ break;
+} } } }
+
+void fnSortCLC(HWND hwnd, struct ClcData *dat, int useInsertionSort)
+{
+ struct ClcContact *selcontact;
+ struct ClcGroup *group = &dat->list, *selgroup;
+ HANDLE hSelItem;
+
+ if ( dat->needsResort ) {
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) == -1)
+ hSelItem = NULL;
+ else
+ hSelItem = cli.pfnContactToHItem(selcontact);
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ continue;
+ }
+ group->scanIndex++;
+ }
+ if (hSelItem)
+ if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL))
+ dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl,selcontact));
+
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ }
+ dat->needsResort = 0;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+}
+
+struct SavedContactState_t
+{
+ HANDLE hContact;
+ BYTE iExtraImage[MAXEXTRACOLUMNS];
+ int checked;
+};
+
+struct SavedGroupState_t
+{
+ int groupId, expanded;
+};
+
+struct SavedInfoState_t
+{
+ int parentId;
+ ClcContact contact;
+};
+
+void fnSaveStateAndRebuildList(HWND hwnd, struct ClcData *dat)
+{
+ NMCLISTCONTROL nm;
+ int i, j;
+ ClcGroup *group;
+ ClcContact *contact;
+
+ cli.pfnHideInfoTip(hwnd, dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ cli.pfnEndRename(hwnd, dat, 1);
+
+ OBJLIST<SavedContactState_t> saveContact( 10, HandleKeySortT );
+ OBJLIST<SavedGroupState_t> saveGroup( 100, NumericKeySortT );
+ OBJLIST<SavedInfoState_t> saveInfo( 10, NumericKeySortT );
+
+ dat->needsResort = 1;
+ group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t* p = new SavedGroupState_t;
+ p->groupId = group->groupId;
+ p->expanded = group->expanded;
+ saveGroup.insert( p );
+ continue;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) {
+ SavedContactState_t* p = new SavedContactState_t;
+ p->hContact = group->cl.items[group->scanIndex]->hContact;
+ CopyMemory( p->iExtraImage, group->cl.items[group->scanIndex]->iExtraImage,
+ sizeof(group->cl.items[group->scanIndex]->iExtraImage));
+ p->checked = group->cl.items[group->scanIndex]->flags & CONTACTF_CHECKED;
+ saveContact.insert( p );
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_INFO) {
+ SavedInfoState_t* p = new SavedInfoState_t;
+ p->parentId = (group->parent == NULL) ? -1 : group->groupId;
+ p->contact = *group->cl.items[group->scanIndex];
+ saveInfo.insert( p );
+ }
+ group->scanIndex++;
+ }
+
+ cli.pfnFreeGroup(&dat->list);
+ cli.pfnRebuildEntireList(hwnd, dat);
+
+ group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t tmp, *p;
+ tmp.groupId = group->groupId;
+ if (( p = saveGroup.find( &tmp )) != NULL )
+ group->expanded = p->expanded;
+ continue;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) {
+ SavedContactState_t tmp, *p;
+ tmp.hContact = group->cl.items[group->scanIndex]->hContact;
+ if (( p = saveContact.find( &tmp )) != NULL ) {
+ CopyMemory(group->cl.items[group->scanIndex]->iExtraImage, p->iExtraImage,
+ SIZEOF(group->cl.items[group->scanIndex]->iExtraImage));
+ if (p->checked)
+ group->cl.items[group->scanIndex]->flags |= CONTACTF_CHECKED;
+ } }
+
+ group->scanIndex++;
+ }
+
+ for (i = 0; i < saveInfo.getCount(); i++) {
+ if (saveInfo[i].parentId == -1)
+ group = &dat->list;
+ else {
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) (saveInfo[i].parentId | HCONTACT_ISGROUP), &contact, NULL, NULL))
+ continue;
+ group = contact->group;
+ }
+ j = cli.pfnAddInfoItemToGroup(group, saveInfo[i].contact.flags, _T(""));
+ *group->cl.items[j] = saveInfo[i].contact;
+ }
+
+ cli.pfnRecalculateGroupCheckboxes(hwnd, dat);
+
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ nm.hdr.code = CLN_LISTREBUILT;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+}
diff --git a/src/modules/clist/clcmsgs.cpp b/src/modules/clist/clcmsgs.cpp
new file mode 100644
index 0000000000..a46777939d
--- /dev/null
+++ b/src/modules/clist/clcmsgs.cpp
@@ -0,0 +1,472 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+//processing of all the CLM_ messages incoming
+
+LRESULT fnProcessExternalMessages(HWND hwnd, struct ClcData *dat, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case CLM_ADDCONTACT:
+ cli.pfnAddContactToTree(hwnd, dat, (HANDLE) wParam, 1, 0);
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ cli.pfnSortCLC(hwnd, dat, 1);
+ break;
+
+ case CLM_ADDGROUP:
+ {
+ DWORD groupFlags;
+ TCHAR *szName = cli.pfnGetGroupName(wParam, &groupFlags);
+ if (szName == NULL)
+ break;
+ cli.pfnAddGroup(hwnd, dat, szName, groupFlags, wParam, 0);
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ break;
+ }
+
+ case CLM_ADDINFOITEMA:
+ case CLM_ADDINFOITEMW:
+ {
+ int i;
+ ClcContact *groupContact;
+ ClcGroup *group;
+ CLCINFOITEM *cii = (CLCINFOITEM *) lParam;
+ if (cii==NULL || cii->cbSize != sizeof(CLCINFOITEM))
+ return (LRESULT) (HANDLE) NULL;
+ if (cii->hParentGroup == NULL)
+ group = &dat->list;
+ else {
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) ((UINT_PTR) cii->hParentGroup | HCONTACT_ISGROUP), &groupContact, NULL, NULL))
+ return (LRESULT) (HANDLE) NULL;
+ group = groupContact->group;
+ }
+ #if defined( _UNICODE )
+ if ( msg == CLM_ADDINFOITEMA )
+ { WCHAR* wszText = mir_a2u(( char* )cii->pszText );
+ i = cli.pfnAddInfoItemToGroup(group, cii->flags, wszText);
+ mir_free( wszText );
+ }
+ else i = cli.pfnAddInfoItemToGroup(group, cii->flags, cii->pszText);
+ #else
+ i = cli.pfnAddInfoItemToGroup(group, cii->flags, cii->pszText);
+ #endif
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ return (LRESULT) group->cl.items[i]->hContact | HCONTACT_ISINFO;
+ }
+
+ case CLM_AUTOREBUILD:
+ KillTimer(hwnd,TIMERID_REBUILDAFTER);
+ cli.pfnSaveStateAndRebuildList(hwnd, dat);
+ break;
+
+ case CLM_DELETEITEM:
+ cli.pfnDeleteItemFromTree(hwnd, (HANDLE) wParam);
+ cli.pfnSortCLC(hwnd, dat, 1);
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ break;
+
+ case CLM_EDITLABEL:
+ SendMessage(hwnd, CLM_SELECTITEM, wParam, 0);
+ cli.pfnBeginRenameSelection(hwnd, dat);
+ break;
+
+ case CLM_ENDEDITLABELNOW:
+ cli.pfnEndRename(hwnd, dat, wParam);
+ break;
+
+ case CLM_ENSUREVISIBLE:
+ {
+ ClcContact *contact;
+ ClcGroup *group, *tgroup;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, &group, NULL))
+ break;
+ for (tgroup = group; tgroup; tgroup = tgroup->parent)
+ cli.pfnSetGroupExpand(hwnd, dat, tgroup, 1);
+ cli.pfnEnsureVisible(hwnd, dat, cli.pfnGetRowsPriorTo(&dat->list, group, List_IndexOf((SortedList*)&group->cl,contact)), 0);
+ break;
+ }
+
+ case CLM_EXPAND:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ if (contact->type != CLCIT_GROUP)
+ break;
+ cli.pfnSetGroupExpand(hwnd, dat, contact->group, lParam);
+ break;
+ }
+
+ case CLM_FINDCONTACT:
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, NULL, NULL, NULL))
+ return (LRESULT) (HANDLE) NULL;
+ return wParam;
+
+ case CLM_FINDGROUP:
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) (wParam | HCONTACT_ISGROUP), NULL, NULL, NULL))
+ return (LRESULT) (HANDLE) NULL;
+ return wParam | HCONTACT_ISGROUP;
+
+ case CLM_GETBKCOLOR:
+ return dat->bkColour;
+
+ case CLM_GETCHECKMARK:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return 0;
+ return (contact->flags & CONTACTF_CHECKED) != 0;
+ }
+
+ case CLM_GETCOUNT:
+ return cli.pfnGetGroupContentsCount(&dat->list, 0);
+
+ case CLM_GETEDITCONTROL:
+ return (LRESULT) dat->hwndRenameEdit;
+
+ case CLM_GETEXPAND:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return CLE_INVALID;
+ if (contact->type != CLCIT_GROUP)
+ return CLE_INVALID;
+ return contact->group->expanded;
+ }
+
+ case CLM_GETEXTRACOLUMNS:
+ return dat->extraColumnsCount;
+
+ case CLM_GETEXTRAIMAGE:
+ {
+ ClcContact *contact;
+ if (LOWORD(lParam) >= dat->extraColumnsCount)
+ return 0xFF;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return 0xFF;
+ return contact->iExtraImage[LOWORD(lParam)];
+ }
+
+ case CLM_GETEXTRAIMAGELIST:
+ return (LRESULT) dat->himlExtraColumns;
+
+ case CLM_GETFONT:
+ if (wParam < 0 || wParam > FONTID_MAX)
+ return 0;
+ return (LRESULT) dat->fontInfo[wParam].hFont;
+
+ case CLM_GETHIDEOFFLINEROOT:
+ return DBGetContactSettingByte(NULL, "CLC", "HideOfflineRoot", 0);
+
+ case CLM_GETINDENT:
+ return dat->groupIndent;
+
+ case CLM_GETISEARCHSTRING:
+ lstrcpy(( TCHAR* ) lParam, dat->szQuickSearch);
+ return lstrlen(dat->szQuickSearch);
+
+ case CLM_GETITEMTEXT:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return 0;
+ lstrcpy(( TCHAR* ) lParam, contact->szText);
+ return lstrlen(contact->szText);
+ }
+
+ case CLM_GETITEMTYPE:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return CLCIT_INVALID;
+ return contact->type;
+ }
+
+ case CLM_GETLEFTMARGIN:
+ return dat->leftMargin;
+
+ case CLM_GETNEXTITEM:
+ {
+ if (wParam == CLGN_ROOT) {
+ if (dat->list.cl.count)
+ return (LRESULT) cli.pfnContactToHItem(dat->list.cl.items[0]);
+ return NULL;
+ }
+
+ ClcContact *contact;
+ ClcGroup *group;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) lParam, &contact, &group, NULL))
+ return NULL;
+
+ int i = List_IndexOf((SortedList*)&group->cl,contact);
+ switch (wParam) {
+ case CLGN_CHILD:
+ if (contact->type != CLCIT_GROUP)
+ return (LRESULT) (HANDLE) NULL;
+ group = contact->group;
+ if (group->cl.count == 0)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[0]);
+
+ case CLGN_PARENT:
+ return group->groupId | HCONTACT_ISGROUP;
+
+ case CLGN_NEXT:
+ do {
+ if (++i >= group->cl.count)
+ return NULL;
+ }
+ while (group->cl.items[i]->type == CLCIT_DIVIDER);
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+
+ case CLGN_PREVIOUS:
+ do {
+ if (--i < 0)
+ return NULL;
+ }
+ while (group->cl.items[i]->type == CLCIT_DIVIDER);
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+
+ case CLGN_NEXTCONTACT:
+ for (i++; i < group->cl.count; i++)
+ if (group->cl.items[i]->type == CLCIT_CONTACT)
+ break;
+ if (i >= group->cl.count)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+
+ case CLGN_PREVIOUSCONTACT:
+ if (i >= group->cl.count)
+ return NULL;
+ for (i--; i >= 0; i--)
+ if (group->cl.items[i]->type == CLCIT_CONTACT)
+ break;
+ if (i < 0)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+
+ case CLGN_NEXTGROUP:
+ for (i++; i < group->cl.count; i++)
+ if (group->cl.items[i]->type == CLCIT_GROUP)
+ break;
+ if (i >= group->cl.count)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+
+ case CLGN_PREVIOUSGROUP:
+ if (i >= group->cl.count)
+ return NULL;
+ for (i--; i >= 0; i--)
+ if (group->cl.items[i]->type == CLCIT_GROUP)
+ break;
+ if (i < 0)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(group->cl.items[i]);
+ }
+ return NULL;
+ }
+
+ case CLM_GETSCROLLTIME:
+ return dat->scrollTime;
+
+ case CLM_GETSELECTION:
+ {
+ ClcContact *contact;
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) == -1)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(contact);
+ }
+
+ case CLM_GETTEXTCOLOR:
+ if (wParam < 0 || wParam > FONTID_MAX)
+ return 0;
+ return (LRESULT) dat->fontInfo[wParam].colour;
+
+ case CLM_HITTEST:
+ {
+ ClcContact *contact;
+ DWORD hitFlags;
+ int hit;
+ hit = cli.pfnHitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), &contact, NULL, &hitFlags);
+ if (wParam)
+ *(PDWORD) wParam = hitFlags;
+ if (hit == -1)
+ return NULL;
+ return (LRESULT) cli.pfnContactToHItem(contact);
+ }
+
+ case CLM_SELECTITEM:
+ {
+ ClcContact *contact;
+ ClcGroup *group, *tgroup;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, &group, NULL))
+ break;
+ for (tgroup = group; tgroup; tgroup = tgroup->parent)
+ cli.pfnSetGroupExpand(hwnd, dat, tgroup, 1);
+ dat->selection = cli.pfnGetRowsPriorTo(&dat->list, group, List_IndexOf((SortedList*)&group->cl,contact));
+ cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0);
+ break;
+ }
+
+ case CLM_SETBKBITMAP:
+ if (!dat->bkChanged && dat->hBmpBackground) {
+ DeleteObject(dat->hBmpBackground);
+ dat->hBmpBackground = NULL;
+ }
+ dat->hBmpBackground = (HBITMAP) lParam;
+ dat->backgroundBmpUse = wParam;
+ dat->bkChanged = 1;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETBKCOLOR:
+ if (!dat->bkChanged && dat->hBmpBackground) {
+ DeleteObject(dat->hBmpBackground);
+ dat->hBmpBackground = NULL;
+ }
+ dat->bkColour = wParam;
+ dat->bkChanged = 1;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETCHECKMARK:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return 0;
+ if (lParam)
+ contact->flags |= CONTACTF_CHECKED;
+ else
+ contact->flags &= ~CONTACTF_CHECKED;
+ cli.pfnRecalculateGroupCheckboxes(hwnd, dat);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+
+ case CLM_SETEXTRACOLUMNS:
+ if (wParam > MAXEXTRACOLUMNS)
+ return 0;
+ dat->extraColumnsCount = wParam;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETEXTRAIMAGE:
+ {
+ ClcContact *contact;
+ if (LOWORD(lParam) >= dat->extraColumnsCount)
+ return 0;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ return 0;
+ contact->iExtraImage[LOWORD(lParam)] = (BYTE) HIWORD(lParam);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+
+ case CLM_SETEXTRAIMAGELIST:
+ dat->himlExtraColumns = (HIMAGELIST) lParam;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETFONT:
+ if (HIWORD(lParam) < 0 || HIWORD(lParam) > FONTID_MAX)
+ return 0;
+ dat->fontInfo[HIWORD(lParam)].hFont = (HFONT) wParam;
+ dat->fontInfo[HIWORD(lParam)].changed = 1;
+ {
+ SIZE fontSize;
+ HDC hdc = GetDC(hwnd);
+ SelectObject(hdc, (HFONT) wParam);
+ GetTextExtentPoint32A(hdc, "x", 1, &fontSize);
+ dat->fontInfo[HIWORD(lParam)].fontHeight = fontSize.cy;
+ if (dat->rowHeight < fontSize.cy + 2)
+ dat->rowHeight = fontSize.cy + 2;
+ ReleaseDC(hwnd, hdc);
+ }
+ if (LOWORD(lParam))
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETGREYOUTFLAGS:
+ dat->greyoutFlags = wParam;
+ break;
+
+ case CLM_SETHIDEEMPTYGROUPS:
+ if (wParam)
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | CLS_HIDEEMPTYGROUPS);
+ else
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~CLS_HIDEEMPTYGROUPS);
+ SendMessage(hwnd, CLM_AUTOREBUILD, 0, 0);
+ break;
+
+ case CLM_SETHIDEOFFLINEROOT:
+ DBWriteContactSettingByte(NULL, "CLC", "HideOfflineRoot", (BYTE) wParam);
+ SendMessage(hwnd, CLM_AUTOREBUILD, 0, 0);
+ break;
+
+ case CLM_SETINDENT:
+ dat->groupIndent = wParam;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETITEMTEXT:
+ {
+ ClcContact *contact;
+ if (!cli.pfnFindItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL))
+ break;
+ lstrcpyn(contact->szText, ( TCHAR* )lParam, SIZEOF( contact->szText ));
+ cli.pfnSortCLC(hwnd, dat, 1);
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+
+ case CLM_SETLEFTMARGIN:
+ dat->leftMargin = wParam;
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ break;
+
+ case CLM_SETOFFLINEMODES:
+ dat->offlineModes = wParam;
+ SendMessage(hwnd, CLM_AUTOREBUILD, 0, 0);
+ break;
+
+ case CLM_SETSCROLLTIME:
+ dat->scrollTime = wParam;
+ break;
+
+ case CLM_SETTEXTCOLOR:
+ if (wParam < 0 || wParam > FONTID_MAX)
+ break;
+ dat->fontInfo[wParam].colour = lParam;
+ break;
+
+ case CLM_SETUSEGROUPS:
+ if (wParam)
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | CLS_USEGROUPS);
+ else
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~CLS_USEGROUPS);
+ SendMessage(hwnd, CLM_AUTOREBUILD, 0, 0);
+ break;
+ }
+ return 0;
+}
diff --git a/src/modules/clist/clcutils.cpp b/src/modules/clist/clcutils.cpp
new file mode 100644
index 0000000000..3205066e2f
--- /dev/null
+++ b/src/modules/clist/clcutils.cpp
@@ -0,0 +1,879 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+//loads of stuff that didn't really fit anywhere else
+
+extern HANDLE hHideInfoTipEvent;
+
+char* fnGetGroupCountsText(struct ClcData *dat, struct ClcContact *contact)
+{
+ static char szName[32];
+ int onlineCount, totalCount;
+ struct ClcGroup *group, *topgroup;
+
+ if (contact->type != CLCIT_GROUP || !(dat->exStyle & CLS_EX_SHOWGROUPCOUNTS))
+ return "";
+
+ group = topgroup = contact->group;
+ onlineCount = 0;
+ totalCount = group->totalMembers;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ if (group == topgroup)
+ break;
+ group = group->parent;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ totalCount += group->totalMembers;
+ continue;
+ }
+ else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT)
+ if (group->cl.items[group->scanIndex]->flags & CONTACTF_ONLINE)
+ onlineCount++;
+ group->scanIndex++;
+ }
+ if (onlineCount == 0 && dat->exStyle & CLS_EX_HIDECOUNTSWHENEMPTY)
+ return "";
+ mir_snprintf(szName, SIZEOF(szName), "(%u/%u)", onlineCount, totalCount);
+ return szName;
+}
+
+int fnHitTest(HWND hwnd, struct ClcData *dat, int testx, int testy, struct ClcContact **contact, struct ClcGroup **group, DWORD * flags)
+{
+ ClcContact *hitcontact = NULL;
+ ClcGroup *hitgroup = NULL;
+ int hit, indent, width, i;
+ int checkboxWidth;
+ SIZE textSize;
+ HDC hdc;
+ RECT clRect;
+ HFONT hFont;
+ DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ POINT pt;
+
+ if ( flags )
+ *flags = 0;
+
+ pt.x = testx;
+ pt.y = testy;
+ ClientToScreen(hwnd, &pt);
+
+ HWND hwndParent = hwnd, hwndTemp;
+ do
+ {
+ hwndTemp = hwndParent;
+ hwndParent = (HWND)GetWindowLongPtr(hwndTemp, GWLP_HWNDPARENT);
+
+ POINT pt1 = pt;
+ ScreenToClient(hwndParent, &pt1);
+ HWND h = ChildWindowFromPointEx(hwndParent ? hwndParent : GetDesktopWindow(),
+ pt1, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
+ if (h != hwndTemp)
+ {
+ if (!hwndParent || !(GetWindowLong(hwndTemp, GWL_STYLE) & BS_GROUPBOX))
+ return -1;
+ }
+ }
+ while (hwndParent);
+
+ GetClientRect(hwnd, &clRect);
+ if ( testx < 0 || testy < 0 || testy >= clRect.bottom || testx >= clRect.right ) {
+ if ( flags ) {
+ if (testx < 0)
+ *flags |= CLCHT_TOLEFT;
+ else if (testx >= clRect.right)
+ *flags |= CLCHT_TORIGHT;
+ if (testy < 0)
+ *flags |= CLCHT_ABOVE;
+ else if (testy >= clRect.bottom)
+ *flags |= CLCHT_BELOW;
+ }
+ return -1;
+ }
+ if (testx < dat->leftMargin) {
+ if (flags)
+ *flags |= CLCHT_INLEFTMARGIN | CLCHT_NOWHERE;
+ return -1;
+ }
+ hit = cli.pfnRowHitTest(dat, dat->yScroll + testy);
+ if ( hit != -1 )
+ hit = cli.pfnGetRowByIndex(dat, hit, &hitcontact, &hitgroup);
+ if (hit == -1) {
+ if (flags)
+ *flags |= CLCHT_NOWHERE | CLCHT_BELOWITEMS;
+ return -1;
+ }
+ if (contact)
+ *contact = hitcontact;
+ if (group)
+ *group = hitgroup;
+ for (indent = 0; hitgroup->parent; indent++, hitgroup = hitgroup->parent);
+ if (testx < dat->leftMargin + indent * dat->groupIndent) {
+ if (flags)
+ *flags |= CLCHT_ONITEMINDENT;
+ return hit;
+ }
+ checkboxWidth = 0;
+ if (style & CLS_CHECKBOXES && hitcontact->type == CLCIT_CONTACT)
+ checkboxWidth = dat->checkboxSize + 2;
+ if (style & CLS_GROUPCHECKBOXES && hitcontact->type == CLCIT_GROUP)
+ checkboxWidth = dat->checkboxSize + 2;
+ if (hitcontact->type == CLCIT_INFO && hitcontact->flags & CLCIIF_CHECKBOX)
+ checkboxWidth = dat->checkboxSize + 2;
+ if (testx < dat->leftMargin + indent * dat->groupIndent + checkboxWidth) {
+ if (flags)
+ *flags |= CLCHT_ONITEMCHECK;
+ return hit;
+ }
+ if (testx < dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace) {
+ if (flags)
+ *flags |= CLCHT_ONITEMICON;
+ return hit;
+ }
+
+ for (i = 0; i < dat->extraColumnsCount; i++) {
+ if (hitcontact->iExtraImage[i] == 0xFF)
+ continue;
+ if (testx >= clRect.right - dat->extraColumnSpacing * (dat->extraColumnsCount - i) &&
+ testx < clRect.right - dat->extraColumnSpacing * (dat->extraColumnsCount - i) + g_IconWidth ) {
+ if (flags)
+ *flags |= CLCHT_ONITEMEXTRA | (i << 24);
+ return hit;
+ }
+ }
+ hdc = GetDC(hwnd);
+ if (hitcontact->type == CLCIT_GROUP)
+ hFont = ( HFONT )SelectObject(hdc, dat->fontInfo[FONTID_GROUPS].hFont);
+ else
+ hFont = ( HFONT )SelectObject(hdc, dat->fontInfo[FONTID_CONTACTS].hFont);
+ GetTextExtentPoint32(hdc, hitcontact->szText, lstrlen(hitcontact->szText), &textSize);
+ width = textSize.cx;
+ if (hitcontact->type == CLCIT_GROUP) {
+ char *szCounts;
+ szCounts = cli.pfnGetGroupCountsText(dat, hitcontact);
+ if (szCounts[0]) {
+ GetTextExtentPoint32A(hdc, " ", 1, &textSize);
+ width += textSize.cx;
+ SelectObject(hdc, dat->fontInfo[FONTID_GROUPCOUNTS].hFont);
+ GetTextExtentPoint32A(hdc, szCounts, lstrlenA(szCounts), &textSize);
+ width += textSize.cx;
+ }
+ }
+ SelectObject(hdc, hFont);
+ ReleaseDC(hwnd, hdc);
+ if (testx < dat->leftMargin + indent * dat->groupIndent + checkboxWidth + dat->iconXSpace + width + 4) {
+ if (flags)
+ *flags |= CLCHT_ONITEMLABEL;
+ return hit;
+ }
+ if (flags)
+ *flags |= CLCHT_NOWHERE;
+ return -1;
+}
+
+void fnScrollTo(HWND hwnd, struct ClcData *dat, int desty, int noSmooth)
+{
+ DWORD startTick, nowTick;
+ int oldy = dat->yScroll;
+ RECT clRect, rcInvalidate;
+ int maxy, previousy;
+
+ if (dat->iHotTrack != -1 && dat->yScroll != desty) {
+ cli.pfnInvalidateItem(hwnd, dat, dat->iHotTrack);
+ dat->iHotTrack = -1;
+ ReleaseCapture();
+ }
+ GetClientRect(hwnd, &clRect);
+ rcInvalidate = clRect;
+ maxy = cli.pfnGetRowTotalHeight(dat) - clRect.bottom;
+ if (desty > maxy)
+ desty = maxy;
+ if (desty < 0)
+ desty = 0;
+ if (abs(desty - dat->yScroll) < 4)
+ noSmooth = 1;
+ if (!noSmooth && dat->exStyle & CLS_EX_NOSMOOTHSCROLLING)
+ noSmooth = 1;
+ previousy = dat->yScroll;
+ if (!noSmooth) {
+ startTick = GetTickCount();
+ for (;;) {
+ nowTick = GetTickCount();
+ if (nowTick >= startTick + dat->scrollTime)
+ break;
+ dat->yScroll = oldy + (desty - oldy) * (int) (nowTick - startTick) / dat->scrollTime;
+ if (dat->backgroundBmpUse & CLBF_SCROLL || dat->hBmpBackground == NULL)
+ ScrollWindowEx(hwnd, 0, previousy - dat->yScroll, NULL, NULL, NULL, NULL, SW_INVALIDATE);
+ else
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ previousy = dat->yScroll;
+ SetScrollPos(hwnd, SB_VERT, dat->yScroll, TRUE);
+ UpdateWindow(hwnd);
+ }
+ }
+ dat->yScroll = desty;
+ if (dat->backgroundBmpUse & CLBF_SCROLL || dat->hBmpBackground == NULL)
+ ScrollWindowEx(hwnd, 0, previousy - dat->yScroll, NULL, NULL, NULL, NULL, SW_INVALIDATE);
+ else
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ SetScrollPos(hwnd, SB_VERT, dat->yScroll, TRUE);
+}
+
+void fnEnsureVisible(HWND hwnd, struct ClcData *dat, int iItem, int partialOk)
+{
+ int itemy = cli.pfnGetRowTopY(dat, iItem), itemh = cli.pfnGetRowHeight(dat, iItem), newY = 0;
+ int moved = 0;
+ RECT clRect;
+
+ GetClientRect(hwnd, &clRect);
+ if (partialOk) {
+ if (itemy + itemh - 1 < dat->yScroll) {
+ newY = itemy;
+ moved = 1;
+ }
+ else if (itemy >= dat->yScroll + clRect.bottom) {
+ newY = itemy - clRect.bottom + itemh;
+ moved = 1;
+ }
+ }
+ else {
+ if (itemy < dat->yScroll) {
+ newY = itemy;
+ moved = 1;
+ }
+ else if (itemy >= dat->yScroll + clRect.bottom - itemh) {
+ newY = itemy - clRect.bottom + itemh;
+ moved = 1;
+ }
+ }
+ if (moved)
+ cli.pfnScrollTo(hwnd, dat, newY, 0);
+}
+
+void fnRecalcScrollBar(HWND hwnd, struct ClcData *dat)
+{
+ SCROLLINFO si = { 0 };
+ RECT clRect;
+ NMCLISTCONTROL nm;
+
+ GetClientRect(hwnd, &clRect);
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ si.nMin = 0;
+ si.nMax = cli.pfnGetRowTotalHeight(dat)-1;
+ si.nPage = clRect.bottom;
+ si.nPos = dat->yScroll;
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CONTACTLIST) {
+ if (dat->noVScrollbar == 0)
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ }
+ else SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+
+ cli.pfnScrollTo(hwnd, dat, dat->yScroll, 1);
+ nm.hdr.code = CLN_LISTSIZECHANGE;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.pt.y = si.nMax;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+}
+
+void fnSetGroupExpand(HWND hwnd, struct ClcData *dat, struct ClcGroup *group, int newState)
+{
+ int contentCount;
+ int groupy;
+ int newY, posY;
+ RECT clRect;
+ NMCLISTCONTROL nm;
+
+ if (newState == -1)
+ group->expanded ^= 1;
+ else {
+ if (group->expanded == (newState != 0))
+ return;
+ group->expanded = newState != 0;
+ }
+ cli.pfnInvalidateRect(hwnd, NULL, FALSE);
+ contentCount = cli.pfnGetGroupContentsCount(group, 1);
+ groupy = cli.pfnGetRowsPriorTo(&dat->list, group, -1);
+ if (dat->selection > groupy && dat->selection < groupy + contentCount)
+ dat->selection = groupy;
+ GetClientRect(hwnd, &clRect);
+ newY = dat->yScroll;
+ posY = cli.pfnGetRowBottomY(dat, groupy + contentCount);
+ if (posY >= newY + clRect.bottom)
+ newY = posY - clRect.bottom;
+ posY = cli.pfnGetRowTopY(dat, groupy);
+ if (newY > posY)
+ newY = posY;
+ cli.pfnRecalcScrollBar(hwnd, dat);
+ if (group->expanded)
+ cli.pfnScrollTo(hwnd, dat, newY, 0);
+ nm.hdr.code = CLN_EXPANDED;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.hItem = (HANDLE) group->groupId;
+ nm.action = group->expanded;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+}
+
+void fnDoSelectionDefaultAction(HWND hwnd, struct ClcData *dat)
+{
+ struct ClcContact *contact;
+
+ if (dat->selection == -1)
+ return;
+ dat->szQuickSearch[0] = 0;
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) == -1)
+ return;
+ if (contact->type == CLCIT_GROUP)
+ cli.pfnSetGroupExpand(hwnd, dat, contact->group, -1);
+ if (contact->type == CLCIT_CONTACT)
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM) contact->hContact, 0);
+}
+
+int fnFindRowByText(HWND hwnd, struct ClcData *dat, const TCHAR *text, int prefixOk)
+{
+ struct ClcGroup *group = &dat->list;
+ int testlen = lstrlen(text);
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.count) {
+ group = group->parent;
+ if (group == NULL)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+ if (group->cl.items[group->scanIndex]->type != CLCIT_DIVIDER) {
+ if ((prefixOk && !_tcsnicmp(text, group->cl.items[group->scanIndex]->szText, testlen)) ||
+ (!prefixOk && !lstrcmpi(text, group->cl.items[group->scanIndex]->szText))) {
+ struct ClcGroup *contactGroup = group;
+ int contactScanIndex = group->scanIndex;
+ for (; group; group = group->parent)
+ cli.pfnSetGroupExpand(hwnd, dat, group, 1);
+ return cli.pfnGetRowsPriorTo(&dat->list, contactGroup, contactScanIndex);
+ }
+ if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) {
+ if (!(dat->exStyle & CLS_EX_QUICKSEARCHVISONLY) || group->cl.items[group->scanIndex]->group->expanded) {
+ group = group->cl.items[group->scanIndex]->group;
+ group->scanIndex = 0;
+ continue;
+ }
+ }
+ }
+ group->scanIndex++;
+ }
+ return -1;
+}
+
+void fnEndRename(HWND, struct ClcData *dat, int save)
+{
+ HWND hwndEdit = dat->hwndRenameEdit;
+ if (hwndEdit == NULL)
+ return;
+
+ dat->hwndRenameEdit = NULL;
+ if (save) {
+ TCHAR text[120]; text[0] = 0;
+ GetWindowText(hwndEdit, text, SIZEOF(text));
+
+ ClcContact *contact;
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) != -1) {
+ if (lstrcmp(contact->szText, text) && !_tcsstr(text, _T("\\"))) {
+ if (contact->type == CLCIT_GROUP) {
+ if (contact->group->parent && contact->group->parent->parent) {
+ TCHAR szFullName[256];
+ mir_sntprintf(szFullName, SIZEOF(szFullName), _T("%s\\%s"),
+ cli.pfnGetGroupName(contact->group->parent->groupId, NULL), text);
+ cli.pfnRenameGroup(contact->groupId, szFullName);
+ }
+ else
+ cli.pfnRenameGroup(contact->groupId, text);
+ }
+ else if (contact->type == CLCIT_CONTACT) {
+ cli.pfnInvalidateDisplayNameCacheEntry(contact->hContact);
+ TCHAR* otherName = cli.pfnGetContactDisplayName(contact->hContact, GCDNF_NOMYHANDLE);
+ if (!text[0] || !lstrcmp(otherName, text))
+ DBDeleteContactSetting(contact->hContact, "CList", "MyHandle");
+ else
+ DBWriteContactSettingTString(contact->hContact, "CList", "MyHandle", text);
+ mir_free(otherName);
+ }
+ }
+ }
+ }
+ DestroyWindow(hwndEdit);
+}
+
+void fnDeleteFromContactList(HWND hwnd, struct ClcData *dat)
+{
+ struct ClcContact *contact;
+ if (dat->selection == -1)
+ return;
+ dat->szQuickSearch[0] = 0;
+ if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) == -1)
+ return;
+ switch (contact->type) {
+ case CLCIT_GROUP:
+ CallService(MS_CLIST_GROUPDELETE, (WPARAM)contact->groupId, 0);
+ break;
+ case CLCIT_CONTACT:
+ CallService("CList/DeleteContactCommand", (WPARAM)contact->hContact, (LPARAM)hwnd );
+ break;
+} }
+
+static WNDPROC OldRenameEditWndProc;
+static LRESULT CALLBACK RenameEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_RETURN:
+ cli.pfnEndRename(GetParent(hwnd), (struct ClcData *) GetWindowLongPtr(GetParent(hwnd), 0), 1);
+ return 0;
+ case VK_ESCAPE:
+ cli.pfnEndRename(GetParent(hwnd), (struct ClcData *) GetWindowLongPtr(GetParent(hwnd), 0), 0);
+ return 0;
+ }
+ break;
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *msg = (MSG *) lParam;
+ if (msg->message == WM_KEYDOWN && msg->wParam == VK_TAB)
+ return 0;
+ if (msg->message == WM_CHAR && msg->wParam == '\t')
+ return 0;
+ }
+ return DLGC_WANTMESSAGE;
+ case WM_KILLFOCUS:
+ cli.pfnEndRename(GetParent(hwnd), (struct ClcData *) GetWindowLongPtr(GetParent(hwnd), 0), 1);
+ return 0;
+ }
+ return CallWindowProc(OldRenameEditWndProc, hwnd, msg, wParam, lParam);
+}
+
+void fnBeginRenameSelection(HWND hwnd, struct ClcData *dat)
+{
+ struct ClcContact *contact;
+ struct ClcGroup *group;
+ RECT clRect;
+ POINT pt;
+ int h;
+
+ KillTimer(hwnd, TIMERID_RENAME);
+ ReleaseCapture();
+ dat->iHotTrack = -1;
+ dat->selection = cli.pfnGetRowByIndex(dat, dat->selection, &contact, &group);
+ if (dat->selection == -1)
+ return;
+ if (contact->type != CLCIT_CONTACT && contact->type != CLCIT_GROUP)
+ return;
+ GetClientRect(hwnd, &clRect);
+ cli.pfnCalcEipPosition( dat, contact, group, &pt );
+ h = cli.pfnGetRowHeight(dat, dat->selection);
+ dat->hwndRenameEdit = CreateWindow( _T("EDIT"), contact->szText, WS_CHILD | WS_BORDER | ES_AUTOHSCROLL, pt.x, pt.y, clRect.right - pt.x, h, hwnd, NULL, cli.hInst, NULL);
+ OldRenameEditWndProc = (WNDPROC) SetWindowLongPtr(dat->hwndRenameEdit, GWLP_WNDPROC, (LONG_PTR) RenameEditSubclassProc);
+ SendMessage(dat->hwndRenameEdit, WM_SETFONT, (WPARAM) (contact->type == CLCIT_GROUP ? dat->fontInfo[FONTID_GROUPS].hFont : dat->fontInfo[FONTID_CONTACTS].hFont), 0);
+ SendMessage(dat->hwndRenameEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN | EC_USEFONTINFO, 0);
+ SendMessage(dat->hwndRenameEdit, EM_SETSEL, 0, (LPARAM) (-1));
+ ShowWindow(dat->hwndRenameEdit, SW_SHOW);
+ SetFocus(dat->hwndRenameEdit);
+}
+
+void fnCalcEipPosition( struct ClcData *dat, struct ClcContact *, struct ClcGroup *group, POINT *result)
+{
+ int indent;
+ for (indent = 0; group->parent; indent++, group = group->parent);
+ result->x = indent * dat->groupIndent + dat->iconXSpace - 2;
+ result->y = cli.pfnGetRowTopY(dat, dat->selection) - dat->yScroll;
+}
+
+int fnGetDropTargetInformation(HWND hwnd, struct ClcData *dat, POINT pt)
+{
+ RECT clRect;
+ int hit;
+ struct ClcContact *contact, *movecontact;
+ struct ClcGroup *group, *movegroup;
+ DWORD hitFlags;
+
+ GetClientRect(hwnd, &clRect);
+ dat->selection = dat->iDragItem;
+ dat->iInsertionMark = -1;
+ if (!PtInRect(&clRect, pt))
+ return DROPTARGET_OUTSIDE;
+
+ hit = cli.pfnHitTest(hwnd, dat, pt.x, pt.y, &contact, &group, &hitFlags);
+ cli.pfnGetRowByIndex(dat, dat->iDragItem, &movecontact, &movegroup);
+ if (hit == dat->iDragItem)
+ return DROPTARGET_ONSELF;
+ if (hit == -1 || hitFlags & CLCHT_ONITEMEXTRA)
+ return DROPTARGET_ONNOTHING;
+
+ if (movecontact->type == CLCIT_GROUP) {
+ struct ClcContact *bottomcontact = NULL, *topcontact = NULL;
+ struct ClcGroup *topgroup = NULL;
+ int topItem = -1, bottomItem = -1;
+ int ok = 0;
+ if (pt.y + dat->yScroll < cli.pfnGetRowTopY(dat, hit) + dat->insertionMarkHitHeight) {
+ //could be insertion mark (above)
+ topItem = hit - 1;
+ bottomItem = hit;
+ bottomcontact = contact;
+ topItem = cli.pfnGetRowByIndex(dat, topItem, &topcontact, &topgroup);
+ ok = 1;
+ }
+ if (pt.y + dat->yScroll >= cli.pfnGetRowBottomY(dat, hit+1) - dat->insertionMarkHitHeight) {
+ //could be insertion mark (below)
+ topItem = hit;
+ bottomItem = hit + 1;
+ topcontact = contact;
+ topgroup = group;
+ bottomItem = cli.pfnGetRowByIndex(dat, bottomItem, &bottomcontact, NULL);
+ ok = 1;
+ }
+ if (ok) {
+ ok = 0;
+ if (bottomItem == -1 || bottomcontact->type != CLCIT_GROUP) { //need to special-case moving to end
+ if (topItem != dat->iDragItem) {
+ for (; topgroup; topgroup = topgroup->parent) {
+ if (topgroup == movecontact->group)
+ break;
+ if (topgroup == movecontact->group->parent) {
+ ok = 1;
+ break;
+ }
+ }
+ if (ok)
+ bottomItem = topItem + 1;
+ }
+ }
+ else if (bottomItem != dat->iDragItem && bottomcontact->type == CLCIT_GROUP && bottomcontact->group->parent == movecontact->group->parent) {
+ if (bottomcontact != movecontact + 1)
+ ok = 1;
+ }
+ if (ok) {
+ dat->iInsertionMark = bottomItem;
+ dat->selection = -1;
+ return DROPTARGET_INSERTION;
+ }
+ }
+ }
+ if (contact->type == CLCIT_GROUP) {
+ if (dat->iInsertionMark == -1) {
+ if (movecontact->type == CLCIT_GROUP) { //check not moving onto its own subgroup
+ for (; group; group = group->parent)
+ if (group == movecontact->group)
+ return DROPTARGET_ONSELF;
+ }
+ dat->selection = hit;
+ return DROPTARGET_ONGROUP;
+ }
+ }
+ return DROPTARGET_ONCONTACT;
+}
+
+int fnClcStatusToPf2(int status)
+{
+ switch(status) {
+ case ID_STATUS_ONLINE: return PF2_ONLINE;
+ case ID_STATUS_AWAY: return PF2_SHORTAWAY;
+ case ID_STATUS_DND: return PF2_HEAVYDND;
+ case ID_STATUS_NA: return PF2_LONGAWAY;
+ case ID_STATUS_OCCUPIED: return PF2_LIGHTDND;
+ case ID_STATUS_FREECHAT: return PF2_FREECHAT;
+ case ID_STATUS_INVISIBLE: return PF2_INVISIBLE;
+ case ID_STATUS_ONTHEPHONE: return PF2_ONTHEPHONE;
+ case ID_STATUS_OUTTOLUNCH: return PF2_OUTTOLUNCH;
+ case ID_STATUS_OFFLINE: return MODEF_OFFLINE;
+ }
+ return 0;
+}
+
+int fnIsHiddenMode(struct ClcData *dat, int status)
+{
+ return dat->offlineModes & cli.pfnClcStatusToPf2(status);
+}
+
+void fnHideInfoTip(HWND, struct ClcData *dat)
+{
+ CLCINFOTIP it = { 0 };
+
+ if (dat->hInfoTipItem == NULL)
+ return;
+ it.isGroup = IsHContactGroup(dat->hInfoTipItem);
+ it.hItem = (HANDLE) ((UINT_PTR) dat->hInfoTipItem & ~HCONTACT_ISGROUP);
+ it.cbSize = sizeof(it);
+ dat->hInfoTipItem = NULL;
+ NotifyEventHooks(hHideInfoTipEvent, 0, (LPARAM) & it);
+}
+
+void fnNotifyNewContact(HWND hwnd, HANDLE hContact)
+{
+ NMCLISTCONTROL nm;
+ nm.hdr.code = CLN_NEWCONTACT;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ nm.flags = 0;
+ nm.hItem = hContact;
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm);
+}
+
+DWORD fnGetDefaultExStyle(void)
+{
+ BOOL param;
+ DWORD ret = CLCDEFAULT_EXSTYLE;
+ if (SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &param, FALSE) && !param)
+ ret |= CLS_EX_NOSMOOTHSCROLLING;
+ if (SystemParametersInfo(SPI_GETHOTTRACKING, 0, &param, FALSE) && !param)
+ ret &= ~CLS_EX_TRACKSELECT;
+ return ret;
+}
+
+#define DBFONTF_BOLD 1
+#define DBFONTF_ITALIC 2
+#define DBFONTF_UNDERLINE 4
+
+void fnGetDefaultFontSetting(int i, LOGFONT* lf, COLORREF* colour)
+{
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), lf, FALSE);
+ *colour = GetSysColor(COLOR_WINDOWTEXT);
+ lf->lfHeight = 8;
+ switch (i) {
+ case FONTID_GROUPS:
+ lf->lfWeight = FW_BOLD;
+ break;
+ case FONTID_GROUPCOUNTS:
+ *colour = GetSysColor(COLOR_3DSHADOW);
+ break;
+ case FONTID_OFFINVIS:
+ case FONTID_INVIS:
+ lf->lfItalic = !lf->lfItalic;
+ break;
+ case FONTID_DIVIDERS:
+ break;
+ case FONTID_NOTONLIST:
+ *colour = GetSysColor(COLOR_3DSHADOW);
+ break;
+} }
+
+void fnGetFontSetting(int i, LOGFONT* lf, COLORREF* colour)
+{
+ DBVARIANT dbv;
+ char idstr[20];
+ BYTE style;
+
+ cli.pfnGetDefaultFontSetting(i, lf, colour);
+ mir_snprintf(idstr, SIZEOF(idstr), "Font%dName", i);
+ if ( !DBGetContactSettingTString(NULL, "CLC", idstr, &dbv )) {
+ lstrcpy(lf->lfFaceName, dbv.ptszVal);
+ mir_free(dbv.pszVal);
+ }
+ mir_snprintf(idstr, SIZEOF(idstr), "Font%dCol", i);
+ *colour = DBGetContactSettingDword(NULL, "CLC", idstr, *colour);
+ mir_snprintf(idstr, SIZEOF(idstr), "Font%dSize", i);
+ lf->lfHeight = (char) DBGetContactSettingByte(NULL, "CLC", idstr, lf->lfHeight);
+ mir_snprintf(idstr, SIZEOF(idstr), "Font%dSty", i);
+ style = (BYTE) DBGetContactSettingByte(NULL, "CLC", idstr, (lf->lfWeight == FW_NORMAL ? 0 : DBFONTF_BOLD) | (lf->lfItalic ? DBFONTF_ITALIC : 0) | (lf->lfUnderline ? DBFONTF_UNDERLINE : 0));
+ lf->lfWidth = lf->lfEscapement = lf->lfOrientation = 0;
+ lf->lfWeight = style & DBFONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = (style & DBFONTF_ITALIC) != 0;
+ lf->lfUnderline = (style & DBFONTF_UNDERLINE) != 0;
+ lf->lfStrikeOut = 0;
+ mir_snprintf(idstr, SIZEOF(idstr), "Font%dSet", i);
+ lf->lfCharSet = DBGetContactSettingByte(NULL, "CLC", idstr, lf->lfCharSet);
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+}
+
+void fnLoadClcOptions(HWND hwnd, struct ClcData *dat)
+{
+ dat->rowHeight = DBGetContactSettingByte(NULL, "CLC", "RowHeight", CLCDEFAULT_ROWHEIGHT);
+ {
+ int i;
+ LOGFONT lf;
+ SIZE fontSize;
+
+ HDC hdc = GetDC(hwnd);
+ for (i = 0; i <= FONTID_MAX; i++)
+ {
+ if (!dat->fontInfo[i].changed)
+ DeleteObject(dat->fontInfo[i].hFont);
+
+ cli.pfnGetFontSetting(i, &lf, &dat->fontInfo[i].colour);
+ lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+
+ dat->fontInfo[i].hFont = CreateFontIndirect(&lf);
+ dat->fontInfo[i].changed = 0;
+
+ HFONT holdfont = (HFONT)SelectObject(hdc,dat->fontInfo[i].hFont);
+ GetTextExtentPoint32(hdc, _T("x"), 1, &fontSize);
+ SelectObject(hdc, holdfont);
+
+ dat->fontInfo[i].fontHeight = fontSize.cy;
+ }
+ ReleaseDC(hwnd, hdc);
+ }
+ dat->leftMargin = DBGetContactSettingByte(NULL, "CLC", "LeftMargin", CLCDEFAULT_LEFTMARGIN);
+ dat->exStyle = DBGetContactSettingDword(NULL, "CLC", "ExStyle", cli.pfnGetDefaultExStyle());
+ dat->scrollTime = DBGetContactSettingWord(NULL, "CLC", "ScrollTime", CLCDEFAULT_SCROLLTIME);
+ dat->groupIndent = DBGetContactSettingByte(NULL, "CLC", "GroupIndent", CLCDEFAULT_GROUPINDENT);
+ dat->gammaCorrection = DBGetContactSettingByte(NULL, "CLC", "GammaCorrect", CLCDEFAULT_GAMMACORRECT);
+ dat->showIdle = DBGetContactSettingByte(NULL, "CLC", "ShowIdle", CLCDEFAULT_SHOWIDLE);
+ dat->noVScrollbar = DBGetContactSettingByte(NULL, "CLC", "NoVScrollBar", 0);
+ SendMessage(hwnd, INTM_SCROLLBARCHANGED, 0, 0);
+ if (!dat->bkChanged) {
+ DBVARIANT dbv;
+ dat->bkColour = DBGetContactSettingDword(NULL, "CLC", "BkColour", CLCDEFAULT_BKCOLOUR);
+ if (dat->hBmpBackground) {
+ DeleteObject(dat->hBmpBackground);
+ dat->hBmpBackground = NULL;
+ }
+ if (DBGetContactSettingByte(NULL, "CLC", "UseBitmap", CLCDEFAULT_USEBITMAP)) {
+ if (!DBGetContactSettingString(NULL, "CLC", "BkBitmap", &dbv)) {
+ dat->hBmpBackground = (HBITMAP) CallService(MS_UTILS_LOADBITMAP, 0, (LPARAM) dbv.pszVal);
+ mir_free(dbv.pszVal);
+ }
+ }
+ dat->backgroundBmpUse = DBGetContactSettingWord(NULL, "CLC", "BkBmpUse", CLCDEFAULT_BKBMPUSE);
+ }
+ dat->greyoutFlags = DBGetContactSettingDword(NULL, "CLC", "GreyoutFlags", CLCDEFAULT_GREYOUTFLAGS);
+ dat->offlineModes = DBGetContactSettingDword(NULL, "CLC", "OfflineModes", CLCDEFAULT_OFFLINEMODES);
+ dat->selBkColour = DBGetContactSettingDword(NULL, "CLC", "SelBkColour", CLCDEFAULT_SELBKCOLOUR);
+ dat->selTextColour = DBGetContactSettingDword(NULL, "CLC", "SelTextColour", CLCDEFAULT_SELTEXTCOLOUR);
+ dat->hotTextColour = DBGetContactSettingDword(NULL, "CLC", "HotTextColour", CLCDEFAULT_HOTTEXTCOLOUR);
+ dat->quickSearchColour = DBGetContactSettingDword(NULL, "CLC", "QuickSearchColour", CLCDEFAULT_QUICKSEARCHCOLOUR);
+ dat->useWindowsColours = DBGetContactSettingByte(NULL, "CLC", "UseWinColours", CLCDEFAULT_USEWINDOWSCOLOURS);
+ {
+ NMHDR hdr;
+ hdr.code = CLN_OPTIONSCHANGED;
+ hdr.hwndFrom = hwnd;
+ hdr.idFrom = GetDlgCtrlID(hwnd);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & hdr);
+ }
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+}
+
+#define GSIF_HASMEMBERS 0x80000000
+#define GSIF_ALLCHECKED 0x40000000
+#define GSIF_INDEXMASK 0x3FFFFFFF
+void fnRecalculateGroupCheckboxes(HWND, struct ClcData *dat)
+{
+ struct ClcGroup *group;
+ int check;
+
+ group = &dat->list;
+ group->scanIndex = GSIF_ALLCHECKED;
+ for (;;) {
+ if ((group->scanIndex & GSIF_INDEXMASK) == group->cl.count) {
+ check = (group->scanIndex & (GSIF_HASMEMBERS | GSIF_ALLCHECKED)) == (GSIF_HASMEMBERS | GSIF_ALLCHECKED);
+ if (group->parent == NULL)
+ break;
+ group->parent->scanIndex |= group->scanIndex & GSIF_HASMEMBERS;
+ group = group->parent;
+ if (check)
+ group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->flags |= CONTACTF_CHECKED;
+ else {
+ group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->flags &= ~CONTACTF_CHECKED;
+ group->scanIndex &= ~GSIF_ALLCHECKED;
+ }
+ }
+ else if (group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->type == CLCIT_GROUP) {
+ group = group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->group;
+ group->scanIndex = GSIF_ALLCHECKED;
+ continue;
+ }
+ else if (group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->type == CLCIT_CONTACT) {
+ group->scanIndex |= GSIF_HASMEMBERS;
+ if (!(group->cl.items[(group->scanIndex & GSIF_INDEXMASK)]->flags & CONTACTF_CHECKED))
+ group->scanIndex &= ~GSIF_ALLCHECKED;
+ }
+ group->scanIndex++;
+ }
+}
+
+void fnSetGroupChildCheckboxes(struct ClcGroup *group, int checked)
+{
+ int i;
+
+ for (i = 0; i < group->cl.count; i++) {
+ if (group->cl.items[i]->type == CLCIT_GROUP) {
+ cli.pfnSetGroupChildCheckboxes(group->cl.items[i]->group, checked);
+ if (checked)
+ group->cl.items[i]->flags |= CONTACTF_CHECKED;
+ else
+ group->cl.items[i]->flags &= ~CONTACTF_CHECKED;
+ }
+ else if (group->cl.items[i]->type == CLCIT_CONTACT) {
+ if (checked)
+ group->cl.items[i]->flags |= CONTACTF_CHECKED;
+ else
+ group->cl.items[i]->flags &= ~CONTACTF_CHECKED;
+ }
+ }
+}
+
+void fnInvalidateItem(HWND hwnd, struct ClcData *dat, int iItem)
+{
+ RECT rc;
+ if ( iItem == -1 )
+ return;
+
+ GetClientRect(hwnd, &rc);
+ rc.top = cli.pfnGetRowTopY(dat, iItem) - dat->yScroll;
+ rc.bottom = rc.top + cli.pfnGetRowHeight(dat, iItem);
+ cli.pfnInvalidateRect(hwnd, &rc, FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// row coord functions
+
+int fnGetRowTopY(struct ClcData *dat, int item)
+{ return item * dat->rowHeight;
+}
+
+int fnGetRowBottomY(struct ClcData *dat, int item)
+{ return (item+1) * dat->rowHeight;
+}
+
+int fnGetRowTotalHeight(struct ClcData *dat)
+{ return dat->rowHeight * cli.pfnGetGroupContentsCount(&dat->list, 1);
+}
+
+int fnGetRowHeight(struct ClcData *dat, int)
+{ return dat->rowHeight;
+}
+
+int fnRowHitTest(struct ClcData *dat, int y)
+{ if (!dat->rowHeight)
+ return y;
+ return y / dat->rowHeight;
+}
diff --git a/src/modules/clist/clistcore.cpp b/src/modules/clist/clistcore.cpp
new file mode 100644
index 0000000000..2fe4a288e9
--- /dev/null
+++ b/src/modules/clist/clistcore.cpp
@@ -0,0 +1,224 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "clc.h"
+#include "genmenu.h"
+
+CLIST_INTERFACE cli = { 0 };
+
+static TCHAR szTip[MAX_TIP_SIZE+1];
+
+int LoadContactListModule2( void );
+int LoadCLCModule( void );
+void BuildProtoMenus( void );
+
+static int interfaceInited = 0;
+
+static void fnPaintClc( HWND, ClcData*, HDC, RECT* )
+{
+}
+
+static struct ClcContact* fnCreateClcContact( void )
+{
+ return ( struct ClcContact* )mir_calloc( sizeof( struct ClcContact ));
+}
+
+static BOOL fnInvalidateRect( HWND hwnd, CONST RECT* lpRect,BOOL bErase )
+{
+ return InvalidateRect( hwnd, lpRect, bErase );
+}
+
+static void fnOnCreateClc( void )
+{
+}
+
+static void fnReloadProtoMenus( void )
+{
+ RebuildMenuOrder();
+ if (DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", FALSE))
+ BuildProtoMenus();
+ cli.pfnCluiProtocolStatusChanged(0,0);
+}
+
+static INT_PTR srvRetrieveInterface( WPARAM, LPARAM lParam )
+{
+ int rc;
+
+ if ( interfaceInited == 0 ) {
+ cli.version = 6;
+ cli.bDisplayLocked = TRUE;
+
+ cli.pfnClcOptionsChanged = fnClcOptionsChanged;
+ cli.pfnClcBroadcast = fnClcBroadcast;
+ cli.pfnContactListControlWndProc = fnContactListControlWndProc;
+ cli.pfnBuildGroupPopupMenu = fnBuildGroupPopupMenu;
+
+ cli.pfnRegisterFileDropping = fnRegisterFileDropping;
+ cli.pfnUnregisterFileDropping = fnUnregisterFileDropping;
+
+ cli.pfnGetRowsPriorTo = fnGetRowsPriorTo;
+ cli.pfnFindItem = fnFindItem;
+ cli.pfnGetRowByIndex = fnGetRowByIndex;
+ cli.pfnContactToHItem = fnContactToHItem;
+ cli.pfnContactToItemHandle = fnContactToItemHandle;
+
+ cli.pfnAddGroup = fnAddGroup;
+ cli.pfnAddItemToGroup = fnAddItemToGroup;
+ cli.pfnCreateClcContact = fnCreateClcContact;
+ cli.pfnRemoveItemFromGroup = fnRemoveItemFromGroup;
+ cli.pfnFreeContact = fnFreeContact;
+ cli.pfnFreeGroup = fnFreeGroup;
+ cli.pfnAddInfoItemToGroup = fnAddInfoItemToGroup;
+ cli.pfnAddContactToGroup = fnAddContactToGroup;
+ cli.pfnAddContactToTree = fnAddContactToTree;
+ cli.pfnDeleteItemFromTree = fnDeleteItemFromTree;
+ cli.pfnRebuildEntireList = fnRebuildEntireList;
+ cli.pfnGetGroupContentsCount = fnGetGroupContentsCount;
+ cli.pfnSortCLC = fnSortCLC;
+ cli.pfnSaveStateAndRebuildList = fnSaveStateAndRebuildList;
+
+ cli.pfnProcessExternalMessages = fnProcessExternalMessages;
+
+ cli.pfnPaintClc = fnPaintClc;
+
+ cli.pfnGetGroupCountsText = fnGetGroupCountsText;
+ cli.pfnHitTest = fnHitTest;
+ cli.pfnScrollTo = fnScrollTo;
+ cli.pfnEnsureVisible = fnEnsureVisible;
+ cli.pfnRecalcScrollBar = fnRecalcScrollBar;
+ cli.pfnSetGroupExpand = fnSetGroupExpand;
+ cli.pfnDoSelectionDefaultAction = fnDoSelectionDefaultAction;
+ cli.pfnFindRowByText = fnFindRowByText;
+ cli.pfnEndRename = fnEndRename;
+ cli.pfnDeleteFromContactList = fnDeleteFromContactList;
+ cli.pfnBeginRenameSelection = fnBeginRenameSelection;
+ cli.pfnCalcEipPosition = fnCalcEipPosition;
+ cli.pfnGetDropTargetInformation = fnGetDropTargetInformation;
+ cli.pfnClcStatusToPf2 = fnClcStatusToPf2;
+ cli.pfnIsHiddenMode = fnIsHiddenMode;
+ cli.pfnHideInfoTip = fnHideInfoTip;
+ cli.pfnNotifyNewContact = fnNotifyNewContact;
+ cli.pfnGetDefaultExStyle = fnGetDefaultExStyle;
+ cli.pfnGetDefaultFontSetting = fnGetDefaultFontSetting;
+ cli.pfnGetFontSetting = fnGetFontSetting;
+ cli.pfnLoadClcOptions = fnLoadClcOptions;
+ cli.pfnRecalculateGroupCheckboxes = fnRecalculateGroupCheckboxes;
+ cli.pfnSetGroupChildCheckboxes = fnSetGroupChildCheckboxes;
+ cli.pfnInvalidateItem = fnInvalidateItem;
+ cli.pfnGetRowBottomY = fnGetRowBottomY;
+ cli.pfnGetRowHeight = fnGetRowHeight;
+ cli.pfnGetRowTopY = fnGetRowTopY;
+ cli.pfnGetRowTotalHeight = fnGetRowTotalHeight;
+ cli.pfnRowHitTest = fnRowHitTest;
+
+ cli.pfnAddEvent = fnAddEvent;
+ cli.pfnCreateEvent = fnCreateEvent;
+ cli.pfnEventsProcessContactDoubleClick = fnEventsProcessContactDoubleClick;
+ cli.pfnEventsProcessTrayDoubleClick = fnEventsProcessTrayDoubleClick;
+ cli.pfnFreeEvent = fnFreeEvent;
+ cli.pfnGetEvent = fnGetEvent;
+ cli.pfnGetImlIconIndex = fnGetImlIconIndex;
+ cli.pfnRemoveEvent = fnRemoveEvent;
+
+ cli.pfnGetContactDisplayName = fnGetContactDisplayName;
+ cli.pfnInvalidateDisplayNameCacheEntry = fnInvalidateDisplayNameCacheEntry;
+ cli.pfnCreateCacheItem = fnCreateCacheItem;
+ cli.pfnCheckCacheItem = fnCheckCacheItem;
+ cli.pfnFreeCacheItem = fnFreeCacheItem;
+ cli.pfnGetCacheEntry = fnGetCacheEntry;
+
+ cli.szTip = szTip;
+ cli.pfnInitTray = fnInitTray;
+ cli.pfnUninitTray = fnUninitTray;
+ cli.pfnLockTray = fnLockTray;
+ cli.pfnUnlockTray = fnUnlockTray;
+
+ cli.pfnTrayCycleTimerProc = fnTrayCycleTimerProc;
+ cli.pfnTrayIconAdd = fnTrayIconAdd;
+ cli.pfnTrayIconDestroy = fnTrayIconDestroy;
+ cli.pfnTrayIconIconsChanged = fnTrayIconIconsChanged;
+ cli.pfnTrayIconInit = fnTrayIconInit;
+ cli.pfnTrayIconMakeTooltip = fnTrayIconMakeTooltip;
+ cli.pfnTrayIconPauseAutoHide = fnTrayIconPauseAutoHide;
+ cli.pfnTrayIconProcessMessage = fnTrayIconProcessMessage;
+ cli.pfnTrayIconRemove = fnTrayIconRemove;
+ cli.pfnTrayIconSetBaseInfo = fnTrayIconSetBaseInfo;
+ cli.pfnTrayIconSetToBase = fnTrayIconSetToBase;
+ cli.pfnTrayIconTaskbarCreated = fnTrayIconTaskbarCreated;
+ cli.pfnTrayIconUpdate = fnTrayIconUpdate;
+ cli.pfnTrayIconUpdateBase = fnTrayIconUpdateBase;
+ cli.pfnTrayIconUpdateWithImageList = fnTrayIconUpdateWithImageList;
+ cli.pfnCListTrayNotify = fnCListTrayNotify;
+
+ cli.pfnContactListWndProc = fnContactListWndProc;
+ cli.pfnLoadCluiGlobalOpts = fnLoadCluiGlobalOpts;
+ cli.pfnCluiProtocolStatusChanged = fnCluiProtocolStatusChanged;
+ cli.pfnDrawMenuItem = fnDrawMenuItem;
+ cli.pfnInvalidateRect = fnInvalidateRect;
+ cli.pfnOnCreateClc = fnOnCreateClc;
+
+ cli.pfnChangeContactIcon = fnChangeContactIcon;
+ cli.pfnLoadContactTree = fnLoadContactTree;
+ cli.pfnCompareContacts = fnCompareContacts;
+ cli.pfnSortContacts = fnSortContacts;
+ cli.pfnSetHideOffline = fnSetHideOffline;
+
+ cli.pfnDocking_ProcessWindowMessage = fnDocking_ProcessWindowMessage;
+
+ cli.pfnGetIconFromStatusMode = fnGetIconFromStatusMode;
+ cli.pfnGetWindowVisibleState = fnGetWindowVisibleState;
+ cli.pfnIconFromStatusMode = fnIconFromStatusMode;
+ cli.pfnShowHide = fnShowHide;
+ cli.pfnGetStatusModeDescription = fnGetStatusModeDescription;
+
+ cli.pfnGetGroupName = fnGetGroupName;
+ cli.pfnRenameGroup = fnRenameGroup;
+
+ cli.pfnHotKeysRegister = fnHotKeysRegister;
+ cli.pfnHotKeysUnregister = fnHotKeysUnregister;
+ cli.pfnHotKeysProcess = fnHotKeysProcess;
+ cli.pfnHotkeysProcessMessage = fnHotkeysProcessMessage;
+
+ cli.pfnGetProtocolVisibility = fnGetProtocolVisibility;
+ cli.pfnGetProtoIndexByPos = fnGetProtoIndexByPos;
+ cli.pfnReloadProtoMenus = fnReloadProtoMenus;
+ cli.pfnGetAccountIndexByPos = fnGetAccountIndexByPos;
+ cli.pfnGetProtocolMenu = fnGetProtocolMenu;
+
+ cli.hInst = ( HMODULE )lParam;
+
+ rc = LoadContactListModule2();
+ if (rc == 0)
+ rc = LoadCLCModule();
+ interfaceInited = 1;
+ }
+
+ return ( LPARAM )&cli;
+}
+
+int LoadContactListModule()
+{
+ CreateServiceFunction( MS_CLIST_RETRIEVE_INTERFACE, srvRetrieveInterface );
+ return 0;
+}
diff --git a/src/modules/clist/clistevents.cpp b/src/modules/clist/clistevents.cpp
new file mode 100644
index 0000000000..20f5995c4e
--- /dev/null
+++ b/src/modules/clist/clistevents.cpp
@@ -0,0 +1,441 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+struct CListEvent
+{
+ int imlIconIndex;
+ int flashesDone;
+ CLISTEVENT cle;
+};
+
+struct CListImlIcon
+{
+ int index;
+ HICON hIcon;
+};
+static struct CListImlIcon *imlIcon;
+static int imlIconCount;
+
+extern HIMAGELIST hCListImages;
+
+static UINT_PTR flashTimerId;
+static int iconsOn;
+static int disableTrayFlash;
+static int disableIconFlash;
+
+int fnGetImlIconIndex(HICON hIcon)
+{
+ int i;
+
+ for (i = 0; i < imlIconCount; i++) {
+ if (imlIcon[i].hIcon == hIcon)
+ return imlIcon[i].index;
+ }
+ imlIcon = (struct CListImlIcon *) mir_realloc(imlIcon, sizeof(struct CListImlIcon) * (imlIconCount + 1));
+ imlIconCount++;
+ imlIcon[i].hIcon = hIcon;
+ imlIcon[i].index = ImageList_AddIcon(hCListImages, hIcon);
+ return imlIcon[i].index;
+}
+
+static char * GetEventProtocol(int idx)
+{
+ if (cli.events.count && idx>=0 && idx<cli.events.count)
+ {
+ char *szProto;
+ if (cli.events.items[idx]->cle.hContact == NULL)
+ {
+ if (cli.events.items[idx]->cle.flags&CLEF_PROTOCOLGLOBAL)
+ szProto = cli.events.items[idx]->cle.lpszProtocol;
+ else
+ szProto = NULL;
+ }
+ else
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) cli.events.items[idx]->cle.hContact, 0);
+ return szProto;
+ }
+ return NULL;
+}
+
+static void ShowOneEventInTray(int idx)
+{
+ cli.pfnTrayIconUpdateWithImageList((iconsOn || disableTrayFlash) ? cli.events.items[idx]->imlIconIndex : 0, cli.events.items[idx]->cle.ptszTooltip, GetEventProtocol(idx));
+}
+
+static void ShowEventsInTray()
+{
+ int i;
+ char ** pTrayProtos;
+ char nTrayProtoCnt;
+ int nTrayCnt=cli.trayIconCount;
+ if (!cli.events.count || !nTrayCnt) return;
+ if (cli.events.count ==1 || nTrayCnt == 1)
+ {
+ ShowOneEventInTray(0); //for only one icon in tray show topmost event
+ return;
+ }
+
+ // in case if we have several icons in tray and several events with different protocols
+ // lets use several icon to show events from protocols in different icons
+ cli.pfnLockTray();
+ pTrayProtos = (char**)_alloca(sizeof(char*)*cli.trayIconCount);
+ nTrayProtoCnt=0;
+ for (i=0; i<cli.trayIconCount; i++)
+ {
+ if (cli.trayIcon[i].id == 0 || !cli.trayIcon[i].szProto) continue;
+ pTrayProtos[nTrayProtoCnt++]=cli.trayIcon[i].szProto;
+ }
+ for (i=0; i<cli.events.count; i++)
+ {
+ char * iEventProto=GetEventProtocol(i);
+ {
+ int j;
+ for (j=0; j<nTrayProtoCnt; j++)
+ if ( iEventProto && pTrayProtos[j] && !lstrcmpA(pTrayProtos[j],iEventProto))
+ break;
+ if ( j>=nTrayProtoCnt ) j=0; //event was not found so assume first icon
+ if ( pTrayProtos[j] ) //if not already set
+ ShowOneEventInTray(i); //show it
+ pTrayProtos[j]=NULL; //and clear slot
+ }
+ }
+ cli.pfnUnlockTray();
+}
+
+static VOID CALLBACK IconFlashTimer(HWND, UINT, UINT_PTR idEvent, DWORD)
+{
+ int i, j;
+ ShowEventsInTray();
+ for (i = 0; i < cli.events.count; i++) {
+ for (j = 0; j < i; j++)
+ if (cli.events.items[j]->cle.hContact == cli.events.items[i]->cle.hContact)
+ break;
+ if (j >= i)
+ cli.pfnChangeContactIcon(cli.events.items[i]->cle.hContact, iconsOn || disableIconFlash ? cli.events.items[i]->imlIconIndex : 0, 0);
+ //decrease eflashes in any case - no need to collect all events
+ if (cli.events.items[i]->cle.flags & CLEF_ONLYAFEW) {
+ if (0 >= --cli.events.items[i]->flashesDone)
+ cli.pfnRemoveEvent( cli.events.items[i]->cle.hContact, cli.events.items[i]->cle.hDbEvent);
+ } }
+
+ if (cli.events.count == 0) {
+ KillTimer(NULL, idEvent);
+ cli.pfnTrayIconSetToBase( NULL );
+ }
+
+ iconsOn = !iconsOn;
+}
+
+struct CListEvent* fnAddEvent( CLISTEVENT *cle )
+{
+ int i;
+ struct CListEvent* p;
+
+ if (cle==NULL || cle->cbSize != sizeof(CLISTEVENT))
+ return NULL;
+
+ if (cle->flags & CLEF_URGENT) {
+ for (i = 0; i < cli.events.count; i++)
+ if (!(cli.events.items[i]->cle.flags & CLEF_URGENT))
+ break;
+ }
+ else i = cli.events.count;
+
+ if (( p = ( struct CListEvent* )cli.pfnCreateEvent()) == NULL )
+ return NULL;
+
+ List_Insert(( SortedList* )&cli.events, p, i );
+ p->cle = *cle;
+ p->imlIconIndex = fnGetImlIconIndex(cli.events.items[i]->cle.hIcon);
+ p->flashesDone = 12;
+ p->cle.pszService = mir_strdup(cli.events.items[i]->cle.pszService);
+ #if defined( _UNICODE )
+ if (p->cle.flags & CLEF_UNICODE)
+ p->cle.ptszTooltip = mir_tstrdup((TCHAR*)p->cle.ptszTooltip);
+ else
+ p->cle.ptszTooltip = mir_a2u((char*)p->cle.pszTooltip); //if no flag defined it handled as unicode
+ #else
+ p->cle.ptszTooltip = mir_tstrdup(p->cle.ptszTooltip);
+ #endif
+ if (cli.events.count == 1) {
+ char *szProto;
+ if (cle->hContact == NULL)
+ {
+ if (cle->flags&CLEF_PROTOCOLGLOBAL)
+ szProto = cle->lpszProtocol;
+ else
+ szProto=NULL;
+ }
+ else
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)cle->hContact, 0);
+ iconsOn = 1;
+ flashTimerId = SetTimer(NULL, 0, DBGetContactSettingWord(NULL, "CList", "IconFlashTime", 550), IconFlashTimer);
+ cli.pfnTrayIconUpdateWithImageList( p->imlIconIndex, p->cle.ptszTooltip, szProto);
+ }
+ cli.pfnChangeContactIcon(cle->hContact, p->imlIconIndex, 1);
+ cli.pfnSortContacts();
+ return p;
+}
+
+// Removes an event from the contact list's queue
+// Returns 0 if the event was successfully removed, or nonzero if the event was not found
+int fnRemoveEvent( HANDLE hContact, HANDLE dbEvent )
+{
+ int i;
+ char *szProto;
+ int nSameProto=0;
+
+ // Find the event that should be removed
+ for (i = 0; i < cli.events.count; i++)
+ if ((cli.events.items[i]->cle.hContact == hContact) && (cli.events.items[i]->cle.hDbEvent == dbEvent))
+ break;
+
+ // Event was not found
+ if (i == cli.events.count)
+ return 1;
+
+ // Update contact's icon
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ cli.pfnChangeContactIcon(cli.events.items[i]->cle.hContact,
+ CallService(MS_CLIST_GETCONTACTICON, (WPARAM)cli.events.items[i]->cle.hContact, 1),
+ 0);
+
+ // Free any memory allocated to the event
+ cli.pfnFreeEvent( cli.events.items[i] );
+ List_Remove(( SortedList* )&cli.events, i );
+ {
+ //count same protocoled events
+ char * szEventProto;
+ for (i = 0; i < cli.events.count; i++)
+ {
+ if (cli.events.items[i]->cle.hContact)
+ szEventProto=(char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)(cli.events.items[i]->cle.hContact), 0);
+ else if (cli.events.items[i]->cle.flags&CLEF_PROTOCOLGLOBAL)
+ szEventProto=(char *) cli.events.items[i]->cle.lpszProtocol;
+ else
+ szEventProto = NULL;
+ if (szEventProto && szProto && !lstrcmpA(szEventProto,szProto))
+ nSameProto++;
+
+ }
+
+ }
+ if (cli.events.count == 0 || nSameProto == 0) {
+ if (cli.events.count == 0)
+ KillTimer(NULL, flashTimerId);
+ cli.pfnTrayIconSetToBase( hContact == NULL ? NULL : szProto);
+ }
+ else {
+ if (cli.events.items[0]->cle.hContact == NULL)
+ szProto = NULL;
+ else
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) cli.events.items[0]->cle.hContact, 0);
+ cli.pfnTrayIconUpdateWithImageList(iconsOn ? cli.events.items[0]->imlIconIndex : 0, cli.events.items[0]->cle.ptszTooltip, szProto);
+ }
+
+ return 0;
+}
+
+CLISTEVENT* fnGetEvent( HANDLE hContact, int idx )
+{
+ if ( hContact == INVALID_HANDLE_VALUE) {
+ if (idx >= cli.events.count)
+ return NULL;
+ return &cli.events.items[idx]->cle;
+ }
+
+ for (int i = 0; i < cli.events.count; i++)
+ if (cli.events.items[i]->cle.hContact == hContact)
+ if (idx-- == 0)
+ return &cli.events.items[i]->cle;
+ return NULL;
+}
+
+int fnEventsProcessContactDoubleClick(HANDLE hContact)
+{
+ for (int i = 0; i < cli.events.count; i++) {
+ if (cli.events.items[i]->cle.hContact == hContact) {
+ HANDLE hDbEvent = cli.events.items[i]->cle.hDbEvent;
+ CallService(cli.events.items[i]->cle.pszService, (WPARAM) (HWND) NULL, (LPARAM) & cli.events.items[i]->cle);
+ cli.pfnRemoveEvent(hContact, hDbEvent);
+ return 0;
+ } }
+
+ return 1;
+}
+
+int fnEventsProcessTrayDoubleClick(int index)
+{
+ BOOL click_in_first_icon=FALSE;
+ if (cli.events.count) {
+ HANDLE hContact, hDbEvent;
+ int eventIndex=0;
+ cli.pfnLockTray();
+ if (cli.trayIconCount>1 && index>0) {
+ int i;
+ char * szProto=NULL;
+ for (i=0; i<cli.trayIconCount; i++)
+ if (cli.trayIcon[i].id==index) {
+ szProto=cli.trayIcon[i].szProto;
+ if (i==0) click_in_first_icon=TRUE;
+ break;
+ }
+ if (szProto) {
+ for(i=0; i<cli.events.count; i++) {
+ char * eventProto=NULL;
+ if (cli.events.items[i]->cle.hContact)
+ eventProto=(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)cli.events.items[i]->cle.hContact, 0);
+ if (!eventProto)
+ eventProto=cli.events.items[i]->cle.lpszProtocol;
+
+ if (!eventProto || !_strcmpi(eventProto, szProto)) {
+ eventIndex=i;
+ break;
+ } }
+
+ if (i==cli.events.count) { //EventNotFound
+ //lets process backward try to find first event without desired proto in tray
+ int j;
+ if (click_in_first_icon)
+ for(i=0; i<cli.events.count; i++) {
+ char * eventProto=NULL;
+ if (cli.events.items[i]->cle.hContact)
+ eventProto=(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)cli.events.items[i]->cle.hContact, 0);
+ if (!eventProto)
+ eventProto=cli.events.items[i]->cle.lpszProtocol;
+ if (eventProto) {
+ for (j=0; j<cli.trayIconCount; j++)
+ if (cli.trayIcon[j].szProto && !_strcmpi(eventProto, cli.trayIcon[j].szProto))
+ break;
+
+ if (j==cli.trayIconCount) {
+ eventIndex=i;
+ break;
+ } } }
+ if (i==cli.events.count) { //not found
+ cli.pfnUnlockTray();
+ return 1; //continue processing to show contact list
+ } } } }
+
+ cli.pfnUnlockTray();
+ hContact = cli.events.items[eventIndex]->cle.hContact;
+ hDbEvent = cli.events.items[eventIndex]->cle.hDbEvent;
+ //if (!ServiceExists(cli.events.items[eventIndex]->cle.pszService))
+ // ; may be better to show send msg?
+ CallService(cli.events.items[eventIndex]->cle.pszService, (WPARAM) NULL, (LPARAM) & cli.events.items[eventIndex]->cle);
+ cli.pfnRemoveEvent(hContact, hDbEvent);
+ return 0;
+ }
+ return 1;
+}
+
+static int RemoveEventsForContact(WPARAM wParam, LPARAM)
+{
+ int j, hit;
+
+ /*
+ the for(;;) loop is used here since the cli.events.count can not be relied upon to take us
+ thru the cli.events.items[] array without suffering from shortsightedness about how many unseen
+ events remain, e.g. three events, we remove the first, we're left with 2, the event
+ loop exits at 2 and we never see the real new 2.
+ */
+
+ for (; cli.events.count > 0;) {
+ for (hit = 0, j = 0; j < cli.events.count; j++) {
+ if (cli.events.items[j]->cle.hContact == (HANDLE) wParam) {
+ cli.pfnRemoveEvent((HANDLE)wParam, cli.events.items[j]->cle.hDbEvent);
+ hit = 1;
+ }
+ }
+ if (j == cli.events.count && hit == 0)
+ return 0; /* got to the end of the array and didnt remove anything */
+ }
+
+ return 0;
+}
+
+static int CListEventSettingsChanged(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
+ if (hContact == NULL && cws && cws->szModule && cws->szSetting && strcmp(cws->szModule, "CList") == 0) {
+ if (strcmp(cws->szSetting, "DisableTrayFlash") == 0)
+ disableTrayFlash = (int) cws->value.bVal;
+ else if (strcmp(cws->szSetting, "NoIconBlink") == 0)
+ disableIconFlash = (int) cws->value.bVal;
+ }
+ return 0;
+}
+
+/***************************************************************************************/
+
+INT_PTR AddEventSyncStub(WPARAM wParam, LPARAM lParam) { return CallServiceSync(MS_CLIST_ADDEVENT"_SYNC",wParam, lParam); }
+INT_PTR AddEventStub(WPARAM, LPARAM lParam) { return cli.pfnAddEvent((CLISTEVENT*)lParam ) == NULL; }
+INT_PTR RemoveEventStub(WPARAM wParam, LPARAM lParam) { return cli.pfnRemoveEvent((HANDLE)wParam,(HANDLE)lParam ); }
+INT_PTR GetEventStub(WPARAM wParam, LPARAM lParam) { return (INT_PTR)cli.pfnGetEvent((HANDLE)wParam,(int)lParam); }
+
+int InitCListEvents(void)
+{
+ memset( &cli.events, 0, sizeof(cli.events));
+ cli.events.increment = 10;
+
+ disableTrayFlash = DBGetContactSettingByte(NULL, "CList", "DisableTrayFlash", 0);
+ disableIconFlash = DBGetContactSettingByte(NULL, "CList", "NoIconBlink", 0);
+ CreateServiceFunction(MS_CLIST_ADDEVENT, AddEventSyncStub); //need to be called through sync to keep flash timer workable
+ CreateServiceFunction(MS_CLIST_ADDEVENT"_SYNC", AddEventStub);
+ CreateServiceFunction(MS_CLIST_REMOVEEVENT, RemoveEventStub);
+ CreateServiceFunction(MS_CLIST_GETEVENT, GetEventStub);
+ HookEvent(ME_DB_CONTACT_DELETED, RemoveEventsForContact);
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, CListEventSettingsChanged);
+ return 0;
+}
+
+struct CListEvent* fnCreateEvent( void )
+{
+ return (struct CListEvent*)mir_calloc( sizeof(struct CListEvent));
+}
+
+void fnFreeEvent( struct CListEvent* p )
+{
+ if ( p->cle.pszService )
+ mir_free( p->cle.pszService );
+ if ( p->cle.pszTooltip )
+ mir_free( p->cle.pszTooltip );
+ mir_free( p );
+}
+
+void UninitCListEvents(void)
+{
+ int i;
+
+ if (cli.events.count) KillTimer(NULL, flashTimerId);
+
+ for (i = 0; i < cli.events.count; i++)
+ cli.pfnFreeEvent(( struct CListEvent* )cli.events.items[i] );
+ List_Destroy(( SortedList* )&cli.events );
+
+ if ( imlIcon != NULL )
+ mir_free( imlIcon );
+}
diff --git a/src/modules/clist/clistmenus.cpp b/src/modules/clist/clistmenus.cpp
new file mode 100644
index 0000000000..6c2a4f7a3f
--- /dev/null
+++ b/src/modules/clist/clistmenus.cpp
@@ -0,0 +1,1443 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#pragma hdrstop
+
+#include "m_hotkeys.h"
+
+#include "clc.h"
+#include "genmenu.h"
+
+#define MS_CLIST_HKSTATUS "Clist/HK/SetStatus"
+
+#define FIRSTCUSTOMMENUITEMID 30000
+#define MENU_CUSTOMITEMMAIN 0x80000000
+//#define MENU_CUSTOMITEMCONTEXT 0x40000000
+//#define MENU_CUSTOMITEMFRAME 0x20000000
+
+typedef struct {
+ WORD id;
+ int iconId;
+ CLISTMENUITEM mi;
+}
+ CListIntMenuItem,*lpCListIntMenuItem;
+
+//new menu sys
+HANDLE hMainMenuObject = 0;
+HANDLE hContactMenuObject = 0;
+HANDLE hStatusMenuObject = 0;
+int UnloadMoveToGroup(void);
+
+int statustopos(int status);
+void Proto_SetStatus(const char* szProto, unsigned status);
+
+bool prochotkey;
+
+HANDLE hPreBuildMainMenuEvent, hStatusModeChangeEvent, hPreBuildContactMenuEvent;
+
+static HANDLE hAckHook;
+
+static HMENU hMainMenu,hStatusMenu = 0;
+static const int statusModeList[ MAX_STATUS_COUNT ] =
+{
+ ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED,
+ ID_STATUS_DND, ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_ONTHEPHONE, ID_STATUS_OUTTOLUNCH
+};
+
+static const int skinIconStatusList[ MAX_STATUS_COUNT ] =
+{
+ SKINICON_STATUS_OFFLINE, SKINICON_STATUS_ONLINE, SKINICON_STATUS_AWAY, SKINICON_STATUS_NA, SKINICON_STATUS_OCCUPIED,
+ SKINICON_STATUS_DND, SKINICON_STATUS_FREE4CHAT, SKINICON_STATUS_INVISIBLE, SKINICON_STATUS_ONTHEPHONE, SKINICON_STATUS_OUTTOLUNCH
+};
+
+static const int statusModePf2List[ MAX_STATUS_COUNT ] =
+{
+ 0xFFFFFFFF, PF2_ONLINE, PF2_SHORTAWAY, PF2_LONGAWAY, PF2_LIGHTDND,
+ PF2_HEAVYDND, PF2_FREECHAT, PF2_INVISIBLE, PF2_ONTHEPHONE, PF2_OUTTOLUNCH
+};
+
+static INT_PTR statusHotkeys[ MAX_STATUS_COUNT ];
+
+PMO_IntMenuItem* hStatusMainMenuHandles;
+int hStatusMainMenuHandlesCnt;
+
+typedef struct
+{
+ int protoindex;
+ int protostatus[ MAX_STATUS_COUNT ];
+ PMO_IntMenuItem menuhandle[ MAX_STATUS_COUNT ];
+}
+ tStatusMenuHandles,*lpStatusMenuHandles;
+
+lpStatusMenuHandles hStatusMenuHandles;
+int hStatusMenuHandlesCnt;
+
+//mainmenu exec param(ownerdata)
+typedef struct
+{
+ char *szServiceName;
+ TCHAR *szMenuName;
+ int Param1;
+}
+ MainMenuExecParam,*lpMainMenuExecParam;
+
+//contactmenu exec param(ownerdata)
+//also used in checkservice
+typedef struct
+{
+ char *szServiceName;
+ char *pszContactOwner;//for check proc
+ int param;
+}
+ ContactMenuExecParam,*lpContactMenuExecParam;
+
+typedef struct
+{
+ char *szProto;
+ int isOnList;
+ int isOnline;
+}
+ BuildContactParam;
+
+typedef struct
+{
+ char *proto; //This is unique protoname
+ int protoindex;
+ int status;
+
+ BOOL custom;
+ char *svc;
+ HANDLE hMenuItem;
+}
+ StatusMenuExecParam,*lpStatusMenuExecParam;
+
+typedef struct _MenuItemHandles
+{
+ HMENU OwnerMenu;
+ int position;
+}
+ MenuItemData;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// service functions
+
+void FreeMenuProtos( void )
+{
+ int i;
+
+ if ( cli.menuProtos ) {
+ for ( i=0; i < cli.menuProtoCount; i++ )
+ if ( cli.menuProtos[i].szProto )
+ mir_free(cli.menuProtos[i].szProto);
+
+ mir_free( cli.menuProtos );
+ cli.menuProtos = NULL;
+ }
+ cli.menuProtoCount = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+int GetAverageMode(int* pNetProtoCount = NULL)
+{
+ int netProtoCount = 0;
+ int averageMode = 0;
+
+ for ( int i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if ( cli.pfnGetProtocolVisibility( pa->szModuleName ) == 0 )
+ continue;
+
+ netProtoCount++;
+
+ if ( averageMode == 0 )
+ averageMode = CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0 );
+ else if ( averageMode > 0 && averageMode != CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0 )) {
+ averageMode = -1;
+ if (pNetProtoCount == NULL) break;
+ }
+ }
+
+ if (pNetProtoCount) *pNetProtoCount = netProtoCount;
+ return averageMode;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MAIN MENU
+
+/*
+wparam=handle to the menu item returned by MS_CLIST_ADDCONTACTMENUITEM
+return 0 on success.
+*/
+
+static INT_PTR RemoveMainMenuItem(WPARAM wParam, LPARAM)
+{
+ CallService(MO_REMOVEMENUITEM,wParam,0);
+ return 0;
+}
+
+static INT_PTR BuildMainMenu(WPARAM, LPARAM)
+{
+ ListParam param = { 0 };
+ param.MenuObjectHandle = hMainMenuObject;
+
+ NotifyEventHooks(hPreBuildMainMenuEvent,(WPARAM)0,(LPARAM)0);
+
+ CallService(MO_BUILDMENU,(WPARAM)hMainMenu,(LPARAM)&param);
+ DrawMenuBar((HWND)CallService("CLUI/GetHwnd",(WPARAM)0,(LPARAM)0));
+ return (INT_PTR)hMainMenu;
+}
+
+static INT_PTR AddMainMenuItem(WPARAM, LPARAM lParam)
+{
+ CLISTMENUITEM* mi = ( CLISTMENUITEM* )lParam;
+ if ( mi->cbSize != sizeof( CLISTMENUITEM ))
+ return NULL;
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = mi->flags;
+ tmi.hIcon = mi->hIcon;
+ tmi.hotKey = mi->hotKey;
+ tmi.ptszName = mi->ptszName;
+ tmi.position = mi->position;
+
+ //pszPopupName for new system mean root level
+ //pszPopupName for old system mean that exists popup
+ tmi.root = ( HGENMENU )mi->pszPopupName;
+ {
+ lpMainMenuExecParam mmep;
+ mmep = ( lpMainMenuExecParam )mir_alloc( sizeof( MainMenuExecParam ));
+ if ( mmep == NULL )
+ return 0;
+
+ //we need just one parametr.
+ mmep->szServiceName = mir_strdup(mi->pszService);
+ mmep->Param1 = mi->popupPosition;
+ mmep->szMenuName = tmi.ptszName;
+ tmi.ownerdata=mmep;
+ }
+
+ PMO_IntMenuItem pimi = MO_AddNewMenuItem( hMainMenuObject, &tmi );
+
+ char* name;
+ bool needFree = false;
+
+ if (mi->pszService)
+ name = mi->pszService;
+ else if (mi->flags & CMIF_UNICODE) {
+ name = mir_t2a( mi->ptszName );
+ needFree = true;
+ }
+ else
+ name = mi->pszName;
+
+ MO_SetOptionsMenuItem( pimi, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )name );
+ if (needFree) mir_free(name);
+
+ return ( INT_PTR )pimi;
+}
+
+int MainMenuCheckService(WPARAM, LPARAM)
+{
+ return 0;
+}
+
+//called with:
+//wparam - ownerdata
+//lparam - lparam from winproc
+INT_PTR MainMenuExecService(WPARAM wParam, LPARAM lParam)
+{
+ lpMainMenuExecParam mmep = ( lpMainMenuExecParam )wParam;
+ if ( mmep != NULL ) {
+ // bug in help.c,it used wparam as parent window handle without reason.
+ if ( !lstrcmpA(mmep->szServiceName,"Help/AboutCommand"))
+ mmep->Param1 = 0;
+
+ CallService(mmep->szServiceName,mmep->Param1,lParam);
+ }
+ return 1;
+}
+
+INT_PTR FreeOwnerDataMainMenu(WPARAM, LPARAM lParam)
+{
+ lpMainMenuExecParam mmep = ( lpMainMenuExecParam )lParam;
+ if ( mmep != NULL ) {
+ FreeAndNil(( void** )&mmep->szServiceName);
+ FreeAndNil(( void** )&mmep);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CONTACT MENU
+
+static INT_PTR RemoveContactMenuItem(WPARAM wParam, LPARAM)
+{
+ CallService(MO_REMOVEMENUITEM,wParam,0);
+ return 0;
+}
+
+static INT_PTR AddContactMenuItem(WPARAM, LPARAM lParam)
+{
+ CLISTMENUITEM *mi=(CLISTMENUITEM*)lParam;
+ if ( mi->cbSize != sizeof( CLISTMENUITEM ))
+ return 0;
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = mi->flags;
+ tmi.hIcon = mi->hIcon;
+ tmi.hotKey = mi->hotKey;
+ tmi.position = mi->position;
+ tmi.ptszName = mi->ptszName;
+ tmi.root = ( HGENMENU )mi->pszPopupName;
+
+ if ( !( mi->flags & CMIF_ROOTHANDLE )) {
+ //old system
+ tmi.flags |= CMIF_ROOTHANDLE;
+ tmi.root = NULL;
+ }
+
+ //owner data
+ lpContactMenuExecParam cmep = ( lpContactMenuExecParam )mir_calloc(sizeof(ContactMenuExecParam));
+ cmep->szServiceName = mir_strdup( mi->pszService );
+ if ( mi->pszContactOwner != NULL )
+ cmep->pszContactOwner = mir_strdup( mi->pszContactOwner );
+ cmep->param = mi->popupPosition;
+ tmi.ownerdata = cmep;
+
+ //may be need to change how UniqueName is formed?
+ PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hContactMenuObject, &tmi );
+ char buf[ 256 ];
+ if (mi->pszService)
+ mir_snprintf( buf, SIZEOF(buf), "%s/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", (mi->pszService) ? mi->pszService : "" );
+ else if (mi->ptszName)
+ {
+ if (tmi.flags&CMIF_UNICODE)
+ {
+ char * temp = mir_t2a(mi->ptszName);
+ mir_snprintf( buf, SIZEOF(buf), "%s/NoService/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", temp );
+ mir_free(temp);
+ }
+ else
+ mir_snprintf( buf, SIZEOF(buf), "%s/NoService/%s", (mi->pszContactOwner) ? mi->pszContactOwner : "", mi->ptszName );
+ }
+ else buf[0]='\0';
+ if (buf[0]) MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
+ return ( INT_PTR )menuHandle;
+}
+
+static INT_PTR BuildContactMenu(WPARAM wParam, LPARAM)
+{
+ HANDLE hContact = ( HANDLE )wParam;
+ NotifyEventHooks(hPreBuildContactMenuEvent,(WPARAM)hContact,0);
+
+ char *szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+
+ BuildContactParam bcp;
+ bcp.szProto = szProto;
+ bcp.isOnList = ( DBGetContactSettingByte(hContact,"CList","NotOnList",0) == 0 );
+ bcp.isOnline = ( szProto != NULL && ID_STATUS_OFFLINE != DBGetContactSettingWord(hContact,szProto,"Status",ID_STATUS_OFFLINE));
+
+ ListParam param = { 0 };
+ param.MenuObjectHandle = hContactMenuObject;
+ param.wParam = (WPARAM)&bcp;
+
+ HMENU hMenu = CreatePopupMenu();
+ CallService(MO_BUILDMENU,(WPARAM)hMenu,(LPARAM)&param);
+
+ return (INT_PTR)hMenu;
+}
+
+//called with:
+//wparam - ownerdata
+//lparam - lparam from winproc
+INT_PTR ContactMenuExecService(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam!=0) {
+ lpContactMenuExecParam cmep=(lpContactMenuExecParam)wParam;
+ //call with wParam=(WPARAM)(HANDLE)hContact,lparam=popupposition
+ CallService(cmep->szServiceName,lParam,cmep->param);
+ }
+ return 0;
+}
+
+//true - ok,false ignore
+INT_PTR ContactMenuCheckService(WPARAM wParam,LPARAM)
+{
+ PCheckProcParam pcpp = ( PCheckProcParam )wParam;
+ BuildContactParam *bcp=NULL;
+ lpContactMenuExecParam cmep=NULL;
+ TMO_MenuItem mi;
+
+ if ( pcpp == NULL )
+ return FALSE;
+
+ bcp = ( BuildContactParam* )pcpp->wParam;
+ if ( bcp == NULL )
+ return FALSE;
+
+ cmep = ( lpContactMenuExecParam )pcpp->MenuItemOwnerData;
+ if ( cmep == NULL ) //this is root...build it
+ return TRUE;
+
+ if ( cmep->pszContactOwner != NULL ) {
+ if ( bcp->szProto == NULL ) return FALSE;
+ if ( strcmp( cmep->pszContactOwner, bcp->szProto )) return FALSE;
+ }
+ if ( MO_GetMenuItem(( WPARAM )pcpp->MenuItemHandle, ( LPARAM )&mi ) == 0 ) {
+ if ( mi.flags & CMIF_HIDDEN ) return FALSE;
+ if ( mi.flags & CMIF_NOTONLIST && bcp->isOnList ) return FALSE;
+ if ( mi.flags & CMIF_NOTOFFLIST && !bcp->isOnList ) return FALSE;
+ if ( mi.flags & CMIF_NOTONLINE && bcp->isOnline ) return FALSE;
+ if ( mi.flags & CMIF_NOTOFFLINE && !bcp->isOnline ) return FALSE;
+ }
+ return TRUE;
+}
+
+INT_PTR FreeOwnerDataContactMenu (WPARAM, LPARAM lParam)
+{
+ lpContactMenuExecParam cmep = ( lpContactMenuExecParam )lParam;
+ if ( cmep != NULL ) {
+ FreeAndNil(( void** )&cmep->szServiceName);
+ FreeAndNil(( void** )&cmep->pszContactOwner);
+ FreeAndNil(( void** )&cmep);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// STATUS MENU
+
+BOOL FindMenuHandleByGlobalID(HMENU hMenu, PMO_IntMenuItem id, MenuItemData* itdat)
+{
+ int i;
+ PMO_IntMenuItem pimi;
+ MENUITEMINFO mii={0};
+ BOOL inSub=FALSE;
+ if (!itdat)
+ return FALSE;
+
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA;
+ for ( i = GetMenuItemCount( hMenu )-1; i >= 0; i-- ) {
+ GetMenuItemInfo(hMenu,i,TRUE,&mii);
+ if ( mii.fType == MFT_SEPARATOR )
+ continue;
+ if ( mii.hSubMenu )
+ inSub = FindMenuHandleByGlobalID(mii.hSubMenu, id, itdat);
+ if ( inSub )
+ return inSub;
+
+ pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData );
+ if ( pimi != NULL ) {
+ if ( pimi == id ) {
+ itdat->OwnerMenu = hMenu;
+ itdat->position = i;
+ return TRUE;
+ } } }
+
+ return FALSE;
+}
+
+INT_PTR StatusMenuCheckService(WPARAM wParam, LPARAM)
+{
+ PCheckProcParam pcpp = ( PCheckProcParam )wParam;
+ if ( !pcpp )
+ return TRUE;
+
+ PMO_IntMenuItem timi = MO_GetIntMenuItem( pcpp->MenuItemHandle );
+ if ( !timi )
+ return TRUE;
+
+ StatusMenuExecParam *smep = ( StatusMenuExecParam* )pcpp->MenuItemOwnerData;
+ if (smep && !smep->status && smep->custom )
+ {
+ if (wildcmp(smep->svc, "*XStatus*"))
+ {
+ int XStatus = CallProtoService(smep->proto, "/GetXStatus", 0, 0);
+ char buf[255];
+ mir_snprintf( buf, sizeof(buf), "*XStatus%d", XStatus );
+
+ bool check = wildcmp(smep->svc, buf);
+ bool reset = wildcmp(smep->svc, "*XStatus0");
+
+ if (check)
+ timi->mi.flags |= CMIF_CHECKED;
+ else
+ timi->mi.flags &= ~CMIF_CHECKED;
+
+ if ( reset || check )
+ {
+ PMO_IntMenuItem timiParent = MO_GetIntMenuItem( timi->mi.root );
+ if (timiParent)
+ {
+ CLISTMENUITEM mi2 = {0};
+ mi2.cbSize = sizeof(mi2);
+ mi2.flags = CMIM_NAME | CMIF_TCHAR;
+ mi2.ptszName = timi->mi.hIcon ? timi->mi.ptszName : TranslateT("Custom status");
+
+ timiParent = MO_GetIntMenuItem( timi->mi.root );
+
+ MenuItemData it = {0};
+
+ if (FindMenuHandleByGlobalID(hStatusMenu, timiParent, &it))
+ {
+ MENUITEMINFO mi ={0};
+ TCHAR d[100];
+ GetMenuString(it.OwnerMenu, it.position, d, SIZEOF(d), MF_BYPOSITION);
+
+ if (!IsWinVer98Plus())
+ {
+ mi.cbSize = MENUITEMINFO_V4_SIZE;
+ mi.fMask = MIIM_TYPE | MIIM_STATE;
+ mi.fType = MFT_STRING;
+ }
+ else
+ {
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_STRING | MIIM_STATE;
+ if ( timi->iconId != -1 )
+ {
+ mi.fMask |= MIIM_BITMAP;
+ if (IsWinVerVistaPlus() && isThemeActive()) {
+ if (timi->hBmp == NULL)
+ timi->hBmp = ConvertIconToBitmap(NULL, timi->parent->m_hMenuIcons, timi->iconId);
+ mi.hbmpItem = timi->hBmp;
+ }
+ else
+ mi.hbmpItem = HBMMENU_CALLBACK;
+ }
+ }
+
+ mi.fState |= (check && !reset ? MFS_CHECKED : MFS_UNCHECKED );
+ mi.dwTypeData = mi2.ptszName;
+ SetMenuItemInfo(it.OwnerMenu, it.position, TRUE, &mi);
+ }
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)timi->mi.root, (LPARAM)&mi2);
+ timiParent->iconId = timi->iconId;
+ if (timiParent->hBmp) DeleteObject(timiParent->hBmp);
+ timiParent->hBmp = NULL;
+ } } }
+ }
+ else if ( smep && smep->status && !smep->custom ) {
+ int curProtoStatus = ( smep->proto ) ? CallProtoService(smep->proto,PS_GETSTATUS,0,0) : GetAverageMode();
+ if ( smep->status == curProtoStatus )
+ timi->mi.flags |= CMIF_CHECKED;
+ else
+ timi->mi.flags &= ~CMIF_CHECKED;
+ }
+ else if (( !smep || smep->proto ) && timi->mi.pszName ) {
+ int curProtoStatus=0;
+ BOOL IconNeedDestroy=FALSE;
+ char* prot;
+ if (smep)
+ prot = smep->proto;
+ else
+ {
+ #ifdef UNICODE
+ char *prn=mir_u2a(timi->mi.ptszName);
+ prot = NEWSTR_ALLOCA( prn );
+ if (prn) mir_free(prn);
+ #else
+ prot = timi->mi.ptszName;
+ #endif
+ }
+ if ( Proto_GetAccount( prot ) == NULL )
+ return TRUE;
+
+ if (( curProtoStatus = CallProtoService(prot,PS_GETSTATUS,0,0)) == CALLSERVICE_NOTFOUND )
+ curProtoStatus = 0;
+
+ if ( curProtoStatus >= ID_STATUS_OFFLINE && curProtoStatus < ID_STATUS_IDLE )
+ timi->mi.hIcon = LoadSkinProtoIcon(prot,curProtoStatus);
+ else {
+ timi->mi.hIcon=(HICON)CallProtoService(prot,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ if ( timi->mi.hIcon == (HICON)CALLSERVICE_NOTFOUND )
+ timi->mi.hIcon = NULL;
+ else
+ IconNeedDestroy = TRUE;
+ }
+
+ if (timi->mi.hIcon) {
+ timi->mi.flags |= CMIM_ICON;
+ MO_ModifyMenuItem( timi, &timi->mi );
+ if ( IconNeedDestroy ) {
+ DestroyIcon( timi->mi.hIcon );
+ timi->mi.hIcon = NULL;
+ }
+ else IconLib_ReleaseIcon(timi->mi.hIcon,0);
+ } }
+
+ return TRUE;
+}
+
+INT_PTR StatusMenuExecService(WPARAM wParam, LPARAM)
+{
+ lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )wParam;
+ if ( smep != NULL ) {
+ if ( smep->custom ) {
+ if (smep->svc && *smep->svc)
+ CallService(smep->svc, 0, (LPARAM)smep->hMenuItem);
+ }
+ else {
+ if ( smep->status == 0 && smep->protoindex !=0 && smep->proto != NULL ) {
+ PMO_IntMenuItem pimi;
+ char *prot = smep->proto;
+ char szHumanName[64]={0};
+ PROTOACCOUNT * acc = Proto_GetAccount( smep->proto );
+ int i=(DBGetContactSettingByte(NULL,prot,"LockMainStatus",0)?0:1);
+ DBWriteContactSettingByte(NULL,prot,"LockMainStatus",(BYTE)i);
+
+ CallProtoService( smep->proto, PS_GETNAME, (WPARAM)SIZEOF(szHumanName), (LPARAM)szHumanName );
+ pimi = MO_GetIntMenuItem(( HGENMENU )smep->protoindex );
+ PMO_IntMenuItem root = (PMO_IntMenuItem)pimi->mi.root;
+ mir_free( pimi->mi.pszName );
+ mir_free( root->mi.pszName );
+ if ( i ) {
+ TCHAR buf[256];
+ pimi->mi.flags|=CMIF_CHECKED;
+ if ( cli.bDisplayLocked ) {
+ mir_sntprintf(buf,SIZEOF(buf),TranslateT("%s (locked)"),acc->tszAccountName);
+ pimi->mi.ptszName = mir_tstrdup( buf );
+ root->mi.ptszName = mir_tstrdup( buf );
+ }
+ else {
+ pimi->mi.ptszName = mir_tstrdup( acc->tszAccountName );
+ root->mi.ptszName = mir_tstrdup( acc->tszAccountName );
+ }
+ }
+ else {
+ pimi->mi.ptszName = mir_tstrdup( acc->tszAccountName );
+ root->mi.ptszName = mir_tstrdup( acc->tszAccountName );
+ pimi->mi.flags &= ~CMIF_CHECKED;
+ }
+ if ( cli.hwndStatus )
+ InvalidateRect( cli.hwndStatus, NULL, TRUE );
+ }
+ else if ( smep->proto != NULL ) {
+ Proto_SetStatus(smep->proto, smep->status);
+ NotifyEventHooks(hStatusModeChangeEvent, smep->status, (LPARAM)smep->proto);
+ }
+ else {
+ int MenusProtoCount = 0;
+
+ for( int i=0; i < accounts.getCount(); i++ )
+ if ( cli.pfnGetProtocolVisibility( accounts[i]->szModuleName ))
+ MenusProtoCount++;
+
+ cli.currentDesiredStatusMode = smep->status;
+
+ for ( int j=0; j < accounts.getCount(); j++ ) {
+ PROTOACCOUNT* pa = accounts[j];
+ if ( !Proto_IsAccountEnabled( pa ))
+ continue;
+ if ( MenusProtoCount > 1 && Proto_IsAccountLocked( pa ))
+ continue;
+
+ Proto_SetStatus(pa->szModuleName, cli.currentDesiredStatusMode);
+ }
+ NotifyEventHooks( hStatusModeChangeEvent, cli.currentDesiredStatusMode, 0 );
+ DBWriteContactSettingWord( NULL, "CList", "Status", ( WORD )cli.currentDesiredStatusMode );
+ return 1;
+ } } }
+
+ return 0;
+}
+
+INT_PTR FreeOwnerDataStatusMenu(WPARAM, LPARAM lParam)
+{
+ lpStatusMenuExecParam smep = (lpStatusMenuExecParam)lParam;
+ if ( smep != NULL ) {
+ FreeAndNil(( void** )&smep->proto);
+ FreeAndNil(( void** )&smep->svc);
+ FreeAndNil(( void** )&smep);
+ }
+
+ return (0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Other menu functions
+
+//wparam MenuItemHandle
+static INT_PTR ModifyCustomMenuItem(WPARAM wParam,LPARAM lParam)
+{
+ CLISTMENUITEM *mi=(CLISTMENUITEM*)lParam;
+ TMO_MenuItem tmi;
+
+ if ( lParam == 0 )
+ return -1;
+ if ( mi->cbSize != sizeof( CLISTMENUITEM ))
+ return 1;
+
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = mi->flags;
+ tmi.hIcon = mi->hIcon;
+ tmi.hotKey = mi->hotKey;
+ tmi.ptszName = mi->ptszName;
+ return MO_ModifyMenuItem(( PMO_IntMenuItem )wParam, &tmi );
+}
+
+INT_PTR MenuProcessCommand(WPARAM wParam,LPARAM lParam)
+{
+ WORD cmd = LOWORD(wParam);
+
+ if ( HIWORD(wParam) & MPCF_MAINMENU ) {
+ int hst = LOWORD( wParam );
+ if ( hst >= ID_STATUS_OFFLINE && hst <= ID_STATUS_OUTTOLUNCH ) {
+ int pos = statustopos( hst );
+ if ( pos != -1 && hStatusMainMenuHandles != NULL )
+ return MO_ProcessCommand( hStatusMainMenuHandles[ pos ], lParam );
+ } }
+
+ if ( !( cmd >= CLISTMENUIDMIN && cmd <= CLISTMENUIDMAX ))
+ return 0; // DO NOT process ids outside from clist menu id range v0.7.0.27+
+
+ //process old menu sys
+ if ( HIWORD(wParam) & MPCF_CONTACTMENU )
+ return MO_ProcessCommandBySubMenuIdent( (int)hContactMenuObject, LOWORD(wParam), lParam );
+
+ //unknown old menu
+ return MO_ProcessCommandByMenuIdent( LOWORD(wParam), lParam );
+}
+
+BOOL FindMenuHanleByGlobalID(HMENU hMenu, PMO_IntMenuItem id, MenuItemData* itdat)
+{
+ int i;
+ PMO_IntMenuItem pimi;
+ MENUITEMINFO mii = {0};
+ BOOL inSub=FALSE;
+
+ if ( !itdat )
+ return FALSE;
+
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA;
+ for ( i = GetMenuItemCount( hMenu )-1; i >= 0; i-- ) {
+ GetMenuItemInfo( hMenu, i, TRUE, &mii );
+ if ( mii.fType == MFT_SEPARATOR )
+ continue;
+
+ if ( mii.hSubMenu )
+ inSub = FindMenuHanleByGlobalID( mii.hSubMenu, id, itdat );
+ if (inSub)
+ return inSub;
+
+ pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData);
+ if ( pimi != NULL ) {
+ if ( pimi == id ) {
+ itdat->OwnerMenu = hMenu;
+ itdat->position = i;
+ return TRUE;
+ } } }
+
+ return FALSE;
+}
+
+static INT_PTR MenuProcessHotkey(WPARAM vKey, LPARAM)
+{
+ prochotkey = true;
+
+ bool res =
+ MO_ProcessHotKeys( hStatusMenuObject, vKey ) ||
+ MO_ProcessHotKeys( hMainMenuObject, vKey );
+
+ prochotkey = false;
+
+ return res;
+}
+
+static int MenuIconsChanged(WPARAM, LPARAM)
+{
+ //just rebuild menu
+ RebuildMenuOrder();
+ cli.pfnCluiProtocolStatusChanged(0,0);
+ return 0;
+}
+
+static INT_PTR MeasureMenuItem(WPARAM, LPARAM lParam)
+{
+ return MO_MeasureMenuItem(( LPMEASUREITEMSTRUCT )lParam );
+}
+
+static INT_PTR DrawMenuItem(WPARAM, LPARAM lParam)
+{
+ return MO_DrawMenuItem(( LPDRAWITEMSTRUCT )lParam );
+}
+
+int RecursiveDeleteMenu(HMENU hMenu)
+{
+ int cnt = GetMenuItemCount(hMenu);
+ for ( int i=0; i < cnt; i++ ) {
+ HMENU submenu = GetSubMenu(hMenu, 0);
+ if (submenu) DestroyMenu(submenu);
+ DeleteMenu(hMenu, 0, MF_BYPOSITION);
+ }
+ return 0;
+}
+
+static INT_PTR MenuGetMain(WPARAM, LPARAM)
+{
+ RecursiveDeleteMenu(hMainMenu);
+ BuildMainMenu(0,0);
+ return (INT_PTR)hMainMenu;
+}
+
+static INT_PTR BuildStatusMenu(WPARAM, LPARAM)
+{
+ ListParam param = { 0 };
+ param.MenuObjectHandle = hStatusMenuObject;
+
+ RecursiveDeleteMenu(hStatusMenu);
+ CallService(MO_BUILDMENU,(WPARAM)hStatusMenu,(LPARAM)&param);
+ return (INT_PTR)hStatusMenu;
+}
+
+static INT_PTR SetStatusMode(WPARAM wParam, LPARAM)
+{
+ prochotkey = true;
+ MenuProcessCommand(MAKEWPARAM(LOWORD(wParam), MPCF_MAINMENU), 0);
+ prochotkey = false;
+ return 0;
+}
+
+int fnGetProtocolVisibility(const char* accName)
+{
+ if ( accName ) {
+ PROTOACCOUNT* pa = Proto_GetAccount( accName );
+ return pa && pa->bIsVisible && Proto_IsAccountEnabled( pa ) &&
+ pa->ppro && (pa->ppro->GetCaps( PFLAGNUM_2, 0 ) & ~pa->ppro->GetCaps( PFLAGNUM_5, 0 ));
+ }
+
+ return FALSE;
+}
+
+int fnGetProtoIndexByPos(PROTOCOLDESCRIPTOR ** proto, int protoCnt, int Pos)
+{
+ int p;
+ char buf[10];
+ DBVARIANT dbv;
+
+ _itoa( Pos, buf, 10 );
+ if ( !DBGetContactSetting( NULL, "Protocols", buf, &dbv )) {
+ for ( p=0; p < protoCnt; p++ ) {
+ if ( lstrcmpA( proto[p]->szName, dbv.pszVal ) == 0 ) {
+ DBFreeVariant( &dbv );
+ return p;
+ } }
+
+ DBFreeVariant( &dbv );
+ }
+
+ return -1;
+}
+
+int fnGetAccountIndexByPos(int Pos)
+{
+ int i;
+ for ( i=0; i < accounts.getCount(); i++ )
+ if ( accounts[i]->iOrder == Pos )
+ return i;
+
+ return -1;
+}
+
+void RebuildMenuOrder( void )
+{
+ int i,j,s;
+ DWORD flags;
+
+ BYTE bHideStatusMenu = DBGetContactSettingByte( NULL, "CLUI", "DontHideStatusMenu", 0 ); // cool perversion, though
+
+ //clear statusmenu
+ RecursiveDeleteMenu(hStatusMenu);
+
+ //status menu
+ if ( hStatusMenuObject != 0 ) {
+ CallService(MO_REMOVEMENUOBJECT,(WPARAM)hStatusMenuObject,0);
+ mir_free( hStatusMainMenuHandles );
+ mir_free( hStatusMenuHandles );
+ }
+
+ TMenuParam tmp = { 0 };
+ tmp.cbSize = sizeof(tmp);
+ tmp.ExecService = "StatusMenuExecService";
+ tmp.CheckService = "StatusMenuCheckService";
+ tmp.name = "StatusMenu";
+
+ hStatusMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
+ MO_SetOptionsMenuObject( hStatusMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataStatusMenu" );
+
+ hStatusMainMenuHandles = ( PMO_IntMenuItem* )mir_calloc( SIZEOF(statusModeList) * sizeof( PMO_IntMenuItem* ));
+ hStatusMainMenuHandlesCnt = SIZEOF(statusModeList);
+
+ hStatusMenuHandles = ( tStatusMenuHandles* )mir_calloc(sizeof(tStatusMenuHandles)*accounts.getCount());
+ hStatusMenuHandlesCnt = accounts.getCount();
+
+ FreeMenuProtos();
+
+ for ( s=0; s < accounts.getCount(); s++ ) {
+ i = cli.pfnGetAccountIndexByPos( s );
+ if ( i == -1 )
+ continue;
+
+ PROTOACCOUNT* pa = accounts[i];
+ int pos = 0;
+ if ( !bHideStatusMenu && !cli.pfnGetProtocolVisibility( pa->szModuleName ))
+ continue;
+
+ flags = pa->ppro->GetCaps( PFLAGNUM_2, 0 ) & ~pa->ppro->GetCaps( PFLAGNUM_5, 0 );
+ int j;
+ HICON ic;
+ TCHAR tbuf[256];
+
+ //adding root
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_TCHAR | CMIF_ROOTHANDLE | CMIF_KEEPUNTRANSLATED;
+ tmi.position = pos++;
+ tmi.hIcon = ic = (HICON)CallProtoService( pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0 );
+
+ if ( Proto_IsAccountLocked( pa ) && cli.bDisplayLocked ) {
+ mir_sntprintf( tbuf, SIZEOF(tbuf), TranslateT("%s (locked)"), pa->tszAccountName );
+ tmi.ptszName = tbuf;
+ }
+ else tmi.ptszName = pa->tszAccountName;
+
+ {
+ //owner data
+ lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_calloc( sizeof( StatusMenuExecParam ));
+ smep->proto = mir_strdup(pa->szModuleName);
+ tmi.ownerdata = smep;
+ }
+ PMO_IntMenuItem rootmenu = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+
+ memset(&tmi,0,sizeof(tmi));
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_TCHAR | CMIF_ROOTHANDLE | CMIF_KEEPUNTRANSLATED;
+ tmi.root = rootmenu;
+ tmi.position = pos++;
+ tmi.hIcon = ic;
+ {
+ //owner data
+ lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_alloc( sizeof( StatusMenuExecParam ));
+ memset( smep, 0, sizeof( *smep ));
+ smep->proto = mir_strdup(pa->szModuleName);
+ tmi.ownerdata = smep;
+ }
+
+ if ( Proto_IsAccountLocked( pa ))
+ tmi.flags |= CMIF_CHECKED;
+
+ if (( tmi.flags & CMIF_CHECKED ) && cli.bDisplayLocked ) {
+ mir_sntprintf( tbuf, SIZEOF(tbuf), TranslateT("%s (locked)"), pa->tszAccountName );
+ tmi.ptszName = tbuf;
+ }
+ else tmi.ptszName = pa->tszAccountName;
+
+ PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+ ((lpStatusMenuExecParam)tmi.ownerdata)->protoindex = ( int )menuHandle;
+ MO_ModifyMenuItem( menuHandle, &tmi );
+
+ cli.menuProtos=(MenuProto*)mir_realloc(cli.menuProtos, sizeof(MenuProto)*(cli.menuProtoCount+1));
+ memset(&(cli.menuProtos[cli.menuProtoCount]),0,sizeof(MenuProto));
+ cli.menuProtos[cli.menuProtoCount].pMenu = rootmenu;
+ cli.menuProtos[cli.menuProtoCount].szProto = mir_strdup(pa->szModuleName);
+
+ cli.menuProtoCount++;
+ {
+ char buf[256];
+ mir_snprintf( buf, SIZEOF(buf), "RootProtocolIcon_%s", pa->szModuleName );
+ MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
+ }
+ DestroyIcon(ic);
+ pos += 500000;
+
+ for ( j=0; j < SIZEOF(statusModeList); j++ ) {
+ if ( !( flags & statusModePf2List[j] ))
+ continue;
+
+ //adding
+ memset( &tmi, 0, sizeof( tmi ));
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
+ if ( statusModeList[j] == ID_STATUS_OFFLINE )
+ tmi.flags |= CMIF_CHECKED;
+ tmi.root = rootmenu;
+ tmi.position = pos++;
+ tmi.ptszName = cli.pfnGetStatusModeDescription( statusModeList[j], GSMDF_UNTRANSLATED );
+ tmi.hIcon = LoadSkinProtoIcon( pa->szModuleName, statusModeList[j] );
+ {
+ //owner data
+ lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_calloc( sizeof( StatusMenuExecParam ));
+ smep->custom = FALSE;
+ smep->status = statusModeList[j];
+ smep->protoindex = i;
+ smep->proto = mir_strdup(pa->szModuleName);
+ tmi.ownerdata = smep;
+ }
+
+ hStatusMenuHandles[i].protoindex = i;
+ hStatusMenuHandles[i].protostatus[j] = statusModeList[j];
+ hStatusMenuHandles[i].menuhandle[j] = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+ {
+ char buf[ 256 ];
+ mir_snprintf(buf, SIZEOF(buf), "ProtocolIcon_%s_%s",pa->szModuleName,tmi.pszName);
+ MO_SetOptionsMenuItem( hStatusMenuHandles[i].menuhandle[j], OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
+ }
+ IconLib_ReleaseIcon(tmi.hIcon,0);
+ } }
+
+ NotifyEventHooks(cli.hPreBuildStatusMenuEvent, 0, 0);
+ int pos = 200000;
+
+ //add to root menu
+ for ( j=0; j < SIZEOF(statusModeList); j++ ) {
+ for ( i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if ( !bHideStatusMenu && !cli.pfnGetProtocolVisibility( pa->szModuleName ))
+ continue;
+
+ flags = pa->ppro->GetCaps(PFLAGNUM_2, 0) & ~pa->ppro->GetCaps(PFLAGNUM_5, 0);
+ if ( !( flags & statusModePf2List[j] ))
+ continue;
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof( tmi );
+ tmi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
+ if ( statusModeList[j] == ID_STATUS_OFFLINE )
+ tmi.flags |= CMIF_CHECKED;
+
+ tmi.hIcon = LoadSkinIcon( skinIconStatusList[j] );
+ tmi.position = pos++;
+ tmi.hotKey = MAKELPARAM(MOD_CONTROL,'0'+j);
+ {
+ //owner data
+ lpStatusMenuExecParam smep = ( lpStatusMenuExecParam )mir_alloc( sizeof( StatusMenuExecParam ));
+ smep->custom = FALSE;
+ smep->status = statusModeList[j];
+ smep->proto = NULL;
+ smep->svc = NULL;
+ tmi.ownerdata = smep;
+ }
+ {
+ TCHAR buf[ 256 ], hotkeyName[ 100 ];
+ WORD hotKey = GetHotkeyValue( statusHotkeys[j] );
+ HotkeyToName( hotkeyName, SIZEOF(hotkeyName), HIBYTE(hotKey), LOBYTE(hotKey));
+ mir_sntprintf( buf, SIZEOF( buf ), TranslateT("%s\t%s"),
+ cli.pfnGetStatusModeDescription( statusModeList[j], 0 ), hotkeyName );
+ tmi.ptszName = buf;
+ tmi.hotKey = MAKELONG(HIBYTE(hotKey), LOBYTE(hotKey));
+ hStatusMainMenuHandles[j] = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+ }
+ {
+ char buf[ 256 ];
+ mir_snprintf( buf, sizeof( buf ), "Root2ProtocolIcon_%s_%s", pa->szModuleName, tmi.pszName );
+ MO_SetOptionsMenuItem( hStatusMainMenuHandles[j], OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
+ }
+ IconLib_ReleaseIcon( tmi.hIcon, 0 );
+ break;
+ } }
+
+ BuildStatusMenu(0,0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int sttRebuildHotkeys( WPARAM, LPARAM )
+{
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof( tmi );
+ tmi.flags = CMIM_HOTKEY | CMIM_NAME | CMIF_TCHAR;
+
+ for ( int j=0; j < SIZEOF(statusModeList); j++ ) {
+ TCHAR buf[ 256 ], hotkeyName[ 100 ];
+ WORD hotKey = GetHotkeyValue( statusHotkeys[j] );
+ HotkeyToName( hotkeyName, SIZEOF(hotkeyName), HIBYTE(hotKey), LOBYTE(hotKey));
+ mir_sntprintf( buf, SIZEOF( buf ), TranslateT("%s\t%s"),
+ cli.pfnGetStatusModeDescription( statusModeList[j], 0 ), hotkeyName );
+ tmi.ptszName = buf;
+ tmi.hotKey = MAKELONG(HIBYTE(hotKey), LOBYTE(hotKey));
+ MO_ModifyMenuItem( hStatusMainMenuHandles[j], &tmi );
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int statustopos(int status)
+{
+ int j;
+ for ( j = 0; j < SIZEOF(statusModeList); j++ )
+ if ( status == statusModeList[j] )
+ return j;
+
+ return -1;
+}
+
+static int MenuProtoAck(WPARAM, LPARAM lParam)
+{
+ int i;
+ ACKDATA* ack=(ACKDATA*)lParam;
+ int overallStatus;
+ TMO_MenuItem tmi;
+
+ if ( ack->type != ACKTYPE_STATUS ) return 0;
+ if ( ack->result != ACKRESULT_SUCCESS ) return 0;
+ if ( hStatusMainMenuHandles == NULL ) return 0;
+
+ if ( cli.pfnGetProtocolVisibility( ack->szModule ) == 0 ) return 0;
+
+ overallStatus = GetAverageMode();
+
+ memset(&tmi,0,sizeof(tmi));
+ tmi.cbSize=sizeof(tmi);
+ if (overallStatus >= ID_STATUS_OFFLINE) {
+ int pos = statustopos(cli.currentStatusMenuItem);
+ if (pos==-1) pos=0;
+ { // reset all current possible checked statuses
+ int pos2;
+ for (pos2=0; pos2<hStatusMainMenuHandlesCnt; pos2++)
+ {
+ if (pos2>=0 && pos2 < hStatusMainMenuHandlesCnt)
+ {
+ tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
+ MO_ModifyMenuItem( hStatusMainMenuHandles[pos2], &tmi );
+ } } }
+
+ cli.currentStatusMenuItem=overallStatus;
+ pos = statustopos(cli.currentStatusMenuItem);
+ if (pos>=0 && pos < hStatusMainMenuHandlesCnt) {
+ tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE | CMIF_CHECKED;
+ MO_ModifyMenuItem( hStatusMainMenuHandles[pos], &tmi );
+ }
+// cli.currentDesiredStatusMode = cli.currentStatusMenuItem;
+ }
+ else {
+ int pos = statustopos( cli.currentStatusMenuItem );
+ if ( pos == -1 ) pos=0;
+ if ( pos >= 0 && pos < hStatusMainMenuHandlesCnt ) {
+ tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
+ MO_ModifyMenuItem( hStatusMainMenuHandles[pos], &tmi );
+ }
+ //SetMenuDefaultItem(hStatusMenu,-1,FALSE);
+ cli.currentStatusMenuItem=0;
+ }
+
+ for ( i=0; i < accounts.getCount(); i++ ) {
+ if ( !lstrcmpA( accounts[i]->szModuleName, ack->szModule )) {
+ //hProcess is previous mode, lParam is new mode
+ if ((( int )ack->hProcess >= ID_STATUS_OFFLINE || ( int )ack->hProcess == 0 ) && ( int )ack->hProcess < ID_STATUS_OFFLINE + SIZEOF(statusModeList)) {
+ int pos = statustopos(( int )ack->hProcess);
+ if ( pos == -1 )
+ pos = 0;
+ for ( pos = 0; pos < SIZEOF(statusModeList); pos++ ) {
+ tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE;
+ MO_ModifyMenuItem( hStatusMenuHandles[i].menuhandle[pos], &tmi );
+ } }
+
+ if ( ack->lParam >= ID_STATUS_OFFLINE && ack->lParam < ID_STATUS_OFFLINE + SIZEOF(statusModeList)) {
+ int pos = statustopos(( int )ack->lParam );
+ if ( pos >= 0 && pos < SIZEOF(statusModeList)) {
+ tmi.flags = CMIM_FLAGS | CMIF_ROOTHANDLE | CMIF_CHECKED;
+ MO_ModifyMenuItem( hStatusMenuHandles[i].menuhandle[pos], &tmi );
+ } }
+ break;
+ } }
+
+ //BuildStatusMenu(0,0);
+ return 0;
+}
+
+static MenuProto* FindProtocolMenu( const char* proto )
+{
+ for (int i=0; i < cli.menuProtoCount; i++)
+ if ( cli.menuProtos[i].pMenu && !lstrcmpiA( cli.menuProtos[i].szProto, proto ))
+ return &cli.menuProtos[i];
+
+ if ( cli.menuProtoCount == 1 )
+ if ( !lstrcmpiA( cli.menuProtos[0].szProto, proto ))
+ return &cli.menuProtos[0];
+
+ return NULL;
+}
+
+HGENMENU fnGetProtocolMenu( const char* proto )
+{
+ MenuProto* mp = FindProtocolMenu( proto );
+ if ( mp )
+ return mp->pMenu;
+
+ return NULL;
+}
+
+static INT_PTR AddStatusMenuItem(WPARAM wParam,LPARAM lParam)
+{
+ CLISTMENUITEM *mi = ( CLISTMENUITEM* )lParam;
+ if ( mi->cbSize != sizeof( CLISTMENUITEM ))
+ return 0;
+
+ PMO_IntMenuItem pRoot = NULL;
+ lpStatusMenuExecParam smep = NULL;
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.hIcon = mi->hIcon;
+ tmi.hotKey = mi->hotKey;
+ tmi.position = mi->position;
+ tmi.pszName = mi->pszName;
+ tmi.flags = mi->flags;
+ tmi.root = mi->hParentMenu;
+
+ // for new style menus the pszPopupName contains the root menu handle
+ if ( mi->flags & CMIF_ROOTHANDLE )
+ pRoot = MO_GetIntMenuItem( mi->hParentMenu );
+
+ // for old style menus the pszPopupName really means the popup name
+ else {
+ MenuProto* mp = FindProtocolMenu( mi->pszContactOwner );
+ if ( mp && mi->pszPopupName ) {
+ if ( mp->pMenu ) {
+ #if defined _UNICODE
+ TCHAR* ptszName = ( mi->flags & CMIF_UNICODE ) ? mir_tstrdup(mi->ptszPopupName) : mir_a2t(mi->pszPopupName);
+ pRoot = MO_RecursiveWalkMenu( mp->pMenu->submenu.first, FindRoot, ptszName );
+ mir_free( ptszName );
+ #else
+ pRoot = MO_RecursiveWalkMenu( mp->pMenu->submenu.first, FindRoot, mi->pszPopupName );
+ #endif
+ }
+ if ( pRoot == NULL ) {
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = (mi->flags & CMIF_UNICODE) | CMIF_ROOTHANDLE;
+ tmi.position = 1001;
+ tmi.root = mp->pMenu;
+ tmi.hIcon = NULL;
+ tmi.pszName = mi->pszPopupName;
+ pRoot = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+ }
+
+ tmi.flags |= CMIF_ROOTHANDLE;
+ tmi.root = pRoot;
+ } }
+
+ if (wParam) {
+ int * res=(int*)wParam;
+ *res = ( int )pRoot;
+ }
+
+ //owner data
+ if ( mi->pszService ) {
+ smep = ( lpStatusMenuExecParam )mir_calloc(sizeof(StatusMenuExecParam));
+ smep->custom = TRUE;
+ smep->svc=mir_strdup(mi->pszService);
+ {
+ char *buf=mir_strdup(mi->pszService);
+ int i=0;
+ while(buf[i]!='\0' && buf[i]!='/') i++;
+ buf[i]='\0';
+ smep->proto=mir_strdup(buf);
+ mir_free(buf);
+ }
+ tmi.ownerdata = smep;
+ }
+ PMO_IntMenuItem menuHandle = MO_AddNewMenuItem( hStatusMenuObject, &tmi );
+ if ( smep )
+ smep->hMenuItem = menuHandle;
+
+ char buf[MAX_PATH+64];
+ #if defined( _UNICODE )
+ {
+ char* p = ( pRoot ) ? mir_t2a( pRoot->mi.ptszName ) : NULL;
+ mir_snprintf( buf, SIZEOF(buf), "%s/%s", ( p ) ? p : "", mi->pszService ? mi->pszService : "" );
+ mir_free( p );
+ }
+ #else
+ mir_snprintf( buf, SIZEOF(buf), "%s/%s", pRoot ? pRoot->mi.ptszName : _T(""), mi->pszService ? mi->pszService : "" );
+ #endif
+ MO_SetOptionsMenuItem( menuHandle, OPT_MENUITEMSETUNIQNAME, ( INT_PTR )buf );
+
+ return ( INT_PTR )menuHandle;
+}
+
+static INT_PTR HotkeySetStatus(WPARAM wParam,LPARAM lParam)
+{
+ return SetStatusMode( lParam, 0 );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// PROTOCOL MENU
+
+static INT_PTR AddProtoMenuItem(WPARAM wParam,LPARAM lParam)
+{
+ if ( DBGetContactSettingByte( NULL, "CList", "MoveProtoMenus", FALSE ))
+ return AddStatusMenuItem( wParam, lParam );
+
+ return AddMainMenuItem( wParam, lParam );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitCustomMenus(void)
+{
+ CreateServiceFunction("MainMenuExecService",MainMenuExecService);
+
+ CreateServiceFunction("ContactMenuExecService",ContactMenuExecService);
+ CreateServiceFunction("ContactMenuCheckService",ContactMenuCheckService);
+
+ CreateServiceFunction("StatusMenuExecService",StatusMenuExecService);
+ CreateServiceFunction("StatusMenuCheckService",StatusMenuCheckService);
+
+ //free services
+ CreateServiceFunction("CLISTMENUS/FreeOwnerDataMainMenu",FreeOwnerDataMainMenu);
+ CreateServiceFunction("CLISTMENUS/FreeOwnerDataContactMenu",FreeOwnerDataContactMenu);
+ CreateServiceFunction("CLISTMENUS/FreeOwnerDataStatusMenu",FreeOwnerDataStatusMenu);
+
+ CreateServiceFunction(MS_CLIST_SETSTATUSMODE, SetStatusMode);
+
+ CreateServiceFunction(MS_CLIST_ADDMAINMENUITEM,AddMainMenuItem);
+ CreateServiceFunction(MS_CLIST_ADDSTATUSMENUITEM,AddStatusMenuItem);
+ CreateServiceFunction(MS_CLIST_MENUGETMAIN,MenuGetMain);
+ CreateServiceFunction(MS_CLIST_REMOVEMAINMENUITEM,RemoveMainMenuItem);
+ CreateServiceFunction(MS_CLIST_MENUBUILDMAIN,BuildMainMenu);
+
+ CreateServiceFunction(MS_CLIST_ADDCONTACTMENUITEM,AddContactMenuItem);
+ CreateServiceFunction(MS_CLIST_MENUBUILDCONTACT,BuildContactMenu);
+ CreateServiceFunction(MS_CLIST_REMOVECONTACTMENUITEM,RemoveContactMenuItem);
+
+ CreateServiceFunction(MS_CLIST_MODIFYMENUITEM,ModifyCustomMenuItem);
+ CreateServiceFunction(MS_CLIST_MENUMEASUREITEM,MeasureMenuItem);
+ CreateServiceFunction(MS_CLIST_MENUDRAWITEM,DrawMenuItem);
+
+ CreateServiceFunction(MS_CLIST_MENUGETSTATUS,BuildStatusMenu);
+ CreateServiceFunction(MS_CLIST_MENUPROCESSCOMMAND,MenuProcessCommand);
+ CreateServiceFunction(MS_CLIST_MENUPROCESSHOTKEY,MenuProcessHotkey);
+
+ CreateServiceFunction(MS_CLIST_ADDPROTOMENUITEM,AddProtoMenuItem);
+
+ hPreBuildContactMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDCONTACTMENU);
+ hPreBuildMainMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDMAINMENU);
+ cli.hPreBuildStatusMenuEvent=CreateHookableEvent(ME_CLIST_PREBUILDSTATUSMENU);
+ hStatusModeChangeEvent = CreateHookableEvent( ME_CLIST_STATUSMODECHANGE );
+
+ hAckHook=(HANDLE)HookEvent(ME_PROTO_ACK,MenuProtoAck);
+
+ hMainMenu = CreatePopupMenu();
+ hStatusMenu = CreatePopupMenu();
+
+ hStatusMainMenuHandles=NULL;
+ hStatusMainMenuHandlesCnt=0;
+
+ hStatusMenuHandles=NULL;
+ hStatusMenuHandlesCnt=0;
+
+ //new menu sys
+ InitGenMenu();
+
+ //main menu
+ {
+ TMenuParam tmp = { 0 };
+ tmp.cbSize=sizeof(tmp);
+ tmp.CheckService=NULL;
+ tmp.ExecService="MainMenuExecService";
+ tmp.name="MainMenu";
+ hMainMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
+ }
+
+ MO_SetOptionsMenuObject( hMainMenuObject, OPT_USERDEFINEDITEMS, TRUE );
+ MO_SetOptionsMenuObject( hMainMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataMainMenu" );
+
+ //contact menu
+ {
+ TMenuParam tmp = { 0 };
+ tmp.cbSize=sizeof(tmp);
+ tmp.CheckService="ContactMenuCheckService";
+ tmp.ExecService="ContactMenuExecService";
+ tmp.name="ContactMenu";
+ hContactMenuObject=(HANDLE)CallService(MO_CREATENEWMENUOBJECT,(WPARAM)0,(LPARAM)&tmp);
+ }
+
+ MO_SetOptionsMenuObject( hContactMenuObject, OPT_USERDEFINEDITEMS, TRUE );
+ MO_SetOptionsMenuObject( hContactMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, (INT_PTR)"CLISTMENUS/FreeOwnerDataContactMenu" );
+
+ // initialize hotkeys
+ CreateServiceFunction(MS_CLIST_HKSTATUS, HotkeySetStatus);
+
+ HOTKEYDESC hkd = { 0 };
+ hkd.cbSize = sizeof( hkd );
+ hkd.ptszSection = _T("Status");
+ hkd.dwFlags = HKD_TCHAR;
+ for ( int i = 0; i < SIZEOF(statusHotkeys); i++ ) {
+ char szName[30];
+ mir_snprintf( szName, SIZEOF(szName), "StatusHotKey_%d", i );
+ hkd.pszName = szName;
+ hkd.lParam = statusModeList[i];
+ hkd.ptszDescription = fnGetStatusModeDescription( hkd.lParam, 0 );
+ hkd.DefHotKey = HOTKEYCODE( HOTKEYF_CONTROL, '0'+i ) | HKF_MIRANDA_LOCAL;
+ hkd.pszService = MS_CLIST_HKSTATUS;
+ statusHotkeys[i] = CallService( MS_HOTKEY_REGISTER, 0, LPARAM( &hkd ));
+ }
+
+ HookEvent( ME_HOTKEYS_CHANGED, sttRebuildHotkeys );
+
+ // add exit command to menu
+ {
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof( mi );
+ mi.position = 0x7fffffff;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.pszService = "CloseAction";
+ mi.pszName = LPGEN("E&xit");
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_EXIT );
+ AddMainMenuItem( 0, ( LPARAM )&mi );
+ }
+
+ cli.currentStatusMenuItem=ID_STATUS_OFFLINE;
+ cli.currentDesiredStatusMode=ID_STATUS_OFFLINE;
+
+ if ( IsWinVer98Plus() )
+ HookEvent(ME_SKIN_ICONSCHANGED, MenuIconsChanged );
+}
+
+void UninitCustomMenus(void)
+{
+ mir_free(hStatusMainMenuHandles);
+ hStatusMainMenuHandles = NULL;
+
+ mir_free( hStatusMenuHandles );
+ hStatusMenuHandles = NULL;
+
+ if ( hMainMenuObject ) CallService( MO_REMOVEMENUOBJECT, (WPARAM)hMainMenuObject, 0 );
+ if ( hStatusMenuObject ) CallService( MO_REMOVEMENUOBJECT, (WPARAM)hMainMenuObject, 0 );
+
+ UnloadMoveToGroup();
+ FreeMenuProtos();
+
+ DestroyMenu(hMainMenu);
+ DestroyMenu(hStatusMenu);
+ UnhookEvent(hAckHook);
+}
diff --git a/src/modules/clist/clistmod.cpp b/src/modules/clist/clistmod.cpp
new file mode 100644
index 0000000000..7de958342b
--- /dev/null
+++ b/src/modules/clist/clistmod.cpp
@@ -0,0 +1,569 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+INT_PTR AddMainMenuItem(WPARAM wParam, LPARAM lParam);
+INT_PTR AddContactMenuItem(WPARAM wParam, LPARAM lParam);
+INT_PTR ContactChangeGroup(WPARAM wParam, LPARAM lParam);
+int InitCListEvents(void);
+void UninitCListEvents(void);
+int ContactSettingChanged(WPARAM wParam, LPARAM lParam);
+int ContactAdded(WPARAM wParam, LPARAM lParam);
+int ContactDeleted(WPARAM wParam, LPARAM lParam);
+INT_PTR GetContactDisplayName(WPARAM wParam, LPARAM lParam);
+INT_PTR InvalidateDisplayName(WPARAM wParam, LPARAM lParam);
+int InitGroupServices(void);
+INT_PTR Docking_IsDocked(WPARAM wParam, LPARAM lParam);
+void InitDisplayNameCache(void);
+void FreeDisplayNameCache(void);
+int LoadCLUIModule(void);
+int InitClistHotKeys(void);
+
+HANDLE hContactDoubleClicked, hContactIconChangedEvent;
+HIMAGELIST hCListImages;
+BOOL(WINAPI * MySetProcessWorkingSetSize) (HANDLE, SIZE_T, SIZE_T);
+
+extern BYTE nameOrder[];
+
+struct ProtoIconIndex
+{
+ char *szProto;
+ int iIconBase;
+};
+
+OBJLIST<ProtoIconIndex> protoIconIndex(5);
+
+static HANDLE hProtoAckHook;
+static HANDLE hContactSettingChanged;
+
+TCHAR* fnGetStatusModeDescription( int mode, int flags )
+{
+ static TCHAR szMode[64];
+ TCHAR* descr;
+ int noPrefixReqd = 0;
+ switch (mode) {
+ case ID_STATUS_OFFLINE:
+ descr = _T("Offline");
+ noPrefixReqd = 1;
+ break;
+ case ID_STATUS_CONNECTING:
+ descr = _T("Connecting");
+ noPrefixReqd = 1;
+ break;
+ case ID_STATUS_ONLINE:
+ descr = _T("Online");
+ noPrefixReqd = 1;
+ break;
+ case ID_STATUS_AWAY:
+ descr = _T("Away");
+ break;
+ case ID_STATUS_DND:
+ descr = _T("DND");
+ break;
+ case ID_STATUS_NA:
+ descr = _T("NA");
+ break;
+ case ID_STATUS_OCCUPIED:
+ descr = _T("Occupied");
+ break;
+ case ID_STATUS_FREECHAT:
+ descr = _T("Free for chat");
+ break;
+ case ID_STATUS_INVISIBLE:
+ descr = _T("Invisible");
+ break;
+ case ID_STATUS_OUTTOLUNCH:
+ descr = _T("Out to lunch");
+ break;
+ case ID_STATUS_ONTHEPHONE:
+ descr = _T("On the phone");
+ break;
+ case ID_STATUS_IDLE:
+ descr = _T("Idle");
+ break;
+ default:
+ if (mode > ID_STATUS_CONNECTING && mode < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES) {
+ const TCHAR* connFmt = _T("Connecting (attempt %d)");
+ mir_sntprintf(szMode, SIZEOF(szMode), (flags&GSMDF_UNTRANSLATED)?connFmt:TranslateTS(connFmt), mode - ID_STATUS_CONNECTING + 1);
+ return szMode;
+ }
+ return NULL;
+ }
+ if (noPrefixReqd || !(flags & GSMDF_PREFIXONLINE))
+ return ( flags & GSMDF_UNTRANSLATED ) ? descr : TranslateTS( descr );
+
+ lstrcpy( szMode, TranslateT( "Online" ));
+ lstrcat( szMode, _T(": "));
+ lstrcat( szMode, ( flags & GSMDF_UNTRANSLATED ) ? descr : TranslateTS( descr ));
+ return szMode;
+}
+
+static INT_PTR GetStatusModeDescription(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR* buf1 = cli.pfnGetStatusModeDescription( wParam, lParam );
+
+ #ifdef UNICODE
+ if ( !( lParam & GSMDF_TCHAR ))
+ {
+ static char szMode[64];
+ char *buf2 = mir_u2a(buf1);
+ mir_snprintf(szMode, SIZEOF(szMode), "%s", buf2);
+ mir_free(buf2);
+ return (INT_PTR)szMode;
+ }
+ #endif
+
+ return (INT_PTR)buf1;
+}
+
+static int ProtocolAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA *) lParam;
+
+ if (ack->type != ACKTYPE_STATUS)
+ return 0;
+ CallService(MS_CLUI_PROTOCOLSTATUSCHANGED, ack->lParam, (LPARAM) ack->szModule);
+
+ if ((int) ack->hProcess < ID_STATUS_ONLINE && ack->lParam >= ID_STATUS_ONLINE) {
+ DWORD caps;
+ caps = (DWORD) CallProtoService(ack->szModule, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (caps & PF1_SERVERCLIST) {
+ HANDLE hContact;
+ char *szProto;
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact) {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, ack->szModule))
+ if (DBGetContactSettingByte(hContact, "CList", "Delete", 0))
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ } } }
+
+ cli.pfnTrayIconUpdateBase(ack->szModule);
+ return 0;
+}
+
+HICON fnGetIconFromStatusMode( HANDLE hContact, const char *szProto, int status )
+{
+ return ImageList_GetIcon( hCListImages, cli.pfnIconFromStatusMode( szProto, status, hContact ), ILD_NORMAL);
+}
+
+int fnIconFromStatusMode(const char *szProto, int status, HANDLE )
+{
+ int index, i;
+
+ for ( index = 0; index < SIZEOF(statusModeList); index++ )
+ if ( status == statusModeList[index] )
+ break;
+
+ if ( index == SIZEOF(statusModeList))
+ index = 0;
+ if (szProto == NULL)
+ return index + 1;
+ for ( i = 0; i < protoIconIndex.getCount(); i++ ) {
+ if (strcmp(szProto, protoIconIndex[i].szProto) == 0)
+ return protoIconIndex[i].iIconBase + index;
+ }
+ return 1;
+}
+
+static INT_PTR GetContactIcon(WPARAM wParam, LPARAM)
+{
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ HANDLE hContact = (HANDLE)wParam;
+
+ return cli.pfnIconFromStatusMode(szProto,
+ szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE), hContact);
+}
+
+static void AddProtoIconIndex( PROTOACCOUNT* pa )
+{
+ ProtoIconIndex *pii = new ProtoIconIndex;
+ pii->szProto = pa->szModuleName;
+ for (int i = 0; i < SIZEOF(statusModeList); i++) {
+ int iImg = ImageList_AddIcon_ProtoIconLibLoaded(hCListImages, pa->szModuleName, statusModeList[i] );
+ if (i == 0)
+ pii->iIconBase = iImg;
+ }
+ protoIconIndex.insert(pii);
+}
+
+static void RemoveProtoIconIndex( PROTOACCOUNT* pa )
+{
+ for (int i = 0; i < protoIconIndex.getCount(); i++)
+ if (strcmp(protoIconIndex[i].szProto, pa->szModuleName) == 0) {
+ protoIconIndex.remove(i);
+ break;
+ }
+}
+
+static int ContactListModulesLoaded(WPARAM, LPARAM)
+{
+ if ( !ServiceExists( MS_DB_CONTACT_GETSETTING_STR )) {
+ MessageBox( NULL, TranslateT( "This plugin requires db3x plugin version 0.5.1.0 or later" ), _T("CList"), MB_OK );
+ return 1;
+ }
+
+ RebuildMenuOrder();
+ for (int i = 0; i < accounts.getCount(); i++)
+ AddProtoIconIndex( accounts[i] );
+
+ cli.pfnLoadContactTree();
+
+ LoadCLUIModule();
+
+ InitClistHotKeys();
+
+ return 0;
+}
+
+static int ContactListAccountsChanged( WPARAM eventCode, LPARAM lParam )
+{
+ switch (eventCode)
+ {
+ case PRAC_ADDED:
+ AddProtoIconIndex(( PROTOACCOUNT* )lParam );
+ break;
+
+ case PRAC_REMOVED:
+ RemoveProtoIconIndex(( PROTOACCOUNT* )lParam );
+ break;
+ }
+ cli.pfnReloadProtoMenus();
+ cli.pfnTrayIconIconsChanged();
+ cli.pfnClcBroadcast( INTM_RELOADOPTIONS, 0, 0 );
+ cli.pfnClcBroadcast( INTM_INVALIDATE, 0, 0 );
+ return 0;
+}
+
+static INT_PTR ContactDoubleClicked(WPARAM wParam, LPARAM)
+{
+ // Try to process event myself
+ if ( cli.pfnEventsProcessContactDoubleClick(( HANDLE )wParam ) == 0 )
+ return 0;
+
+ // Allow third-party plugins to process a dblclick
+ if ( NotifyEventHooks( hContactDoubleClicked, wParam, 0 ))
+ return 0;
+
+ // Otherwise try to execute the default action
+ TryProcessDoubleClick(( HANDLE )wParam );
+ return 0;
+}
+
+static INT_PTR GetIconsImageList(WPARAM, LPARAM)
+{
+ return (INT_PTR)hCListImages;
+}
+
+static INT_PTR ContactFilesDropped(WPARAM wParam, LPARAM lParam)
+{
+ CallService(MS_FILE_SENDSPECIFICFILES, wParam, lParam);
+ return 0;
+}
+
+static int CListIconsChanged(WPARAM, LPARAM)
+{
+ int i, j;
+
+ for (i = 0; i < SIZEOF(statusModeList); i++)
+ ImageList_ReplaceIcon_IconLibLoaded(hCListImages, i + 1, LoadSkinIcon( skinIconStatusList[i] ));
+ ImageList_ReplaceIcon_IconLibLoaded(hCListImages, IMAGE_GROUPOPEN, LoadSkinIcon( SKINICON_OTHER_GROUPOPEN ));
+ ImageList_ReplaceIcon_IconLibLoaded(hCListImages, IMAGE_GROUPSHUT, LoadSkinIcon( SKINICON_OTHER_GROUPSHUT ));
+ for (i = 0; i < protoIconIndex.getCount(); i++)
+ for (j = 0; j < SIZEOF(statusModeList); j++)
+ ImageList_ReplaceIcon_IconLibLoaded(hCListImages, protoIconIndex[i].iIconBase + j, LoadSkinProtoIcon(protoIconIndex[i].szProto, statusModeList[j] ));
+ cli.pfnTrayIconIconsChanged();
+ cli.pfnInvalidateRect( cli.hwndContactList, NULL, TRUE);
+ return 0;
+}
+
+/*
+Begin of Hrk's code for bug
+*/
+#define GWVS_HIDDEN 1
+#define GWVS_VISIBLE 2
+#define GWVS_COVERED 3
+#define GWVS_PARTIALLY_COVERED 4
+
+int fnGetWindowVisibleState(HWND hWnd, int iStepX, int iStepY)
+{
+ RECT rc, rcWin, rcWorkArea;
+ POINT pt;
+ register int i, j, width, height, iCountedDots = 0, iNotCoveredDots = 0;
+ BOOL bPartiallyCovered = FALSE;
+ HWND hAux = 0;
+
+ if (hWnd == NULL) {
+ SetLastError(0x00000006); //Wrong handle
+ return -1;
+ }
+ //Some defaults now. The routine is designed for thin and tall windows.
+ if (iStepX <= 0)
+ iStepX = 4;
+ if (iStepY <= 0)
+ iStepY = 16;
+
+ if (IsIconic(hWnd) || !IsWindowVisible(hWnd))
+ return GWVS_HIDDEN;
+ else
+ {
+ if (CallService(MS_CLIST_DOCKINGISDOCKED, 0, 0))
+ return GWVS_VISIBLE;
+
+ GetWindowRect(hWnd, &rcWin);
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ if (MyMonitorFromWindow)
+ {
+ HMONITOR hMon = MyMonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (MyGetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+ }
+
+ IntersectRect(&rc, &rcWin, &rcWorkArea);
+
+ width = rc.right - rc.left;
+ height = rc.bottom - rc.top;
+
+ for (i = rc.top; i < rc.bottom; i += (height / iStepY)) {
+ pt.y = i;
+ for (j = rc.left; j < rc.right; j += (width / iStepX)) {
+ pt.x = j;
+ hAux = WindowFromPoint(pt);
+ while (GetParent(hAux) != NULL)
+ hAux = GetParent(hAux);
+ if (hAux != hWnd && hAux != NULL) //There's another window!
+ bPartiallyCovered = TRUE;
+ else
+ iNotCoveredDots++; //Let's count the not covered dots.
+ iCountedDots++; //Let's keep track of how many dots we checked.
+ }
+ }
+ if (iNotCoveredDots == iCountedDots) //Every dot was not covered: the window is visible.
+ return GWVS_VISIBLE;
+ else if (iNotCoveredDots == 0) //They're all covered!
+ return GWVS_COVERED;
+ else //There are dots which are visible, but they are not as many as the ones we counted: it's partially covered.
+ return GWVS_PARTIALLY_COVERED;
+ }
+}
+
+int fnShowHide(WPARAM, LPARAM)
+{
+ BOOL bShow = FALSE;
+
+ int iVisibleState = cli.pfnGetWindowVisibleState(cli.hwndContactList, 0, 0);
+
+ //bShow is FALSE when we enter the switch.
+ switch (iVisibleState) {
+ case GWVS_PARTIALLY_COVERED:
+ //If we don't want to bring it to top, we can use a simple break. This goes against readability ;-) but the comment explains it.
+ if (!DBGetContactSettingByte(NULL, "CList", "BringToFront", SETTING_BRINGTOFRONT_DEFAULT))
+ break;
+ case GWVS_COVERED: //Fall through (and we're already falling)
+ case GWVS_HIDDEN:
+ bShow = TRUE;
+ break;
+ case GWVS_VISIBLE: //This is not needed, but goes for readability.
+ bShow = FALSE;
+ break;
+ case -1: //We can't get here, both cli.hwndContactList and iStepX and iStepY are right.
+ return 0;
+ }
+ if (bShow == TRUE) {
+ RECT rcWindow;
+
+ ShowWindow(cli.hwndContactList, SW_RESTORE);
+ if (!DBGetContactSettingByte(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT))
+ SetWindowPos(cli.hwndContactList, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ else
+ SetWindowPos(cli.hwndContactList, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+
+ SetForegroundWindow(cli.hwndContactList);
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_NORMAL);
+
+ //this forces the window onto the visible screen
+ GetWindowRect(cli.hwndContactList, &rcWindow);
+ if (Utils_AssertInsideScreen(&rcWindow) == 1)
+ {
+ MoveWindow(cli.hwndContactList, rcWindow.left, rcWindow.top,
+ rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, TRUE);
+ }
+ }
+ else { //It needs to be hidden
+ if (DBGetContactSettingByte(NULL, "CList", "ToolWindow", SETTING_TOOLWINDOW_DEFAULT) ||
+ DBGetContactSettingByte(NULL, "CList", "Min2Tray", SETTING_MIN2TRAY_DEFAULT))
+ {
+ ShowWindow(cli.hwndContactList, SW_HIDE);
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_HIDDEN);
+ }
+ else
+ {
+ ShowWindow(cli.hwndContactList, SW_MINIMIZE);
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_MINIMIZED);
+ }
+
+ if (MySetProcessWorkingSetSize != NULL && DBGetContactSettingByte(NULL, "CList", "DisableWorkingSet", 1))
+ MySetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// old evil code. hopefully it will be deleted soon, cause nobody uses it now
+
+#define SAFESTRING(a) a?a:""
+
+int GetStatusModeOrdering(int statusMode);
+extern int sortByStatus, sortByProto;
+
+static INT_PTR CompareContacts( WPARAM wParam, LPARAM lParam )
+{
+ HANDLE a = (HANDLE) wParam, b = (HANDLE) lParam;
+ TCHAR namea[128], *nameb;
+ int statusa, statusb;
+ char *szProto1, *szProto2;
+ int rc;
+
+ szProto1 = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) a, 0);
+ szProto2 = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) b, 0);
+ statusa = DBGetContactSettingWord((HANDLE) a, SAFESTRING(szProto1), "Status", ID_STATUS_OFFLINE);
+ statusb = DBGetContactSettingWord((HANDLE) b, SAFESTRING(szProto2), "Status", ID_STATUS_OFFLINE);
+
+ if (sortByProto) {
+ /* deal with statuses, online contacts have to go above offline */
+ if ((statusa == ID_STATUS_OFFLINE) != (statusb == ID_STATUS_OFFLINE)) {
+ return 2 * (statusa == ID_STATUS_OFFLINE) - 1;
+ }
+ /* both are online, now check protocols */
+ rc = strcmp(SAFESTRING(szProto1), SAFESTRING(szProto2)); /* strcmp() doesn't like NULL so feed in "" as needed */
+ if (rc != 0 && (szProto1 != NULL && szProto2 != NULL))
+ return rc;
+ /* protocols are the same, order by display name */
+ }
+
+ if (sortByStatus) {
+ int ordera, orderb;
+ ordera = GetStatusModeOrdering(statusa);
+ orderb = GetStatusModeOrdering(statusb);
+ if (ordera != orderb)
+ return ordera - orderb;
+ }
+ else {
+ //one is offline: offline goes below online
+ if ((statusa == ID_STATUS_OFFLINE) != (statusb == ID_STATUS_OFFLINE)) {
+ return 2 * (statusa == ID_STATUS_OFFLINE) - 1;
+ }
+ }
+
+ nameb = cli.pfnGetContactDisplayName( a, 0);
+ _tcsncpy(namea, nameb, SIZEOF(namea));
+ namea[ SIZEOF(namea)-1 ] = 0;
+ nameb = cli.pfnGetContactDisplayName( b, 0);
+
+ //otherwise just compare names
+ return _tcsicmp(namea, nameb);
+}
+
+/***************************************************************************************/
+
+static INT_PTR TrayIconProcessMessageStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnTrayIconProcessMessage( wParam, lParam ); }
+static INT_PTR TrayIconPauseAutoHideStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnTrayIconPauseAutoHide( wParam, lParam ); }
+static INT_PTR ShowHideStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnShowHide( wParam, lParam ); }
+static INT_PTR SetHideOfflineStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnSetHideOffline( wParam, lParam ); }
+static INT_PTR Docking_ProcessWindowMessageStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnDocking_ProcessWindowMessage( wParam, lParam ); }
+static INT_PTR HotkeysProcessMessageStub( WPARAM wParam, LPARAM lParam ) { return cli.pfnHotkeysProcessMessage( wParam, lParam ); }
+
+int LoadContactListModule2(void)
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, ContactListModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, ContactListAccountsChanged);
+ hContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChanged);
+ HookEvent(ME_DB_CONTACT_ADDED, ContactAdded);
+ HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted);
+ hProtoAckHook = (HANDLE) HookEvent(ME_PROTO_ACK, ProtocolAck);
+ hContactDoubleClicked = CreateHookableEvent(ME_CLIST_DOUBLECLICKED);
+ hContactIconChangedEvent = CreateHookableEvent(ME_CLIST_CONTACTICONCHANGED);
+ CreateServiceFunction(MS_CLIST_CONTACTDOUBLECLICKED, ContactDoubleClicked);
+ CreateServiceFunction(MS_CLIST_CONTACTFILESDROPPED, ContactFilesDropped);
+ CreateServiceFunction(MS_CLIST_GETSTATUSMODEDESCRIPTION, GetStatusModeDescription);
+ CreateServiceFunction(MS_CLIST_GETCONTACTDISPLAYNAME, GetContactDisplayName);
+ CreateServiceFunction(MS_CLIST_INVALIDATEDISPLAYNAME, InvalidateDisplayName);
+ CreateServiceFunction(MS_CLIST_TRAYICONPROCESSMESSAGE, TrayIconProcessMessageStub );
+ CreateServiceFunction(MS_CLIST_PAUSEAUTOHIDE, TrayIconPauseAutoHideStub);
+ CreateServiceFunction(MS_CLIST_CONTACTSCOMPARE, CompareContacts);
+ CreateServiceFunction(MS_CLIST_CONTACTCHANGEGROUP, ContactChangeGroup);
+ CreateServiceFunction(MS_CLIST_SHOWHIDE, ShowHideStub);
+ CreateServiceFunction(MS_CLIST_SETHIDEOFFLINE, SetHideOfflineStub);
+ CreateServiceFunction(MS_CLIST_DOCKINGPROCESSMESSAGE, Docking_ProcessWindowMessageStub);
+ CreateServiceFunction(MS_CLIST_DOCKINGISDOCKED, Docking_IsDocked);
+ CreateServiceFunction(MS_CLIST_HOTKEYSPROCESSMESSAGE, HotkeysProcessMessageStub);
+ CreateServiceFunction(MS_CLIST_GETCONTACTICON, GetContactIcon);
+ MySetProcessWorkingSetSize = (BOOL(WINAPI *) (HANDLE, SIZE_T, SIZE_T)) GetProcAddress(GetModuleHandleA("kernel32"), "SetProcessWorkingSetSize");
+ InitDisplayNameCache();
+ InitCListEvents();
+ InitGroupServices();
+ cli.pfnInitTray();
+
+ hCListImages = ImageList_Create(16, 16, ILC_MASK | (IsWinVerXPPlus()? ILC_COLOR32 : ILC_COLOR16), 13, 0);
+ HookEvent(ME_SKIN_ICONSCHANGED, CListIconsChanged);
+ CreateServiceFunction(MS_CLIST_GETICONSIMAGELIST, GetIconsImageList);
+
+ ImageList_AddIcon_NotShared(hCListImages, MAKEINTRESOURCE(IDI_BLANK));
+
+ {
+ int i;
+ //now all core skin icons are loaded via icon lib. so lets release them
+ for (i = 0; i < SIZEOF(statusModeList); i++)
+ ImageList_AddIcon_IconLibLoaded(hCListImages, skinIconStatusList[i] );
+ }
+
+ //see IMAGE_GROUP... in clist.h if you add more images above here
+ ImageList_AddIcon_IconLibLoaded(hCListImages, SKINICON_OTHER_GROUPOPEN );
+ ImageList_AddIcon_IconLibLoaded(hCListImages, SKINICON_OTHER_GROUPSHUT );
+ return 0;
+}
+
+void UnloadContactListModule()
+{
+ if ( hCListImages ) {
+ //remove transitory contacts
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL) {
+ HANDLE hNext = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
+ hContact = hNext;
+ }
+ ImageList_Destroy(hCListImages);
+ UnhookEvent(hProtoAckHook);
+ UninitCListEvents();
+ protoIconIndex.destroy();
+ DestroyHookableEvent(hContactDoubleClicked);
+ UnhookEvent(hContactSettingChanged);
+} }
diff --git a/src/modules/clist/clistsettings.cpp b/src/modules/clist/clistsettings.cpp
new file mode 100644
index 0000000000..df92386ded
--- /dev/null
+++ b/src/modules/clist/clistsettings.cpp
@@ -0,0 +1,331 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+SortedList* clistCache = NULL;
+
+static int compareContacts( ClcCacheEntryBase* p1, ClcCacheEntryBase* p2 )
+{
+ return ( char* )p1->hContact - ( char* )p2->hContact;
+}
+
+void InitDisplayNameCache(void)
+{
+ clistCache = List_Create( 0, 50 );
+ clistCache->sortFunc = ( FSortFunc )compareContacts;
+}
+
+void FreeDisplayNameCache(void)
+{
+ if ( clistCache != NULL ) {
+ int i;
+ for ( i = 0; i < clistCache->realCount; i++) {
+ cli.pfnFreeCacheItem(( ClcCacheEntryBase* )clistCache->items[i] );
+ mir_free( clistCache->items[i] );
+ }
+
+ List_Destroy( clistCache );
+ mir_free(clistCache);
+ clistCache = NULL;
+} }
+
+// default handlers for the cache item creation and destruction
+
+ClcCacheEntryBase* fnCreateCacheItem( HANDLE hContact )
+{
+ ClcCacheEntryBase* p = ( ClcCacheEntryBase* )mir_calloc( sizeof( ClcCacheEntryBase ));
+ if ( p == NULL )
+ return NULL;
+
+ p->hContact = hContact;
+ return p;
+}
+
+void fnCheckCacheItem( ClcCacheEntryBase* p )
+{
+ DBVARIANT dbv;
+ if ( p->group == NULL ) {
+ if ( !DBGetContactSettingTString( p->hContact, "CList", "Group", &dbv )) {
+ p->group = mir_tstrdup( dbv.ptszVal );
+ mir_free( dbv.ptszVal );
+ }
+ else p->group = mir_tstrdup( _T("") );
+ }
+
+ if ( p->isHidden == -1 )
+ p->isHidden = DBGetContactSettingByte( p->hContact, "CList", "Hidden", 0 );
+}
+
+void fnFreeCacheItem( ClcCacheEntryBase* p )
+{
+ if ( p->name ) { mir_free( p->name ); p->name = NULL; }
+ #if defined( _UNICODE )
+ if ( p->szName ) { mir_free( p->szName); p->szName = NULL; }
+ #endif
+ if ( p->group ) { mir_free( p->group ); p->group = NULL; }
+ p->isHidden = -1;
+}
+
+ClcCacheEntryBase* fnGetCacheEntry(HANDLE hContact)
+{
+ ClcCacheEntryBase* p;
+ int idx;
+ if ( !List_GetIndex( clistCache, &hContact, &idx )) {
+ if (( p = cli.pfnCreateCacheItem( hContact )) != NULL ) {
+ List_Insert( clistCache, p, idx );
+ cli.pfnInvalidateDisplayNameCacheEntry( p );
+ }
+ }
+ else p = ( ClcCacheEntryBase* )clistCache->items[idx];
+
+ cli.pfnCheckCacheItem( p );
+ return p;
+}
+
+void fnInvalidateDisplayNameCacheEntry(HANDLE hContact)
+{
+ if (hContact == INVALID_HANDLE_VALUE) {
+ FreeDisplayNameCache();
+ InitDisplayNameCache();
+ SendMessage(cli.hwndContactTree, CLM_AUTOREBUILD, 0, 0);
+ }
+ else {
+ int idx;
+ if ( List_GetIndex( clistCache, &hContact, &idx ))
+ cli.pfnFreeCacheItem(( ClcCacheEntryBase* )clistCache->items[idx] );
+} }
+
+TCHAR* fnGetContactDisplayName( HANDLE hContact, int mode )
+{
+ CONTACTINFO ci;
+ TCHAR *buffer;
+ ClcCacheEntryBase* cacheEntry = NULL;
+
+ if ( mode & GCDNF_NOCACHE )
+ mode &= ~GCDNF_NOCACHE;
+ else if ( mode != GCDNF_NOMYHANDLE) {
+ cacheEntry = cli.pfnGetCacheEntry( hContact );
+ if ( cacheEntry->name )
+ return cacheEntry->name;
+ }
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ if (ci.hContact == NULL)
+ ci.szProto = "ICQ";
+ ci.dwFlag = ((mode == GCDNF_NOMYHANDLE) ? CNF_DISPLAYNC : CNF_DISPLAY) | CNF_TCHAR;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ if (ci.type == CNFT_ASCIIZ) {
+ if (cacheEntry == NULL)
+ return ci.pszVal;
+
+ cacheEntry->name = ci.pszVal;
+ #if defined( _UNICODE )
+ cacheEntry->szName = mir_u2a( ci.pszVal );
+ #endif
+ return ci.pszVal;
+ }
+
+ if (ci.type == CNFT_DWORD) {
+ if (cacheEntry == NULL) {
+ buffer = (TCHAR*) mir_alloc(15 * sizeof( TCHAR ));
+ _ltot(ci.dVal, buffer, 10 );
+ return buffer;
+ }
+ else {
+ buffer = (TCHAR*) mir_alloc(15 * sizeof( TCHAR ));
+ _ltot(ci.dVal, buffer, 10 );
+ cacheEntry->name = buffer;
+ #if defined( _UNICODE )
+ cacheEntry->szName = mir_u2a( buffer );
+ #endif
+ return buffer;
+ } } }
+
+ CallContactService(hContact, PSS_GETINFO, SGIF_MINIMAL, 0);
+ buffer = TranslateT("(Unknown Contact)");
+ return ( cacheEntry == NULL ) ? mir_tstrdup( buffer ) : buffer;
+}
+
+INT_PTR GetContactDisplayName(WPARAM wParam, LPARAM lParam)
+{
+ CONTACTINFO ci;
+ ClcCacheEntryBase* cacheEntry = NULL;
+ char *buffer;
+ HANDLE hContact = (HANDLE)wParam;
+
+ if ( lParam & GCDNF_UNICODE )
+ return ( INT_PTR )cli.pfnGetContactDisplayName(hContact, lParam & ~GCDNF_UNICODE );
+
+ if ((int) lParam != GCDNF_NOMYHANDLE) {
+ cacheEntry = cli.pfnGetCacheEntry(hContact);
+ #if defined( _UNICODE )
+ if ( cacheEntry->szName )
+ return (INT_PTR)cacheEntry->szName;
+ #else
+ if ( cacheEntry->name )
+ return (INT_PTR)cacheEntry->name;
+ #endif
+ }
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ if (ci.hContact == NULL)
+ ci.szProto = "ICQ";
+ ci.dwFlag = ((lParam == GCDNF_NOMYHANDLE) ? CNF_DISPLAYNC : CNF_DISPLAY) | CNF_TCHAR;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ if (ci.type == CNFT_ASCIIZ) {
+ if (cacheEntry == NULL) {
+ #if defined( _UNICODE )
+ buffer = mir_u2a( ci.pszVal );
+ mir_free(ci.pszVal);
+ #else
+ buffer = ci.pszVal;
+ #endif
+ return (INT_PTR) buffer;
+ }
+ else {
+ cacheEntry->name = ci.pszVal;
+ #if defined( _UNICODE )
+ cacheEntry->szName = mir_u2a( ci.pszVal );
+ return (INT_PTR)cacheEntry->szName;
+ #else
+ return (INT_PTR)cacheEntry->name;
+ #endif
+ }
+ }
+ if (ci.type == CNFT_DWORD) {
+ if (cacheEntry == NULL) {
+ buffer = ( char* )mir_alloc(15);
+ _ltoa(ci.dVal, buffer, 10 );
+ return (INT_PTR) buffer;
+ }
+ else {
+ buffer = ( char* )mir_alloc(15);
+ _ltoa(ci.dVal, buffer, 10 );
+ #if defined( _UNICODE )
+ cacheEntry->szName = buffer;
+ cacheEntry->name = mir_a2u( buffer );
+ #else
+ cacheEntry->name = buffer;
+ #endif
+ return (INT_PTR) buffer;
+ } } }
+
+ CallContactService(hContact, PSS_GETINFO, SGIF_MINIMAL, 0);
+ buffer = Translate("(Unknown Contact)");
+ return (INT_PTR) buffer;
+}
+
+INT_PTR InvalidateDisplayName(WPARAM wParam, LPARAM)
+{
+ cli.pfnInvalidateDisplayNameCacheEntry((HANDLE)wParam);
+ return 0;
+}
+
+int ContactAdded(WPARAM wParam, LPARAM)
+{
+ cli.pfnChangeContactIcon((HANDLE)wParam, cli.pfnIconFromStatusMode((char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0), ID_STATUS_OFFLINE, NULL), 1);
+ cli.pfnSortContacts();
+ return 0;
+}
+
+int ContactDeleted(WPARAM wParam, LPARAM)
+{
+ CallService(MS_CLUI_CONTACTDELETED, wParam, 0);
+ return 0;
+}
+
+int ContactSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
+ DBVARIANT dbv;
+ HANDLE hContact = (HANDLE)wParam;
+
+ // Early exit
+ if ( hContact == NULL)
+ return 0;
+
+ dbv.pszVal = NULL;
+ if (!DBGetContactSetting(hContact, "Protocol", "p", &dbv)) {
+ if (!strcmp(cws->szModule, dbv.pszVal)) {
+ cli.pfnInvalidateDisplayNameCacheEntry(hContact);
+ if (!strcmp(cws->szSetting, "UIN") || !strcmp(cws->szSetting, "Nick") || !strcmp(cws->szSetting, "FirstName")
+ || !strcmp(cws->szSetting, "LastName") || !strcmp(cws->szSetting, "e-mail")) {
+ CallService(MS_CLUI_CONTACTRENAMED, wParam, 0);
+ }
+ else if (!strcmp(cws->szSetting, "Status")) {
+ if (!DBGetContactSettingByte(hContact, "CList", "Hidden", 0)) {
+ if (DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT)) {
+ // User's state is changing, and we are hideOffline-ing
+ if (cws->value.wVal == ID_STATUS_OFFLINE) {
+ cli.pfnChangeContactIcon(hContact, cli.pfnIconFromStatusMode(cws->szModule, cws->value.wVal, hContact), 0);
+ CallService(MS_CLUI_CONTACTDELETED, wParam, 0);
+ mir_free(dbv.pszVal);
+ return 0;
+ }
+ cli.pfnChangeContactIcon(hContact, cli.pfnIconFromStatusMode(cws->szModule, cws->value.wVal, hContact), 1);
+ }
+ cli.pfnChangeContactIcon(hContact, cli.pfnIconFromStatusMode(cws->szModule, cws->value.wVal, hContact), 0);
+ }
+ }
+ else {
+ mir_free(dbv.pszVal);
+ return 0;
+ }
+ cli.pfnSortContacts();
+ } }
+
+ if (!strcmp(cws->szModule, "CList")) {
+ if (!strcmp(cws->szSetting, "Hidden")) {
+ if (cws->value.type == DBVT_DELETED || cws->value.bVal == 0) {
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ cli.pfnChangeContactIcon(hContact, cli.pfnIconFromStatusMode(szProto, szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE), hContact), 1);
+ }
+ else
+ CallService(MS_CLUI_CONTACTDELETED, wParam, 0);
+ }
+ if (!strcmp(cws->szSetting, "MyHandle"))
+ cli.pfnInvalidateDisplayNameCacheEntry(hContact);
+ }
+
+ if (!strcmp(cws->szModule, "Protocol")) {
+ if (!strcmp(cws->szSetting, "p")) {
+ char *szProto;
+ if (cws->value.type == DBVT_DELETED)
+ szProto = NULL;
+ else
+ szProto = cws->value.pszVal;
+ cli.pfnChangeContactIcon(hContact,
+ cli.pfnIconFromStatusMode(szProto,
+ szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord(hContact, szProto, "Status",
+ ID_STATUS_OFFLINE), hContact), 0);
+ } }
+
+ // Clean up
+ if (dbv.pszVal)
+ mir_free(dbv.pszVal);
+
+ return 0;
+}
diff --git a/src/modules/clist/clisttray.cpp b/src/modules/clist/clisttray.cpp
new file mode 100644
index 0000000000..2292020c48
--- /dev/null
+++ b/src/modules/clist/clisttray.cpp
@@ -0,0 +1,980 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+#define TOOLTIP_TOLERANCE 5
+
+extern HIMAGELIST hCListImages;
+extern BOOL(WINAPI * MySetProcessWorkingSetSize) (HANDLE, SIZE_T, SIZE_T);
+
+int GetAverageMode(int* pNetProtoCount = NULL);
+
+static UINT WM_TASKBARCREATED;
+static UINT WM_TASKBARBUTTONCREATED;
+static BOOL mToolTipTrayTips = FALSE;
+static UINT_PTR cycleTimerId = 0;
+static int cycleStep = 0;
+static UINT_PTR RefreshTimerId=0; /////by FYR
+static CRITICAL_SECTION trayLockCS;
+
+// don't move to win2k.h, need new and old versions to work on 9x/2000/XP
+#define NIF_STATE 0x00000008
+#define NIF_INFO 0x00000010
+
+#define lock cli.pfnLockTray()
+#define ulock cli.pfnUnlockTray()
+
+#define initcheck if(!fTrayInited) return
+
+static BOOL fTrayInited=FALSE;
+
+static TCHAR* sttGetXStatus( const char* szProto )
+{
+ TCHAR* result = NULL;
+
+ if ( CallProtoService( szProto, PS_GETSTATUS, 0, 0 ) > ID_STATUS_OFFLINE ) {
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf( str, sizeof(str), "%s/GetXStatus", szProto );
+ if ( ServiceExists( str )) {
+ char* dbTitle = "XStatusName";
+ char* dbTitle2 = NULL;
+ int xstatus = CallProtoService( szProto, "/GetXStatus", ( WPARAM )&dbTitle, ( LPARAM )&dbTitle2 );
+ if ( dbTitle && xstatus ) {
+ DBVARIANT dbv={0};
+ if ( !DBGetContactSettingTString(NULL, szProto, dbTitle, &dbv )) {
+ if ( dbv.ptszVal[0] != 0 )
+ result = mir_tstrdup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } } } }
+
+ return result;
+}
+
+static HICON lastTaskBarIcon;
+static void SetTaskBarIcon(const HICON hIcon, const TCHAR *szNewTip)
+{
+ if (pTaskbarInterface)
+ {
+ wchar_t *szTip = mir_t2u(szNewTip);
+ pTaskbarInterface->SetOverlayIcon(cli.hwndContactList, hIcon, szTip);
+ mir_free(szTip);
+ lastTaskBarIcon = hIcon;
+ }
+}
+
+TCHAR* fnTrayIconMakeTooltip( const TCHAR *szPrefix, const char *szProto )
+{
+ TCHAR *szStatus, *szSeparator;
+ TCHAR *ProtoXStatus=NULL;
+ int t;
+ PROTOACCOUNT* pa;
+ initcheck NULL;
+ lock;
+ if ( !mToolTipTrayTips )
+ szSeparator = (IsWinVerMEPlus()) ? szSeparator = _T("\n") : _T(" | ");
+ else
+ szSeparator = _T("\n");
+
+ if (szProto == NULL) {
+ if (accounts.getCount() == 0) {
+ ulock;
+ return NULL;
+ }
+ if (accounts.getCount() == 1) {
+ ulock;
+ return cli.pfnTrayIconMakeTooltip(szPrefix, accounts[0]->szModuleName);
+ }
+
+ if (szPrefix && szPrefix[0]) {
+ lstrcpyn(cli.szTip, szPrefix, MAX_TIP_SIZE);
+ if (!DBGetContactSettingByte(NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT))
+ { ulock; return cli.szTip; }
+ }
+ else cli.szTip[0] = '\0';
+ cli.szTip[ MAX_TIP_SIZE-1 ] = '\0';
+
+ for ( t = 0; t < accounts.getCount(); t++ ) {
+ int i = cli.pfnGetAccountIndexByPos( t );
+ if ( i == -1 )
+ continue;
+
+ pa = accounts[i];
+ if ( !cli.pfnGetProtocolVisibility( pa->szModuleName ))
+ continue;
+
+ szStatus = cli.pfnGetStatusModeDescription( CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0), 0);
+ if ( !szStatus )
+ continue;
+
+ ProtoXStatus = sttGetXStatus( pa->szModuleName );
+
+ if ( mToolTipTrayTips ) {
+ TCHAR tipline[256];
+ mir_sntprintf(tipline, SIZEOF(tipline), _T("<b>%-12.12s</b>\t%s"), pa->tszAccountName, szStatus);
+ if ( cli.szTip[0] )
+ _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ _tcsncat(cli.szTip, tipline, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ if (ProtoXStatus) {
+ mir_sntprintf(tipline, SIZEOF(tipline), _T("%-24.24s\n"), ProtoXStatus);
+ if ( cli.szTip[0] )
+ _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ _tcsncat(cli.szTip, tipline, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ }
+ }
+ else {
+ if (cli.szTip[0])
+ _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip));
+
+ _tcsncat(cli.szTip, pa->tszAccountName, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ _tcsncat(cli.szTip, _T(" "), MAX_TIP_SIZE - _tcslen(cli.szTip));
+ _tcsncat(cli.szTip, szStatus, MAX_TIP_SIZE - _tcslen(cli.szTip));
+ }
+ mir_free( ProtoXStatus );
+ }
+ }
+ else {
+ if (( pa = Proto_GetAccount( szProto )) != NULL ) {
+ ProtoXStatus = sttGetXStatus( szProto );
+ szStatus = cli.pfnGetStatusModeDescription(CallProtoService(szProto, PS_GETSTATUS, 0, 0), 0);
+ if ( szPrefix && szPrefix[0] ) {
+ if ( DBGetContactSettingByte( NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT )) {
+ if ( mToolTipTrayTips ) {
+ if ( ProtoXStatus )
+ mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s<b>%-12.12s</b>\t%s%s%-24.24s"), szPrefix, szSeparator, pa->tszAccountName, szStatus,szSeparator,ProtoXStatus);
+ else
+ mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s<b>%-12.12s</b>\t%s"), szPrefix, szSeparator, pa->tszAccountName, szStatus);
+ }
+ else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s%s %s"), szPrefix, szSeparator, pa->tszAccountName, szStatus);
+ }
+ else lstrcpyn(cli.szTip, szPrefix, MAX_TIP_SIZE);
+ }
+ else {
+ if ( mToolTipTrayTips ) {
+ if ( ProtoXStatus )
+ mir_sntprintf( cli.szTip, MAX_TIP_SIZE, _T("<b>%-12.12s</b>\t%s\n%-24.24s"), pa->tszAccountName, szStatus,ProtoXStatus);
+ else
+ mir_sntprintf( cli.szTip, MAX_TIP_SIZE, _T("<b>%-12.12s</b>\t%s"), pa->tszAccountName, szStatus);
+ }
+ else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s %s"), pa->tszAccountName, szStatus);
+ }
+ mir_free(ProtoXStatus);
+ } }
+
+ ulock;
+ return cli.szTip;
+}
+
+int fnTrayIconAdd(HWND hwnd, const char *szProto, const char *szIconProto, int status)
+{
+ NOTIFYICONDATA nid = { 0 };
+ int i;
+ initcheck 0;
+ lock;
+ for (i = 0; i < cli.trayIconCount; i++)
+ if (cli.trayIcon[i].id == 0)
+ break;
+
+ cli.trayIcon[i].id = TRAYICON_ID_BASE + i;
+ cli.trayIcon[i].szProto = (char *) szProto;
+ cli.trayIcon[i].hBaseIcon = cli.pfnGetIconFromStatusMode( NULL, szIconProto ? szIconProto : cli.trayIcon[i].szProto, status );
+
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE;
+ nid.hWnd = hwnd;
+ nid.uID = cli.trayIcon[i].id;
+ nid.uFlags = mToolTipTrayTips ? NIF_ICON | NIF_MESSAGE : NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ nid.uCallbackMessage = TIM_CALLBACK;
+ nid.hIcon = cli.trayIcon[i].hBaseIcon;
+
+ if (cli.shellVersion >= 5)
+ nid.uFlags |= NIF_INFO;
+
+ cli.pfnTrayIconMakeTooltip( NULL, cli.trayIcon[i].szProto );
+ if ( !mToolTipTrayTips )
+ lstrcpyn( nid.szTip, cli.szTip, SIZEOF( nid.szTip ));
+ cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip );
+
+ Shell_NotifyIcon(NIM_ADD, &nid);
+ cli.trayIcon[i].isBase = 1;
+
+ if (cli.trayIconCount == 1)
+ SetTaskBarIcon(cli.trayIcon[0].hBaseIcon, cli.szTip);
+
+ ulock; return i;
+}
+
+void fnTrayIconRemove(HWND hwnd, const char *szProto)
+{
+ int i;
+ initcheck;
+ lock;
+ for ( i = 0; i < cli.trayIconCount; i++ ) {
+ struct trayIconInfo_t* pii = &cli.trayIcon[i];
+ if ( pii->id != 0 && !lstrcmpA( szProto, pii->szProto )) {
+ NOTIFYICONDATA nid = { 0 };
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE;
+ nid.hWnd = hwnd;
+ nid.uID = pii->id;
+ Shell_NotifyIcon(NIM_DELETE, &nid);
+
+ DestroyIcon(pii->hBaseIcon);
+ mir_free(pii->ptszToolTip); pii->ptszToolTip = NULL;
+ pii->id = 0;
+ break;
+ } }
+
+ if (cli.trayIconCount == 1)
+ SetTaskBarIcon(NULL, NULL);
+
+ ulock;
+}
+
+int fnTrayIconInit(HWND hwnd)
+{
+ int netProtoCount = 0;
+ initcheck 0;
+ lock;
+
+ int averageMode = GetAverageMode(&netProtoCount);
+ mToolTipTrayTips = ServiceExists("mToolTip/ShowTip") != 0;
+
+ if ( cli.cycleTimerId ) {
+ KillTimer(NULL, cli.cycleTimerId);
+ cli.cycleTimerId = 0;
+ }
+
+ cli.trayIconCount = 1;
+
+ if (netProtoCount)
+ {
+ cli.trayIcon = (trayIconInfo_t *) mir_calloc(sizeof(trayIconInfo_t) * accounts.getCount());
+
+ int trayIconSetting = DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT);
+
+ if (trayIconSetting == SETTING_TRAYICON_SINGLE)
+ {
+ DBVARIANT dbv = { DBVT_DELETED };
+ char *szProto;
+ if (!DBGetContactSettingString(NULL, "CList", "PrimaryStatus", &dbv)
+ && (averageMode < 0 || DBGetContactSettingByte(NULL, "CList", "AlwaysPrimary", 0) ))
+ szProto = dbv.pszVal;
+ else
+ szProto = NULL;
+
+ cli.pfnTrayIconAdd(hwnd, NULL, szProto, szProto ? CallProtoService(szProto, PS_GETSTATUS, 0, 0) : CallService(MS_CLIST_GETSTATUSMODE, 0, 0));
+ DBFreeVariant(&dbv);
+ }
+ else if (trayIconSetting == SETTING_TRAYICON_MULTI &&
+ (averageMode < 0 || DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT )))
+ {
+ cli.trayIconCount = netProtoCount;
+ for (int i = 0; i < accounts.getCount(); ++i)
+ {
+ int j = cli.pfnGetAccountIndexByPos(i);
+ if (j >= 0)
+ {
+ PROTOACCOUNT* pa = accounts[j];
+ if (cli.pfnGetProtocolVisibility(pa->szModuleName))
+ cli.pfnTrayIconAdd(hwnd, pa->szModuleName, NULL, CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0));
+ }
+ }
+ }
+ else
+ {
+ cli.pfnTrayIconAdd(hwnd, NULL, NULL, averageMode);
+
+ if (trayIconSetting == SETTING_TRAYICON_CYCLE && averageMode < 0)
+ cli.cycleTimerId = SetTimer(NULL, 0, DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT) * 1000, cli.pfnTrayCycleTimerProc);
+ }
+ }
+ else
+ {
+ cli.trayIcon = (trayIconInfo_t *) mir_calloc(sizeof(trayIconInfo_t));
+ cli.pfnTrayIconAdd(hwnd, NULL, NULL, CallService(MS_CLIST_GETSTATUSMODE, 0, 0));
+ }
+
+ ulock;
+ return 0;
+}
+
+int fnTrayIconDestroy(HWND hwnd)
+{
+ NOTIFYICONDATA nid = { 0 };
+ int i;
+ initcheck 0;
+ lock;
+
+ if (cli.trayIconCount == 1)
+ SetTaskBarIcon(NULL, NULL);
+
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE;
+ nid.hWnd = hwnd;
+ for ( i = 0; i < cli.trayIconCount; i++ ) {
+ if ( cli.trayIcon[i].id == 0 )
+ continue;
+ nid.uID = cli.trayIcon[i].id;
+ Shell_NotifyIcon( NIM_DELETE, &nid );
+ DestroyIcon( cli.trayIcon[i].hBaseIcon );
+ mir_free( cli.trayIcon[i].ptszToolTip );
+ }
+ mir_free(cli.trayIcon);
+ cli.trayIcon = NULL;
+ cli.trayIconCount = 0;
+
+ ulock;
+ return 0;
+}
+
+//called when Explorer crashes and the taskbar is remade
+void fnTrayIconTaskbarCreated(HWND hwnd)
+{
+ initcheck;
+ cli.pfnTrayIconDestroy(hwnd);
+ cli.pfnTrayIconInit(hwnd);
+}
+
+static VOID CALLBACK RefreshTimerProc(HWND, UINT, UINT_PTR, DWORD)
+{
+ int i;
+ if ( RefreshTimerId ) {
+ KillTimer(NULL,RefreshTimerId);
+ RefreshTimerId=0;
+ }
+ for (i=0; i < accounts.getCount(); i++) {
+ cli.pfnTrayIconUpdateBase( accounts[i]->szModuleName );
+ }
+}
+
+int fnTrayIconUpdate(HICON hNewIcon, const TCHAR *szNewTip, const char *szPreferredProto, int isBase)
+{
+ NOTIFYICONDATA nid = { 0 };
+ int i;
+
+ initcheck -1;
+ lock;
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE;
+ nid.hWnd = cli.hwndContactList;
+ nid.uFlags = mToolTipTrayTips ? NIF_ICON : NIF_ICON | NIF_TIP;
+ nid.hIcon = hNewIcon;
+ if (!hNewIcon)
+ { ulock; return -1; }
+
+ for (i = 0; i < cli.trayIconCount; i++) {
+ if (cli.trayIcon[i].id == 0)
+ continue;
+ if (lstrcmpA(cli.trayIcon[i].szProto, szPreferredProto))
+ continue;
+
+ nid.uID = cli.trayIcon[i].id;
+ cli.pfnTrayIconMakeTooltip(szNewTip, cli.trayIcon[i].szProto);
+ mir_free( cli.trayIcon[i].ptszToolTip );
+ cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip );
+ if (!mToolTipTrayTips)
+ lstrcpyn(nid.szTip, cli.szTip, SIZEOF(nid.szTip));
+ Shell_NotifyIcon(NIM_MODIFY, &nid);
+
+ if (cli.trayIconCount == 1)
+ SetTaskBarIcon(hNewIcon, cli.szTip);
+ else
+ SetTaskBarIcon(NULL, NULL);
+
+ cli.trayIcon[i].isBase = isBase;
+ { ulock; return i; }
+ }
+
+ //if there wasn't a suitable icon, change all the icons
+ {
+ for (i = 0; i < cli.trayIconCount; i++) {
+ if (cli.trayIcon[i].id == 0)
+ continue;
+ nid.uID = cli.trayIcon[i].id;
+
+ cli.pfnTrayIconMakeTooltip(szNewTip, cli.trayIcon[i].szProto);
+ mir_free( cli.trayIcon[i].ptszToolTip );
+ cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip );
+ if(!mToolTipTrayTips)
+ lstrcpyn(nid.szTip, cli.szTip, SIZEOF(nid.szTip));
+ Shell_NotifyIcon(NIM_MODIFY, &nid);
+
+ if (cli.trayIconCount == 1)
+ SetTaskBarIcon(hNewIcon, cli.szTip);
+ else
+ SetTaskBarIcon(NULL, NULL);
+
+ cli.trayIcon[i].isBase = isBase;
+ if (DBGetContactSettingByte(NULL,"CList","TrayIcon",SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_MULTI)
+ {
+ DWORD time1=DBGetContactSettingWord(NULL,"CList","CycleTime",SETTING_CYCLETIME_DEFAULT)*200;
+ DWORD time2=DBGetContactSettingWord(NULL,"CList","IconFlashTime",550)+1000;
+ DWORD time=max(max(2000,time1),time2);
+ if(RefreshTimerId) {KillTimer(NULL,RefreshTimerId); RefreshTimerId=0;}
+ RefreshTimerId=SetTimer(NULL,0,time,RefreshTimerProc); // if unknown base was changed - than show preffered proto icon for 2 sec and reset it to original one after timeout
+ }
+ { ulock; return i; }
+ }
+ }
+ { ulock; return -1; }
+}
+
+int fnTrayIconSetBaseInfo(HICON hIcon, const char *szPreferredProto)
+{
+ int i;
+ initcheck -1;
+ lock;
+ if (szPreferredProto)
+ {
+ for (i = 0; i < cli.trayIconCount; i++) {
+ if (cli.trayIcon[i].id == 0)
+ continue;
+ if (lstrcmpA(cli.trayIcon[i].szProto, szPreferredProto))
+ continue;
+
+ DestroyIcon(cli.trayIcon[i].hBaseIcon);
+ cli.trayIcon[i].hBaseIcon = hIcon;
+ ulock; return i;
+ }
+ if ((cli.pfnGetProtocolVisibility(szPreferredProto))
+ && (GetAverageMode()==-1)
+ && (DBGetContactSettingByte(NULL,"CList","TrayIcon",SETTING_TRAYICON_DEFAULT)==SETTING_TRAYICON_MULTI)
+ && !(DBGetContactSettingByte(NULL,"CList","AlwaysMulti",SETTING_ALWAYSMULTI_DEFAULT)))
+ goto LBL_Error;
+ }
+
+ //if there wasn't a specific icon, there will only be one suitable
+ for (i = 0; i < cli.trayIconCount; i++) {
+ if (cli.trayIcon[i].id == 0)
+ continue;
+
+ DestroyIcon(cli.trayIcon[i].hBaseIcon);
+ cli.trayIcon[i].hBaseIcon = hIcon;
+ ulock; return i;
+ }
+
+LBL_Error:
+ DestroyIcon(hIcon);
+ ulock; return -1;
+}
+
+void fnTrayIconUpdateWithImageList(int iImage, const TCHAR *szNewTip, char *szPreferredProto)
+{
+ HICON hIcon = ImageList_GetIcon(hCListImages, iImage, ILD_NORMAL);
+ cli.pfnTrayIconUpdate(hIcon, szNewTip, szPreferredProto, 0);
+ DestroyIcon(hIcon);
+}
+
+VOID CALLBACK fnTrayCycleTimerProc(HWND, UINT, UINT_PTR, DWORD)
+{
+ initcheck;
+ lock;
+
+ int i;
+ for (i = accounts.getCount() + 1; --i;) {
+ cycleStep = (cycleStep + 1) % accounts.getCount();
+ if ( cli.pfnGetProtocolVisibility( accounts[cycleStep]->szModuleName ))
+ break;
+ }
+
+ if (i)
+ {
+ DestroyIcon(cli.trayIcon[0].hBaseIcon);
+ cli.trayIcon[0].hBaseIcon = cli.pfnGetIconFromStatusMode(NULL, accounts[cycleStep]->szModuleName,
+ CallProtoService( accounts[cycleStep]->szModuleName, PS_GETSTATUS, 0, 0 ));
+ if (cli.trayIcon[0].isBase)
+ cli.pfnTrayIconUpdate(cli.trayIcon[0].hBaseIcon, NULL, NULL, 1);
+ }
+
+ ulock;
+}
+
+void fnTrayIconUpdateBase(const char *szChangedProto)
+{
+ if ( !cli.pfnGetProtocolVisibility( szChangedProto )) return;
+
+ int i, netProtoCount, changed = -1;
+ HWND hwnd = cli.hwndContactList;
+ initcheck;
+ lock;
+ int averageMode = GetAverageMode(&netProtoCount);
+
+ if (cli.cycleTimerId) {
+ KillTimer(NULL, cli.cycleTimerId);
+ cli.cycleTimerId = 0;
+ }
+
+ for (i = 0; i < accounts.getCount(); i++) {
+ if (!lstrcmpA(szChangedProto, accounts[i]->szModuleName ))
+ cycleStep = i - 1;
+ }
+
+ if (netProtoCount > 0)
+ {
+ int trayIconSetting = DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT);
+
+ if (averageMode > 0) {
+ if (trayIconSetting == SETTING_TRAYICON_MULTI) {
+ if (DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT))
+ //changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode((char*)szChangedProto, NULL, averageMode), (char*)szChangedProto);
+ changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0)), (char*)szChangedProto );
+ else if (cli.trayIcon && cli.trayIcon[0].szProto != NULL) {
+ cli.pfnTrayIconDestroy(hwnd);
+ cli.pfnTrayIconInit(hwnd);
+ }
+ else
+ changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode(NULL, NULL, averageMode), NULL );
+ }
+ else
+ changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode(NULL, NULL, averageMode), NULL);
+ }
+ else {
+ switch (trayIconSetting) {
+ case SETTING_TRAYICON_SINGLE:
+ {
+ DBVARIANT dbv = { DBVT_DELETED };
+ char *szProto;
+ if (DBGetContactSettingString(NULL, "CList", "PrimaryStatus", &dbv))
+ szProto = NULL;
+ else
+ szProto = dbv.pszVal;
+ changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szProto, szProto ? CallProtoService(szProto, PS_GETSTATUS, 0,0) : CallService(MS_CLIST_GETSTATUSMODE, 0, 0)), szProto );
+ DBFreeVariant(&dbv);
+ break;
+ }
+ case SETTING_TRAYICON_CYCLE:
+ cli.cycleTimerId =
+ SetTimer(NULL, 0, DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT) * 1000, cli.pfnTrayCycleTimerProc);
+ changed =
+ cli.pfnTrayIconSetBaseInfo(ImageList_GetIcon
+ (hCListImages, cli.pfnIconFromStatusMode(szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0), NULL),
+ ILD_NORMAL), NULL);
+ break;
+ case SETTING_TRAYICON_MULTI:
+ if (!cli.trayIcon) {
+ cli.pfnTrayIconRemove(NULL, NULL);
+ }
+ else if ((cli.trayIconCount > 1 || netProtoCount == 1) || DBGetContactSettingByte( NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT ))
+ changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0)), (char*)szChangedProto );
+ else {
+ cli.pfnTrayIconDestroy(hwnd);
+ cli.pfnTrayIconInit(hwnd);
+ }
+ break;
+ }
+ }
+ }
+ else
+ changed = cli.pfnTrayIconSetBaseInfo(ImageList_GetIcon(hCListImages, cli.pfnIconFromStatusMode(NULL, averageMode, NULL), ILD_NORMAL), NULL);
+
+ if (changed != -1 && cli.trayIcon[changed].isBase)
+ cli.pfnTrayIconUpdate(cli.trayIcon[changed].hBaseIcon, NULL, cli.trayIcon[changed].szProto, 1);
+ ulock;
+}
+
+void fnTrayIconSetToBase(char *szPreferredProto)
+{
+ int i;
+ initcheck;
+ lock;
+
+ for (i = 0; i < cli.trayIconCount; i++) {
+ if ( cli.trayIcon[i].id == 0 )
+ continue;
+ if ( lstrcmpA( cli.trayIcon[i].szProto, szPreferredProto ))
+ continue;
+ cli.pfnTrayIconUpdate( cli.trayIcon[i].hBaseIcon, NULL, szPreferredProto, 1);
+ ulock; return;
+ }
+
+ //if there wasn't a specific icon, there will only be one suitable
+ for ( i = 0; i < cli.trayIconCount; i++) {
+ if ( cli.trayIcon[i].id == 0 )
+ continue;
+ cli.pfnTrayIconUpdate( cli.trayIcon[i].hBaseIcon, NULL, szPreferredProto, 1);
+ ulock; return;
+ }
+ ulock; return;
+}
+
+void fnTrayIconIconsChanged(void)
+{
+ initcheck;
+ lock;
+ cli.pfnTrayIconDestroy(cli.hwndContactList);
+ cli.pfnTrayIconInit(cli.hwndContactList);
+ ulock;
+}
+
+static UINT_PTR autoHideTimerId;
+static VOID CALLBACK TrayIconAutoHideTimer(HWND hwnd, UINT, UINT_PTR idEvent, DWORD)
+{
+ HWND hwndClui;
+ initcheck;
+ lock;
+ KillTimer(hwnd, idEvent);
+ hwndClui = cli.hwndContactList;
+ if (GetActiveWindow() != hwndClui) {
+ ShowWindow(hwndClui, SW_HIDE);
+ if (MySetProcessWorkingSetSize != NULL)
+ MySetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
+ }
+ ulock; return;
+}
+
+int fnTrayIconPauseAutoHide(WPARAM, LPARAM)
+{
+ initcheck 0;
+ lock;
+ if (DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT)) {
+ if ( GetActiveWindow() != cli.hwndContactList ) {
+ KillTimer(NULL, autoHideTimerId);
+ autoHideTimerId = SetTimer(NULL, 0, 1000 * DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), TrayIconAutoHideTimer);
+ }
+ }
+ ulock; return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// processes tray icon's messages
+
+static BYTE s_LastHoverIconID = 0;
+static BOOL g_trayTooltipActive = FALSE;
+static POINT tray_hover_pos = {0};
+
+static void CALLBACK TrayHideToolTipTimerProc(HWND hwnd, UINT, UINT_PTR, DWORD)
+{
+ if ( g_trayTooltipActive ) {
+ POINT pt;
+ GetCursorPos(&pt);
+ if ( abs(pt.x - tray_hover_pos.x) > TOOLTIP_TOLERANCE || abs(pt.y - tray_hover_pos.y) > TOOLTIP_TOLERANCE ) {
+ CallService("mToolTip/HideTip", 0, 0);
+ g_trayTooltipActive = FALSE;
+ KillTimer( hwnd, TIMERID_TRAYHOVER_2 );
+ }
+ }
+ else KillTimer( hwnd, TIMERID_TRAYHOVER_2 );
+}
+
+static void CALLBACK TrayToolTipTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD)
+{
+ if ( !g_trayTooltipActive && !cli.bTrayMenuOnScreen ) {
+ CLCINFOTIP ti = {0};
+ POINT pt;
+ GetCursorPos( &pt );
+ if ( abs(pt.x - tray_hover_pos.x) <= TOOLTIP_TOLERANCE && abs(pt.y - tray_hover_pos.y) <= TOOLTIP_TOLERANCE ) {
+ TCHAR* szTipCur = cli.szTip;
+ {
+ int n = s_LastHoverIconID-100;
+ if ( n >= 0 && n < cli.trayIconCount )
+ szTipCur = cli.trayIcon[n].ptszToolTip;
+ }
+ ti.rcItem.left = pt.x - 10;
+ ti.rcItem.right = pt.x + 10;
+ ti.rcItem.top = pt.y - 10;
+ ti.rcItem.bottom = pt.y + 10;
+ ti.cbSize = sizeof( ti );
+ ti.isTreeFocused = GetFocus() == cli.hwndContactList ? 1 : 0;
+ #if defined( _UNICODE )
+ if (CallService( "mToolTip/ShowTipW", (WPARAM)szTipCur, (LPARAM)&ti ) == CALLSERVICE_NOTFOUND)
+ {
+ char* p = mir_u2a( szTipCur );
+ CallService( "mToolTip/ShowTip", (WPARAM)p, (LPARAM)&ti );
+ mir_free( p );
+ }
+ #else
+ CallService( "mToolTip/ShowTip", (WPARAM)szTipCur, (LPARAM)&ti );
+ #endif
+ GetCursorPos( &tray_hover_pos );
+ SetTimer( cli.hwndContactList, TIMERID_TRAYHOVER_2, 600, TrayHideToolTipTimerProc );
+ g_trayTooltipActive = TRUE;
+ } }
+
+ KillTimer(hwnd, id);
+}
+
+INT_PTR fnTrayIconProcessMessage(WPARAM wParam, LPARAM lParam)
+{
+ MSG *msg = (MSG *) wParam;
+ switch (msg->message) {
+ case WM_CREATE: {
+ WM_TASKBARCREATED = RegisterWindowMessage( _T("TaskbarCreated"));
+ WM_TASKBARBUTTONCREATED = RegisterWindowMessage( _T("TaskbarButtonCreated"));
+ PostMessage(msg->hwnd, TIM_CREATE, 0, 0);
+ break;
+ }
+ case TIM_CREATE:
+ cli.pfnTrayIconInit(msg->hwnd);
+ break;
+
+ case WM_ACTIVATE:
+ if (DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT)) {
+ if (LOWORD(msg->wParam) == WA_INACTIVE)
+ autoHideTimerId = SetTimer(NULL, 0, 1000 * DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), TrayIconAutoHideTimer);
+ else
+ KillTimer(NULL, autoHideTimerId);
+ }
+ break;
+
+ case WM_DESTROY:
+ cli.pfnTrayIconDestroy(msg->hwnd);
+ cli.pfnUninitTray();
+ break;
+
+ case TIM_CALLBACK:
+ if ( msg->lParam == WM_RBUTTONDOWN || msg->lParam == WM_LBUTTONDOWN || msg->lParam == WM_RBUTTONDOWN && g_trayTooltipActive ) {
+ CallService("mToolTip/HideTip", 0, 0);
+ g_trayTooltipActive = FALSE;
+ }
+
+ if ( msg->lParam == WM_MBUTTONUP )
+ cli.pfnShowHide(0, 0);
+ else if (msg->lParam == (DBGetContactSettingByte(NULL, "CList", "Tray1Click", SETTING_TRAY1CLICK_DEFAULT) ? WM_LBUTTONUP : WM_LBUTTONDBLCLK)) {
+ if ((GetAsyncKeyState(VK_CONTROL) & 0x8000))
+ {
+ POINT pt;
+ HMENU hMenu = (HMENU)CallService(MS_CLIST_MENUGETSTATUS, 0, 0);
+
+ for (int i = 0; i < cli.trayIconCount; ++i)
+ {
+ if ((unsigned)cli.trayIcon[i].id == msg->wParam)
+ {
+ if (!cli.trayIcon[i].szProto) break;
+
+ int ind = 0;
+ for (int j = 0; j < accounts.getCount(); ++j)
+ {
+ int k = cli.pfnGetAccountIndexByPos(j);
+ if (k >= 0)
+ {
+ if (!strcmp(cli.trayIcon[i].szProto, accounts[k]->szModuleName))
+ {
+ HMENU hm = GetSubMenu(hMenu, ind);
+ if (hm) hMenu = hm;
+ break;
+ }
+
+ if (cli.pfnGetProtocolVisibility(accounts[k]->szModuleName))
+ ++ind;
+ }
+ }
+ break;
+ }
+ }
+
+ SetForegroundWindow(msg->hwnd);
+ SetFocus(msg->hwnd);
+ GetCursorPos(&pt);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, msg->hwnd, NULL);
+ }
+ else if (cli.pfnEventsProcessTrayDoubleClick(msg->wParam))
+ cli.pfnShowHide(0, 0);
+ }
+ else if (msg->lParam == WM_RBUTTONUP) {
+ MENUITEMINFO mi;
+ POINT pt;
+ HMENU hMainMenu = LoadMenu(cli.hInst, MAKEINTRESOURCE(IDR_CONTEXT));
+ HMENU hMenu = GetSubMenu(hMainMenu, 0);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hMenu, 0);
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = MENUITEMINFO_V4_SIZE;
+ mi.fMask = MIIM_SUBMENU | MIIM_TYPE;
+ mi.fType = MFT_STRING;
+ mi.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETMAIN, 0, 0);
+ mi.dwTypeData = TranslateT("&Main Menu");
+ InsertMenuItem(hMenu, 1, TRUE, &mi);
+ mi.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS, 0, 0);
+ mi.dwTypeData = TranslateT("&Status");
+ InsertMenuItem(hMenu, 2, TRUE, &mi);
+ SetMenuDefaultItem(hMenu, ID_TRAY_HIDE, FALSE);
+
+ SetForegroundWindow(msg->hwnd);
+ SetFocus(msg->hwnd);
+ GetCursorPos(&pt);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN, pt.x, pt.y, 0, msg->hwnd, NULL);
+
+ RemoveMenu(hMenu, 1, MF_BYPOSITION);
+ RemoveMenu(hMenu, 1, MF_BYPOSITION);
+ DestroyMenu(hMainMenu);
+ }
+ else if ( msg->lParam == WM_MOUSEMOVE ) {
+ s_LastHoverIconID = msg->wParam;
+ if ( g_trayTooltipActive ) {
+ POINT pt;
+ GetCursorPos( &pt );
+ if ( abs(pt.x - tray_hover_pos.x) > TOOLTIP_TOLERANCE || abs(pt.y - tray_hover_pos.y) > TOOLTIP_TOLERANCE ) {
+ CallService("mToolTip/HideTip", 0, 0);
+ g_trayTooltipActive = FALSE;
+ ReleaseCapture();
+ }
+ }
+ else {
+ GetCursorPos(&tray_hover_pos);
+ SetTimer(cli.hwndContactList, TIMERID_TRAYHOVER, 600, TrayToolTipTimerProc);
+ }
+ break;
+ }
+
+ *((LRESULT *) lParam) = 0;
+ return TRUE;
+
+ default:
+ if (msg->message == WM_TASKBARCREATED) {
+ cli.pfnTrayIconTaskbarCreated(msg->hwnd);
+ *((LRESULT *) lParam) = 0;
+ return TRUE;
+ }
+ else if (msg->message == WM_TASKBARBUTTONCREATED) {
+ SetTaskBarIcon(lastTaskBarIcon, NULL);
+ *((LRESULT *) lParam) = 0;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// processes tray icon's notifications
+
+int fnCListTrayNotify( MIRANDASYSTRAYNOTIFY* msn )
+{
+ UINT iconId = 0;
+
+ if ( msn == NULL )
+ return 1;
+
+ if ( msn->cbSize != sizeof(MIRANDASYSTRAYNOTIFY) || msn->szInfo == NULL || msn->szInfoTitle == NULL )
+ return 1;
+
+ if ( cli.trayIcon == NULL )
+ return 2;
+
+ if ( msn->szProto ) {
+ int j;
+ for ( j = 0; j < cli.trayIconCount; j++ ) {
+ if ( cli.trayIcon[j].szProto != NULL ) {
+ if ( !strcmp( msn->szProto, cli.trayIcon[j].szProto )) {
+ iconId = cli.trayIcon[j].id;
+ break;
+ }
+ }
+ else if ( cli.trayIcon[j].isBase ) {
+ iconId = cli.trayIcon[j].id;
+ break;
+ } }
+ }
+ else iconId = cli.trayIcon[0].id;
+
+#if defined(_UNICODE)
+ if ( msn->dwInfoFlags & NIIF_INTERN_UNICODE ) {
+ NOTIFYICONDATAW nid = {0};
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATAW_V1_SIZE;
+ nid.hWnd = cli.hwndContactList;
+ nid.uID = iconId;
+ nid.uFlags = NIF_INFO;
+ lstrcpynW( nid.szInfo, msn->tszInfo, SIZEOF( nid.szInfo ));
+ lstrcpynW( nid.szInfoTitle, msn->tszInfoTitle, SIZEOF( nid.szInfoTitle ));
+ nid.szInfo[ SIZEOF(nid.szInfo)-1 ] = 0;
+ nid.szInfoTitle[ SIZEOF(nid.szInfoTitle)-1 ] = 0;
+ nid.uTimeout = msn->uTimeout;
+ nid.dwInfoFlags = (msn->dwInfoFlags & ~NIIF_INTERN_UNICODE);
+ return Shell_NotifyIconW( NIM_MODIFY, &nid ) == 0;
+ }
+ else
+#endif
+ {
+ NOTIFYICONDATAA nid = { 0 };
+ nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATAA_V1_SIZE;
+ nid.hWnd = cli.hwndContactList;
+ nid.uID = iconId;
+ nid.uFlags = NIF_INFO;
+ lstrcpynA( nid.szInfo, msn->szInfo, sizeof( nid.szInfo ));
+ lstrcpynA( nid.szInfoTitle, msn->szInfoTitle, sizeof( nid.szInfoTitle ));
+ nid.uTimeout = msn->uTimeout;
+ nid.dwInfoFlags = msn->dwInfoFlags;
+ return Shell_NotifyIconA( NIM_MODIFY, &nid ) == 0;
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct _DllVersionInfo
+{
+ DWORD cbSize;
+ DWORD dwMajorVersion; // Major version
+ DWORD dwMinorVersion; // Minor version
+ DWORD dwBuildNumber; // Build number
+ DWORD dwPlatformID; // DLLVER_PLATFORM_*
+}
+ DLLVERSIONINFO;
+
+typedef HRESULT(CALLBACK * DLLGETVERSIONPROC) (DLLVERSIONINFO *);
+
+static DLLVERSIONINFO dviShell;
+
+static INT_PTR pfnCListTrayNotifyStub(WPARAM, LPARAM lParam )
+{ return cli.pfnCListTrayNotify(( MIRANDASYSTRAYNOTIFY* )lParam );
+}
+
+void fnInitTray( void )
+{
+ HMODULE hLib = GetModuleHandleA("shell32");
+ if ( hLib ) {
+ DLLGETVERSIONPROC proc;
+ dviShell.cbSize = sizeof(dviShell);
+ proc = ( DLLGETVERSIONPROC )GetProcAddress( hLib, "DllGetVersion" );
+ if (proc) {
+ proc( &dviShell );
+ cli.shellVersion = dviShell.dwMajorVersion;
+ }
+ FreeLibrary(hLib);
+ }
+ InitializeCriticalSection(&trayLockCS);
+ if ( cli.shellVersion >= 5 )
+ CreateServiceFunction(MS_CLIST_SYSTRAY_NOTIFY, pfnCListTrayNotifyStub );
+ fTrayInited=TRUE;
+}
+
+void fnUninitTray( void )
+{
+ fTrayInited=FALSE;
+ DeleteCriticalSection( &trayLockCS );
+}
+void fnLockTray( void )
+{
+// return; //stub to be removed
+ initcheck;
+ EnterCriticalSection( &trayLockCS );
+}
+
+void fnUnlockTray( void )
+{
+// return; //stub to be removed
+ initcheck;
+#ifdef _DEBUG
+ if (trayLockCS.RecursionCount==0) DebugBreak(); //try to unlock already
+#endif
+ LeaveCriticalSection( &trayLockCS );
+}
+
+#undef lock
+#undef ulock
+#undef initcheck
diff --git a/src/modules/clist/clui.cpp b/src/modules/clist/clui.cpp
new file mode 100644
index 0000000000..13ebf74a21
--- /dev/null
+++ b/src/modules/clist/clui.cpp
@@ -0,0 +1,1136 @@
+/*
+
+ Miranda IM: the free IM client for Microsoft* Windows*
+
+ Copyright 2000-2010 Miranda ICQ/IM project,
+ all portions of this codebase are copyrighted to the people
+ listed in contributors.txt.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "../database/profilemanager.h"
+#include "clc.h"
+
+#define TM_AUTOALPHA 1
+#define MENU_MIRANDAMENU 0xFFFF1234
+
+extern BOOL(WINAPI * MySetProcessWorkingSetSize) (HANDLE, SIZE_T, SIZE_T);
+
+static HMODULE hUserDll;
+static HANDLE hContactDraggingEvent, hContactDroppedEvent, hContactDragStopEvent;
+static int transparentFocus = 1;
+UINT uMsgProcessProfile;
+
+#define M_RESTORESTATUS (WM_USER+7)
+
+void LoadCluiServices();
+
+typedef struct {
+ int showsbar;
+ int showgrip;
+ int transparent;
+ int alpha;
+}
+ CluiOpts;
+
+static CluiOpts cluiopt = {0};
+
+void fnLoadCluiGlobalOpts()
+{
+ cluiopt.showsbar = DBGetContactSettingByte(NULL, "CLUI", "ShowSBar", 1);
+ cluiopt.showgrip = DBGetContactSettingByte(NULL, "CLUI", "ShowGrip", 1);
+ cluiopt.transparent = DBGetContactSettingByte(NULL,"CList","Transparent",SETTING_TRANSPARENT_DEFAULT);
+ cluiopt.alpha = DBGetContactSettingByte(NULL, "CList", "Alpha", SETTING_ALPHA_DEFAULT);
+}
+
+static int CluiModulesLoaded(WPARAM, LPARAM)
+{
+ if (cli.hMenuMain) {
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_SUBMENU;
+ mii.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETMAIN, 0, 0);
+ SetMenuItemInfo(cli.hMenuMain, 0, TRUE, &mii);
+ mii.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS, 0, 0);
+ SetMenuItemInfo(cli.hMenuMain, 1, TRUE, &mii);
+ }
+ return 0;
+}
+
+// Disconnect all protocols.
+// Happens on shutdown and standby.
+static void DisconnectAll()
+{
+ int nProto;
+ for (nProto = 0; nProto < accounts.getCount(); nProto++)
+ CallProtoService( accounts[nProto]->szModuleName, PS_SETSTATUS, ID_STATUS_OFFLINE, 0);
+}
+
+static int CluiIconsChanged(WPARAM, LPARAM)
+{
+ DrawMenuBar(cli.hwndContactList);
+ return 0;
+}
+
+static HANDLE hRenameMenuItem;
+
+static int MenuItem_PreBuild(WPARAM, LPARAM)
+{
+ TCHAR cls[128];
+ HANDLE hItem;
+ HWND hwndClist = GetFocus();
+ CLISTMENUITEM mi;
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS;
+ GetClassName(hwndClist, cls, SIZEOF(cls));
+ hwndClist = (!lstrcmp(CLISTCONTROL_CLASS, cls)) ? hwndClist : cli.hwndContactList;
+ hItem = (HANDLE) SendMessage(hwndClist, CLM_GETSELECTION, 0, 0);
+ if (!hItem) {
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hRenameMenuItem, (LPARAM) & mi);
+ return 0;
+}
+
+static INT_PTR MenuItem_RenameContact(WPARAM, LPARAM)
+{
+ TCHAR cls[128];
+ HANDLE hItem;
+ HWND hwndClist = GetFocus();
+ GetClassName(hwndClist, cls, SIZEOF(cls));
+ // worst case scenario, the rename is sent to the main contact list
+ hwndClist = (!lstrcmp(CLISTCONTROL_CLASS, cls)) ? hwndClist : cli.hwndContactList;
+ hItem = (HANDLE) SendMessage(hwndClist, CLM_GETSELECTION, 0, 0);
+ if (hItem) {
+ SetFocus(hwndClist);
+ SendMessage(hwndClist, CLM_EDITLABEL, (WPARAM) hItem, 0);
+ }
+ return 0;
+}
+
+static INT_PTR CALLBACK AskForConfirmationDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hWnd);
+ {
+ LOGFONT lf;
+ HFONT hFont;
+
+ hFont = (HFONT) SendDlgItemMessage(hWnd, IDYES, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ SendDlgItemMessage(hWnd, IDC_TOPLINE, WM_SETFONT, (WPARAM) CreateFontIndirect(&lf), 0);
+ }
+ {
+ TCHAR szFormat[256];
+ TCHAR szFinal[256];
+
+ GetDlgItemText(hWnd, IDC_TOPLINE, szFormat, SIZEOF(szFormat));
+ mir_sntprintf(szFinal, SIZEOF(szFinal), szFormat, cli.pfnGetContactDisplayName((HANDLE)lParam, 0));
+ SetDlgItemText(hWnd, IDC_TOPLINE, szFinal);
+ }
+ SetFocus(GetDlgItem(hWnd, IDNO));
+ SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDYES:
+ if (IsDlgButtonChecked(hWnd, IDC_HIDE)) {
+ EndDialog(hWnd, IDC_HIDE);
+ break;
+ }
+ //fall through
+ case IDCANCEL:
+ case IDNO:
+ EndDialog(hWnd, LOWORD(wParam));
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDNO, BN_CLICKED), 0);
+ break;
+
+ case WM_DESTROY:
+ DeleteObject((HFONT) SendDlgItemMessage(hWnd, IDC_TOPLINE, WM_GETFONT, 0, 0));
+ break;
+ }
+
+ return FALSE;
+}
+
+static INT_PTR MenuItem_DeleteContact(WPARAM wParam, LPARAM lParam)
+{
+ //see notes about deleting contacts on PF1_SERVERCLIST servers in m_protosvc.h
+ UINT_PTR action;
+
+ if (DBGetContactSettingByte(NULL, "CList", "ConfirmDelete", SETTING_CONFIRMDELETE_DEFAULT) &&
+ !(GetKeyState(VK_SHIFT)&0x8000) )
+ // Ask user for confirmation, and if the contact should be archived (hidden, not deleted)
+ action = DialogBoxParam(hMirandaInst, MAKEINTRESOURCE(IDD_DELETECONTACT), (HWND) lParam, AskForConfirmationDlgProc, wParam);
+ else
+ action = IDYES;
+
+ switch (action) {
+
+ // Delete contact
+ case IDYES:
+ {
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto != NULL) {
+ // Check if protocol uses server side lists
+ DWORD caps;
+
+ caps = (DWORD) CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (caps & PF1_SERVERCLIST) {
+ int status;
+
+ status = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ if (status == ID_STATUS_OFFLINE || (status >= ID_STATUS_CONNECTING && status < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES)) {
+ // Set a flag so we remember to delete the contact when the protocol goes online the next time
+ DBWriteContactSettingByte((HANDLE) wParam, "CList", "Delete", 1);
+ MessageBox( NULL,
+ TranslateT("This contact is on an instant messaging system which stores its contact list on a central server. The contact will be removed from the server and from your contact list when you next connect to that network."),
+ TranslateT("Delete Contact"), MB_OK);
+ return 0;
+ } } }
+
+ CallService(MS_DB_CONTACT_DELETE, wParam, 0);
+ }
+ break;
+
+ // Archive contact
+ case IDC_HIDE:
+ DBWriteContactSettingByte((HANDLE) wParam, "CList", "Hidden", 1);
+ break;
+ }
+
+ return 0;
+}
+
+static INT_PTR MenuItem_AddContactToList(WPARAM wParam, LPARAM)
+{
+ ADDCONTACTSTRUCT acs = { 0 };
+
+ acs.handle = (HANDLE) wParam;
+ acs.handleType = HANDLE_CONTACT;
+ acs.szProto = "";
+
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM) NULL, (LPARAM) & acs);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// this is the smallest available window procedure
+
+#ifndef CS_DROPSHADOW
+#define CS_DROPSHADOW 0x00020000
+#endif
+
+LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT result;
+ MSG m;
+ m.hwnd=hwnd;
+ m.message=msg;
+ m.wParam=wParam;
+ m.lParam=lParam;
+ if ( cli.pfnDocking_ProcessWindowMessage(( WPARAM )&m, ( LPARAM )&result ))
+ return result;
+ if ( cli.pfnTrayIconProcessMessage(( WPARAM )&m, ( LPARAM )&result ))
+ return result;
+ if ( cli.pfnHotkeysProcessMessage(( WPARAM )&m, ( LPARAM )&result ))
+ return result;
+
+ return cli.pfnContactListWndProc( hwnd, msg, wParam, lParam );
+}
+
+int LoadCLUIModule(void)
+{
+ DBVARIANT dbv;
+ TCHAR titleText[256];
+
+ uMsgProcessProfile = RegisterWindowMessage( _T("Miranda::ProcessProfile"));
+ cli.pfnLoadCluiGlobalOpts();
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded);
+ HookEvent(ME_SKIN_ICONSCHANGED, CluiIconsChanged);
+
+ hContactDraggingEvent = CreateHookableEvent(ME_CLUI_CONTACTDRAGGING);
+ hContactDroppedEvent = CreateHookableEvent(ME_CLUI_CONTACTDROPPED);
+ hContactDragStopEvent = CreateHookableEvent(ME_CLUI_CONTACTDRAGSTOP);
+ LoadCluiServices();
+
+ WNDCLASSEX wndclass;
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_GLOBALCLASS;
+ wndclass.lpfnWndProc = cli.pfnContactListControlWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof(void *);
+ wndclass.hInstance = cli.hInst;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = NULL;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = CLISTCONTROL_CLASS;
+ wndclass.hIconSm = NULL;
+ RegisterClassEx(&wndclass);
+
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | ((IsWinVerXPPlus() &&
+ DBGetContactSettingByte(NULL, "CList", "WindowShadow", 0) == 1) ? CS_DROPSHADOW : 0);
+ wndclass.lpfnWndProc = ContactListWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = cli.hInst;
+ wndclass.hIcon = LoadSkinIcon(SKINICON_OTHER_MIRANDA, true);
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
+ wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_CLISTMENU);
+ wndclass.lpszClassName = _T(MIRANDACLASS);
+ wndclass.hIconSm = LoadSkinIcon(SKINICON_OTHER_MIRANDA);
+ RegisterClassEx(&wndclass);
+
+ if (DBGetContactSettingTString(NULL, "CList", "TitleText", &dbv))
+ lstrcpyn(titleText, _T(MIRANDANAME), SIZEOF( titleText ));
+ else {
+ lstrcpyn(titleText, dbv.ptszVal, SIZEOF(titleText));
+ DBFreeVariant(&dbv);
+ }
+
+ RECT pos;
+ pos.left = (int) DBGetContactSettingDword(NULL, "CList", "x", 700);
+ pos.top = (int) DBGetContactSettingDword(NULL, "CList", "y", 221);
+ pos.right = pos.left + (int) DBGetContactSettingDword(NULL, "CList", "Width", 108);
+ pos.bottom = pos.top + (int) DBGetContactSettingDword(NULL, "CList", "Height", 310);
+
+ Utils_AssertInsideScreen(&pos);
+
+ cli.hwndContactList = CreateWindowEx(
+ (DBGetContactSettingByte(NULL, "CList", "ToolWindow", SETTING_TOOLWINDOW_DEFAULT) ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW),
+ _T(MIRANDACLASS),
+ titleText,
+ WS_POPUPWINDOW | WS_THICKFRAME | WS_CLIPCHILDREN |
+ (DBGetContactSettingByte(NULL, "CLUI", "ShowCaption", SETTING_SHOWCAPTION_DEFAULT) ? WS_CAPTION | WS_SYSMENU |
+ (DBGetContactSettingByte(NULL, "CList", "Min2Tray", SETTING_MIN2TRAY_DEFAULT) ? 0 : WS_MINIMIZEBOX) : 0),
+ pos.left, pos.top, pos.right - pos.left, pos.bottom - pos.top,
+ NULL, NULL, cli.hInst, NULL);
+
+ if (DBGetContactSettingByte(NULL, "CList", "OnDesktop", 0)) {
+ HWND hProgMan = FindWindow(_T("Progman"), NULL);
+ if (IsWindow(hProgMan))
+ SetParent(cli.hwndContactList, hProgMan);
+ }
+
+ cli.pfnOnCreateClc();
+
+ PostMessage(cli.hwndContactList, M_RESTORESTATUS, 0, 0);
+
+ {
+ int state = DBGetContactSettingByte(NULL, "CList", "State", SETTING_STATE_NORMAL);
+ cli.hMenuMain = GetMenu(cli.hwndContactList);
+ if (!DBGetContactSettingByte(NULL, "CLUI", "ShowMainMenu", SETTING_SHOWMAINMENU_DEFAULT))
+ SetMenu(cli.hwndContactList, NULL);
+ if (state == SETTING_STATE_NORMAL)
+ ShowWindow(cli.hwndContactList, SW_SHOW);
+ else if (state == SETTING_STATE_MINIMIZED)
+ ShowWindow(cli.hwndContactList, SW_SHOWMINIMIZED);
+ SetWindowPos(cli.hwndContactList,
+ DBGetContactSettingByte(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST,
+ 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ }
+ {
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+
+ CreateServiceFunction("CList/DeleteContactCommand", MenuItem_DeleteContact);
+ mi.position = 2000070000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_DELETE );
+ mi.pszContactOwner = NULL; //on every contact
+ mi.pszName = LPGEN("De&lete");
+ mi.pszService = "CList/DeleteContactCommand";
+ CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) & mi);
+
+ CreateServiceFunction("CList/RenameContactCommand", MenuItem_RenameContact);
+ mi.position = 2000050000;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_RENAME );
+ mi.pszContactOwner = NULL; //on every contact
+ mi.pszName = LPGEN("&Rename");
+ mi.pszService = "CList/RenameContactCommand";
+ hRenameMenuItem = (HANDLE) CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) & mi);
+
+ CreateServiceFunction("CList/AddToListContactCommand", MenuItem_AddContactToList);
+ mi.position = -2050000000;
+ mi.flags |= CMIF_NOTONLIST;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_ADDCONTACT );
+ mi.pszName = LPGEN("&Add permanently to list");
+ mi.pszService = "CList/AddToListContactCommand";
+ CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) & mi);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, MenuItem_PreBuild);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// default contact list window procedure
+
+void fnDrawMenuItem(DRAWITEMSTRUCT *dis, HICON hIcon, HICON eventIcon)
+{
+ if (!IsWinVerXPPlus()) {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_MENU));
+ if (dis->itemState & ODS_HOTLIGHT)
+ DrawEdge(dis->hDC, &dis->rcItem, BDR_RAISEDINNER, BF_RECT);
+ else if (dis->itemState & ODS_SELECTED)
+ DrawEdge(dis->hDC, &dis->rcItem, BDR_SUNKENOUTER, BF_RECT);
+ if (eventIcon != 0) {
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) eventIcon, 0, 2, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, 4 + g_IconWidth, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ }
+ else DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, (dis->rcItem.right + dis->rcItem.left - g_IconWidth) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ }
+ else {
+ HBRUSH hBr;
+ BOOL bfm = FALSE;
+ SystemParametersInfo(SPI_GETFLATMENU, 0, &bfm, 0);
+ if (bfm) {
+ /* flat menus: fill with COLOR_MENUHILIGHT and outline with COLOR_HIGHLIGHT, otherwise use COLOR_MENUBAR */
+ if (dis->itemState & ODS_SELECTED || dis->itemState & ODS_HOTLIGHT) {
+ /* selected or hot lighted, no difference */
+ hBr = GetSysColorBrush(COLOR_MENUHILIGHT);
+ FillRect(dis->hDC, &dis->rcItem, hBr);
+ DeleteObject(hBr);
+ /* draw the frame */
+ hBr = GetSysColorBrush(COLOR_HIGHLIGHT);
+ FrameRect(dis->hDC, &dis->rcItem, hBr);
+ DeleteObject(hBr);
+ } else {
+ /* flush the DC with the menu bar colour (only supported on XP) and then draw the icon */
+ hBr = GetSysColorBrush(COLOR_MENUBAR);
+ FillRect(dis->hDC, &dis->rcItem, hBr);
+ DeleteObject(hBr);
+ } //if
+ /* draw the icon */
+ if (eventIcon != 0) {
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) eventIcon, 0, 2, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, 4 + g_IconWidth, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ }
+ else DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, (dis->rcItem.right + dis->rcItem.left - g_IconWidth) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ }
+ else {
+ /* non-flat menus, flush the DC with a normal menu colour */
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_MENU));
+ if (dis->itemState & ODS_HOTLIGHT)
+ DrawEdge(dis->hDC, &dis->rcItem, BDR_RAISEDINNER, BF_RECT);
+ else if (dis->itemState & ODS_SELECTED)
+ DrawEdge(dis->hDC, &dis->rcItem, BDR_SUNKENOUTER, BF_RECT);
+
+ if (eventIcon != 0) {
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) eventIcon, 0, 2, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, 4 + g_IconWidth, (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ }
+ else DrawState(dis->hDC, NULL, NULL, (LPARAM) hIcon, 0, (dis->rcItem.right + dis->rcItem.left - g_IconWidth) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), (dis->rcItem.bottom + dis->rcItem.top - g_IconHeight) / 2 + (dis->itemState & ODS_SELECTED ? 1 : 0), 0, 0, DST_ICON | (dis->itemState & ODS_INACTIVE ? DSS_DISABLED : DSS_NORMAL));
+ } }
+
+ DestroyIcon(hIcon);
+ return;
+}
+
+#define M_CREATECLC (WM_USER+1)
+LRESULT CALLBACK fnContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == uMsgProcessProfile)
+ {
+ TCHAR profile[MAX_PATH];
+ int rc;
+ // wParam = (ATOM)hProfileAtom, lParam = 0
+ if (GlobalGetAtomName((ATOM) wParam, profile, SIZEOF(profile)))
+ {
+ TCHAR *pfd = Utils_ReplaceVarsT(_T("%miranda_userdata%\\%miranda_profilename%.dat"));
+ rc = lstrcmpi(profile, pfd) == 0;
+ mir_free(pfd);
+ ReplyMessage(rc);
+ if (rc) {
+ ShowWindow(hwnd, SW_RESTORE);
+ ShowWindow(hwnd, SW_SHOW);
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ }
+ }
+ return 0;
+ }
+
+ switch (msg) {
+ case WM_NCCREATE:
+ {
+ MENUITEMINFO mii = { 0 };
+/*
+ if (IsWinVerVistaPlus() && isThemeActive())
+ {
+ HICON hIcon = LoadSkinnedIcon(SKINICON_OTHER_MAINMENU);
+ HBITMAP hBmp = ConvertIconToBitmap(hIcon, NULL, 0);
+ IconLib_ReleaseIcon(hIcon, NULL);
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_DATA;
+ mii.hbmpItem = hBmp;
+ }
+ else
+*/
+ {
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_TYPE | MIIM_DATA;
+ mii.dwItemData = MENU_MIRANDAMENU;
+ mii.fType = MFT_OWNERDRAW;
+ }
+ SetMenuItemInfo(GetMenu(hwnd), 0, TRUE, &mii);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ case WM_CREATE:
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) GetMenu(hwnd), 0);
+ DrawMenuBar(hwnd);
+
+ //create the status wnd
+ {
+ int flags = WS_CHILD | CCS_BOTTOM;
+ flags |= cluiopt.showsbar ? WS_VISIBLE : 0;
+ flags |= cluiopt.showgrip ? SBARS_SIZEGRIP : 0;
+ cli.hwndStatus = CreateWindow(STATUSCLASSNAME, NULL, flags, 0, 0, 0, 0, hwnd, NULL, cli.hInst, NULL);
+ }
+ cli.pfnCluiProtocolStatusChanged(0, 0);
+
+ //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded)
+ PostMessage(hwnd, M_CREATECLC, 0, 0);
+
+ if (cluiopt.transparent) {
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ if (setLayeredWindowAttributes)
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (BYTE) cluiopt.alpha, LWA_ALPHA);
+ }
+ transparentFocus = 1;
+ return FALSE;
+
+ case M_CREATECLC:
+ cli.hwndContactTree = CreateWindow( CLISTCONTROL_CLASS, _T(""),
+ WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
+ | CLS_CONTACTLIST
+ | (DBGetContactSettingByte(NULL, "CList", "UseGroups", SETTING_USEGROUPS_DEFAULT) ? CLS_USEGROUPS : 0)
+ | (DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT) ? CLS_HIDEOFFLINE : 0)
+ | (DBGetContactSettingByte(NULL, "CList", "HideEmptyGroups", SETTING_HIDEEMPTYGROUPS_DEFAULT) ?
+ CLS_HIDEEMPTYGROUPS : 0), 0, 0, 0, 0, hwnd, NULL, cli.hInst, NULL);
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ break;
+
+ case M_RESTORESTATUS:
+ #ifndef _DEBUG
+ {
+ int nStatus = DBGetContactSettingWord(NULL, "CList", "Status", ID_STATUS_OFFLINE);
+ if (nStatus != ID_STATUS_OFFLINE) CallService(MS_CLIST_SETSTATUSMODE, nStatus, 0);
+ }
+ #endif
+ break;
+
+ // Power management
+ case WM_POWERBROADCAST:
+ switch ((DWORD) wParam) {
+ case PBT_APMSUSPEND:
+ // Computer is suspending, disconnect all protocols
+ DisconnectAll();
+ break;
+
+ case PBT_APMRESUMEAUTOMATIC:
+ case PBT_APMRESUMESUSPEND:
+ // Computer is resuming, restore all protocols
+ PostMessage(hwnd, M_RESTORESTATUS, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_SYSCOLORCHANGE:
+ SendMessage(cli.hwndContactTree, msg, wParam, lParam);
+ SendMessage(cli.hwndStatus, msg, wParam, lParam);
+ // XXX: only works with 4.71 with 95, IE4.
+ SendMessage(cli.hwndStatus, SB_SETBKCOLOR, 0, GetSysColor(COLOR_3DFACE));
+ break;
+
+ case WM_SIZE:
+ if (IsZoomed(hwnd))
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+ {
+ RECT rect, rcStatus;
+ GetClientRect(hwnd, &rect);
+ if (cluiopt.showsbar) {
+ SetWindowPos(cli.hwndStatus, NULL, 0, rect.bottom - 20, rect.right - rect.left, 20, SWP_NOZORDER);
+ GetWindowRect(cli.hwndStatus, &rcStatus);
+ cli.pfnCluiProtocolStatusChanged(0, 0);
+ }
+ else
+ rcStatus.top = rcStatus.bottom = 0;
+ SetWindowPos(cli.hwndContactTree, NULL, 0, 0, rect.right, rect.bottom - (rcStatus.bottom - rcStatus.top), SWP_NOZORDER);
+ }
+ if (wParam == SIZE_MINIMIZED)
+ {
+ if ((GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) ||
+ DBGetContactSettingByte(NULL, "CList", "Min2Tray", SETTING_MIN2TRAY_DEFAULT))
+ {
+ ShowWindow(hwnd, SW_HIDE);
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_HIDDEN);
+ }
+ else
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_MINIMIZED);
+
+ if (MySetProcessWorkingSetSize != NULL && DBGetContactSettingByte(NULL, "CList", "DisableWorkingSet", 1))
+ MySetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
+ }
+ // drop thru
+ case WM_MOVE:
+ if (!IsIconic(hwnd)) {
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+
+ if (!CallService(MS_CLIST_DOCKINGISDOCKED, 0, 0)) { //if docked, dont remember pos (except for width)
+ DBWriteContactSettingDword(NULL, "CList", "Height", (DWORD) (rc.bottom - rc.top));
+ DBWriteContactSettingDword(NULL, "CList", "x", (DWORD) rc.left);
+ DBWriteContactSettingDword(NULL, "CList", "y", (DWORD) rc.top);
+ }
+ DBWriteContactSettingDword(NULL, "CList", "Width", (DWORD) (rc.right - rc.left));
+ }
+ return FALSE;
+
+ case WM_SETFOCUS:
+ SetFocus(cli.hwndContactTree);
+ return 0;
+
+ case WM_ACTIVATE:
+ if (wParam == WA_INACTIVE) {
+ if ((HWND) wParam != hwnd)
+ if (cluiopt.transparent)
+ if (transparentFocus)
+ SetTimer(hwnd, TM_AUTOALPHA, 250, NULL);
+ }
+ else {
+ if (cluiopt.transparent) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ if (setLayeredWindowAttributes)
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (BYTE) cluiopt.alpha, LWA_ALPHA);
+ transparentFocus = 1;
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_SETCURSOR:
+ if(cluiopt.transparent) {
+ if (!transparentFocus && GetForegroundWindow()!=hwnd && setLayeredWindowAttributes) {
+ setLayeredWindowAttributes(hwnd, RGB(0,0,0), (BYTE)cluiopt.alpha, LWA_ALPHA);
+ transparentFocus=1;
+ SetTimer(hwnd, TM_AUTOALPHA,250,NULL);
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_NCHITTEST:
+ {
+ LRESULT result;
+ result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
+ if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT ||
+ result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT)
+ if (DBGetContactSettingByte(NULL, "CLUI", "AutoSize", 0))
+ return HTCLIENT;
+ return result;
+ }
+
+ case WM_TIMER:
+ if ((int) wParam == TM_AUTOALPHA) {
+ int inwnd;
+
+ if (GetForegroundWindow() == hwnd) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ inwnd = 1;
+ }
+ else {
+ POINT pt;
+ HWND hwndPt;
+ pt.x = (short) LOWORD(GetMessagePos());
+ pt.y = (short) HIWORD(GetMessagePos());
+ hwndPt = WindowFromPoint(pt);
+ inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd);
+ }
+ if (inwnd != transparentFocus && setLayeredWindowAttributes) { //change
+ transparentFocus = inwnd;
+ if (transparentFocus)
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (BYTE) cluiopt.alpha, LWA_ALPHA);
+ else
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (BYTE) DBGetContactSettingByte(NULL, "CList", "AutoAlpha", SETTING_AUTOALPHA_DEFAULT), LWA_ALPHA);
+ }
+ if (!transparentFocus)
+ KillTimer(hwnd, TM_AUTOALPHA);
+ }
+ return TRUE;
+
+ case WM_SHOWWINDOW:
+ {
+ static int noRecurse = 0;
+ if (lParam)
+ break;
+ if (noRecurse)
+ break;
+ if (!DBGetContactSettingByte(NULL, "CLUI", "FadeInOut", 0) || !IsWinVer2000Plus())
+ break;
+ if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) {
+ DWORD thisTick, startTick;
+ int sourceAlpha, destAlpha;
+ if (wParam) {
+ sourceAlpha = 0;
+ destAlpha = (BYTE) cluiopt.alpha;
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_ALPHA);
+ noRecurse = 1;
+ ShowWindow(hwnd, SW_SHOW);
+ noRecurse = 0;
+ }
+ else {
+ sourceAlpha = (BYTE) cluiopt.alpha;
+ destAlpha = 0;
+ }
+ for (startTick = GetTickCount();;) {
+ thisTick = GetTickCount();
+ if (thisTick >= startTick + 200)
+ break;
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0),
+ (BYTE) (sourceAlpha + (destAlpha - sourceAlpha) * (int) (thisTick - startTick) / 200), LWA_ALPHA);
+ }
+ setLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (BYTE) destAlpha, LWA_ALPHA);
+ }
+ else {
+ if (wParam)
+ SetForegroundWindow(hwnd);
+ animateWindow(hwnd, 200, AW_BLEND | (wParam ? 0 : AW_HIDE));
+ SetWindowPos(cli.hwndContactTree, 0, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ break;
+ }
+ case WM_MENURBUTTONUP: /* this API is so badly documented at MSDN!! */
+ {
+ UINT id = 0;
+
+ id = GetMenuItemID((HMENU) lParam, LOWORD(wParam)); /* LOWORD(wParam) contains the menu pos in its parent menu */
+ if (id != (-1))
+ SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(id, 0), 0);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ case WM_SYSCOMMAND:
+ switch (wParam)
+ {
+ case SC_MAXIMIZE:
+ return 0;
+
+ case SC_MINIMIZE:
+ case SC_CLOSE:
+ if ((GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) ||
+ DBGetContactSettingByte(NULL, "CList", "Min2Tray", SETTING_MIN2TRAY_DEFAULT))
+ {
+ ShowWindow(hwnd, SW_HIDE);
+ DBWriteContactSettingByte(NULL, "CList", "State", SETTING_STATE_HIDDEN);
+
+ if (MySetProcessWorkingSetSize != NULL && DBGetContactSettingByte(NULL, "CList", "DisableWorkingSet", 1))
+ MySetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
+
+ return 0;
+ }
+ else if (wParam == SC_CLOSE)
+ wParam = SC_MINIMIZE;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_COMMAND:
+ if (CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_MAINMENU), (LPARAM) (HANDLE) NULL))
+ break;
+ switch (LOWORD(wParam)) {
+ case ID_TRAY_EXIT:
+ case ID_ICQ_EXIT:
+ if (CallService(MS_SYSTEM_OKTOEXIT, 0, 0))
+ DestroyWindow(hwnd);
+ break;
+ case ID_TRAY_HIDE:
+ CallService(MS_CLIST_SHOWHIDE, 0, 0);
+ break;
+ case POPUP_NEWGROUP:
+ SendMessage(cli.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0);
+ CallService(MS_CLIST_GROUPCREATE, 0, 0);
+ break;
+ case POPUP_HIDEOFFLINE:
+ CallService(MS_CLIST_SETHIDEOFFLINE, (WPARAM) (-1), 0);
+ break;
+ case POPUP_HIDEOFFLINEROOT:
+ SendMessage(cli.hwndContactTree, CLM_SETHIDEOFFLINEROOT, !SendMessage(cli.hwndContactTree, CLM_GETHIDEOFFLINEROOT, 0, 0), 0);
+ break;
+ case POPUP_HIDEEMPTYGROUPS:
+ {
+ int newVal = !(GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_HIDEEMPTYGROUPS);
+ DBWriteContactSettingByte(NULL, "CList", "HideEmptyGroups", (BYTE) newVal);
+ SendMessage(cli.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, newVal, 0);
+ break;
+ }
+ case POPUP_DISABLEGROUPS:
+ {
+ int newVal = !(GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_USEGROUPS);
+ DBWriteContactSettingByte(NULL, "CList", "UseGroups", (BYTE) newVal);
+ SendMessage(cli.hwndContactTree, CLM_SETUSEGROUPS, newVal, 0);
+ break;
+ }
+ case POPUP_HIDEMIRANDA:
+ {
+ CallService(MS_CLIST_SHOWHIDE, 0, 0);
+ break;
+ } }
+ return FALSE;
+ case WM_KEYDOWN:
+ CallService(MS_CLIST_MENUPROCESSHOTKEY, wParam, MPCF_MAINMENU | MPCF_CONTACTMENU);
+ break;
+
+ case WM_GETMINMAXINFO:
+ DefWindowProc(hwnd, msg, wParam, lParam);
+ ((LPMINMAXINFO) lParam)->ptMinTrackSize.x = 16 + GetSystemMetrics(SM_CXHTHUMB);
+ ((LPMINMAXINFO) lParam)->ptMinTrackSize.y = 16;
+ return 0;
+
+ case WM_SETTINGCHANGE:
+ if (wParam == SPI_SETWORKAREA && (GetWindowLong(hwnd, GWL_STYLE) & (WS_VISIBLE | WS_MINIMIZE)) == WS_VISIBLE &&
+ !CallService(MS_CLIST_DOCKINGISDOCKED, 0, 0))
+ {
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+ if (Utils_AssertInsideScreen(&rc) == 1)
+ MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_DISPLAYCHANGE:
+ DefWindowProc(hwnd, msg, wParam, lParam);
+ SendMessage(cli.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ break;
+
+ //MSG FROM CHILD CONTROL
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->hwndFrom == cli.hwndContactTree) {
+ switch (((LPNMHDR) lParam)->code) {
+ case CLN_EXPANDED:
+ {
+ NMCLISTCONTROL *nmc = (NMCLISTCONTROL *) lParam;
+ CallService(MS_CLIST_GROUPSETEXPANDED, (WPARAM) nmc->hItem, nmc->action);
+ return FALSE;
+ }
+ case CLN_DRAGGING:
+ {
+ NMCLISTCONTROL *nmc = (NMCLISTCONTROL *) lParam;
+ ClientToScreen(hwnd, &nmc->pt);
+ if (!(nmc->flags & CLNF_ISGROUP))
+ if (NotifyEventHooks(hContactDraggingEvent, (WPARAM) nmc->hItem, MAKELPARAM(nmc->pt.x, nmc->pt.y))) {
+ SetCursor(LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER)));
+ return TRUE;
+ }
+ break;
+ }
+ case CLN_DRAGSTOP:
+ {
+ NMCLISTCONTROL *nmc = (NMCLISTCONTROL *) lParam;
+ if (!(nmc->flags & CLNF_ISGROUP))
+ NotifyEventHooks(hContactDragStopEvent, (WPARAM) nmc->hItem, 0);
+ break;
+ }
+ case CLN_DROPPED:
+ {
+ NMCLISTCONTROL *nmc = (NMCLISTCONTROL *) lParam;
+ ClientToScreen(hwnd, &nmc->pt);
+ if (!(nmc->flags & CLNF_ISGROUP))
+ if (NotifyEventHooks(hContactDroppedEvent, (WPARAM) nmc->hItem, MAKELPARAM(nmc->pt.x, nmc->pt.y))) {
+ SetCursor(LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER)));
+ return TRUE;
+ }
+ break;
+ }
+ case NM_KEYDOWN:
+ {
+ NMKEY *nmkey = (NMKEY *) lParam;
+ return CallService(MS_CLIST_MENUPROCESSHOTKEY, nmkey->nVKey, MPCF_MAINMENU | MPCF_CONTACTMENU);
+ }
+ case CLN_LISTSIZECHANGE:
+ {
+ NMCLISTCONTROL *nmc = (NMCLISTCONTROL *) lParam;
+ RECT rcWindow, rcTree, rcWorkArea;
+ int maxHeight, newHeight;
+
+ if (!DBGetContactSettingByte(NULL, "CLUI", "AutoSize", 0))
+ break;
+ if (CallService(MS_CLIST_DOCKINGISDOCKED, 0, 0))
+ break;
+ maxHeight = DBGetContactSettingByte(NULL, "CLUI", "MaxSizeHeight", 75);
+ GetWindowRect(hwnd, &rcWindow);
+ GetWindowRect(cli.hwndContactTree, &rcTree);
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ if (MyMonitorFromWindow)
+ {
+ HMONITOR hMon = MyMonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (MyGetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+ }
+
+ newHeight = max(nmc->pt.y, 9) + 1 + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top);
+ if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100)
+ newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100;
+ if (DBGetContactSettingByte(NULL, "CLUI", "AutoSizeUpward", 0)) {
+ rcWindow.top = rcWindow.bottom - newHeight;
+ if (rcWindow.top < rcWorkArea.top)
+ rcWindow.top = rcWorkArea.top;
+ }
+ else {
+ rcWindow.bottom = rcWindow.top + newHeight;
+ if (rcWindow.bottom > rcWorkArea.bottom)
+ rcWindow.bottom = rcWorkArea.bottom;
+ }
+ SetWindowPos(hwnd, 0, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ break;
+ }
+ case NM_CLICK:
+ {
+ NMCLISTCONTROL *nm = (NMCLISTCONTROL *) lParam;
+ DWORD hitFlags;
+
+ if (SendMessage(cli.hwndContactTree, CLM_HITTEST, (WPARAM) & hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y)))
+ break;
+ if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0)
+ break;
+ if (DBGetContactSettingByte(NULL, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) {
+ POINT pt;
+ pt = nm->pt;
+ ClientToScreen(cli.hwndContactTree, &pt);
+ return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ break;
+ } }
+ }
+ else if (((LPNMHDR) lParam)->hwndFrom == cli.hwndStatus) {
+ if (((LPNMHDR) lParam)->code == NM_CLICK)
+ {
+ unsigned int nParts, nPanel;
+ NMMOUSE *nm = (NMMOUSE *) lParam;
+ HMENU hMenu;
+ RECT rc;
+ POINT pt;
+
+ hMenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS, 0, 0);
+ nParts = SendMessage(cli.hwndStatus, SB_GETPARTS, 0, 0);
+ if (nm->dwItemSpec == 0xFFFFFFFE) {
+ nPanel = nParts - 1;
+ SendMessage(cli.hwndStatus, SB_GETRECT, nPanel, (LPARAM) & rc);
+ if (nm->pt.x < rc.left)
+ return FALSE;
+ }
+ else nPanel = nm->dwItemSpec;
+
+ if (nParts > 0)
+ {
+ unsigned int cpnl = 0;
+ int mcnt = GetMenuItemCount(hMenu);
+ for (int i=0; i<mcnt; ++i) {
+ HMENU hMenus = GetSubMenu(hMenu, i);
+ if (hMenus && cpnl++ == nPanel) {
+ hMenu = hMenus;
+ break;
+ }
+ }
+ }
+ SendMessage(cli.hwndStatus, SB_GETRECT, nPanel, (LPARAM) & rc);
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ClientToScreen(cli.hwndStatus, &pt);
+ TrackPopupMenu(hMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, NULL);
+ } }
+ return FALSE;
+
+ case WM_MENUSELECT:
+ if(lParam && (HMENU)lParam == cli.hMenuMain) {
+ int pos = LOWORD(wParam);
+ POINT pt;
+ GetCursorPos(&pt);
+ if ((pos == 0 || pos == 1) && (HIWORD(wParam) & MF_POPUP) &&
+ (!(HIWORD(wParam) & MF_MOUSESELECT) || MenuItemFromPoint(hwnd, cli.hMenuMain, pt) != -1)) {
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_SUBMENU;
+ mii.hSubMenu = (HMENU)CallService((pos == 0) ? MS_CLIST_MENUGETMAIN : MS_CLIST_MENUGETSTATUS, 0, 0);
+ SetMenuItemInfo(cli.hMenuMain, pos, TRUE, &mii);
+ } }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ RECT rc;
+ POINT pt;
+
+ pt.x = (short) LOWORD(lParam);
+ pt.y = (short) HIWORD(lParam);
+ // x/y might be -1 if it was generated by a kb click
+ GetWindowRect(cli.hwndContactTree, &rc);
+ if (pt.x == -1 && pt.y == -1) {
+ // all this is done in screen-coords!
+ GetCursorPos(&pt);
+ // the mouse isnt near the window, so put it in the middle of the window
+ if (!PtInRect(&rc, pt)) {
+ pt.x = rc.left + (rc.right - rc.left) / 2;
+ pt.y = rc.top + (rc.bottom - rc.top) / 2;
+ }
+ }
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu;
+ hMenu = GetSubMenu(LoadMenu(cli.hInst, MAKEINTRESOURCE(IDR_CONTEXT)), 1);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hMenu, 0);
+ CheckMenuItem(hMenu, POPUP_HIDEOFFLINE,
+ DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, POPUP_HIDEOFFLINEROOT, SendMessage(cli.hwndContactTree, CLM_GETHIDEOFFLINEROOT, 0, 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, POPUP_HIDEEMPTYGROUPS,
+ GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_HIDEEMPTYGROUPS ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, POPUP_DISABLEGROUPS, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_USEGROUPS ? MF_UNCHECKED : MF_CHECKED);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ GetWindowRect(cli.hwndStatus, &rc);
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu;
+ if (DBGetContactSettingByte(NULL, "CLUI", "SBarRightClk", 0))
+ hMenu = (HMENU) CallService(MS_CLIST_MENUGETMAIN, 0, 0);
+ else
+ hMenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS, 0, 0);
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
+ return 0;
+ } }
+ break;
+
+ case WM_MEASUREITEM:
+ if (((LPMEASUREITEMSTRUCT) lParam)->itemData == MENU_MIRANDAMENU) {
+ ((LPMEASUREITEMSTRUCT) lParam)->itemWidth = g_IconWidth * 4 / 3;
+ ((LPMEASUREITEMSTRUCT) lParam)->itemHeight = 0;
+ return TRUE;
+ }
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam;
+ if (dis->hwndItem == cli.hwndStatus) {
+ char *szProto = (char *) dis->itemData;
+ if (szProto == NULL) return 0;
+ int status, x;
+ SIZE textSize;
+ BYTE showOpts = DBGetContactSettingByte(NULL, "CLUI", "SBarShow", 1);
+ status = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ SetBkMode(dis->hDC, TRANSPARENT);
+ x = dis->rcItem.left;
+ if (showOpts & 1) {
+ HICON hIcon = LoadSkinProtoIcon(szProto, status);
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - g_IconHeight) >> 1, hIcon,
+ g_IconWidth, g_IconHeight, 0, NULL, DI_NORMAL);
+ IconLib_ReleaseIcon(hIcon,0);
+ if ( Proto_IsAccountLocked( Proto_GetAccount( szProto ))) {
+ hIcon = LoadSkinnedIcon(SKINICON_OTHER_STATUS_LOCKED);
+ if (hIcon != NULL) {
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - g_IconHeight) >> 1, hIcon,
+ g_IconWidth, g_IconHeight, 0, NULL, DI_NORMAL);
+ IconLib_ReleaseIcon(hIcon,0);
+ }
+
+ }
+ x += g_IconWidth + 2;
+ }
+ else
+ x += 2;
+ if (showOpts & 2) {
+ PROTOACCOUNT* pa;
+ TCHAR tszName[64];
+ if (( pa = Proto_GetAccount( szProto )) != NULL )
+ mir_sntprintf( tszName, SIZEOF(tszName), _T("%s "), pa->tszAccountName );
+ else
+ tszName[0] = 0;
+
+ GetTextExtentPoint32(dis->hDC, tszName, lstrlen(tszName), &textSize);
+ TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, tszName, lstrlen(tszName));
+ x += textSize.cx;
+ }
+ if (showOpts & 4) {
+ TCHAR* szStatus = cli.pfnGetStatusModeDescription( status, 0 );
+ if ( !szStatus )
+ szStatus = _T("");
+ GetTextExtentPoint32( dis->hDC, szStatus, lstrlen(szStatus), &textSize );
+ TextOut( dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy ) >> 1, szStatus, lstrlen( szStatus ));
+ }
+ }
+ else if (dis->CtlType == ODT_MENU) {
+ if (dis->itemData == MENU_MIRANDAMENU) {
+ HICON hIcon = LoadSkinnedIcon(SKINICON_OTHER_MAINMENU);
+ fnDrawMenuItem(dis, CopyIcon(hIcon), NULL);
+ IconLib_ReleaseIcon(hIcon, NULL);
+ return TRUE;
+ }
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+ } }
+ return 0;
+
+ case WM_CLOSE:
+ if (CallService(MS_SYSTEM_OKTOEXIT, 0, 0))
+ DestroyWindow(hwnd);
+ return FALSE;
+
+ case WM_DESTROY:
+ if (!IsIconic(hwnd)) {
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+
+ if (!CallService(MS_CLIST_DOCKINGISDOCKED, 0, 0)) { //if docked, dont remember pos (except for width)
+ DBWriteContactSettingDword(NULL, "CList", "Height", (DWORD) (rc.bottom - rc.top));
+ DBWriteContactSettingDword(NULL, "CList", "x", (DWORD) rc.left);
+ DBWriteContactSettingDword(NULL, "CList", "y", (DWORD) rc.top);
+ }
+ DBWriteContactSettingDword(NULL, "CList", "Width", (DWORD) (rc.right - rc.left));
+ }
+
+ RemoveMenu(cli.hMenuMain, 0, MF_BYPOSITION);
+ RemoveMenu(cli.hMenuMain, 0, MF_BYPOSITION);
+
+ if ( cli.hwndStatus ) {
+ DestroyWindow( cli.hwndStatus );
+ cli.hwndStatus = NULL;
+ }
+
+ // Disconnect all protocols
+ DisconnectAll();
+
+ ShowWindow(hwnd, SW_HIDE);
+ DestroyWindow(cli.hwndContactTree);
+ FreeLibrary(hUserDll);
+ PostQuitMessage(0);
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return TRUE;
+}
diff --git a/src/modules/clist/cluiservices.cpp b/src/modules/clist/cluiservices.cpp
new file mode 100644
index 0000000000..c6b6efda71
--- /dev/null
+++ b/src/modules/clist/cluiservices.cpp
@@ -0,0 +1,221 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+static INT_PTR GetHwnd(WPARAM, LPARAM)
+{
+ return (INT_PTR)cli.hwndContactList;
+}
+
+static INT_PTR GetHwndTree(WPARAM, LPARAM)
+{
+ return (INT_PTR)cli.hwndContactTree;
+}
+
+static INT_PTR CluiProtocolStatusChanged(WPARAM wParam, LPARAM lParam)
+{
+ cli.pfnCluiProtocolStatusChanged( wParam, (const char*)lParam );
+ return 0;
+}
+
+INT_PTR SortList(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR GroupAdded(WPARAM wParam, LPARAM lParam)
+{
+ //CLC does this automatically unless it's a new group
+ if (lParam) {
+ HANDLE hItem;
+ TCHAR szFocusClass[64];
+ HWND hwndFocus = GetFocus();
+
+ GetClassName(hwndFocus, szFocusClass, SIZEOF(szFocusClass));
+ if (!lstrcmp(szFocusClass, CLISTCONTROL_CLASS)) {
+ hItem = (HANDLE) SendMessage(hwndFocus, CLM_FINDGROUP, wParam, 0);
+ if (hItem)
+ SendMessage(hwndFocus, CLM_EDITLABEL, (WPARAM) hItem, 0);
+ }
+ }
+ return 0;
+}
+
+static INT_PTR ContactSetIcon(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR ContactDeleted(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR ContactAdded(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR ListBeginRebuild(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR ListEndRebuild(WPARAM, LPARAM)
+{
+ int rebuild = 0;
+ //CLC does this automatically, but we need to force it if hideoffline or hideempty has changed
+ if ((DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT) == 0) != ((GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_HIDEOFFLINE) == 0)) {
+ if (DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT))
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) | CLS_HIDEOFFLINE);
+ else
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & ~CLS_HIDEOFFLINE);
+ rebuild = 1;
+ }
+ if ((DBGetContactSettingByte(NULL, "CList", "HideEmptyGroups", SETTING_HIDEEMPTYGROUPS_DEFAULT) == 0) != ((GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) == 0)) {
+ if (DBGetContactSettingByte(NULL, "CList", "HideEmptyGroups", SETTING_HIDEEMPTYGROUPS_DEFAULT))
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) | CLS_HIDEEMPTYGROUPS);
+ else
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & ~CLS_HIDEEMPTYGROUPS);
+ rebuild = 1;
+ }
+ if ((DBGetContactSettingByte(NULL, "CList", "UseGroups", SETTING_USEGROUPS_DEFAULT) == 0) != ((GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & CLS_USEGROUPS) == 0)) {
+ if (DBGetContactSettingByte(NULL, "CList", "UseGroups", SETTING_USEGROUPS_DEFAULT))
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) | CLS_USEGROUPS);
+ else
+ SetWindowLongPtr(cli.hwndContactTree, GWL_STYLE, GetWindowLongPtr(cli.hwndContactTree, GWL_STYLE) & ~CLS_USEGROUPS);
+ rebuild = 1;
+ }
+ if (rebuild)
+ SendMessage(cli.hwndContactTree, CLM_AUTOREBUILD, 0, 0);
+ return 0;
+}
+
+static INT_PTR ContactRenamed(WPARAM, LPARAM)
+{
+ //unnecessary: CLC does this automatically
+ return 0;
+}
+
+static INT_PTR GetCaps(WPARAM wParam, LPARAM)
+{
+ switch (wParam) {
+ case CLUICAPS_FLAGS1:
+ return CLUIF_HIDEEMPTYGROUPS | CLUIF_DISABLEGROUPS | CLUIF_HASONTOPOPTION | CLUIF_HASAUTOHIDEOPTION;
+ }
+ return 0;
+}
+
+void LoadCluiServices(void)
+{
+ CreateServiceFunction(MS_CLUI_GETHWND, GetHwnd);
+ CreateServiceFunction(MS_CLUI_GETHWNDTREE,GetHwndTree);
+ CreateServiceFunction(MS_CLUI_PROTOCOLSTATUSCHANGED, CluiProtocolStatusChanged);
+ CreateServiceFunction(MS_CLUI_GROUPADDED, GroupAdded);
+ CreateServiceFunction(MS_CLUI_CONTACTSETICON, ContactSetIcon);
+ CreateServiceFunction(MS_CLUI_CONTACTADDED, ContactAdded);
+ CreateServiceFunction(MS_CLUI_CONTACTDELETED, ContactDeleted);
+ CreateServiceFunction(MS_CLUI_CONTACTRENAMED, ContactRenamed);
+ CreateServiceFunction(MS_CLUI_LISTBEGINREBUILD, ListBeginRebuild);
+ CreateServiceFunction(MS_CLUI_LISTENDREBUILD, ListEndRebuild);
+ CreateServiceFunction(MS_CLUI_SORTLIST, SortList);
+ CreateServiceFunction(MS_CLUI_GETCAPS, GetCaps);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// default protocol status notification handler
+
+void fnCluiProtocolStatusChanged(int, const char* )
+{
+ int i, *partWidths;
+ int borders[3];
+ int flags = 0;
+
+ if ( cli.menuProtoCount == 0 ) {
+ SendMessage(cli.hwndStatus, SB_SETPARTS, 0, 0);
+ SendMessage( cli.hwndStatus, SB_SETTEXT, SBT_OWNERDRAW, 0 );
+ return;
+ }
+
+ SendMessage(cli.hwndStatus, SB_GETBORDERS, 0, ( LPARAM )&borders);
+
+ partWidths = ( int* )alloca( cli.menuProtoCount * sizeof( int ));
+ if ( DBGetContactSettingByte( NULL, "CLUI", "EqualSections", 0 )) {
+ RECT rc;
+ GetClientRect( cli.hwndStatus, &rc );
+ rc.right -= borders[0] * 2 + ( DBGetContactSettingByte( NULL, "CLUI", "ShowGrip", 1 ) ? GetSystemMetrics( SM_CXVSCROLL ) : 0 );
+ for ( i = 0; i < cli.menuProtoCount; i++ )
+ partWidths[ i ] = ( i+1 ) * rc.right / cli.menuProtoCount - (borders[2] >> 1);
+ }
+ else {
+ HDC hdc;
+ SIZE textSize;
+ BYTE showOpts = DBGetContactSettingByte(NULL, "CLUI", "SBarShow", 1);
+
+ hdc = GetDC(NULL);
+ SelectObject(hdc, (HFONT) SendMessage(cli.hwndStatus, WM_GETFONT, 0, 0));
+ for ( i = 0; i < cli.menuProtoCount; i++ ) { //count down since built in ones tend to go at the end
+ int x = 2;
+ if ( showOpts & 1 )
+ x += g_IconWidth;
+ if ( showOpts & 2 ) {
+ TCHAR tszName[64];
+ PROTOACCOUNT* pa = Proto_GetAccount( cli.menuProtos[i].szProto );
+ if ( pa )
+ mir_sntprintf( tszName, SIZEOF(tszName), _T("%s "), pa->tszAccountName );
+ else
+ tszName[0] = 0;
+
+ if ( showOpts & 4 && lstrlen(tszName) < SIZEOF(tszName)-1 )
+ lstrcat( tszName, _T(" "));
+ GetTextExtentPoint32(hdc, tszName, lstrlen(tszName), &textSize);
+ x += textSize.cx;
+ x += GetSystemMetrics(SM_CXBORDER) * 4; // The SB panel doesnt allocate enough room
+ }
+ if ( showOpts & 4 ) {
+ TCHAR* modeDescr = cli.pfnGetStatusModeDescription( CallProtoService( cli.menuProtos[i].szProto, PS_GETSTATUS, 0, 0), 0 );
+ GetTextExtentPoint32(hdc, modeDescr, lstrlen(modeDescr), &textSize);
+ x += textSize.cx;
+ x += GetSystemMetrics(SM_CXBORDER) * 4; // The SB panel doesnt allocate enough room
+ }
+ partWidths[ i ] = ( i ? partWidths[ i-1] : 0 ) + x + 2;
+ }
+ ReleaseDC(NULL, hdc);
+ }
+
+ partWidths[ cli.menuProtoCount-1 ] = -1;
+ SendMessage(cli.hwndStatus, SB_SETMINHEIGHT, g_IconHeight, 0);
+ SendMessage(cli.hwndStatus, SB_SETPARTS, cli.menuProtoCount, ( LPARAM )partWidths);
+ flags = SBT_OWNERDRAW;
+ if ( DBGetContactSettingByte( NULL, "CLUI", "SBarBevel", 1 ) == 0 )
+ flags |= SBT_NOBORDERS;
+ for ( i = 0; i < cli.menuProtoCount; i++ ) {
+ SendMessage( cli.hwndStatus, SB_SETTEXT, i | flags, ( LPARAM )cli.menuProtos[i].szProto );
+ }
+}
diff --git a/src/modules/clist/contact.cpp b/src/modules/clist/contact.cpp
new file mode 100644
index 0000000000..f996dd11ac
--- /dev/null
+++ b/src/modules/clist/contact.cpp
@@ -0,0 +1,193 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+extern HANDLE hContactIconChangedEvent;
+extern HANDLE hGroupChangeEvent;
+
+int sortByStatus;
+int sortByProto;
+
+static const struct {
+ int status,order;
+} statusModeOrder[]={
+ {ID_STATUS_OFFLINE,500},
+ {ID_STATUS_ONLINE,10},
+ {ID_STATUS_AWAY,200},
+ {ID_STATUS_DND,110},
+ {ID_STATUS_NA,450},
+ {ID_STATUS_OCCUPIED,100},
+ {ID_STATUS_FREECHAT,0},
+ {ID_STATUS_INVISIBLE,20},
+ {ID_STATUS_ONTHEPHONE,150},
+ {ID_STATUS_OUTTOLUNCH,425}};
+
+static int GetContactStatus(HANDLE hContact)
+{
+ char* szProto = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto == NULL)
+ return ID_STATUS_OFFLINE;
+ return DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE);
+}
+
+void fnChangeContactIcon(HANDLE hContact, int iIcon, int add)
+{
+ CallService(add ? MS_CLUI_CONTACTADDED : MS_CLUI_CONTACTSETICON, (WPARAM) hContact, iIcon);
+ NotifyEventHooks(hContactIconChangedEvent, (WPARAM) hContact, iIcon);
+}
+
+int GetStatusModeOrdering(int statusMode)
+{
+ int i;
+ for (i = 0; i < SIZEOF(statusModeOrder); i++)
+ if (statusModeOrder[i].status == statusMode)
+ return statusModeOrder[i].order;
+ return 1000;
+}
+
+void fnLoadContactTree(void)
+{
+ HANDLE hContact;
+ int i, status, hideOffline;
+
+ CallService(MS_CLUI_LISTBEGINREBUILD, 0, 0);
+ for (i = 1;; i++) {
+ if ( cli.pfnGetGroupName(i, NULL) == NULL)
+ break;
+ CallService(MS_CLUI_GROUPADDED, i, 0);
+ }
+
+ hideOffline = DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT);
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL) {
+ status = GetContactStatus(hContact);
+ if ((!hideOffline || status != ID_STATUS_OFFLINE) && !DBGetContactSettingByte(hContact, "CList", "Hidden", 0))
+ cli.pfnChangeContactIcon(hContact, cli.pfnIconFromStatusMode((char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0), status, hContact), 1);
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ sortByStatus = DBGetContactSettingByte(NULL, "CList", "SortByStatus", SETTING_SORTBYSTATUS_DEFAULT);
+ sortByProto = DBGetContactSettingByte(NULL, "CList", "SortByProto", SETTING_SORTBYPROTO_DEFAULT);
+ CallService(MS_CLUI_SORTLIST, 0, 0);
+ CallService(MS_CLUI_LISTENDREBUILD, 0, 0);
+}
+
+int fnCompareContacts(const struct ClcContact* c1, const struct ClcContact* c2)
+{
+ HANDLE a = c1->hContact, b = c2->hContact;
+ TCHAR namea[128], *nameb;
+ int statusa, statusb;
+ int rc;
+
+ statusa = DBGetContactSettingWord((HANDLE) a, c1->proto, "Status", ID_STATUS_OFFLINE);
+ statusb = DBGetContactSettingWord((HANDLE) b, c2->proto, "Status", ID_STATUS_OFFLINE);
+
+ if (sortByProto) {
+ /* deal with statuses, online contacts have to go above offline */
+ if ((statusa == ID_STATUS_OFFLINE) != (statusb == ID_STATUS_OFFLINE)) {
+ return 2 * (statusa == ID_STATUS_OFFLINE) - 1;
+ }
+ /* both are online, now check protocols */
+ rc = lstrcmpA( c1->proto, c2->proto);
+ if (rc != 0 && (c1->proto != NULL && c2->proto != NULL))
+ return rc;
+ /* protocols are the same, order by display name */
+ }
+
+ if (sortByStatus) {
+ int ordera, orderb;
+ ordera = GetStatusModeOrdering(statusa);
+ orderb = GetStatusModeOrdering(statusb);
+ if (ordera != orderb)
+ return ordera - orderb;
+ }
+ else {
+ //one is offline: offline goes below online
+ if ((statusa == ID_STATUS_OFFLINE) != (statusb == ID_STATUS_OFFLINE)) {
+ return 2 * (statusa == ID_STATUS_OFFLINE) - 1;
+ }
+ }
+
+ nameb = cli.pfnGetContactDisplayName( a, 0);
+ _tcsncpy(namea, nameb, SIZEOF(namea));
+ namea[ SIZEOF(namea)-1 ] = 0;
+ nameb = cli.pfnGetContactDisplayName( b, 0);
+
+ //otherwise just compare names
+ return _tcsicmp(namea, nameb);
+}
+
+static UINT_PTR resortTimerId = 0;
+static VOID CALLBACK SortContactsTimer(HWND, UINT, UINT_PTR, DWORD)
+{
+ KillTimer(NULL, resortTimerId);
+ resortTimerId = 0;
+ CallService(MS_CLUI_SORTLIST, 0, 0);
+}
+
+void fnSortContacts(void)
+{
+ //avoid doing lots of resorts in quick succession
+ sortByStatus = DBGetContactSettingByte(NULL, "CList", "SortByStatus", SETTING_SORTBYSTATUS_DEFAULT);
+ sortByProto = DBGetContactSettingByte(NULL, "CList", "SortByProto", SETTING_SORTBYPROTO_DEFAULT);
+ if (resortTimerId)
+ KillTimer(NULL, resortTimerId);
+ // setting this to a higher delay causes shutdown waits.
+ resortTimerId = SetTimer(NULL, 0, 500, SortContactsTimer);
+}
+
+INT_PTR ContactChangeGroup(WPARAM wParam, LPARAM lParam)
+{
+ CLISTGROUPCHANGE grpChg = { sizeof(CLISTGROUPCHANGE), NULL, NULL };
+
+ CallService(MS_CLUI_CONTACTDELETED, wParam, 0);
+ if ((HANDLE) lParam == NULL)
+ DBDeleteContactSetting((HANDLE) wParam, "CList", "Group");
+ else {
+ grpChg.pszNewName = cli.pfnGetGroupName(lParam, NULL);
+ DBWriteContactSettingTString((HANDLE) wParam, "CList", "Group", grpChg.pszNewName);
+ }
+ CallService(MS_CLUI_CONTACTADDED, wParam,
+ cli.pfnIconFromStatusMode((char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0), GetContactStatus((HANDLE) wParam), (HANDLE) wParam));
+
+ NotifyEventHooks(hGroupChangeEvent, wParam, (LPARAM)&grpChg);
+ return 0;
+}
+
+int fnSetHideOffline(WPARAM wParam, LPARAM)
+{
+ switch(( int )wParam ) {
+ case 0:
+ DBWriteContactSettingByte(NULL, "CList", "HideOffline", 0);
+ break;
+ case 1:
+ DBWriteContactSettingByte(NULL, "CList", "HideOffline", 1);
+ break;
+ case -1:
+ DBWriteContactSettingByte(NULL, "CList", "HideOffline",
+ (BYTE) ! DBGetContactSettingByte(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT));
+ break;
+ }
+ cli.pfnLoadContactTree();
+ return 0;
+}
diff --git a/src/modules/clist/genmenu.cpp b/src/modules/clist/genmenu.cpp
new file mode 100644
index 0000000000..92ec6d3edc
--- /dev/null
+++ b/src/modules/clist/genmenu.cpp
@@ -0,0 +1,1294 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "genmenu.h"
+
+static bool bIsGenMenuInited;
+bool bIconsDisabled;
+static CRITICAL_SECTION csMenuHook;
+
+static int NextObjectId = 0x100, NextObjectMenuItemId = CLISTMENUIDMIN;
+
+#if defined( _DEBUG )
+static void DumpMenuItem( TMO_IntMenuItem* pParent, int level = 0 )
+{
+ char temp[ 30 ];
+ memset( temp, '\t', level );
+ temp[ level ] = 0;
+
+ for ( PMO_IntMenuItem pimi = pParent; pimi != NULL; pimi = pimi->next ) {
+ Netlib_Logf( NULL, "%sMenu item %08p [%08p]: %S", temp, pimi, pimi->mi.root, pimi->mi.ptszName );
+
+ PMO_IntMenuItem submenu = pimi->submenu.first;
+ if ( submenu )
+ DumpMenuItem( submenu, level+1 );
+} }
+
+#endif
+
+static int CompareMenus( const TIntMenuObject* p1, const TIntMenuObject* p2 )
+{
+ return lstrcmpA( p1->Name, p2->Name );
+}
+
+LIST<TIntMenuObject> g_menus( 10, CompareMenus );
+
+void FreeAndNil( void **p )
+{
+ if ( p == NULL )
+ return;
+
+ if ( *p != NULL ) {
+ mir_free( *p );
+ *p = NULL;
+} }
+
+int GetMenuObjbyId( const int id )
+{
+ for ( int i=0; i < g_menus.getCount(); i++ )
+ if ( g_menus[i]->id == id )
+ return i;
+
+ return -1;
+}
+
+PMO_IntMenuItem MO_RecursiveWalkMenu( PMO_IntMenuItem parent, pfnWalkFunc func, void* param )
+{
+ if ( parent == NULL )
+ return FALSE;
+
+ PMO_IntMenuItem pnext;
+ for ( PMO_IntMenuItem pimi = parent; pimi != NULL; pimi = pnext ) {
+ PMO_IntMenuItem submenu = pimi->submenu.first;
+ pnext = pimi->next;
+ if ( func( pimi, param )) // it can destroy the menu item
+ return pimi;
+
+ if ( submenu ) {
+ PMO_IntMenuItem res = MO_RecursiveWalkMenu( submenu, func, param );
+ if ( res )
+ return res;
+ }
+ }
+
+ return FALSE;
+}
+
+//wparam=0
+//lparam=LPMEASUREITEMSTRUCT
+int MO_MeasureMenuItem( LPMEASUREITEMSTRUCT mis )
+{
+ // prevent win9x from ugly menus displaying when there is no icon
+ mis->itemWidth = 0;
+ mis->itemHeight = 0;
+
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ if ( mis == NULL )
+ return FALSE;
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )mis->itemData );
+ if ( pimi == NULL )
+ return FALSE;
+
+ if ( pimi->iconId == -1 )
+ return FALSE;
+
+ mis->itemWidth = max(0,GetSystemMetrics(SM_CXSMICON)-GetSystemMetrics(SM_CXMENUCHECK)+4);
+ mis->itemHeight = GetSystemMetrics(SM_CYSMICON)+2;
+ return TRUE;
+}
+
+//wparam=0
+//lparam=LPDRAWITEMSTRUCT
+int MO_DrawMenuItem( LPDRAWITEMSTRUCT dis )
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ if ( dis == NULL )
+ return FALSE;
+
+ EnterCriticalSection( &csMenuHook );
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )dis->itemData );
+ if ( pimi == NULL || pimi->iconId == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return FALSE;
+ }
+
+ int y = (dis->rcItem.bottom - dis->rcItem.top - GetSystemMetrics(SM_CYSMICON))/2+1;
+ if ( dis->itemState & ODS_SELECTED ) {
+ if ( dis->itemState & ODS_CHECKED ) {
+ RECT rc;
+ rc.left = 2; rc.right = GetSystemMetrics(SM_CXSMICON)+2;
+ rc.top = y; rc.bottom = rc.top+GetSystemMetrics(SM_CYSMICON)+2;
+ FillRect(dis->hDC, &rc, GetSysColorBrush( COLOR_HIGHLIGHT ));
+ ImageList_DrawEx( pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_SELECTED );
+ }
+ else ImageList_DrawEx( pimi->parent->m_hMenuIcons, pimi->iconId, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_FOCUS );
+ }
+ else {
+ if ( dis->itemState & ODS_CHECKED) {
+ RECT rc;
+ rc.left = 0; rc.right = GetSystemMetrics(SM_CXSMICON)+4;
+ rc.top = y-2; rc.bottom = rc.top + GetSystemMetrics(SM_CYSMICON)+4;
+ DrawEdge(dis->hDC,&rc,BDR_SUNKENOUTER,BF_RECT);
+ InflateRect(&rc,-1,-1);
+ COLORREF menuCol = GetSysColor(COLOR_MENU);
+ COLORREF hiliteCol = GetSysColor(COLOR_3DHIGHLIGHT);
+ HBRUSH hBrush = CreateSolidBrush(RGB((GetRValue(menuCol)+GetRValue(hiliteCol))/2,(GetGValue(menuCol)+GetGValue(hiliteCol))/2,(GetBValue(menuCol)+GetBValue(hiliteCol))/2));
+ FillRect(dis->hDC,&rc,GetSysColorBrush(COLOR_MENU));
+ DeleteObject(hBrush);
+ ImageList_DrawEx(pimi->parent->m_hMenuIcons,pimi->iconId,dis->hDC,2,y,0,0,CLR_NONE,GetSysColor(COLOR_MENU),ILD_BLEND50);
+ }
+ else ImageList_DrawEx(pimi->parent->m_hMenuIcons,pimi->iconId,dis->hDC,2,y,0,0,CLR_NONE,CLR_NONE,ILD_NORMAL);
+ }
+ LeaveCriticalSection( &csMenuHook );
+ return TRUE;
+}
+
+int MO_RemoveAllObjects()
+{
+ int i;
+ for ( i=0; i < g_menus.getCount(); i++ )
+ delete g_menus[i];
+
+ g_menus.destroy();
+ return 0;
+}
+
+//wparam=MenuObjectHandle
+INT_PTR MO_RemoveMenuObject(WPARAM wParam, LPARAM)
+{
+ int objidx;
+
+ if ( !bIsGenMenuInited) return -1;
+ EnterCriticalSection( &csMenuHook );
+
+ objidx = GetMenuObjbyId(( int )wParam );
+ if ( objidx == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ delete g_menus[ objidx ];
+ g_menus.remove( objidx );
+ LeaveCriticalSection( &csMenuHook );
+ return 0;
+}
+
+//wparam=MenuObjectHandle
+//lparam=vKey
+INT_PTR MO_ProcessHotKeys( HANDLE menuHandle, INT_PTR vKey )
+{
+ if ( !bIsGenMenuInited)
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+
+ int objidx = GetMenuObjbyId( (int)menuHandle );
+ if ( objidx == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return FALSE;
+ }
+
+ for ( PMO_IntMenuItem pimi = g_menus[objidx]->m_items.first; pimi != NULL; pimi = pimi->next ) {
+ if ( pimi->mi.hotKey == 0 ) continue;
+ if ( HIWORD(pimi->mi.hotKey) != vKey) continue;
+ if ( !(LOWORD(pimi->mi.hotKey) & MOD_ALT ) != !( GetKeyState( VK_MENU ) & 0x8000)) continue;
+ if ( !(LOWORD(pimi->mi.hotKey) & MOD_CONTROL ) != !( GetKeyState( VK_CONTROL ) & 0x8000)) continue;
+ if ( !(LOWORD(pimi->mi.hotKey) & MOD_SHIFT ) != !( GetKeyState( VK_SHIFT ) & 0x8000)) continue;
+
+ MO_ProcessCommand( pimi, 0 );
+ LeaveCriticalSection( &csMenuHook );
+ return TRUE;
+ }
+
+ LeaveCriticalSection( &csMenuHook );
+ return FALSE;
+}
+
+INT_PTR MO_GetProtoRootMenu(WPARAM wParam,LPARAM lParam)
+{
+ char* szProto = ( char* )wParam;
+ if ( szProto == NULL )
+ return 0;
+
+ if ( DBGetContactSettingByte( NULL, "CList", "MoveProtoMenus", FALSE ))
+ return ( INT_PTR )cli.pfnGetProtocolMenu( szProto );
+
+ int objidx = GetMenuObjbyId(( int )hMainMenuObject );
+ if ( objidx == -1 )
+ return NULL;
+
+ EnterCriticalSection( &csMenuHook );
+
+ TIntMenuObject* pmo = g_menus[objidx];
+ PMO_IntMenuItem p;
+ for ( p = pmo->m_items.first; p != NULL; p = p->next )
+ if ( !lstrcmpA( p->UniqName, szProto ))
+ break;
+
+ LeaveCriticalSection( &csMenuHook );
+ return ( INT_PTR )p;
+}
+
+//wparam=MenuItemHandle
+//lparam=PMO_MenuItem
+INT_PTR MO_GetMenuItem(WPARAM wParam,LPARAM lParam)
+{
+ PMO_MenuItem mi = (PMO_MenuItem)lParam;
+ if ( !bIsGenMenuInited || mi == NULL )
+ return -1;
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )wParam);
+ EnterCriticalSection( &csMenuHook );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ *mi = pimi->mi;
+ LeaveCriticalSection( &csMenuHook );
+ return 0;
+}
+
+static int FindDefaultItem( PMO_IntMenuItem pimi, void* )
+{
+ if ( pimi->mi.flags & ( CMIF_GRAYED | CMIF_HIDDEN ))
+ return FALSE;
+
+ return ( pimi->mi.flags & CMIF_DEFAULT ) ? TRUE : FALSE;
+}
+
+INT_PTR MO_GetDefaultMenuItem(WPARAM wParam, LPARAM)
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )wParam);
+ EnterCriticalSection( &csMenuHook );
+ if ( pimi )
+ pimi = MO_RecursiveWalkMenu( pimi, FindDefaultItem, NULL );
+
+ LeaveCriticalSection( &csMenuHook );
+ return ( INT_PTR )pimi;
+}
+
+//wparam MenuItemHandle
+//lparam PMO_MenuItem
+int MO_ModifyMenuItem( PMO_IntMenuItem menuHandle, PMO_MenuItem pmi )
+{
+ int oldflags;
+
+ if ( !bIsGenMenuInited || pmi == NULL || pmi->cbSize != sizeof( TMO_MenuItem ))
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )menuHandle );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ if ( pmi->flags & CMIM_NAME ) {
+ FreeAndNil(( void** )&pimi->mi.pszName );
+#if defined( _UNICODE )
+ if ( pmi->flags & CMIF_UNICODE )
+ pimi->mi.ptszName = mir_tstrdup(( pmi->flags & CMIF_KEEPUNTRANSLATED ) ? pmi->ptszName : TranslateTS( pmi->ptszName ));
+ else {
+ if ( pmi->flags & CMIF_KEEPUNTRANSLATED ) {
+ int len = lstrlenA( pmi->pszName );
+ pimi->mi.ptszName = ( TCHAR* )mir_alloc( sizeof( TCHAR )*( len+1 ));
+ MultiByteToWideChar( CP_ACP, 0, pmi->pszName, -1, pimi->mi.ptszName, len+1 );
+ pimi->mi.ptszName[ len ] = 0;
+ }
+ else pimi->mi.ptszName = LangPackPcharToTchar( pmi->pszName );
+ }
+#else
+ pimi->mi.ptszName = mir_strdup(( pmi->flags & CMIF_KEEPUNTRANSLATED ) ? pmi->ptszName : Translate( pmi->ptszName ));
+#endif
+ }
+ if ( pmi->flags & CMIM_FLAGS ) {
+ oldflags = pimi->mi.flags & ( CMIF_ROOTHANDLE | CMIF_ICONFROMICOLIB );
+ pimi->mi.flags = (pmi->flags & ~CMIM_ALL) | oldflags;
+ }
+ if ( (pmi->flags & CMIM_ICON) && !bIconsDisabled ) {
+ if ( pimi->mi.flags & CMIF_ICONFROMICOLIB ) {
+ HICON hIcon = IcoLib_GetIconByHandle( pmi->hIcolibItem, false );
+ if ( hIcon != NULL ) {
+ pimi->hIcolibItem = pmi->hIcolibItem;
+ pimi->iconId = ImageList_ReplaceIcon( pimi->parent->m_hMenuIcons, pimi->iconId, hIcon );
+ IconLib_ReleaseIcon( hIcon, 0 );
+ }
+ else pimi->iconId = -1, pimi->hIcolibItem = NULL;
+ }
+ else {
+ pimi->mi.hIcon = pmi->hIcon;
+ if ( pmi->hIcon != NULL )
+ pimi->iconId = ImageList_ReplaceIcon( pimi->parent->m_hMenuIcons, pimi->iconId, pmi->hIcon );
+ else
+ pimi->iconId = -1; //fixme, should remove old icon & shuffle all iconIds
+ }
+ if (pimi->hBmp) DeleteObject(pimi->hBmp); pimi->hBmp = NULL;
+ }
+
+ if ( pmi->flags & CMIM_HOTKEY )
+ pimi->mi.hotKey = pmi->hotKey;
+
+ LeaveCriticalSection( &csMenuHook );
+ return 0;
+}
+
+//wparam MenuItemHandle
+//return ownerdata useful to free ownerdata before delete menu item,
+//NULL on error.
+INT_PTR MO_MenuItemGetOwnerData(WPARAM wParam, LPARAM)
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )wParam );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ INT_PTR res = ( INT_PTR )pimi->mi.ownerdata;
+ LeaveCriticalSection( &csMenuHook );
+ return res;
+}
+
+PMO_IntMenuItem MO_GetIntMenuItem(HGENMENU wParam)
+{
+ PMO_IntMenuItem result = ( PMO_IntMenuItem )wParam;
+ if ( result == NULL || wParam == (HGENMENU)0xffff1234 || wParam == HGENMENU_ROOT)
+ return NULL;
+
+ __try
+ {
+ if ( result->signature != MENUITEM_SIGNATURE )
+ result = NULL;
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ result = NULL;
+ }
+
+ return result;
+}
+
+//LOWORD(wparam) menuident
+
+static int FindMenuByCommand( PMO_IntMenuItem pimi, void* pCommand )
+{
+ return ( pimi->iCommand == (int)pCommand );
+}
+
+int MO_ProcessCommandBySubMenuIdent(int menuID, int command, LPARAM lParam)
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+
+ int objidx = GetMenuObjbyId( menuID );
+ if ( objidx == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ PMO_IntMenuItem pimi = MO_RecursiveWalkMenu( g_menus[objidx]->m_items.first, FindMenuByCommand, ( void* )command );
+ if ( pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return MO_ProcessCommand( pimi, lParam );
+ }
+
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+}
+
+INT_PTR MO_ProcessCommandByMenuIdent(WPARAM wParam,LPARAM lParam)
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+
+ for ( int i=0; i < g_menus.getCount(); i++ ) {
+ PMO_IntMenuItem pimi = MO_RecursiveWalkMenu( g_menus[i]->m_items.first, FindMenuByCommand, ( void* )wParam );
+ if ( pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return MO_ProcessCommand( pimi, lParam );
+ } }
+
+ LeaveCriticalSection( &csMenuHook );
+ return FALSE;
+}
+
+int MO_ProcessCommand( PMO_IntMenuItem aHandle, LPARAM lParam )
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem( aHandle );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ char *srvname = pimi->parent->ExecService;
+ void *ownerdata = pimi->mi.ownerdata;
+ LeaveCriticalSection( &csMenuHook );
+ CallService( srvname, ( WPARAM )ownerdata, lParam );
+ return 1;
+}
+
+int MO_SetOptionsMenuItem( PMO_IntMenuItem aHandle, int setting, INT_PTR value )
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem( aHandle );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ int res = -1;
+ __try
+ {
+ res = 1;
+ if ( setting == OPT_MENUITEMSETUNIQNAME ) {
+ mir_free( pimi->UniqName );
+ pimi->UniqName = mir_strdup(( char* )value );
+ }
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER ) {}
+
+ LeaveCriticalSection( &csMenuHook );
+ return res;
+}
+
+int MO_SetOptionsMenuObject( HANDLE handle, int setting, INT_PTR value )
+{
+ int pimoidx;
+ int res = 0;
+
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+ __try
+ {
+ pimoidx = GetMenuObjbyId( (int)handle );
+ res = pimoidx != -1;
+ if ( res ) {
+ TIntMenuObject* pmo = g_menus[pimoidx];
+
+ switch ( setting ) {
+ case OPT_MENUOBJECT_SET_ONADD_SERVICE:
+ FreeAndNil(( void** )&pmo->onAddService );
+ pmo->onAddService = mir_strdup(( char* )value );
+ break;
+
+ case OPT_MENUOBJECT_SET_FREE_SERVICE:
+ FreeAndNil(( void** )&pmo->FreeService );
+ pmo->FreeService = mir_strdup(( char* )value );
+ break;
+
+ case OPT_MENUOBJECT_SET_CHECK_SERVICE:
+ FreeAndNil(( void** )&pmo->CheckService );
+ pmo->CheckService = mir_strdup(( char* )value);
+ break;
+
+ case OPT_USERDEFINEDITEMS:
+ pmo->m_bUseUserDefinedItems = ( BOOL )value;
+ break;
+ }
+ }
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER ) {}
+
+ LeaveCriticalSection( &csMenuHook );
+ return res;
+}
+
+//wparam=0;
+//lparam=PMenuParam;
+//result=MenuObjectHandle
+INT_PTR MO_CreateNewMenuObject(WPARAM, LPARAM lParam)
+{
+ PMenuParam pmp = ( PMenuParam )lParam;
+ if ( !bIsGenMenuInited || pmp == NULL )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+ TIntMenuObject* p = new TIntMenuObject();
+ p->id = NextObjectId++;
+ p->Name = mir_strdup( pmp->name );
+ p->CheckService = mir_strdup( pmp->CheckService );
+ p->ExecService = mir_strdup( pmp->ExecService );
+ p->m_hMenuIcons = ImageList_Create( GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 15, 100 );
+ g_menus.insert(p);
+
+ LeaveCriticalSection( &csMenuHook );
+ return p->id;
+}
+
+//wparam=MenuItemHandle
+//lparam=0
+
+static int FreeMenuItem( TMO_IntMenuItem* pimi, void* )
+{
+ pimi->parent->freeItem( pimi );
+ return FALSE;
+}
+
+static int FindParent( TMO_IntMenuItem* pimi, void* p )
+{
+ return pimi->next == p;
+}
+
+INT_PTR MO_RemoveMenuItem(WPARAM wParam, LPARAM)
+{
+ EnterCriticalSection( &csMenuHook );
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )wParam );
+ if ( !pimi ) {
+ LeaveCriticalSection( &csMenuHook );
+ return -1;
+ }
+
+ if ( pimi->submenu.first ) {
+ MO_RecursiveWalkMenu( pimi->submenu.first, FreeMenuItem, NULL );
+ pimi->submenu.first = NULL;
+ }
+
+ PMO_IntMenuItem prev = MO_RecursiveWalkMenu( pimi->owner->first, FindParent, pimi );
+ if ( prev )
+ prev->next = pimi->next;
+ if ( pimi->owner->first == pimi )
+ pimi->owner->first = pimi->next;
+ if ( pimi->owner->last == pimi )
+ pimi->owner->last = prev;
+
+ pimi->signature = 0; // invalidate all future calls to that object
+ pimi->parent->freeItem( pimi );
+
+ LeaveCriticalSection( &csMenuHook );
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// we presume that this function is being called inside csMenuHook only
+
+static int PackMenuItems( PMO_IntMenuItem pimi, void* )
+{
+ pimi->iCommand = NextObjectMenuItemId++;
+ return FALSE;
+}
+
+static int GetNextObjectMenuItemId()
+{
+ // if menu commands are exausted, pack the menu array
+ if ( NextObjectMenuItemId >= CLISTMENUIDMAX ) {
+ NextObjectMenuItemId = CLISTMENUIDMIN;
+ for ( int i=0; i < g_menus.getCount(); i++ )
+ MO_RecursiveWalkMenu( g_menus[i]->m_items.first, PackMenuItems, NULL );
+ }
+
+ return NextObjectMenuItemId++;
+}
+
+//wparam=MenuObjectHandle
+//lparam=PMO_MenuItem
+//return MenuItemHandle
+PMO_IntMenuItem MO_AddNewMenuItem( HANDLE menuobjecthandle, PMO_MenuItem pmi )
+{
+ if ( !bIsGenMenuInited || pmi == NULL || pmi->cbSize != sizeof( TMO_MenuItem ))
+ return NULL;
+
+ //old mode
+ if ( !( pmi->flags & CMIF_ROOTHANDLE ))
+ return MO_AddOldNewMenuItem( menuobjecthandle, pmi );
+
+ EnterCriticalSection( &csMenuHook );
+ int objidx = GetMenuObjbyId( (int)menuobjecthandle );
+ if ( objidx == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return NULL;
+ }
+
+ TIntMenuObject* pmo = g_menus[objidx];
+
+ TMO_IntMenuItem* p = ( TMO_IntMenuItem* )mir_calloc( sizeof( TMO_IntMenuItem ));
+ p->parent = pmo;
+ p->signature = MENUITEM_SIGNATURE;
+ p->iCommand = GetNextObjectMenuItemId();
+ p->mi = *pmi;
+ p->iconId = -1;
+ p->OverrideShow = TRUE;
+ p->originalPosition = pmi->position;
+ #if defined( _UNICODE )
+ if ( pmi->flags & CMIF_UNICODE )
+ p->mi.ptszName = mir_tstrdup(( pmi->flags & CMIF_KEEPUNTRANSLATED ) ? pmi->ptszName : TranslateTS( pmi->ptszName ));
+ else {
+ if ( pmi->flags & CMIF_KEEPUNTRANSLATED )
+ p->mi.ptszName = mir_a2u(pmi->pszName);
+ else
+ p->mi.ptszName = LangPackPcharToTchar( pmi->pszName );
+ }
+ #else
+ p->mi.ptszName = mir_strdup(( pmi->flags & CMIF_KEEPUNTRANSLATED ) ? pmi->ptszName : Translate( pmi->ptszName ));
+ #endif
+
+ if ( pmi->hIcon != NULL && !bIconsDisabled ) {
+ if ( pmi->flags & CMIF_ICONFROMICOLIB ) {
+ HICON hIcon = IcoLib_GetIconByHandle( pmi->hIcolibItem, false );
+ p->iconId = ImageList_AddIcon( pmo->m_hMenuIcons, hIcon );
+ p->hIcolibItem = pmi->hIcolibItem;
+ IconLib_ReleaseIcon( hIcon, 0 );
+ }
+ else {
+ HANDLE hIcolibItem = IcoLib_IsManaged( pmi->hIcon );
+ if ( hIcolibItem ) {
+ p->iconId = ImageList_AddIcon( pmo->m_hMenuIcons, pmi->hIcon );
+ p->hIcolibItem = hIcolibItem;
+ }
+ else p->iconId = ImageList_AddIcon( pmo->m_hMenuIcons, pmi->hIcon );
+ } }
+
+ if ( p->mi.root == HGENMENU_ROOT )
+ p->mi.root = NULL;
+
+ PMO_IntMenuItem pRoot = ( p->mi.root != NULL ) ? MO_GetIntMenuItem( p->mi.root ) : NULL;
+ if ( pRoot )
+ p->owner = &pRoot->submenu;
+ else
+ p->owner = &pmo->m_items;
+
+ if ( !p->owner->first )
+ p->owner->first = p;
+ if ( p->owner->last )
+ p->owner->last->next = p;
+ p->owner->last = p;
+
+ LeaveCriticalSection( &csMenuHook );
+ return p;
+}
+
+//wparam=MenuObjectHandle
+//lparam=PMO_MenuItem
+
+int FindRoot( PMO_IntMenuItem pimi, void* param )
+{
+ if ( pimi->mi.pszName != NULL )
+ if ( pimi->submenu.first && !_tcscmp( pimi->mi.ptszName, ( TCHAR* )param ))
+ return TRUE;
+
+ return FALSE;
+}
+
+PMO_IntMenuItem MO_AddOldNewMenuItem( HANDLE menuobjecthandle, PMO_MenuItem pmi )
+{
+ if ( !bIsGenMenuInited || pmi == NULL )
+ return NULL;
+
+ int objidx = GetMenuObjbyId( (int)menuobjecthandle );
+ if ( objidx == -1 )
+ return NULL;
+
+ if ( pmi->cbSize != sizeof( TMO_MenuItem ))
+ return NULL;
+
+ if ( pmi->flags & CMIF_ROOTHANDLE )
+ return NULL;
+
+ //is item with popup or not
+ if ( pmi->root == 0 ) {
+ //yes,this without popup
+ pmi->root = NULL; //first level
+ }
+ else { // no,search for needed root and create it if need
+ TCHAR* tszRoot;
+#if defined( _UNICODE )
+ if ( pmi->flags & CMIF_UNICODE )
+ tszRoot = mir_tstrdup(TranslateTS(( TCHAR* )pmi->root ));
+ else
+ tszRoot = LangPackPcharToTchar(( char* )pmi->root );
+#else
+ tszRoot = mir_tstrdup(TranslateTS(( TCHAR* )pmi->root ));
+#endif
+
+ PMO_IntMenuItem oldroot = MO_RecursiveWalkMenu( g_menus[objidx]->m_items.first, FindRoot, tszRoot );
+ mir_free( tszRoot );
+
+ if ( oldroot == NULL ) {
+ //not found,creating root
+ TMO_MenuItem tmi = { 0 };
+ tmi = *pmi;
+ tmi.flags |= CMIF_ROOTHANDLE;
+ tmi.ownerdata = 0;
+ tmi.root = NULL;
+ //copy pszPopupName
+ tmi.ptszName = ( TCHAR* )pmi->root;
+ if (( oldroot = MO_AddNewMenuItem( menuobjecthandle, &tmi )) != NULL )
+ MO_SetOptionsMenuItem( oldroot, OPT_MENUITEMSETUNIQNAME, (INT_PTR)pmi->root );
+ }
+ pmi->root = oldroot;
+
+ //popup will be created in next commands
+ }
+ pmi->flags |= CMIF_ROOTHANDLE;
+ //add popup(root allready exists)
+ return MO_AddNewMenuItem( menuobjecthandle, pmi );
+}
+
+static int WhereToPlace( HMENU hMenu, PMO_MenuItem mi )
+{
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA;
+ for ( int i=GetMenuItemCount( hMenu )-1; i >= 0; i-- ) {
+ GetMenuItemInfo( hMenu, i, TRUE, &mii );
+ if ( mii.fType != MFT_SEPARATOR ) {
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData);
+ if ( pimi != NULL )
+ if ( pimi->mi.position <= mi->position )
+ return i+1;
+ } }
+
+ return 0;
+}
+
+static void InsertMenuItemWithSeparators(HMENU hMenu, int uItem, MENUITEMINFO *lpmii)
+{
+ int needSeparator = 0;
+ MENUITEMINFO mii;
+
+ PMO_IntMenuItem pimi = MO_GetIntMenuItem(( HGENMENU )lpmii->dwItemData );
+ if ( pimi == NULL )
+ return;
+
+ int thisItemPosition = pimi->mi.position;
+
+ ZeroMemory( &mii, sizeof( mii ));
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ //check for separator before
+ if ( uItem ) {
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_TYPE;
+ GetMenuItemInfo( hMenu, uItem-1, TRUE, &mii );
+ pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData );
+ if ( pimi != NULL ) {
+ if ( mii.fType == MFT_SEPARATOR )
+ needSeparator = 0;
+ else
+ needSeparator = ( pimi->mi.position / SEPARATORPOSITIONINTERVAL ) != thisItemPosition / SEPARATORPOSITIONINTERVAL;
+ }
+ if ( needSeparator) {
+ //but might be supposed to be after the next one instead
+ mii.fType = 0;
+ if ( uItem < GetMenuItemCount( hMenu )) {
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_TYPE;
+ GetMenuItemInfo( hMenu, uItem, TRUE, &mii );
+ }
+ if ( mii.fType != MFT_SEPARATOR) {
+ mii.fMask = MIIM_TYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem( hMenu, uItem, TRUE, &mii );
+ }
+ uItem++;
+ } }
+
+ //check for separator after
+ if ( uItem < GetMenuItemCount( hMenu )) {
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_TYPE;
+ mii.cch = 0;
+ GetMenuItemInfo( hMenu, uItem, TRUE, &mii );
+ pimi = MO_GetIntMenuItem(( HGENMENU )mii.dwItemData );
+ if ( pimi != NULL ) {
+ if ( mii.fType == MFT_SEPARATOR )
+ needSeparator=0;
+ else
+ needSeparator = pimi->mi.position / SEPARATORPOSITIONINTERVAL != thisItemPosition / SEPARATORPOSITIONINTERVAL;
+ }
+ if ( needSeparator) {
+ mii.fMask = MIIM_TYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem( hMenu, uItem, TRUE, &mii );
+ } }
+
+ if ( uItem == GetMenuItemCount( hMenu )-1 ) {
+ TCHAR str[32];
+ mii.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_TYPE;
+ mii.dwTypeData = str;
+ mii.cch = SIZEOF( str );
+ GetMenuItemInfo( hMenu, uItem, TRUE, &mii );
+ }
+
+ // create local copy *lpmii so we can change some flags
+ MENUITEMINFO mii_copy = *lpmii;
+ lpmii = &mii_copy;
+
+ if (( GetMenuItemCount( hMenu ) % 35 ) == 33 /* will be 34 after addition :) */ && pimi != NULL )
+ if ( pimi->mi.root != NULL ) {
+ if ( !( lpmii->fMask & MIIM_FTYPE ))
+ lpmii->fType = 0;
+ lpmii->fMask |= MIIM_FTYPE;
+ lpmii->fType |= MFT_MENUBARBREAK;
+ }
+
+ InsertMenuItem( hMenu, uItem, TRUE, lpmii );
+}
+
+//wparam started hMenu
+//lparam ListParam*
+//result hMenu
+INT_PTR MO_BuildMenu(WPARAM wParam,LPARAM lParam)
+{
+ if ( !bIsGenMenuInited )
+ return -1;
+
+ EnterCriticalSection( &csMenuHook );
+
+ ListParam *lp = ( ListParam* )lParam;
+ int pimoidx = GetMenuObjbyId( (int)lp->MenuObjectHandle );
+ if ( pimoidx == -1 ) {
+ LeaveCriticalSection( &csMenuHook );
+ return 0;
+ }
+
+ #if defined( _DEBUG )
+ // DumpMenuItem( g_menus[pimoidx]->m_items.first );
+ #endif
+
+ INT_PTR res = (INT_PTR)BuildRecursiveMenu(( HMENU )wParam, g_menus[pimoidx]->m_items.first, ( ListParam* )lParam );
+ LeaveCriticalSection( &csMenuHook );
+ return res;
+}
+
+#ifdef _DEBUG
+#define PUTPOSITIONSONMENU
+#endif
+
+void GetMenuItemName( PMO_IntMenuItem pMenuItem, char* pszDest, size_t cbDestSize )
+{
+ if ( pMenuItem->UniqName )
+ mir_snprintf( pszDest, cbDestSize, "{%s}", pMenuItem->UniqName );
+ else if (pMenuItem->mi.flags & CMIF_UNICODE) {
+ char* name = mir_t2a( pMenuItem->mi.ptszName );
+ mir_snprintf( pszDest, cbDestSize, "{%s}", name );
+ mir_free(name);
+ }
+ else
+ mir_snprintf( pszDest, cbDestSize, "{%s}", pMenuItem->mi.pszName );
+}
+
+HMENU BuildRecursiveMenu(HMENU hMenu, PMO_IntMenuItem pRootMenu, ListParam *param)
+{
+ if ( param == NULL || pRootMenu == NULL )
+ return NULL;
+
+ TIntMenuObject* pmo = pRootMenu->parent;
+
+ int rootlevel = ( param->rootlevel == -1 ) ? 0 : param->rootlevel;
+
+ ListParam localparam = *param;
+
+ while ( rootlevel == 0 && GetMenuItemCount( hMenu ) > 0 )
+ DeleteMenu( hMenu, 0, MF_BYPOSITION );
+
+ for ( PMO_IntMenuItem pmi = pRootMenu; pmi != NULL; pmi = pmi->next ) {
+ PMO_MenuItem mi = &pmi->mi;
+ if ( mi->cbSize != sizeof( TMO_MenuItem ))
+ continue;
+
+ if ( mi->flags & CMIF_HIDDEN )
+ continue;
+
+ if ( pmo->CheckService != NULL ) {
+ TCheckProcParam CheckParam;
+ CheckParam.lParam = param->lParam;
+ CheckParam.wParam = param->wParam;
+ CheckParam.MenuItemOwnerData = mi->ownerdata;
+ CheckParam.MenuItemHandle = pmi;
+ if ( CallService( pmo->CheckService, ( WPARAM )&CheckParam, 0 ) == FALSE )
+ continue;
+ }
+
+ /**************************************/
+ if ( rootlevel == 0 && mi->root == NULL && pmo->m_bUseUserDefinedItems ) {
+ char DBString[256];
+ DBVARIANT dbv = { 0 };
+ int pos;
+ char MenuNameItems[256];
+ mir_snprintf(MenuNameItems, SIZEOF(MenuNameItems), "%s_Items", pmo->Name);
+
+ char menuItemName[256];
+ GetMenuItemName( pmi, menuItemName, sizeof( menuItemName ));
+
+ // check if it visible
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_visible", menuItemName );
+ if ( DBGetContactSettingByte( NULL, MenuNameItems, DBString, -1 ) == -1 )
+ DBWriteContactSettingByte( NULL, MenuNameItems, DBString, 1 );
+
+ pmi->OverrideShow = TRUE;
+ if ( !DBGetContactSettingByte( NULL, MenuNameItems, DBString, 1 )) {
+ pmi->OverrideShow = FALSE;
+ continue; // find out what value to return if not getting added
+ }
+
+ // mi.pszName
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_name", menuItemName );
+ if ( !DBGetContactSettingTString( NULL, MenuNameItems, DBString, &dbv )) {
+ if ( _tcslen( dbv.ptszVal ) > 0 ) {
+ if ( pmi->CustomName ) mir_free( pmi->CustomName );
+ pmi->CustomName = mir_tstrdup( dbv.ptszVal );
+ }
+ DBFreeVariant( &dbv );
+ }
+
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_pos", menuItemName );
+ if (( pos = DBGetContactSettingDword( NULL, MenuNameItems, DBString, -1 )) == -1 ) {
+ DBWriteContactSettingDword( NULL, MenuNameItems, DBString, mi->position );
+ if ( pmi->submenu.first )
+ mi->position = 0;
+ }
+ else mi->position = pos;
+ }
+
+ /**************************************/
+
+ if ( rootlevel != (int)pmi->mi.root )
+ continue;
+
+ MENUITEMINFO mii = { 0 };
+ mii.dwItemData = ( LPARAM )pmi;
+
+ int i = WhereToPlace( hMenu, mi );
+
+ if ( !IsWinVer98Plus()) {
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ }
+ else {
+ mii.cbSize = sizeof( mii );
+ mii.fMask = MIIM_DATA | MIIM_ID | MIIM_STRING;
+ if ( pmi->iconId != -1 ) {
+ mii.fMask |= MIIM_BITMAP;
+ if (IsWinVerVistaPlus() && isThemeActive()) {
+ if (pmi->hBmp == NULL)
+ pmi->hBmp = ConvertIconToBitmap(NULL, pmi->parent->m_hMenuIcons, pmi->iconId);
+ mii.hbmpItem = pmi->hBmp;
+ }
+ else
+ mii.hbmpItem = HBMMENU_CALLBACK;
+ }
+ }
+
+ mii.fMask |= MIIM_STATE;
+ mii.fState = (( pmi->mi.flags & CMIF_GRAYED ) ? MFS_GRAYED : MFS_ENABLED );
+ mii.fState |= (( pmi->mi.flags & CMIF_CHECKED) ? MFS_CHECKED : MFS_UNCHECKED );
+ if ( pmi->mi.flags & CMIF_DEFAULT ) mii.fState |= MFS_DEFAULT;
+
+ mii.dwTypeData = ( pmi->CustomName ) ? pmi->CustomName : mi->ptszName;
+
+ // it's a submenu
+ if ( pmi->submenu.first ) {
+ mii.fMask |= MIIM_SUBMENU;
+ mii.hSubMenu = CreatePopupMenu();
+
+ #ifdef PUTPOSITIONSONMENU
+ if ( GetKeyState(VK_CONTROL) & 0x8000) {
+ TCHAR str[256];
+ mir_sntprintf( str, SIZEOF(str), _T( "%s (%d,id %x)" ), mi->pszName, mi->position, mii.dwItemData );
+ mii.dwTypeData = str;
+ }
+ #endif
+
+ InsertMenuItemWithSeparators( hMenu, i, &mii);
+ localparam.rootlevel = LPARAM( pmi );
+ BuildRecursiveMenu( mii.hSubMenu, pmi->submenu.first, &localparam );
+ }
+ else {
+ mii.wID = pmi->iCommand;
+
+ #ifdef PUTPOSITIONSONMENU
+ if ( GetKeyState(VK_CONTROL) & 0x8000) {
+ TCHAR str[256];
+ mir_sntprintf( str, SIZEOF(str), _T("%s (%d,id %x)"), mi->pszName, mi->position, mii.dwItemData );
+ mii.dwTypeData = str;
+ }
+ #endif
+
+ if ( pmo->onAddService != NULL )
+ if ( CallService( pmo->onAddService, ( WPARAM )&mii, ( LPARAM )pmi ) == FALSE )
+ continue;
+
+ InsertMenuItemWithSeparators( hMenu, i, &mii );
+ } }
+
+ return hMenu;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// iconlib in menu
+
+static int MO_ReloadIcon( PMO_IntMenuItem pmi, void* )
+{
+ if ( pmi->hIcolibItem ) {
+ HICON newIcon = IcoLib_GetIconByHandle( pmi->hIcolibItem, false );
+ if ( newIcon )
+ ImageList_ReplaceIcon( pmi->parent->m_hMenuIcons, pmi->iconId, newIcon );
+
+ IconLib_ReleaseIcon(newIcon,0);
+ }
+
+ return FALSE;
+}
+
+int OnIconLibChanges(WPARAM, LPARAM)
+{
+ EnterCriticalSection( &csMenuHook );
+ for ( int mo=0; mo < g_menus.getCount(); mo++ )
+ if ( (int)hStatusMenuObject != g_menus[mo]->id ) //skip status menu
+ MO_RecursiveWalkMenu( g_menus[mo]->m_items.first, MO_ReloadIcon, 0 );
+
+ LeaveCriticalSection( &csMenuHook );
+
+ cli.pfnReloadProtoMenus();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+
+static int MO_RegisterIcon( PMO_IntMenuItem pmi, void* )
+{
+ char *uname, *descr;
+ uname = pmi->UniqName;
+ if ( uname == NULL )
+ #ifdef UNICODE
+ uname = mir_u2a(pmi->CustomName);
+ descr = mir_u2a(pmi->mi.ptszName);
+ #else
+ uname = pmi->CustomName;
+ descr = pmi->mi.pszName;
+ #endif
+
+ if ( !uname && !descr )
+ return FALSE;
+
+ if ( !pmi->hIcolibItem ) {
+ HICON hIcon = ImageList_GetIcon( pmi->parent->m_hMenuIcons, pmi->iconId, 0 );
+ char* buf = NEWSTR_ALLOCA( descr );
+
+ char sectionName[256], iconame[256];
+ mir_snprintf( sectionName, sizeof(sectionName), "Menu Icons/%s", pmi->parent->Name );
+
+ // remove '&'
+ char* start = buf;
+ while ( start ) {
+ if (( start = strchr( start, '&' )) == NULL )
+ break;
+
+ memmove(start,start+1,strlen(start+1)+1);
+ if (*start!='\0') start++;
+ else break;
+ }
+
+ mir_snprintf(iconame, sizeof(iconame), "genmenu_%s_%s", pmi->parent->Name, uname && *uname ? uname : descr);
+
+ SKINICONDESC sid={0};
+ sid.cbSize = sizeof(sid);
+ sid.cx = 16;
+ sid.cy = 16;
+ sid.pszSection = sectionName;
+ sid.pszName = iconame;
+ sid.pszDefaultFile = NULL;
+ sid.pszDescription = buf;
+ sid.hDefaultIcon = hIcon;
+ pmi->hIcolibItem = ( HANDLE )CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ Safe_DestroyIcon( hIcon );
+ if ( hIcon = ( HICON )CallService( MS_SKIN2_GETICON, 0, (LPARAM)iconame )) {
+ ImageList_ReplaceIcon( pmi->parent->m_hMenuIcons, pmi->iconId, hIcon );
+ IconLib_ReleaseIcon( hIcon, 0 );
+ } }
+
+ #ifdef UNICODE
+ if ( !pmi->UniqName )
+ mir_free( uname );
+ mir_free( descr );
+ #endif
+
+ return FALSE;
+}
+
+int RegisterAllIconsInIconLib()
+{
+ //register all icons
+ for ( int mo=0; mo < g_menus.getCount(); mo++ ) {
+ if ( (int)hStatusMenuObject == g_menus[mo]->id ) //skip status menu
+ continue;
+
+ MO_RecursiveWalkMenu( g_menus[mo]->m_items.first, MO_RegisterIcon, 0 );
+ }
+
+ return 0;
+}
+
+int TryProcessDoubleClick( HANDLE hContact )
+{
+ int iMenuID = GetMenuObjbyId( (int)hContactMenuObject );
+ if ( iMenuID != -1 ) {
+ NotifyEventHooks(hPreBuildContactMenuEvent,(WPARAM)hContact,0);
+
+ PMO_IntMenuItem pimi = ( PMO_IntMenuItem )MO_GetDefaultMenuItem(( WPARAM )g_menus[ iMenuID ]->m_items.first, 0 );
+ if ( pimi != NULL ) {
+ MO_ProcessCommand( pimi, ( LPARAM )hContact );
+ return 0;
+ } }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Static services
+
+int posttimerid;
+
+static VOID CALLBACK PostRegisterIcons( HWND, UINT, UINT_PTR, DWORD )
+{
+ KillTimer( 0, posttimerid );
+ RegisterAllIconsInIconLib();
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ posttimerid = SetTimer(( HWND )NULL, 0, 5, ( TIMERPROC )PostRegisterIcons );
+ HookEvent(ME_SKIN2_ICONSCHANGED,OnIconLibChanges);
+ return 0;
+}
+
+static INT_PTR SRVMO_SetOptionsMenuObject( WPARAM, LPARAM lParam)
+{
+ lpOptParam lpop = ( lpOptParam )lParam;
+ if ( lpop == NULL )
+ return 0;
+
+ return MO_SetOptionsMenuObject( lpop->Handle, lpop->Setting, lpop->Value );
+}
+
+static INT_PTR SRVMO_SetOptionsMenuItem( WPARAM, LPARAM lParam)
+{
+ lpOptParam lpop = ( lpOptParam )lParam;
+ if ( lpop == NULL )
+ return 0;
+
+ return MO_SetOptionsMenuItem(( PMO_IntMenuItem )lpop->Handle, lpop->Setting, lpop->Value );
+}
+
+int InitGenMenu()
+{
+ InitializeCriticalSection( &csMenuHook );
+ CreateServiceFunction( MO_BUILDMENU, MO_BuildMenu );
+
+ CreateServiceFunction( MO_PROCESSCOMMAND, ( MIRANDASERVICE )MO_ProcessCommand );
+ CreateServiceFunction( MO_CREATENEWMENUOBJECT, MO_CreateNewMenuObject );
+ CreateServiceFunction( MO_REMOVEMENUITEM, MO_RemoveMenuItem );
+ CreateServiceFunction( MO_ADDNEWMENUITEM, ( MIRANDASERVICE )MO_AddNewMenuItem );
+ CreateServiceFunction( MO_MENUITEMGETOWNERDATA, MO_MenuItemGetOwnerData );
+ CreateServiceFunction( MO_MODIFYMENUITEM, ( MIRANDASERVICE )MO_ModifyMenuItem );
+ CreateServiceFunction( MO_GETMENUITEM, MO_GetMenuItem );
+ CreateServiceFunction( MO_GETDEFAULTMENUITEM, MO_GetDefaultMenuItem );
+ CreateServiceFunction( MO_PROCESSCOMMANDBYMENUIDENT, MO_ProcessCommandByMenuIdent );
+ CreateServiceFunction( MO_PROCESSHOTKEYS, ( MIRANDASERVICE )MO_ProcessHotKeys );
+ CreateServiceFunction( MO_REMOVEMENUOBJECT, MO_RemoveMenuObject );
+ CreateServiceFunction( MO_GETPROTOROOTMENU, MO_GetProtoRootMenu );
+
+ CreateServiceFunction( MO_SETOPTIONSMENUOBJECT, SRVMO_SetOptionsMenuObject );
+ CreateServiceFunction( MO_SETOPTIONSMENUITEM, SRVMO_SetOptionsMenuItem );
+
+ bIconsDisabled = DBGetContactSettingByte(NULL, "CList", "DisableMenuIcons", 0) != 0;
+
+ EnterCriticalSection( &csMenuHook );
+ bIsGenMenuInited = true;
+ LeaveCriticalSection( &csMenuHook );
+
+ HookEvent( ME_SYSTEM_MODULESLOADED, OnModulesLoaded );
+ HookEvent( ME_OPT_INITIALISE, GenMenuOptInit );
+ return 0;
+}
+
+int UnitGenMenu()
+{
+ if ( bIsGenMenuInited ) {
+ EnterCriticalSection( &csMenuHook );
+ MO_RemoveAllObjects();
+ bIsGenMenuInited=false;
+
+ LeaveCriticalSection( &csMenuHook );
+ DeleteCriticalSection(&csMenuHook);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TIntMenuObject::TIntMenuObject()
+{
+}
+
+TIntMenuObject::~TIntMenuObject()
+{
+ MO_RecursiveWalkMenu( m_items.first, FreeMenuItem, NULL );
+
+ FreeAndNil(( void** )&FreeService );
+ FreeAndNil(( void** )&onAddService );
+ FreeAndNil(( void** )&CheckService );
+ FreeAndNil(( void** )&ExecService );
+ FreeAndNil(( void** )&Name );
+
+ ImageList_Destroy(m_hMenuIcons);
+}
+
+void TIntMenuObject::freeItem( TMO_IntMenuItem* p )
+{
+ if ( FreeService )
+ CallService( FreeService, ( WPARAM )p, ( LPARAM )p->mi.ownerdata );
+
+ FreeAndNil(( void** )&p->mi.pszName );
+ FreeAndNil(( void** )&p->UniqName );
+ FreeAndNil(( void** )&p->CustomName );
+ if ( p->hBmp ) DeleteObject( p->hBmp );
+ mir_free( p );
+}
diff --git a/src/modules/clist/genmenu.h b/src/modules/clist/genmenu.h
new file mode 100644
index 0000000000..63c665b940
--- /dev/null
+++ b/src/modules/clist/genmenu.h
@@ -0,0 +1,146 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef GENMENU_H
+#define GENMENU_H
+//general menu object module
+#include "m_genmenu.h"
+
+/* genmenu structs */
+
+#define MENUITEM_SIGNATURE 0xDEADBEEF
+
+typedef struct
+{
+ struct _tagIntMenuItem *first, // first element of submenu, or NULL
+ *last; // last element of submenu, or NULL
+}
+ TMO_LinkedList;
+
+typedef struct _tagIntMenuItem
+{
+ DWORD signature;
+ int iCommand;
+ int iconId; // icon index in the section's image list
+ TMO_MenuItem mi; // user-defined data
+ BOOL OverrideShow;
+ char* UniqName; // unique name
+ TCHAR* CustomName;
+ HANDLE hIcolibItem; // handle of iconlib item
+ HBITMAP hBmp;
+ int originalPosition;
+
+ struct _tagIntMenuItem *next; // next item in list
+ struct TIntMenuObject *parent;
+ TMO_LinkedList *owner;
+ TMO_LinkedList submenu;
+}
+ TMO_IntMenuItem,*PMO_IntMenuItem;
+
+struct TIntMenuObject
+{
+ TIntMenuObject();
+ ~TIntMenuObject();
+
+ __inline void* operator new( size_t size )
+ { return mir_calloc( size );
+ }
+ __inline void operator delete( void* p )
+ { mir_free( p );
+ }
+
+ char* Name;
+ int id;
+
+ //ExecService
+ //LPARAM lParam;//owner data
+ //WPARAM wParam;//allways lparam from winproc
+ char *ExecService;
+
+ //CheckService called when building menu
+ //return false to skip item.
+ //LPARAM lParam;//0
+ //WPARAM wParam;//CheckParam
+ char *CheckService;//analog to check_proc
+
+ //LPARAM lParam;//ownerdata
+ //WPARAM wParam;//menuitemhandle
+ char *FreeService;//callback service used to free ownerdata for menuitems
+
+ //LPARAM lParam;//MENUITEMINFO filled with all needed data
+ //WPARAM wParam;//menuitemhandle
+ char *onAddService;//called just before add MENUITEMINFO to hMenu
+
+ TMO_LinkedList m_items;
+ HIMAGELIST m_hMenuIcons;
+ BOOL m_bUseUserDefinedItems;
+
+ void freeItem( TMO_IntMenuItem* );
+};
+
+extern LIST<TIntMenuObject> g_menus;
+
+#define SEPARATORPOSITIONINTERVAL 100000
+
+//internal usage
+HMENU BuildRecursiveMenu(HMENU hMenu, PMO_IntMenuItem, ListParam *param);
+void GetMenuItemName( PMO_IntMenuItem pMenuItem, char* pszDest, size_t cbDestSize );
+
+PMO_IntMenuItem MO_GetIntMenuItem( HGENMENU );
+
+PMO_IntMenuItem MO_AddNewMenuItem( HANDLE menuobjecthandle, PMO_MenuItem pmi );
+PMO_IntMenuItem MO_AddOldNewMenuItem( HANDLE menuobjecthandle, PMO_MenuItem pmi );
+
+int MO_DrawMenuItem( LPDRAWITEMSTRUCT dis );
+int MO_MeasureMenuItem( LPMEASUREITEMSTRUCT mis );
+int MO_ModifyMenuItem( PMO_IntMenuItem menuHandle, PMO_MenuItem pmiparam );
+int MO_ProcessCommand( PMO_IntMenuItem pimi, LPARAM lParam );
+INT_PTR MO_ProcessHotKeys( HANDLE menuHandle, INT_PTR vKey );
+int MO_SetOptionsMenuItem( PMO_IntMenuItem menuobjecthandle, int setting, INT_PTR value );
+int MO_SetOptionsMenuObject( HANDLE menuobjecthandle, int setting, INT_PTR value );
+
+INT_PTR MO_ProcessCommandByMenuIdent(WPARAM wParam,LPARAM lParam);
+int MO_ProcessCommandBySubMenuIdent(int menuID, int command, LPARAM lParam);
+
+// function returns TRUE if the walk should be immediately stopped
+typedef int ( *pfnWalkFunc )( PMO_IntMenuItem, void* );
+
+// returns the item, on which pfnWalkFunc returned TRUE
+PMO_IntMenuItem MO_RecursiveWalkMenu( PMO_IntMenuItem, pfnWalkFunc, void* );
+
+//general stuff
+int InitGenMenu();
+int UnitGenMenu();
+
+int FindRoot( PMO_IntMenuItem pimi, void* param );
+
+TMO_IntMenuItem * GetMenuItemByGlobalID(int globalMenuID);
+BOOL FindMenuHanleByGlobalID(HMENU hMenu, int globalID, struct _MenuItemHandles * dat); //GenMenu.c
+
+int GenMenuOptInit(WPARAM wParam, LPARAM lParam);
+int GetMenuObjbyId(const int id);
+int GetMenuItembyId(const int objpos,const int id);
+INT_PTR MO_GetMenuItem(WPARAM wParam,LPARAM lParam);
+void FreeAndNil(void **p);
+static int RemoveFromList(int pos,void **lpList,int *ListElemCount,int ElemSize);
+static int RemoveFromList(int pos,void **lpList,int *ListElemCount,int ElemSize);
+#endif
diff --git a/src/modules/clist/genmenuopt.cpp b/src/modules/clist/genmenuopt.cpp
new file mode 100644
index 0000000000..7fefbf8dcb
--- /dev/null
+++ b/src/modules/clist/genmenuopt.cpp
@@ -0,0 +1,870 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "genmenu.h"
+
+#define STR_SEPARATOR _T("-----------------------------------")
+
+extern bool bIconsDisabled;
+extern int DefaultImageListColorDepth;
+long handleCustomDraw(HWND hWndTreeView, LPNMTVCUSTOMDRAW pNMTVCD);
+void RebuildProtoMenus( int );
+
+struct OrderData
+{
+ int dragging;
+ HTREEITEM hDragItem;
+ int iInitMenuValue;
+};
+
+typedef struct tagMenuItemOptData
+{
+ TCHAR* name;
+ TCHAR* defname;
+ char* uniqname;
+
+ int pos;
+ boolean show;
+ DWORD isSelected;
+ int id;
+
+ PMO_IntMenuItem pimi;
+}
+ MenuItemOptData,*lpMenuItemOptData;
+
+static BOOL GetCurrentMenuObjectID(HWND hwndDlg, int* result)
+{
+ TVITEM tvi;
+ HWND hTree = GetDlgItem(hwndDlg, IDC_MENUOBJECTS);
+ HTREEITEM hti = TreeView_GetSelection(hTree);
+ if ( hti == NULL )
+ return FALSE;
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hti;
+ TreeView_GetItem(hTree, &tvi);
+ *result = (int)tvi.lParam;
+ return TRUE;
+}
+
+static int SaveTree(HWND hwndDlg)
+{
+ TVITEM tvi;
+ int count;
+ TCHAR idstr[100];
+ char menuItemName[256], DBString[256], MenuNameItems[256];
+ int menupos;
+ int MenuObjectId, runtimepos;
+ TIntMenuObject* pimo;
+ MenuItemOptData* iod;
+ HWND hTree = GetDlgItem( hwndDlg, IDC_MENUITEMS );
+
+ if ( !GetCurrentMenuObjectID( hwndDlg, &MenuObjectId ))
+ return 0;
+
+ tvi.hItem = TreeView_GetRoot( hTree );
+ tvi.cchTextMax = 99;
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE;
+ tvi.pszText = idstr;
+ count = 0;
+
+ menupos = GetMenuObjbyId( MenuObjectId );
+ if ( menupos == -1 )
+ return -1;
+
+ pimo = g_menus[menupos];
+
+ mir_snprintf( MenuNameItems, sizeof(MenuNameItems), "%s_Items", pimo->Name);
+ runtimepos = 100;
+
+ while ( tvi.hItem != NULL ) {
+ TreeView_GetItem( hTree, &tvi );
+ iod = ( MenuItemOptData* )tvi.lParam;
+ if ( iod->pimi ) {
+ GetMenuItemName( iod->pimi, menuItemName, sizeof( menuItemName ));
+
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_visible", menuItemName );
+ DBWriteContactSettingByte( NULL, MenuNameItems, DBString, iod->show );
+
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_pos", menuItemName );
+ DBWriteContactSettingDword( NULL, MenuNameItems, DBString, runtimepos );
+
+ mir_snprintf( DBString, SIZEOF(DBString), "%s_name", menuItemName );
+ if ( lstrcmp( iod->name, iod->defname ) != 0 )
+ DBWriteContactSettingTString( NULL, MenuNameItems, DBString, iod->name );
+ else
+ DBDeleteContactSetting( NULL, MenuNameItems, DBString );
+
+ runtimepos += 100;
+ }
+
+ if ( iod->name && !_tcscmp( iod->name, STR_SEPARATOR) && iod->show )
+ runtimepos += SEPARATORPOSITIONINTERVAL;
+
+ tvi.hItem = TreeView_GetNextSibling( hTree, tvi.hItem );
+ count++;
+ }
+ return 1;
+}
+
+static int BuildMenuObjectsTree(HWND hwndDlg)
+{
+ TVINSERTSTRUCT tvis;
+ HWND hTree = GetDlgItem(hwndDlg,IDC_MENUOBJECTS);
+ int i;
+
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ TreeView_DeleteAllItems( hTree );
+ if ( g_menus.getCount() == 0 )
+ return FALSE;
+
+ for ( i=0; i < g_menus.getCount(); i++ ) {
+ if ( g_menus[i]->id == (int)hStatusMenuObject || !g_menus[i]->m_bUseUserDefinedItems )
+ continue;
+
+ tvis.item.lParam = ( LPARAM )g_menus[i]->id;
+ tvis.item.pszText = LangPackPcharToTchar( g_menus[i]->Name );
+ tvis.item.iImage = tvis.item.iSelectedImage = TRUE;
+ TreeView_InsertItem( hTree, &tvis );
+ mir_free( tvis.item.pszText );
+ }
+ return 1;
+}
+
+static int sortfunc(const void *a,const void *b)
+{
+ lpMenuItemOptData *sd1,*sd2;
+ sd1=(lpMenuItemOptData *)a;
+ sd2=(lpMenuItemOptData *)b;
+ if ((*sd1)->pos > (*sd2)->pos)
+ return 1;
+
+ if ((*sd1)->pos < (*sd2)->pos)
+ return -1;
+
+ return 0;
+}
+
+static int InsertSeparator(HWND hwndDlg)
+{
+ MenuItemOptData *PD;
+ TVINSERTSTRUCT tvis = {0};
+ TVITEM tvi = {0};
+ HTREEITEM hti = {0};
+ HWND hMenuTree = GetDlgItem( hwndDlg, IDC_MENUITEMS );
+
+ hti = TreeView_GetSelection( hMenuTree );
+ if ( hti == NULL )
+ return 1;
+
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hti;
+ if ( TreeView_GetItem( hMenuTree, &tvi) == FALSE )
+ return 1;
+
+ PD = ( MenuItemOptData* )mir_alloc( sizeof( MenuItemOptData ));
+ ZeroMemory( PD, sizeof( MenuItemOptData ));
+ PD->id = -1;
+ PD->name = mir_tstrdup( STR_SEPARATOR );
+ PD->show = TRUE;
+ PD->pos = ((MenuItemOptData *)tvi.lParam)->pos-1;
+
+ tvis.item.lParam = (LPARAM)(PD);
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = PD->show;
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ TreeView_InsertItem( hMenuTree, &tvis );
+ return 1;
+}
+
+static void FreeTreeData( HWND hwndDlg )
+{
+ HTREEITEM hItem = TreeView_GetRoot(GetDlgItem(hwndDlg,IDC_MENUITEMS));
+ while ( hItem != NULL ) {
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ TreeView_GetItem( GetDlgItem( hwndDlg, IDC_MENUITEMS ), &tvi );
+ { MenuItemOptData* O = (MenuItemOptData *)tvi.lParam;
+ if ( O->name ) mir_free( O->name );
+ if ( O->defname ) mir_free( O->defname );
+ if ( O->uniqname ) mir_free( O->uniqname );
+ mir_free( O );
+ }
+
+ tvi.lParam = 0;
+ TreeView_SetItem(GetDlgItem(hwndDlg,IDC_MENUITEMS), &tvi);
+
+ hItem = TreeView_GetNextSibling(GetDlgItem(hwndDlg,IDC_MENUITEMS), hItem);
+} }
+
+static int BuildTree(HWND hwndDlg,int MenuObjectId, BOOL bReread)
+{
+ char menuItemName[256],MenuNameItems[256];
+ char buf[256];
+
+ FreeTreeData( hwndDlg );
+ TreeView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_MENUITEMS));
+
+ int menupos = GetMenuObjbyId( MenuObjectId );
+ if ( menupos == -1 )
+ return FALSE;
+
+ TIntMenuObject* pimo = g_menus[menupos];
+ if ( pimo->m_items.first == NULL )
+ return FALSE;
+
+ mir_snprintf( MenuNameItems, sizeof(MenuNameItems), "%s_Items", pimo->Name );
+
+ int count = 0;
+ {
+ for ( PMO_IntMenuItem p = pimo->m_items.first; p != NULL; p = p->next )
+ if ( p->mi.root == ( HGENMENU )-1 || p->mi.root == NULL )
+ count++;
+ }
+
+ lpMenuItemOptData *PDar = ( lpMenuItemOptData* )mir_alloc( sizeof( lpMenuItemOptData )*count );
+
+ count = 0;
+ {
+ for ( PMO_IntMenuItem p = pimo->m_items.first; p != NULL; p = p->next ) {
+ if ( p->mi.root != ( HGENMENU )-1 && p->mi.root != NULL )
+ continue;
+
+ MenuItemOptData *PD = ( MenuItemOptData* )mir_calloc( sizeof( MenuItemOptData ));
+ GetMenuItemName( p, menuItemName, sizeof( menuItemName ));
+ {
+ DBVARIANT dbv;
+ mir_snprintf(buf, SIZEOF(buf), "%s_name", menuItemName);
+
+ if ( !DBGetContactSettingTString( NULL, MenuNameItems, buf, &dbv )) {
+ PD->name = mir_tstrdup( dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+ else PD->name = mir_tstrdup( p->mi.ptszName );
+ }
+
+ PD->pimi = p;
+ PD->defname = mir_tstrdup( p->mi.ptszName );
+
+ mir_snprintf( buf, SIZEOF(buf), "%s_visible", menuItemName );
+ PD->show = DBGetContactSettingByte( NULL, MenuNameItems, buf, 1 );
+
+ if ( bReread ) {
+ mir_snprintf( buf, SIZEOF(buf), "%s_pos", menuItemName );
+ PD->pos = DBGetContactSettingDword( NULL, MenuNameItems, buf, 1 );
+ }
+ else PD->pos = ( PD->pimi ) ? PD->pimi->originalPosition : 0;
+
+ PD->id = p->iCommand;
+
+ if ( p->UniqName )
+ PD->uniqname = mir_strdup( p->UniqName );
+
+ PDar[ count ] = PD;
+ count++;
+ } }
+
+ qsort( PDar, count, sizeof( lpMenuItemOptData ), sortfunc );
+
+ SendDlgItemMessage(hwndDlg, IDC_MENUITEMS, WM_SETREDRAW, FALSE, 0);
+ int lastpos = 0;
+ bool first = TRUE;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ for ( int i=0; i < count; i++ ) {
+ if ( PDar[i]->pos - lastpos >= SEPARATORPOSITIONINTERVAL ) {
+ MenuItemOptData *PD = ( MenuItemOptData* )mir_calloc( sizeof( MenuItemOptData ));
+ PD->id = -1;
+ PD->name = mir_tstrdup( STR_SEPARATOR );
+ PD->pos = PDar[i]->pos - 1;
+ PD->show = TRUE;
+
+ tvis.item.lParam = ( LPARAM )PD;
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = PD->show;
+ SendDlgItemMessage(hwndDlg, IDC_MENUITEMS, TVM_INSERTITEM, 0, (LPARAM)&tvis);
+ }
+
+ tvis.item.lParam = ( LPARAM )PDar[i];
+ tvis.item.pszText = PDar[i]->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = PDar[i]->show;
+
+ HTREEITEM hti = (HTREEITEM)SendDlgItemMessage(hwndDlg, IDC_MENUITEMS, TVM_INSERTITEM, 0, (LPARAM)&tvis);
+ if ( first ) {
+ TreeView_SelectItem(GetDlgItem(hwndDlg,IDC_MENUITEMS),hti);
+ first=FALSE;
+ }
+
+ lastpos = PDar[i]->pos;
+ }
+
+ SendDlgItemMessage( hwndDlg, IDC_MENUITEMS, WM_SETREDRAW, TRUE, 0 );
+ mir_free( PDar );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_NOTSUPPORTWARNING ),( pimo->m_bUseUserDefinedItems ) ? SW_HIDE : SW_SHOW );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_MENUITEMS ), pimo->m_bUseUserDefinedItems );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_INSERTSEPARATOR ), pimo->m_bUseUserDefinedItems );
+ return 1;
+}
+
+static void RebuildCurrent( HWND hwndDlg )
+{
+ int MenuObjectID;
+ if ( GetCurrentMenuObjectID( hwndDlg, &MenuObjectID ))
+ BuildTree( hwndDlg, MenuObjectID, TRUE );
+}
+
+static void ResetMenuItems( HWND hwndDlg )
+{
+ int MenuObjectID;
+ if ( GetCurrentMenuObjectID( hwndDlg, &MenuObjectID ))
+ BuildTree( hwndDlg, MenuObjectID, FALSE );
+}
+
+static HTREEITEM MoveItemAbove(HWND hTreeWnd, HTREEITEM hItem, HTREEITEM hInsertAfter)
+{
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ if ( !SendMessage(hTreeWnd, TVM_GETITEM, 0, ( LPARAM )&tvi ))
+ return NULL;
+ if ( hItem && hInsertAfter ) {
+ TVINSERTSTRUCT tvis;
+ TCHAR name[128];
+ if ( hItem == hInsertAfter )
+ return hItem;
+
+ tvis.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.stateMask = 0xFFFFFFFF;
+ tvis.item.pszText = name;
+ tvis.item.cchTextMax = sizeof( name );
+ tvis.item.hItem = hItem;
+ tvis.item.iImage = tvis.item.iSelectedImage = (( MenuItemOptData* )tvi.lParam)->show;
+ if(!SendMessage(hTreeWnd, TVM_GETITEM, 0, (LPARAM)&tvis.item))
+ return NULL;
+ if (!TreeView_DeleteItem(hTreeWnd,hItem))
+ return NULL;
+ tvis.hParent=NULL;
+ tvis.hInsertAfter=hInsertAfter;
+ return TreeView_InsertItem(hTreeWnd, &tvis);
+ }
+ return NULL;
+}
+
+WNDPROC MyOldWindowProc=NULL;
+
+LRESULT CALLBACK LBTNDOWNProc(HWND hwnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg==WM_LBUTTONDOWN && !(GetKeyState(VK_CONTROL)&0x8000)) {
+
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ // ClientToScreen(hwndDlg,&hti.pt);
+ // ScreenToClient(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti.pt);
+ TreeView_HitTest(hwnd,&hti);
+ if (hti.flags&TVHT_ONITEMLABEL) {
+ /// LabelClicked Set/unset selection
+ TVITEM tvi;
+ HWND tvw=hwnd;
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hti.hItem;
+ TreeView_GetItem( tvw, &tvi );
+
+ if (!((MenuItemOptData *)tvi.lParam)->isSelected) { /* is not Selected*/
+ // reset all selection except current
+ HTREEITEM hit;
+ hit=TreeView_GetRoot(tvw);
+ if (hit)
+ do {
+ TVITEM tvi={0};
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hit;
+ TreeView_GetItem(tvw, &tvi);
+
+ if (hti.hItem!=hit)
+ ((MenuItemOptData *)tvi.lParam)->isSelected=0;
+ else
+ ((MenuItemOptData *)tvi.lParam)->isSelected=1;
+ TreeView_SetItem(tvw, &tvi);
+ }
+ while (hit=TreeView_GetNextSibling(tvw,hit));
+ } } }
+
+ return CallWindowProc(MyOldWindowProc,hwnd,uMsg,wParam,lParam);
+}
+
+static INT_PTR CALLBACK GenMenuOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct OrderData *dat = (struct OrderData*)GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MENUITEMS),GWLP_USERDATA);
+ LPNMHDR hdr;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat=(struct OrderData*)mir_alloc(sizeof(struct OrderData));
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MENUITEMS),GWLP_USERDATA,(LONG_PTR)dat);
+ dat->dragging = 0;
+ dat->iInitMenuValue = DBGetContactSettingByte( NULL, "CList", "MoveProtoMenus", FALSE );
+ MyOldWindowProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MENUITEMS),GWLP_WNDPROC);
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MENUITEMS),GWLP_WNDPROC,(LONG_PTR)&LBTNDOWNProc);
+ {
+ HIMAGELIST himlCheckBoxes;
+ himlCheckBoxes=ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 2, 2);
+
+ ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK);
+ ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK);
+
+ TreeView_SetImageList(GetDlgItem(hwndDlg,IDC_MENUOBJECTS),himlCheckBoxes,TVSIL_NORMAL);
+ TreeView_SetImageList(GetDlgItem(hwndDlg,IDC_MENUITEMS),himlCheckBoxes,TVSIL_NORMAL);
+ }
+ CheckDlgButton(hwndDlg, dat->iInitMenuValue ? IDC_RADIO2 : IDC_RADIO1, TRUE );
+ CheckDlgButton(hwndDlg, IDC_DISABLEMENUICONS, bIconsDisabled );
+ BuildMenuObjectsTree(hwndDlg);
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( HIWORD(wParam) == BN_CLICKED || HIWORD( wParam ) == BN_DBLCLK ) {
+ switch ( LOWORD( wParam )) {
+ case IDC_INSERTSEPARATOR:
+ InsertSeparator(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_RESETMENU:
+ ResetMenuItems( hwndDlg );
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ break;
+
+ case IDC_DISABLEMENUICONS:
+ case IDC_RADIO1:
+ case IDC_RADIO2:
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ break;
+
+ case IDC_GENMENU_DEFAULT:
+ {
+ TVITEM tvi;
+ HTREEITEM hti;
+ MenuItemOptData *iod;
+
+ hti=TreeView_GetSelection(GetDlgItem(hwndDlg,IDC_MENUITEMS));
+ if (hti==NULL)
+ break;
+
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
+ tvi.hItem=hti;
+ TreeView_GetItem(GetDlgItem(hwndDlg,IDC_MENUITEMS),&tvi);
+ iod = ( MenuItemOptData * )tvi.lParam;
+
+ if ( iod->name && _tcsstr( iod->name, STR_SEPARATOR ))
+ break;
+
+ if (iod->name)
+ mir_free(iod->name);
+ iod->name = mir_tstrdup( iod->defname );
+
+ SaveTree(hwndDlg);
+ RebuildCurrent(hwndDlg);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+
+ case IDC_GENMENU_SET:
+ {
+ TVITEM tvi;
+ TCHAR buf[256];
+ MenuItemOptData *iod;
+
+ HTREEITEM hti = TreeView_GetSelection( GetDlgItem( hwndDlg,IDC_MENUITEMS ));
+ if ( hti == NULL )
+ break;
+
+ tvi.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
+ tvi.hItem = hti;
+ SendDlgItemMessage(hwndDlg, IDC_MENUITEMS, TVM_GETITEM, 0, (LPARAM)&tvi);
+ iod = ( MenuItemOptData * )tvi.lParam;
+
+ if ( iod->name && _tcsstr(iod->name, STR_SEPARATOR ))
+ break;
+
+ ZeroMemory(buf,sizeof( buf ));
+ GetDlgItemText( hwndDlg, IDC_GENMENU_CUSTOMNAME, buf, SIZEOF( buf ));
+ if (iod->name)
+ mir_free(iod->name);
+
+ iod->name = mir_tstrdup(buf);
+
+ SaveTree(hwndDlg);
+ RebuildCurrent(hwndDlg);
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ }
+ break;
+ } }
+ break;
+
+ case WM_NOTIFY:
+ hdr = (LPNMHDR)lParam;
+ switch( hdr->idFrom ) {
+ case 0:
+ if (hdr->code == PSN_APPLY ) {
+ bIconsDisabled = IsDlgButtonChecked(hwndDlg, IDC_DISABLEMENUICONS) != 0;
+ DBWriteContactSettingByte(NULL, "CList", "DisableMenuIcons", bIconsDisabled);
+ SaveTree(hwndDlg);
+ int iNewMenuValue = IsDlgButtonChecked(hwndDlg, IDC_RADIO1) ? 0 : 1;
+ if ( iNewMenuValue != dat->iInitMenuValue ) {
+ RebuildProtoMenus( iNewMenuValue );
+ dat->iInitMenuValue = iNewMenuValue;
+ }
+ RebuildCurrent(hwndDlg);
+ }
+ break;
+
+ case IDC_MENUOBJECTS:
+ if (hdr->code == TVN_SELCHANGEDA )
+ RebuildCurrent( hwndDlg );
+ break;
+
+ case IDC_MENUITEMS:
+ switch (hdr->code) {
+ case NM_CUSTOMDRAW:
+ {
+ int i= handleCustomDraw(GetDlgItem(hwndDlg,IDC_MENUITEMS),(LPNMTVCUSTOMDRAW) lParam);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, i);
+ return TRUE;
+ }
+
+ case TVN_BEGINDRAGA:
+ SetCapture(hwndDlg);
+ dat->dragging=1;
+ dat->hDragItem=((LPNMTREEVIEW)lParam)->itemNew.hItem;
+ TreeView_SelectItem(GetDlgItem(hwndDlg,IDC_MENUITEMS),dat->hDragItem);
+ break;
+
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(GetMessagePos());
+ hti.pt.y=(short)HIWORD(GetMessagePos());
+ ScreenToClient(hdr->hwndFrom,&hti.pt);
+ if (TreeView_HitTest(hdr->hwndFrom,&hti)) {
+ if (hti.flags&TVHT_ONITEMICON) {
+ TVITEM tvi;
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
+ tvi.hItem=hti.hItem;
+ TreeView_GetItem(hdr->hwndFrom,&tvi);
+
+ tvi.iImage=tvi.iSelectedImage=!tvi.iImage;
+ ((MenuItemOptData *)tvi.lParam)->show=tvi.iImage;
+ TreeView_SetItem(hdr->hwndFrom,&tvi);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+
+ //all changes take effect in runtime
+ //ShowWindow(GetDlgItem(hwndDlg,IDC_BUTTONORDERTREEWARNING),SW_SHOW);
+ }
+ /*--------MultiSelection----------*/
+ if (hti.flags&TVHT_ONITEMLABEL) {
+ /// LabelClicked Set/unset selection
+ TVITEM tvi;
+ HWND tvw=hdr->hwndFrom;
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hti.hItem;
+ TreeView_GetItem(tvw,&tvi);
+ if (GetKeyState(VK_CONTROL)&0x8000) {
+ if (((MenuItemOptData *)tvi.lParam)->isSelected)
+ ((MenuItemOptData *)tvi.lParam)->isSelected=0;
+ else
+ ((MenuItemOptData *)tvi.lParam)->isSelected=1; //current selection order++.
+ TreeView_SetItem(tvw,&tvi);
+ }
+ else if (GetKeyState(VK_SHIFT)&0x8000) {
+ ; // shifted click
+ }
+ else {
+ // reset all selection except current
+ HTREEITEM hit;
+ hit=TreeView_GetRoot(tvw);
+ if (hit)
+ do {
+ TVITEM tvi={0};
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hit;
+ TreeView_GetItem(tvw,&tvi);
+
+ if (hti.hItem!=hit)
+ ((MenuItemOptData *)tvi.lParam)->isSelected=0;
+ else
+ ((MenuItemOptData *)tvi.lParam)->isSelected=1;
+ TreeView_SetItem(tvw,&tvi);
+ }
+ while (hit=TreeView_GetNextSibling(tvw,hit));
+ } } }
+ break;
+ }
+ case TVN_SELCHANGING:
+ {
+ LPNMTREEVIEW pn;
+ pn = (LPNMTREEVIEW) lParam;
+ //((MenuItemOptData *)(pn->itemNew.lParam))->isSelected=1;
+ /*if (pn->action==NotKeyPressed)
+ {
+ remove all selection
+ }
+ */
+ }
+ case TVN_SELCHANGEDA:
+ {
+ TVITEM tvi;
+ HTREEITEM hti;
+ MenuItemOptData *iod;
+
+ SetDlgItemTextA(hwndDlg,IDC_GENMENU_CUSTOMNAME,"");
+ SetDlgItemTextA(hwndDlg,IDC_GENMENU_SERVICE,"");
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_CUSTOMNAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_DEFAULT),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_SET),FALSE);
+
+ hti=TreeView_GetSelection(GetDlgItem(hwndDlg,IDC_MENUITEMS));
+ if (hti==NULL)
+ break;
+
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
+ tvi.hItem=hti;
+ TreeView_GetItem(GetDlgItem(hwndDlg,IDC_MENUITEMS),&tvi);
+
+ if ( tvi.lParam == 0 )
+ break;
+
+ iod = ( MenuItemOptData * )tvi.lParam;
+
+ if ( iod->name && _tcsstr(iod->name, STR_SEPARATOR))
+ break;
+
+ SetDlgItemText(hwndDlg,IDC_GENMENU_CUSTOMNAME,iod->name);
+
+ if (iod->pimi->submenu.first == NULL && iod->uniqname)
+ SetDlgItemTextA(hwndDlg, IDC_GENMENU_SERVICE, iod->uniqname);
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_DEFAULT), lstrcmp(iod->name, iod->defname) != 0);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_SET),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_GENMENU_CUSTOMNAME),TRUE);
+ break;
+ }
+ break;
+ } }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (!dat||!dat->dragging) break;
+ {
+ TVHITTESTINFO hti;
+
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ ClientToScreen(hwndDlg,&hti.pt);
+ ScreenToClient(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti.pt);
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti);
+ if (hti.flags&(TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ HTREEITEM it = hti.hItem;
+ hti.pt.y -= TreeView_GetItemHeight(GetDlgItem(hwndDlg,IDC_MENUITEMS))/2;
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti);
+ if (!(hti.flags&TVHT_ABOVE))
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_MENUITEMS),hti.hItem,1);
+ else
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_MENUITEMS),it,0);
+ }
+ else {
+ if (hti.flags&TVHT_ABOVE) SendDlgItemMessage(hwndDlg,IDC_MENUITEMS,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0);
+ if (hti.flags&TVHT_BELOW) SendDlgItemMessage(hwndDlg,IDC_MENUITEMS,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_MENUITEMS),NULL,0);
+ } }
+ break;
+
+ case WM_LBUTTONUP:
+ if (!dat->dragging)
+ break;
+
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_MENUITEMS),NULL,0);
+ dat->dragging=0;
+ ReleaseCapture();
+ {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ ClientToScreen(hwndDlg,&hti.pt);
+ ScreenToClient(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti.pt);
+ hti.pt.y-=TreeView_GetItemHeight(GetDlgItem(hwndDlg,IDC_MENUITEMS))/2;
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_MENUITEMS),&hti);
+ if (hti.flags&TVHT_ABOVE) hti.hItem=TVI_FIRST;
+ if (dat->hDragItem==hti.hItem) break;
+ dat->hDragItem=NULL;
+ if (hti.flags&(TVHT_ONITEM|TVHT_ONITEMRIGHT)||(hti.hItem==TVI_FIRST)) {
+ HWND tvw;
+ HTREEITEM * pSIT;
+ HTREEITEM FirstItem=NULL;
+ UINT uITCnt,uSic ;
+ tvw=GetDlgItem(hwndDlg,IDC_MENUITEMS);
+ uITCnt=TreeView_GetCount(tvw);
+ uSic=0;
+ if (uITCnt) {
+ pSIT=(HTREEITEM *)mir_alloc(sizeof(HTREEITEM)*uITCnt);
+ if (pSIT) {
+ HTREEITEM hit;
+ hit=TreeView_GetRoot(tvw);
+ if (hit)
+ do {
+ TVITEM tvi={0};
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hit;
+ TreeView_GetItem(tvw,&tvi);
+ if (((MenuItemOptData *)tvi.lParam)->isSelected) {
+ pSIT[uSic]=tvi.hItem;
+
+ uSic++;
+ }
+ }while (hit=TreeView_GetNextSibling(tvw,hit));
+ // Proceed moving
+ {
+ UINT i;
+ HTREEITEM insertAfter;
+ insertAfter=hti.hItem;
+ for (i=0; i<uSic; i++) {
+ if (insertAfter) insertAfter=MoveItemAbove(tvw,pSIT[i],insertAfter);
+ else break;
+ if (!i) FirstItem=insertAfter;
+ } }
+ // free pointers...
+ mir_free(pSIT);
+ } }
+
+ if (FirstItem) TreeView_SelectItem(tvw,FirstItem);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SaveTree(hwndDlg);
+ } }
+ break;
+
+ case WM_DESTROY:
+ if ( dat )
+ mir_free( dat );
+
+ ImageList_Destroy(TreeView_SetImageList(GetDlgItem(hwndDlg,IDC_MENUOBJECTS),NULL,TVSIL_NORMAL));
+ FreeTreeData( hwndDlg );
+ break;
+
+ }
+ return FALSE;
+}
+
+long handleCustomDraw(HWND hWndTreeView, LPNMTVCUSTOMDRAW pNMTVCD)
+{
+ if ( pNMTVCD == NULL )
+ return -1;
+
+ switch ( pNMTVCD->nmcd.dwDrawStage ) {
+ case CDDS_PREPAINT:
+ return CDRF_NOTIFYITEMDRAW;
+
+ case CDDS_ITEMPREPAINT:
+ {
+ HTREEITEM hItem = (HTREEITEM) pNMTVCD->nmcd.dwItemSpec;
+ TCHAR buf[255];
+ TVITEM tvi = {0};
+ int k=0;
+ tvi.mask = TVIF_HANDLE |TVIF_PARAM|TVIS_SELECTED|TVIF_TEXT|TVIF_IMAGE;
+ tvi.stateMask=TVIS_SELECTED;
+ tvi.hItem = hItem;
+ tvi.pszText=(LPTSTR)(&buf);
+ tvi.cchTextMax=254;
+ TreeView_GetItem(hWndTreeView, &tvi);
+ if (((MenuItemOptData *)tvi.lParam)->isSelected) {
+ pNMTVCD->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
+ pNMTVCD->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ }
+ else {
+ pNMTVCD->clrTextBk = GetSysColor(COLOR_WINDOW);
+ pNMTVCD->clrText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+
+ /* At this point, you can change the background colors for the item
+ and any subitems and return CDRF_NEWFONT. If the list-view control
+ is in report mode, you can simply return CDRF_NOTIFYSUBITEMREDRAW
+ to customize the item's subitems individually */
+ if ( tvi.iImage == -1 ) {
+ HBRUSH br;
+ SIZE sz;
+ RECT rc;
+ k=1;
+
+ GetTextExtentPoint32(pNMTVCD->nmcd.hdc,tvi.pszText,lstrlen(tvi.pszText),&sz);
+
+ if (sz.cx+3>pNMTVCD->nmcd.rc.right-pNMTVCD->nmcd.rc.left) rc=pNMTVCD->nmcd.rc;
+ else SetRect(&rc,pNMTVCD->nmcd.rc.left,pNMTVCD->nmcd.rc.top,pNMTVCD->nmcd.rc.left+sz.cx+3,pNMTVCD->nmcd.rc.bottom);
+
+ br=CreateSolidBrush(pNMTVCD->clrTextBk);
+ SetTextColor(pNMTVCD->nmcd.hdc,pNMTVCD->clrText);
+ SetBkColor(pNMTVCD->nmcd.hdc,pNMTVCD->clrTextBk);
+ FillRect(pNMTVCD->nmcd.hdc,&rc,br);
+ DeleteObject(br);
+ DrawText(pNMTVCD->nmcd.hdc,tvi.pszText,lstrlen(tvi.pszText),&pNMTVCD->nmcd.rc,DT_LEFT|DT_VCENTER|DT_NOPREFIX);
+ }
+
+ return CDRF_NEWFONT|(k?CDRF_SKIPDEFAULT:0);
+ }
+ }
+ return 0;
+}
+
+INT_PTR CALLBACK ProtocolOrderOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+int GenMenuOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize=sizeof(odp);
+ odp.hInstance = hMirandaInst;
+ odp.pszGroup = LPGEN("Customize");
+
+ odp.position = -1000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPT_GENMENU );
+ odp.pszTitle = LPGEN("Menus");
+ odp.pfnDlgProc = GenMenuOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+
+ odp.position = -10000000;
+ odp.groupPosition = 1000000;
+ odp.pszTemplate = MAKEINTRESOURCEA( IDD_OPT_PROTOCOLORDER );
+ odp.pszTitle = LPGEN("Accounts");
+ odp.pfnDlgProc = ProtocolOrderOpts;
+ odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
diff --git a/src/modules/clist/groups.cpp b/src/modules/clist/groups.cpp
new file mode 100644
index 0000000000..0693c25036
--- /dev/null
+++ b/src/modules/clist/groups.cpp
@@ -0,0 +1,577 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+
+HANDLE hGroupChangeEvent;
+
+static INT_PTR RenameGroup(WPARAM wParam, LPARAM lParam);
+static INT_PTR MoveGroupBefore(WPARAM wParam, LPARAM lParam);
+
+static int CountGroups(void)
+{
+ DBVARIANT dbv;
+ int i;
+ char str[33];
+
+ for (i = 0;; i++) {
+ _itoa(i, str, 10);
+ if (DBGetContactSetting(NULL, "CListGroups", str, &dbv))
+ break;
+ DBFreeVariant(&dbv);
+ }
+ return i;
+}
+
+static int GroupNameExists(const TCHAR *name, int skipGroup)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ int i;
+
+ for (i = 0;; i++) {
+ if (i == skipGroup)
+ continue;
+ _itoa(i, idstr, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+ if (!_tcscmp(dbv.ptszVal + 1, name)) {
+ DBFreeVariant(&dbv);
+ return i+1;
+ }
+ DBFreeVariant(&dbv);
+ }
+ return 0;
+}
+
+static INT_PTR CreateGroup(WPARAM wParam, LPARAM lParam)
+{
+ int newId = CountGroups();
+ TCHAR newBaseName[127], newName[128];
+ char str[33];
+ int i;
+ DBVARIANT dbv;
+
+ const TCHAR* grpName = lParam ? (TCHAR*)lParam : TranslateT("New Group");
+ if (wParam) {
+ _itoa(wParam - 1, str, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv))
+ return 0;
+
+ mir_sntprintf( newBaseName, SIZEOF(newBaseName), _T("%s\\%s"), dbv.ptszVal + 1, grpName );
+ mir_free(dbv.pszVal);
+ }
+ else lstrcpyn( newBaseName, grpName, SIZEOF( newBaseName ));
+
+ _itoa(newId, str, 10);
+ lstrcpyn( newName + 1, newBaseName, SIZEOF(newName) - 1);
+ if (lParam) {
+ i = GroupNameExists(newBaseName, -1);
+ if (i) newId = i - 1;
+ i = !i;
+ }
+ else {
+ i = 1;
+ while (GroupNameExists(newName + 1, -1))
+ mir_sntprintf( newName + 1, SIZEOF(newName) - 1, _T("%s (%d)"), newBaseName, ++i );
+ }
+ if (i) {
+ const CLISTGROUPCHANGE grpChg = { sizeof(CLISTGROUPCHANGE), NULL, newName };
+
+ newName[0] = 1 | GROUPF_EXPANDED; //1 is required so we never get '\0'
+ DBWriteContactSettingTString(NULL, "CListGroups", str, newName);
+ CallService(MS_CLUI_GROUPADDED, newId + 1, 1);
+
+ NotifyEventHooks(hGroupChangeEvent, 0, (LPARAM)&grpChg);
+ }
+
+ return newId + 1;
+}
+
+static INT_PTR GetGroupName2(WPARAM wParam, LPARAM lParam)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ static char name[128];
+
+ _itoa(wParam - 1, idstr, 10);
+ if (DBGetContactSettingString(NULL, "CListGroups", idstr, &dbv))
+ return (INT_PTR) (char *) NULL;
+ lstrcpynA(name, dbv.pszVal + 1, SIZEOF(name));
+ if ((DWORD *) lParam != NULL)
+ *(DWORD *) lParam = dbv.pszVal[0];
+ DBFreeVariant(&dbv);
+ return (INT_PTR) name;
+}
+
+TCHAR* fnGetGroupName( int idx, DWORD* pdwFlags )
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ static TCHAR name[128];
+
+ _itoa( idx-1, idstr, 10);
+ if (DBGetContactSettingTString( NULL, "CListGroups", idstr, &dbv ))
+ return NULL;
+
+ lstrcpyn( name, dbv.ptszVal + 1, SIZEOF( name ));
+ if ( pdwFlags != NULL )
+ *pdwFlags = dbv.ptszVal[0];
+ DBFreeVariant( &dbv );
+ return name;
+}
+
+static INT_PTR GetGroupName(WPARAM wParam, LPARAM lParam)
+{
+ INT_PTR ret;
+ ret = GetGroupName2(wParam, lParam);
+ if ((int *) lParam)
+ *(int *) lParam = 0 != (*(int *) lParam & GROUPF_EXPANDED);
+ return ret;
+}
+
+static INT_PTR DeleteGroup(WPARAM wParam, LPARAM)
+{
+ int i;
+ char str[33];
+ DBVARIANT dbv;
+ HANDLE hContact;
+ TCHAR name[256], szNewParent[256], *pszLastBackslash;
+
+ //get the name
+ _itoa(wParam - 1, str, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv))
+ return 1;
+ lstrcpyn(name, dbv.ptszVal + 1, SIZEOF(name));
+ DBFreeVariant(&dbv);
+ if (DBGetContactSettingByte(NULL, "CList", "ConfirmDelete", SETTING_CONFIRMDELETE_DEFAULT))
+ {
+ TCHAR szQuestion[256+100];
+ mir_sntprintf( szQuestion, SIZEOF(szQuestion), TranslateT("Are you sure you want to delete group '%s'? This operation can not be undone."), name );
+ if (MessageBox(cli.hwndContactList, szQuestion, TranslateT("Delete Group"), MB_YESNO|MB_ICONQUESTION)==IDNO)
+ return 1;
+ }
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+ //must remove setting from all child contacts too
+ //children are demoted to the next group up, not deleted.
+ lstrcpy(szNewParent, name);
+ pszLastBackslash = _tcsrchr(szNewParent, '\\');
+ if (pszLastBackslash)
+ pszLastBackslash[0] = '\0';
+ else
+ szNewParent[0] = '\0';
+
+ CLISTGROUPCHANGE grpChg = { sizeof(CLISTGROUPCHANGE), NULL, NULL };
+
+ for (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ hContact ;
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0))
+ {
+ if (DBGetContactSettingTString(hContact, "CList", "Group", &dbv))
+ continue;
+
+ if (_tcscmp(dbv.ptszVal, name))
+ {
+ DBFreeVariant(&dbv);
+ continue;
+ }
+ DBFreeVariant(&dbv);
+
+ if (szNewParent[0])
+ {
+ DBWriteContactSettingTString(hContact, "CList", "Group", szNewParent);
+ grpChg.pszNewName = szNewParent;
+ }
+ else
+ {
+ DBDeleteContactSetting(hContact, "CList", "Group");
+ grpChg.pszNewName = NULL;
+ }
+ NotifyEventHooks(hGroupChangeEvent, (WPARAM)hContact, (LPARAM)&grpChg);
+ }
+ //shuffle list of groups up to fill gap
+ for (i = wParam - 1;; i++) {
+ _itoa(i + 1, str, 10);
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", str, &dbv))
+ break;
+ _itoa(i, str, 10);
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", str, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ _itoa(i, str, 10);
+ DBDeleteContactSetting(NULL, "CListGroups", str);
+ //rename subgroups
+ {
+ TCHAR szNewName[256];
+ int len;
+
+ len = lstrlen(name);
+ for (i = 0;; i++) {
+ _itoa(i, str, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv))
+ break;
+ if (!_tcsncmp(dbv.ptszVal + 1, name, len) && dbv.pszVal[len + 1] == '\\' && _tcschr(dbv.ptszVal + len + 2, '\\') == NULL) {
+ if (szNewParent[0])
+ mir_sntprintf(szNewName, SIZEOF(szNewName), _T("%s\\%s"), szNewParent, dbv.ptszVal + len + 2);
+ else
+ lstrcpyn(szNewName, dbv.ptszVal + len + 2, SIZEOF(szNewName));
+ cli.pfnRenameGroup(i + 1, szNewName);
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ cli.pfnLoadContactTree();
+
+ {
+ const CLISTGROUPCHANGE grpChg = { sizeof(CLISTGROUPCHANGE), name, NULL };
+ NotifyEventHooks(hGroupChangeEvent, 0, (LPARAM)&grpChg);
+ }
+ return 0;
+}
+
+static int RenameGroupWithMove(int groupId, const TCHAR *szName, int move)
+{
+ char idstr[33];
+ TCHAR str[256], oldName[256];
+ DBVARIANT dbv;
+ HANDLE hContact;
+
+ if (GroupNameExists(szName, groupId)) {
+ MessageBox(NULL, TranslateT("You already have a group with that name. Please enter a unique name for the group."), TranslateT("Rename Group"), MB_OK);
+ return 1;
+ }
+
+ //do the change
+ _itoa(groupId, idstr, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ return 1;
+ str[0] = dbv.pszVal[0] & 0x7F;
+ lstrcpyn(oldName, dbv.ptszVal + 1, SIZEOF(oldName));
+ DBFreeVariant(&dbv);
+ lstrcpyn(str + 1, szName, SIZEOF(str) - 1);
+ DBWriteContactSettingTString(NULL, "CListGroups", idstr, str);
+
+ //must rename setting in all child contacts too
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ do {
+ ClcCacheEntryBase* cache = cli.pfnGetCacheEntry( hContact );
+ if ( !lstrcmp(cache->group, oldName)) {
+ DBWriteContactSettingTString(hContact, "CList", "Group", szName);
+ mir_free(cache->group);
+ cache->group = 0;
+ cli.pfnCheckCacheItem(cache);
+ }
+ }
+ while ((hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0)) != NULL);
+
+ //rename subgroups
+ {
+ TCHAR szNewName[256];
+ int len, i;
+
+ len = lstrlen(oldName);
+ for (i = 0;; i++) {
+ if (i == groupId)
+ continue;
+ _itoa(i, idstr, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+ if ( !_tcsncmp(dbv.ptszVal + 1, oldName, len) && dbv.ptszVal[len + 1] == '\\' && _tcschr(dbv.ptszVal + len + 2, '\\') == NULL) {
+ mir_sntprintf( szNewName, SIZEOF(szNewName), _T("%s\\%s"), szName, dbv.ptszVal + len + 2 );
+ RenameGroupWithMove(i, szNewName, 0); //luckily, child groups will never need reordering
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ //finally must make sure it's after any parent items
+ if (move) {
+ TCHAR *pszLastBackslash;
+ int i;
+
+ lstrcpyn(str, szName, SIZEOF(str));
+ pszLastBackslash = _tcsrchr(str, '\\');
+ if (pszLastBackslash != NULL) {
+ *pszLastBackslash = '\0';
+ for (i = 0;; i++) {
+ _itoa(i, idstr, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+ if (!lstrcmp(dbv.ptszVal + 1, str)) {
+ if (i < groupId)
+ break; //is OK
+ MoveGroupBefore(groupId + 1, i + 2);
+ break;
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+ {
+ const CLISTGROUPCHANGE grpChg = { sizeof(CLISTGROUPCHANGE), oldName, (TCHAR*)szName };
+ NotifyEventHooks(hGroupChangeEvent, 0, (LPARAM)&grpChg);
+ }
+ return 0;
+}
+
+int fnRenameGroup( int groupID, TCHAR* newName )
+{
+ return -1 != RenameGroupWithMove( groupID-1, newName, 1);
+}
+
+static INT_PTR RenameGroup(WPARAM wParam, LPARAM lParam)
+{
+ #if defined( _UNICODE )
+ WCHAR* temp = mir_a2u(( char* )lParam );
+ int result = ( -1 != RenameGroupWithMove(wParam - 1, temp, 1));
+ mir_free( temp );
+ return result;
+ #else
+ return -1 != RenameGroupWithMove(wParam - 1, (TCHAR*) lParam, 1);
+ #endif
+}
+
+static INT_PTR SetGroupExpandedState(WPARAM wParam, LPARAM lParam)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+
+ _itoa(wParam - 1, idstr, 10);
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", idstr, &dbv))
+ return 1;
+ if (lParam)
+ dbv.pszVal[0] |= GROUPF_EXPANDED;
+ else
+ dbv.pszVal[0] = dbv.pszVal[0] & ~GROUPF_EXPANDED;
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", idstr, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ return 0;
+}
+
+static INT_PTR SetGroupFlags(WPARAM wParam, LPARAM lParam)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ int flags, oldval, newval;
+
+ _itoa(wParam - 1, idstr, 10);
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", idstr, &dbv))
+ return 1;
+ flags = LOWORD(lParam) & HIWORD(lParam);
+ oldval = dbv.pszVal[0];
+ newval = dbv.pszVal[0] = ((oldval & ~HIWORD(lParam)) | flags) & 0x7f;
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", idstr, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ if ((oldval & GROUPF_HIDEOFFLINE) != (newval & GROUPF_HIDEOFFLINE))
+ cli.pfnLoadContactTree();
+ return 0;
+}
+
+static INT_PTR MoveGroupBefore(WPARAM wParam, LPARAM lParam)
+{
+ int i, shuffleFrom, shuffleTo, shuffleDir;
+ char str[33];
+ TCHAR *szMoveName;
+ DBVARIANT dbv;
+
+ if (wParam == 0 || (LPARAM) wParam == lParam)
+ return 0;
+ _itoa(wParam - 1, str, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv))
+ return 0;
+ szMoveName = dbv.ptszVal;
+ //shuffle list of groups up to fill gap
+ if (lParam == 0) {
+ shuffleFrom = wParam - 1;
+ shuffleTo = -1;
+ shuffleDir = -1;
+ }
+ else {
+ if ((LPARAM) wParam < lParam) {
+ shuffleFrom = wParam - 1;
+ shuffleTo = lParam - 2;
+ shuffleDir = -1;
+ }
+ else {
+ shuffleFrom = wParam - 1;
+ shuffleTo = lParam - 1;
+ shuffleDir = 1;
+ }
+ }
+ if (shuffleDir == -1) {
+ for (i = shuffleFrom; i != shuffleTo; i++) {
+ _itoa(i + 1, str, 10);
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", str, &dbv)) {
+ shuffleTo = i;
+ break;
+ }
+ _itoa(i, str, 10);
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", str, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ else {
+ for (i = shuffleFrom; i != shuffleTo; i--) {
+ _itoa(i - 1, str, 10);
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", str, &dbv)) {
+ mir_free(szMoveName);
+ return 1;
+ } //never happens
+ _itoa(i, str, 10);
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", str, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ _itoa(shuffleTo, str, 10);
+ DBWriteContactSettingTString(NULL, "CListGroups", str, szMoveName);
+ mir_free(szMoveName);
+ return shuffleTo + 1;
+}
+
+static INT_PTR BuildGroupMenu(WPARAM, LPARAM)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ int groupId;
+ HMENU hRootMenu, hThisMenu;
+ int nextMenuId = 100;
+ TCHAR *pBackslash, *pNextField, szThisField[128], szThisMenuItem[128];
+ int menuId, compareResult, menuItemCount;
+ MENUITEMINFO mii = { 0 };
+
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", "0", &dbv))
+ return (INT_PTR) (HMENU) NULL;
+ DBFreeVariant(&dbv);
+ hRootMenu = CreateMenu();
+ for (groupId = 0;; groupId++) {
+ _itoa(groupId, idstr, 10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", idstr, &dbv))
+ break;
+
+ pNextField = dbv.ptszVal + 1;
+ hThisMenu = hRootMenu;
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ do {
+ pBackslash = _tcschr(pNextField, '\\');
+ if (pBackslash == NULL) {
+ lstrcpyn(szThisField, pNextField, SIZEOF(szThisField));
+ pNextField = NULL;
+ }
+ else {
+ lstrcpyn(szThisField, pNextField, min( SIZEOF(szThisField), pBackslash - pNextField + 1));
+ pNextField = pBackslash + 1;
+ }
+ compareResult = 1;
+ menuItemCount = GetMenuItemCount(hThisMenu);
+ for (menuId = 0; menuId < menuItemCount; menuId++) {
+ mii.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_DATA;
+ mii.cch = SIZEOF(szThisMenuItem);
+ mii.dwTypeData = szThisMenuItem;
+ GetMenuItemInfo(hThisMenu, menuId, TRUE, &mii);
+ compareResult = lstrcmp(szThisField, szThisMenuItem);
+ if (compareResult == 0) {
+ if (pNextField == NULL) {
+ mii.fMask = MIIM_DATA;
+ mii.dwItemData = groupId + 1;
+ SetMenuItemInfo(hThisMenu, menuId, TRUE, &mii);
+ }
+ else {
+ if (mii.hSubMenu == NULL) {
+ mii.fMask = MIIM_SUBMENU;
+ mii.hSubMenu = CreateMenu();
+ SetMenuItemInfo(hThisMenu, menuId, TRUE, &mii);
+ mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
+ //dwItemData doesn't change
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = TranslateT("This group");
+ mii.wID = nextMenuId++;
+ InsertMenuItem(mii.hSubMenu, 0, TRUE, &mii);
+ mii.fMask = MIIM_TYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(mii.hSubMenu, 1, TRUE, &mii);
+ }
+ hThisMenu = mii.hSubMenu;
+ }
+ break;
+ }
+ if ((int) mii.dwItemData - 1 > groupId)
+ break;
+ }
+ if (compareResult) {
+ mii.fMask = MIIM_TYPE | MIIM_ID;
+ mii.wID = nextMenuId++;
+ mii.dwTypeData = szThisField;
+ mii.fType = MFT_STRING;
+ if (pNextField) {
+ mii.fMask |= MIIM_SUBMENU;
+ mii.hSubMenu = CreateMenu();
+ }
+ else {
+ mii.fMask |= MIIM_DATA;
+ mii.dwItemData = groupId + 1;
+ }
+ InsertMenuItem(hThisMenu, menuId, TRUE, &mii);
+ if (pNextField) {
+ hThisMenu = mii.hSubMenu;
+ }
+ }
+ } while (pNextField);
+
+ DBFreeVariant(&dbv);
+ }
+ return (INT_PTR) hRootMenu;
+}
+
+int InitGroupServices(void)
+{
+ for (int i = 0; ; i++)
+ {
+ char str[32];
+ _itoa(i, str, 10);
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingStringUtf(NULL, "CListGroups", str, &dbv))
+ break;
+ if (dbv.pszVal[0] & 0x80)
+ {
+ dbv.pszVal[0] &= 0x7f;
+ DBWriteContactSettingStringUtf(NULL, "CListGroups", str, dbv.pszVal);
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ CreateServiceFunction(MS_CLIST_GROUPCREATE, CreateGroup);
+ CreateServiceFunction(MS_CLIST_GROUPDELETE, DeleteGroup);
+ CreateServiceFunction(MS_CLIST_GROUPRENAME, RenameGroup);
+ CreateServiceFunction(MS_CLIST_GROUPGETNAME, GetGroupName);
+ CreateServiceFunction(MS_CLIST_GROUPGETNAME2, GetGroupName2);
+ CreateServiceFunction(MS_CLIST_GROUPSETEXPANDED, SetGroupExpandedState);
+ CreateServiceFunction(MS_CLIST_GROUPSETFLAGS, SetGroupFlags);
+ CreateServiceFunction(MS_CLIST_GROUPMOVEBEFORE, MoveGroupBefore);
+ CreateServiceFunction(MS_CLIST_GROUPBUILDMENU, BuildGroupMenu);
+
+ hGroupChangeEvent = CreateHookableEvent( ME_CLIST_GROUPCHANGE );
+
+ return 0;
+}
diff --git a/src/modules/clist/keyboard.cpp b/src/modules/clist/keyboard.cpp
new file mode 100644
index 0000000000..2c9cdce69c
--- /dev/null
+++ b/src/modules/clist/keyboard.cpp
@@ -0,0 +1,173 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+#include <m_hotkeys.h>
+
+static INT_PTR hkHideShow(WPARAM, LPARAM)
+{
+ cli.pfnShowHide(0,0);
+ return 0;
+}
+/*
+INT_PTR hkSearch(WPARAM wParam,LPARAM lParam)
+{
+ DBVARIANT dbv={0};
+ if(!DBGetContactSettingString(NULL,"CList","SearchUrl",&dbv)) {
+ CallService(MS_UTILS_OPENURL,DBGetContactSettingByte(NULL,"CList","HKSearchNewWnd",0),(LPARAM)dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ return 0;
+}
+*/
+static INT_PTR hkRead(WPARAM, LPARAM)
+{
+ if(cli.pfnEventsProcessTrayDoubleClick(0)==0) return TRUE;
+ SetForegroundWindow(cli.hwndContactList);
+ SetFocus(cli.hwndContactList);
+ return 0;
+}
+
+static INT_PTR hkOpts(WPARAM, LPARAM)
+{
+ CallService("Options/OptionsCommand",0, 0);
+ return 0;
+}
+/*
+static INT_PTR hkCloseMiranda(WPARAM wParam,LPARAM lParam)
+{
+ CallService("CloseAction", 0, 0);
+ return 0;
+}
+
+INT_PTR hkRestoreStatus(WPARAM wParam,LPARAM lParam)
+{
+ int nStatus = DBGetContactSettingWord(NULL, "CList", "Status", ID_STATUS_OFFLINE);
+ CallService(MS_CLIST_SETSTATUSMODE, nStatus, 0);
+ return 0;
+}
+
+static INT_PTR hkAllOffline(WPARAM, LPARAM)
+{
+ CallService(MS_CLIST_SETSTATUSMODE, ID_STATUS_OFFLINE, 0);
+ return 0;
+}
+*/
+int InitClistHotKeys(void)
+{
+ HOTKEYDESC shk = {0};
+
+ CreateServiceFunction("CLIST/HK/SHOWHIDE",hkHideShow);
+ CreateServiceFunction("CLIST/HK/Opts",hkOpts);
+ CreateServiceFunction("CLIST/HK/Read",hkRead);
+// CreateServiceFunction("CLIST/HK/CloseMiranda",hkCloseMiranda);
+// CreateServiceFunction("CLIST/HK/RestoreStatus",hkRestoreStatus);
+// CreateServiceFunction("CLIST/HK/AllOffline",hkAllOffline);
+
+ shk.cbSize=sizeof(shk);
+ shk.pszDescription="Show Hide Contact List";
+ shk.pszName="ShowHide";
+ shk.pszSection="Main";
+ shk.pszService="CLIST/HK/SHOWHIDE";
+ shk.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'A');
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+ shk.pszDescription="Read Message";
+ shk.pszName="ReadMessage";
+ shk.pszSection="Main";
+ shk.pszService="CLIST/HK/Read";
+ shk.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'I');
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+/*
+ shk.pszDescription="Search in site";
+ shk.pszName="SearchInWeb";
+ shk.pszSection="Main";
+ shk.pszService="CLIST/HK/Search";
+ shk.DefHotKey=846;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+*/
+ shk.pszDescription = "Open Options Page";
+ shk.pszName = "ShowOptions";
+ shk.pszSection = "Main";
+ shk.pszService = "CLIST/HK/Opts";
+ shk.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'O') | HKF_MIRANDA_LOCAL;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+ shk.pszDescription = "Open Logging Options";
+ shk.pszName = "ShowLogOptions";
+ shk.pszSection = "Main";
+ shk.pszService = "Netlib/Log/Win";
+ shk.DefHotKey = 0;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+ shk.pszDescription="Open Find User Dialog";
+ shk.pszName="FindUsers";
+ shk.pszSection="Main";
+ shk.pszService="FindAdd/FindAddCommand";
+ shk.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'F') | HKF_MIRANDA_LOCAL;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+/*
+ shk.pszDescription="Close Miranda";
+ shk.pszName="CloseMiranda";
+ shk.pszSection="Main";
+ shk.pszService="CLIST/HK/CloseMiranda";
+ shk.DefHotKey=0;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+ shk.pszDescription="Restore last status";
+ shk.pszName="RestoreLastStatus";
+ shk.pszSection="Status";
+ shk.pszService="CLIST/HK/RestoreStatus";
+ shk.DefHotKey=0;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+
+ shk.pszDescription="Set All Offline";
+ shk.pszName="AllOffline";
+ shk.pszSection="Status";
+ shk.pszService="CLIST/HK/AllOffline";
+ shk.DefHotKey=0;
+ CallService(MS_HOTKEY_REGISTER,0,(LPARAM)&shk);
+*/
+ return 0;
+}
+
+
+int fnHotKeysRegister(HWND)
+{
+ return 0;
+}
+
+void fnHotKeysUnregister(HWND)
+{
+}
+
+int fnHotKeysProcess(HWND, WPARAM, LPARAM)
+{
+ return TRUE;
+}
+
+int fnHotkeysProcessMessage(WPARAM, LPARAM)
+{
+ return FALSE;
+}
diff --git a/src/modules/clist/movetogroup.cpp b/src/modules/clist/movetogroup.cpp
new file mode 100644
index 0000000000..9924bef2aa
--- /dev/null
+++ b/src/modules/clist/movetogroup.cpp
@@ -0,0 +1,160 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+HANDLE hOnCntMenuBuild;
+HGENMENU hMoveToGroupItem=0, hPriorityItem = 0, hFloatingItem = 0;
+
+LIST<HANDLE> lphGroupsItems(5);
+
+//service
+//wparam - hcontact
+//lparam .popupposition from CLISTMENUITEM
+
+#define MTG_MOVE "MoveToGroup/Move"
+
+struct GroupItemSort
+{
+ TCHAR* name;
+ int position;
+
+ GroupItemSort(TCHAR* pname, int pos)
+ : name(mir_tstrdup(pname)), position(pos) {}
+
+ ~GroupItemSort() { mir_free(name); }
+
+ static int compare(const GroupItemSort* d1, const GroupItemSort* d2)
+ { return _tcscoll(d1->name, d2->name); }
+};
+
+static TCHAR* PrepareGroupName( TCHAR* str )
+{
+ TCHAR* p = _tcschr( str, '&' ), *d;
+ if ( p == NULL )
+ return mir_tstrdup( str );
+
+ d = p = ( TCHAR* )mir_alloc( sizeof( TCHAR )*( 2*_tcslen( str )+1 ));
+ while ( *str ) {
+ if ( *str == '&' )
+ *d++ = '&';
+ *d++ = *str++;
+ }
+
+ *d++ = 0;
+ return p;
+}
+
+static void AddGroupItem(HGENMENU hRoot, TCHAR* name, int pos, WPARAM param, bool checked)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.hParentMenu = hRoot;
+ mi.popupPosition = param; // param to pszService - only with CMIF_CHILDPOPUP !!!!!!
+ mi.position = pos;
+ mi.ptszName = PrepareGroupName( name );
+ mi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ if ( checked )
+ mi.flags |= CMIF_CHECKED;
+ mi.pszService = MTG_MOVE;
+ HANDLE result = ( HANDLE )CallService(MS_CLIST_ADDCONTACTMENUITEM, param, (LPARAM)&mi);
+ mir_free( mi.ptszName );
+
+ lphGroupsItems.insert((HANDLE*)result);
+}
+
+static int OnContactMenuBuild(WPARAM wParam,LPARAM)
+{
+ int i;
+ OBJLIST<GroupItemSort> groups(10, GroupItemSort::compare);
+
+ if (!hMoveToGroupItem)
+ {
+ CLISTMENUITEM mi = {0};
+
+ mi.cbSize = sizeof(mi);
+ mi.position = 100000;
+ mi.pszName = LPGEN("&Move to Group");
+ mi.flags = CMIF_ROOTHANDLE | CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_GROUP);
+
+ hMoveToGroupItem = (HGENMENU)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+ }
+
+ for (i = 0; i < lphGroupsItems.getCount(); i++)
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)lphGroupsItems[i], 0);
+ lphGroupsItems.destroy();
+
+ TCHAR *szContactGroup = DBGetStringT((HANDLE)wParam, "CList", "Group");
+
+ int pos = 1000;
+
+ AddGroupItem(hMoveToGroupItem, TranslateT("<Root Group>"), pos, -1, !szContactGroup);
+
+ pos += 100000; // Separator
+
+ for (i = 0; ; ++i)
+ {
+ char intname[20];
+ _itoa(i, intname, 10);
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(NULL, "CListGroups", intname, &dbv))
+ break;
+
+ if (dbv.ptszVal[0])
+ groups.insert(new GroupItemSort(dbv.ptszVal + 1, i + 1));
+
+ mir_free(dbv.ptszVal);
+ }
+
+ for (i = 0; i < groups.getCount(); ++i)
+ {
+ bool checked = szContactGroup && !_tcscmp(szContactGroup, groups[i].name);
+ AddGroupItem(hMoveToGroupItem, groups[i].name, ++pos, groups[i].position, checked);
+ }
+
+ groups.destroy();
+ mir_free(szContactGroup);
+
+ return 0;
+}
+
+static INT_PTR MTG_DOMOVE(WPARAM wParam,LPARAM lParam)
+{
+ CallService(MS_CLIST_CONTACTCHANGEGROUP, wParam, lParam < 0 ? 0 : lParam);
+ return 0;
+}
+
+void MTG_OnmodulesLoad()
+{
+ hOnCntMenuBuild=HookEvent(ME_CLIST_PREBUILDCONTACTMENU,OnContactMenuBuild);
+ CreateServiceFunction(MTG_MOVE,MTG_DOMOVE);
+}
+
+int UnloadMoveToGroup(void)
+{
+ UnhookEvent(hOnCntMenuBuild);
+ lphGroupsItems.destroy();
+
+ return 0;
+}
diff --git a/src/modules/clist/protocolorder.cpp b/src/modules/clist/protocolorder.cpp
new file mode 100644
index 0000000000..ff77babfec
--- /dev/null
+++ b/src/modules/clist/protocolorder.cpp
@@ -0,0 +1,351 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// options dialog for protocol order and visibility
+// written by daniel vijge
+// gpl license ect...
+
+#include "commonheaders.h"
+#include "clc.h"
+
+typedef struct tagProtocolData
+{
+ char *RealName;
+ int protopos;
+ int show, enabled;
+}
+ ProtocolData;
+
+struct ProtocolOrderData
+{
+ int dragging;
+ HTREEITEM hDragItem;
+};
+
+typedef struct {
+ char* protoName;
+ int visible;
+}
+ tempProtoItem;
+
+int isProtoSuitable( PROTO_INTERFACE* ppi )
+{
+ if ( ppi == NULL )
+ return TRUE;
+
+ return ppi->GetCaps( PFLAGNUM_2, 0 ) & ~ppi->GetCaps( PFLAGNUM_5, 0 );
+}
+
+bool CheckProtocolOrder(void)
+{
+ bool changed = false;
+ int i, id = 0;
+
+ for (;;)
+ {
+ // Find account with this id
+ for (i = 0; i < accounts.getCount(); i++)
+ if (accounts[i]->iOrder == id) break;
+
+ // Account with id not found
+ if (i == accounts.getCount())
+ {
+ // Check if this is skipped id, if it is decrement all other ids
+ bool found = false;
+ for (i = 0; i < accounts.getCount(); i++)
+ {
+ if (accounts[i]->iOrder < 1000000 && accounts[i]->iOrder > id)
+ {
+ --accounts[i]->iOrder;
+ found = true;
+ }
+ }
+ if (found) changed = true;
+ else break;
+ }
+ else
+ ++id;
+ }
+
+ if (id < accounts.getCount())
+ {
+ // Remove huge ids
+ for (i = 0; i < accounts.getCount(); i++)
+ {
+ if (accounts[i]->iOrder >= 1000000)
+ accounts[i]->iOrder = id++;
+ }
+ changed = true;
+ }
+
+ if (id < accounts.getCount())
+ {
+ // Remove duplicate ids
+ for (i = 0; i < accounts.getCount(); i++)
+ {
+ bool found = false;
+ for (int j = 0; j < accounts.getCount(); j++)
+ {
+ if (accounts[j]->iOrder == i)
+ {
+ if (found) accounts[j]->iOrder = id++;
+ else found = true;
+ }
+ }
+ }
+ changed = true;
+ }
+
+ return changed;
+}
+
+
+int FillTree(HWND hwnd)
+{
+ ProtocolData *PD;
+ int i;
+ PROTOACCOUNT* pa;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM|TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+
+ TreeView_DeleteAllItems(hwnd);
+ if ( accounts.getCount() == 0 )
+ return FALSE;
+
+ for ( i = 0; i < accounts.getCount(); i++ ) {
+ int idx = cli.pfnGetAccountIndexByPos( i );
+ if ( idx == -1 )
+ continue;
+
+ pa = accounts[idx];
+
+ PD = ( ProtocolData* )mir_alloc( sizeof( ProtocolData ));
+ PD->RealName = pa->szModuleName;
+ PD->protopos = pa->iOrder;
+ PD->enabled = Proto_IsAccountEnabled( pa ) && isProtoSuitable( pa->ppro );
+ PD->show = PD->enabled ? pa->bIsVisible : 100;
+
+ tvis.item.lParam = ( LPARAM )PD;
+ tvis.item.pszText = pa->tszAccountName;
+ tvis.item.iImage = tvis.item.iSelectedImage = PD->show;
+ TreeView_InsertItem( hwnd, &tvis );
+ }
+
+ return 0;
+}
+
+INT_PTR CALLBACK ProtocolOrderOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndProtoOrder = GetDlgItem(hwndDlg, IDC_PROTOCOLORDER);
+ struct ProtocolOrderData *dat = (ProtocolOrderData*)GetWindowLongPtr(hwndProtoOrder, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(hwndProtoOrder, TVSIL_NORMAL));
+ mir_free( dat );
+ break;
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat = (ProtocolOrderData*)mir_calloc(sizeof(ProtocolOrderData));
+ SetWindowLongPtr(hwndProtoOrder, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->dragging=0;
+
+ SetWindowLong(hwndProtoOrder, GWL_STYLE, GetWindowLong(hwndProtoOrder, GWL_STYLE) | TVS_NOHSCROLL);
+ {
+ HIMAGELIST himlCheckBoxes = ImageList_Create( GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), ILC_COLOR32|ILC_MASK, 2, 2 );
+ ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK);
+ ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK);
+ TreeView_SetImageList(hwndProtoOrder, himlCheckBoxes, TVSIL_NORMAL);
+ }
+
+ FillTree(hwndProtoOrder);
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_RESETPROTOCOLDATA && HIWORD(wParam) == BN_CLICKED)
+ {
+ for ( int i = 0; i < accounts.getCount(); i++ )
+ accounts[i]->iOrder = i;
+
+ FillTree(hwndProtoOrder);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY ) {
+ int count = 0;
+
+ TVITEM tvi;
+ tvi.hItem = TreeView_GetRoot(hwndProtoOrder);
+ tvi.cchTextMax = 32;
+ tvi.mask = TVIF_PARAM | TVIF_HANDLE;
+
+ while ( tvi.hItem != NULL ) {
+ TreeView_GetItem(hwndProtoOrder, &tvi);
+
+ if (tvi.lParam!=0) {
+ ProtocolData* ppd = ( ProtocolData* )tvi.lParam;
+ PROTOACCOUNT* pa = Proto_GetAccount( ppd->RealName );
+ if ( pa != NULL ) {
+ pa->iOrder = count++;
+ if ( ppd->enabled )
+ pa->bIsVisible = ppd->show;
+ }
+ }
+
+ tvi.hItem = TreeView_GetNextSibling(hwndProtoOrder, tvi.hItem );
+ }
+
+ WriteDbAccounts();
+ cli.pfnReloadProtoMenus();
+ cli.pfnTrayIconIconsChanged();
+ cli.pfnClcBroadcast( INTM_RELOADOPTIONS, 0, 0 );
+ cli.pfnClcBroadcast( INTM_INVALIDATE, 0, 0 );
+ }
+ break;
+
+ case IDC_PROTOCOLORDER:
+ switch (((LPNMHDR)lParam)->code) {
+ case TVN_DELETEITEMA:
+ {
+ NMTREEVIEWA * pnmtv = (NMTREEVIEWA *) lParam;
+ if (pnmtv && pnmtv->itemOld.lParam)
+ mir_free((ProtocolData*)pnmtv->itemOld.lParam);
+ }
+ break;
+
+ case TVN_BEGINDRAGA:
+ SetCapture(hwndDlg);
+ dat->dragging=1;
+ dat->hDragItem=((LPNMTREEVIEW)lParam)->itemNew.hItem;
+ TreeView_SelectItem(hwndProtoOrder, dat->hDragItem);
+ break;
+
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(GetMessagePos());
+ hti.pt.y=(short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom,&hti.pt);
+ if ( TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti )) {
+ if ( hti.flags & TVHT_ONITEMICON ) {
+ TVITEMA tvi;
+ tvi.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvi.hItem = hti.hItem;
+ TreeView_GetItem(((LPNMHDR)lParam)->hwndFrom,&tvi);
+
+ ProtocolData *pData = ( ProtocolData* )tvi.lParam;
+ if ( pData->enabled ) {
+ tvi.iImage = tvi.iSelectedImage = !tvi.iImage;
+ pData->show = tvi.iImage;
+ TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom,&tvi);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+ } } } } }
+ break;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if ( dat->dragging ) {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ ClientToScreen(hwndDlg, &hti.pt);
+ ScreenToClient(hwndProtoOrder, &hti.pt);
+ TreeView_HitTest(hwndProtoOrder, &hti);
+ if ( hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT ))
+ {
+ HTREEITEM it = hti.hItem;
+ hti.pt.y -= TreeView_GetItemHeight(hwndProtoOrder) / 2;
+ TreeView_HitTest(hwndProtoOrder, &hti);
+ if ( !( hti.flags & TVHT_ABOVE ))
+ TreeView_SetInsertMark(hwndProtoOrder, hti.hItem, 1);
+ else
+ TreeView_SetInsertMark(hwndProtoOrder, it, 0);
+ }
+ else {
+ if (hti.flags&TVHT_ABOVE) SendMessage(hwndProtoOrder, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
+ if (hti.flags&TVHT_BELOW) SendMessage(hwndProtoOrder, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
+ TreeView_SetInsertMark(hwndProtoOrder, NULL, 0);
+ } }
+ break;
+
+ case WM_LBUTTONUP:
+ if ( dat->dragging ) {
+ TVHITTESTINFO hti;
+ TVITEM tvi;
+
+ TreeView_SetInsertMark(hwndProtoOrder, NULL, 0);
+ dat->dragging = 0;
+ ReleaseCapture();
+
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam);
+ ClientToScreen(hwndDlg, &hti.pt);
+ ScreenToClient(hwndProtoOrder, &hti.pt);
+ hti.pt.y -= TreeView_GetItemHeight(hwndProtoOrder) / 2;
+ TreeView_HitTest(hwndProtoOrder, &hti);
+ if (dat->hDragItem == hti.hItem) break;
+ if (hti.flags & TVHT_ABOVE) hti.hItem = TVI_FIRST;
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = dat->hDragItem;
+ TreeView_GetItem(hwndProtoOrder, &tvi);
+ if ( hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT) || (hti.hItem == TVI_FIRST))
+ {
+ TVINSERTSTRUCT tvis;
+ TCHAR name[128];
+ ProtocolData * lpOldData;
+ tvis.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.item.stateMask = 0xFFFFFFFF;
+ tvis.item.pszText = name;
+ tvis.item.cchTextMax = SIZEOF(name);
+ tvis.item.hItem = dat->hDragItem;
+ tvis.item.iImage = tvis.item.iSelectedImage = ((ProtocolData *)tvi.lParam)->show;
+ TreeView_GetItem(hwndProtoOrder, &tvis.item);
+
+ //the pointed lParam will be freed inside TVN_DELETEITEM
+ //so lets substitute it with 0
+ lpOldData=(ProtocolData *)tvis.item.lParam;
+ tvis.item.lParam=0;
+ TreeView_SetItem(hwndProtoOrder, &tvis.item);
+ tvis.item.lParam=(LPARAM)lpOldData;
+
+ //now current item contain lParam=0 we can delete it. the memory will be kept.
+ TreeView_DeleteItem(hwndProtoOrder, dat->hDragItem);
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = hti.hItem;
+ TreeView_SelectItem(hwndProtoOrder, TreeView_InsertItem(hwndProtoOrder, &tvis));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+ } }
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/contacts/contacts.cpp b/src/modules/contacts/contacts.cpp
new file mode 100644
index 0000000000..f8bc787286
--- /dev/null
+++ b/src/modules/contacts/contacts.cpp
@@ -0,0 +1,513 @@
+/*
+Miranda IM
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define NAMEORDERCOUNT 8
+static TCHAR* nameOrderDescr[ NAMEORDERCOUNT ] =
+{
+ _T( "My custom name (not moveable)" ),
+ _T( "Nick" ),
+ _T( "FirstName" ),
+ _T( "E-mail" ),
+ _T( "LastName" ),
+ _T( "Username" ),
+ _T( "FirstName LastName" ),
+ _T( "'(Unknown Contact)' (not moveable)" )
+};
+
+BYTE nameOrder[NAMEORDERCOUNT];
+
+static int GetDatabaseString( CONTACTINFO *ci, const char* setting, DBVARIANT* dbv )
+{
+ if (strcmp(ci->szProto, "CList") && CallProtoService(ci->szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_INFOSETTINGSVC)
+ {
+ DBCONTACTGETSETTING cgs = { ci->szProto, setting, dbv };
+ dbv->type = (ci->dwFlag & CNF_UNICODE) ? DBVT_WCHAR : DBVT_ASCIIZ;
+
+ int res = CallProtoService(ci->szProto, PS_GETINFOSETTING, (WPARAM)ci->hContact, (LPARAM)&cgs);
+ if (res != CALLSERVICE_NOTFOUND) return res;
+ }
+
+ if ( ci->dwFlag & CNF_UNICODE )
+ return DBGetContactSettingWString(ci->hContact,ci->szProto,setting,dbv);
+
+ return DBGetContactSettingString(ci->hContact,ci->szProto,setting,dbv);
+}
+
+static int ProcessDatabaseValueDefault(CONTACTINFO *ci, const char* setting)
+{
+ DBVARIANT dbv;
+ if ( !GetDatabaseString( ci, setting, &dbv )) {
+ switch (dbv.type) {
+ case DBVT_ASCIIZ:
+ if (!dbv.pszVal[0]) break;
+ case DBVT_WCHAR:
+ if (!dbv.pwszVal[0]) break;
+ ci->type = CNFT_ASCIIZ;
+ ci->pszVal = dbv.ptszVal;
+ return 0;
+ }
+ DBFreeVariant( &dbv );
+ }
+
+ if ( DBGetContactSetting( ci->hContact, ci->szProto, setting, &dbv ))
+ return 1;
+
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ ci->type = CNFT_BYTE;
+ ci->bVal = dbv.bVal;
+ return 0;
+ case DBVT_WORD:
+ ci->type = CNFT_WORD;
+ ci->wVal = dbv.wVal;
+ return 0;
+ case DBVT_DWORD:
+ ci->type = CNFT_DWORD;
+ ci->dVal = dbv.dVal;
+ return 0;
+ }
+
+ DBFreeVariant( &dbv );
+ return 1;
+}
+
+static INT_PTR GetContactInfo(WPARAM, LPARAM lParam) {
+ DBVARIANT dbv;
+ CONTACTINFO *ci = (CONTACTINFO*)lParam;
+
+ if (ci==NULL) return 1;
+ if (ci->szProto==NULL) ci->szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEACCOUNT,(WPARAM)ci->hContact,0);
+ if (ci->szProto==NULL) return 1;
+ ci->type = 0;
+ switch(ci->dwFlag & 0x7F) {
+ case CNF_FIRSTNAME: return ProcessDatabaseValueDefault( ci, "FirstName" );
+ case CNF_LASTNAME: return ProcessDatabaseValueDefault( ci, "LastName" );
+ case CNF_NICK: return ProcessDatabaseValueDefault( ci, "Nick" );
+ case CNF_EMAIL: return ProcessDatabaseValueDefault( ci, "e-mail" );
+ case CNF_CITY: return ProcessDatabaseValueDefault( ci, "City" );
+ case CNF_STATE: return ProcessDatabaseValueDefault( ci, "State" );
+ case CNF_PHONE: return ProcessDatabaseValueDefault( ci, "Phone" );
+ case CNF_HOMEPAGE: return ProcessDatabaseValueDefault( ci, "Homepage" );
+ case CNF_ABOUT: return ProcessDatabaseValueDefault( ci, "About" );
+ case CNF_AGE: return ProcessDatabaseValueDefault( ci, "Age" );
+ case CNF_GENDER: return ProcessDatabaseValueDefault( ci, "Gender" );
+ case CNF_FAX: return ProcessDatabaseValueDefault( ci, "Fax" );
+ case CNF_CELLULAR: return ProcessDatabaseValueDefault( ci, "Cellular" );
+ case CNF_BIRTHDAY: return ProcessDatabaseValueDefault( ci, "BirthDay" );
+ case CNF_BIRTHMONTH: return ProcessDatabaseValueDefault( ci, "BirthMonth" );
+ case CNF_BIRTHYEAR: return ProcessDatabaseValueDefault( ci, "BirthYear" );
+ case CNF_STREET: return ProcessDatabaseValueDefault( ci, "Street" );
+ case CNF_ZIP: return ProcessDatabaseValueDefault( ci, "ZIP" );
+ case CNF_LANGUAGE1: return ProcessDatabaseValueDefault( ci, "Language1" );
+ case CNF_LANGUAGE2: return ProcessDatabaseValueDefault( ci, "Language2" );
+ case CNF_LANGUAGE3: return ProcessDatabaseValueDefault( ci, "Language3" );
+ case CNF_CONAME: return ProcessDatabaseValueDefault( ci, "Company" );
+ case CNF_CODEPT: return ProcessDatabaseValueDefault( ci, "CompanyDepartment" );
+ case CNF_COPOSITION: return ProcessDatabaseValueDefault( ci, "CompanyPosition" );
+ case CNF_COSTREET: return ProcessDatabaseValueDefault( ci, "CompanyStreet" );
+ case CNF_COCITY: return ProcessDatabaseValueDefault( ci, "CompanyCity" );
+ case CNF_COSTATE: return ProcessDatabaseValueDefault( ci, "CompanyState" );
+ case CNF_COZIP: return ProcessDatabaseValueDefault( ci, "CompanyZIP" );
+ case CNF_COHOMEPAGE: return ProcessDatabaseValueDefault( ci, "CompanyHomepage" );
+
+ case CNF_CUSTOMNICK:
+ {
+ char* saveProto = ci->szProto; ci->szProto = "CList";
+ if ( ci->hContact != NULL && !ProcessDatabaseValueDefault( ci, "MyHandle" )) {
+ ci->szProto = saveProto;
+ return 0;
+ }
+ ci->szProto = saveProto;
+ break;
+ }
+ case CNF_COUNTRY:
+ case CNF_COCOUNTRY:
+ if ( !GetDatabaseString( ci, (ci->dwFlag & 0x7F) == CNF_COUNTRY ? "CountryName" : "CompanyCountryName", &dbv ))
+ return 0;
+
+ if ( !DBGetContactSetting( ci->hContact, ci->szProto, (ci->dwFlag & 0x7F)==CNF_COUNTRY ? "Country" : "CompanyCountry", &dbv )) {
+ if ( dbv.type == DBVT_WORD ) {
+ int i,countryCount;
+ struct CountryListEntry *countries;
+ CallService(MS_UTILS_GETCOUNTRYLIST,(WPARAM)&countryCount,(LPARAM)&countries);
+ for(i=0;i<countryCount;i++) {
+ if(countries[i].id!=dbv.wVal) continue;
+
+ if ( ci->dwFlag & CNF_UNICODE ) {
+ int cbLen = MultiByteToWideChar( CP_ACP, 0, ( LPCSTR )countries[i].szName, -1, NULL, 0 );
+ WCHAR* buf = ( WCHAR* )mir_alloc( sizeof( WCHAR )*(cbLen+1) );
+ if ( buf != NULL )
+ MultiByteToWideChar( CP_ACP, 0, ( LPCSTR )countries[i].szName, -1, buf, cbLen );
+ ci->pszVal = ( TCHAR* )buf;
+ }
+ else ci->pszVal = ( TCHAR* )mir_strdup(countries[i].szName);
+
+ ci->type = CNFT_ASCIIZ;
+ DBFreeVariant(&dbv);
+ return 0;
+ }
+ }
+ else return ProcessDatabaseValueDefault( ci, (ci->dwFlag & 0x7F)==CNF_COUNTRY ? "Country" : "CompanyCountry" );
+ DBFreeVariant(&dbv);
+ }
+ break;
+
+ case CNF_FIRSTLAST:
+ if( !GetDatabaseString( ci, "FirstName", &dbv )) {
+ DBVARIANT dbv2;
+ if(!GetDatabaseString(ci,"LastName",&dbv2)) {
+ ci->type = CNFT_ASCIIZ;
+ if ( ci->dwFlag & CNF_UNICODE ) {
+ size_t len = wcslen(dbv.pwszVal) + wcslen(dbv2.pwszVal) + 2;
+ WCHAR* buf = ( WCHAR* )mir_alloc( sizeof( WCHAR )*len );
+ if ( buf != NULL )
+ wcscat( wcscat( wcscpy( buf, dbv.pwszVal ), L" " ), dbv2.pwszVal );
+ ci->pszVal = ( TCHAR* )buf;
+ }
+ else {
+ size_t len = strlen(dbv.pszVal) + strlen(dbv2.pszVal) + 2;
+ char* buf = ( char* )mir_alloc( len );
+ if ( buf != NULL )
+ strcat( strcat( strcpy( buf, dbv.pszVal ), " " ), dbv2.pszVal );
+ ci->pszVal = ( TCHAR* )buf;
+ }
+ DBFreeVariant( &dbv );
+ DBFreeVariant( &dbv2 );
+ return 0;
+ }
+ DBFreeVariant( &dbv );
+ }
+ break;
+
+ case CNF_UNIQUEID:
+ {
+ char *uid = (char*)CallProtoService(ci->szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
+ if ((INT_PTR)uid!=CALLSERVICE_NOTFOUND&&uid)
+ if (!ProcessDatabaseValueDefault(ci,uid))
+ return 0;
+
+ break;
+ }
+ case CNF_DISPLAYUID:
+ {
+ if (!ProcessDatabaseValueDefault(ci, "display_uid"))
+ return 0;
+ char *uid = (char*)CallProtoService(ci->szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
+ if ((INT_PTR)uid!=CALLSERVICE_NOTFOUND&&uid)
+ if (!ProcessDatabaseValueDefault(ci,uid))
+ return 0;
+
+ break;
+ }
+ case CNF_DISPLAYNC:
+ case CNF_DISPLAY:
+ {
+ int i;
+ for( i=0; i < NAMEORDERCOUNT; i++ ) {
+ switch(nameOrder[i]) {
+ case 0: // custom name
+ {
+ // make sure we aren't in CNF_DISPLAYNC mode
+ // don't get custom name for NULL contact
+ char* saveProto = ci->szProto; ci->szProto = "CList";
+ if (ci->hContact!=NULL && (ci->dwFlag&0x7F)==CNF_DISPLAY && !ProcessDatabaseValueDefault(ci,"MyHandle")) {
+ ci->szProto = saveProto;
+ return 0;
+ }
+ ci->szProto = saveProto;
+ break;
+ }
+ case 1:
+ if ( !ProcessDatabaseValueDefault( ci, "Nick" )) // nick
+ return 0;
+ break;
+ case 2:
+ if ( !ProcessDatabaseValueDefault( ci, "FirstName" )) // First Name
+ return 0;
+ break;
+ case 3:
+ if ( !ProcessDatabaseValueDefault( ci, "e-mail" )) // E-mail
+ return 0;
+ break;
+ case 4:
+ if ( !ProcessDatabaseValueDefault( ci, "LastName" )) // Last Name
+ return 0;
+ break;
+ case 5: // Unique id
+ {
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ char *uid = (char*)CallProtoService(ci->szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
+ if ((INT_PTR)uid!=CALLSERVICE_NOTFOUND&&uid) {
+ if (!GetDatabaseString(ci,uid,&dbv)) {
+ if ( dbv.type == DBVT_BYTE || dbv.type == DBVT_WORD || dbv.type == DBVT_DWORD ) {
+ long value = (dbv.type == DBVT_BYTE) ? dbv.bVal:(dbv.type==DBVT_WORD ? dbv.wVal : dbv.dVal);
+ if ( ci->dwFlag & CNF_UNICODE ) {
+ WCHAR buf[ 40 ];
+ _ltow( value, buf, 10 );
+ ci->pszVal = ( TCHAR* )mir_wstrdup( buf );
+ }
+ else {
+ char buf[ 40 ];
+ _ltoa( value, buf, 10 );
+ ci->pszVal = ( TCHAR* )mir_strdup(buf);
+ }
+ ci->type = CNFT_ASCIIZ;
+ return 0;
+ }
+ if (dbv.type == DBVT_ASCIIZ && !(ci->dwFlag & CNF_UNICODE)) {
+ ci->type = CNFT_ASCIIZ;
+ ci->pszVal = dbv.ptszVal;
+ return 0;
+ }
+ if (dbv.type == DBVT_WCHAR && (ci->dwFlag & CNF_UNICODE)) {
+ ci->type = CNFT_ASCIIZ;
+ ci->pszVal = dbv.ptszVal;
+ return 0;
+ } } }
+ break;
+ }
+ case 6: // first + last name
+ if(!GetDatabaseString(ci,"FirstName",&dbv)) {
+ DBVARIANT dbv2;
+ if(!GetDatabaseString(ci,"LastName",&dbv2)) {
+ ci->type = CNFT_ASCIIZ;
+
+ if ( ci->dwFlag & CNF_UNICODE ) {
+ size_t len = wcslen(dbv.pwszVal) + wcslen(dbv2.pwszVal) + 2;
+ WCHAR* buf = ( WCHAR* )mir_alloc( sizeof( WCHAR )*len );
+ if ( buf != NULL )
+ wcscat( wcscat( wcscpy( buf, dbv.pwszVal ), L" " ), dbv2.pwszVal );
+ ci->pszVal = ( TCHAR* )buf;
+ }
+ else {
+ size_t len = strlen(dbv.pszVal) + strlen(dbv2.pszVal) + 2;
+ char* buf = ( char* )mir_alloc( len );
+ if ( buf != NULL )
+ strcat( strcat( strcpy( buf, dbv.pszVal ), " " ), dbv2.pszVal );
+ ci->pszVal = ( TCHAR* )buf;
+ }
+
+ DBFreeVariant( &dbv );
+ DBFreeVariant( &dbv2 );
+ return 0;
+ }
+ DBFreeVariant( &dbv );
+ }
+ break;
+
+ case 7:
+ if ( ci->dwFlag & CNF_UNICODE )
+ ci->pszVal = ( TCHAR* )mir_wstrdup( TranslateW( L"'(Unknown Contact)'" ));
+ else
+ ci->pszVal = ( TCHAR* )mir_strdup( Translate("'(Unknown Contact)'"));
+ ci->type = CNFT_ASCIIZ;
+ return 0;
+ } } }
+ break;
+
+ case CNF_TIMEZONE: {
+ HANDLE hTz = tmi.createByContact(ci->hContact, TZF_KNOWNONLY);
+ if (hTz)
+ {
+ LPTIME_ZONE_INFORMATION tzi = tmi.getTzi(hTz);
+ int offset = tzi->Bias + tzi->StandardBias;
+
+ char str[80];
+ mir_snprintf(str, SIZEOF(str), offset ? "UTC%+d:%02d" : "UTC", offset / -60, abs(offset % 60));
+ ci->pszVal = ci->dwFlag & CNF_UNICODE ? (TCHAR*)mir_a2u(str) : (TCHAR*)mir_strdup(str);
+ ci->type = CNFT_ASCIIZ;
+ return 0;
+ }
+ break;
+ }
+ case CNF_MYNOTES: {
+ char* saveProto = ci->szProto; ci->szProto = "UserInfo";
+ if (!ProcessDatabaseValueDefault(ci,"MyNotes")) {
+ ci->szProto = saveProto;
+ return 0;
+ }
+ ci->szProto = saveProto;
+ break;
+ } }
+
+ return 1;
+}
+
+struct ContactOptionsData {
+ int dragging;
+ HTREEITEM hDragItem;
+};
+
+static INT_PTR CALLBACK ContactOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{ struct ContactOptionsData *dat;
+
+ dat=(struct ContactOptionsData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ { TranslateDialogDefault(hwndDlg);
+ dat=(struct ContactOptionsData*)mir_alloc(sizeof(struct ContactOptionsData));
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)dat);
+ dat->dragging=0;
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_NAMEORDER),GWL_STYLE,GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_NAMEORDER),GWL_STYLE)|TVS_NOHSCROLL);
+ { TVINSERTSTRUCT tvis;
+ int i;
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT|TVIF_PARAM;
+ for(i=0; i < SIZEOF(nameOrderDescr); i++ ) {
+ tvis.item.lParam = nameOrder[i];
+ tvis.item.pszText = TranslateTS( nameOrderDescr[ nameOrder[i]] );
+ TreeView_InsertItem( GetDlgItem(hwndDlg,IDC_NAMEORDER), &tvis );
+ } }
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY)
+ { DBCONTACTWRITESETTING cws;
+ TVITEM tvi;
+ int i;
+ cws.szModule = "Contact";
+ cws.szSetting = "NameOrder";
+ cws.value.type = DBVT_BLOB;
+ cws.value.cpbVal = SIZEOF(nameOrderDescr);
+ cws.value.pbVal = nameOrder;
+ tvi.hItem = TreeView_GetRoot(GetDlgItem(hwndDlg,IDC_NAMEORDER));
+ i=0;
+ while( tvi.hItem != NULL ) {
+ tvi.mask = TVIF_PARAM | TVIF_HANDLE;
+ TreeView_GetItem( GetDlgItem(hwndDlg,IDC_NAMEORDER), &tvi );
+ nameOrder[i++] = (BYTE)tvi.lParam;
+ tvi.hItem = TreeView_GetNextSibling(GetDlgItem(hwndDlg,IDC_NAMEORDER),tvi.hItem);
+ }
+ CallService(MS_DB_CONTACT_WRITESETTING,(WPARAM)(HANDLE)NULL,(LPARAM)&cws);
+ CallService(MS_CLIST_INVALIDATEDISPLAYNAME,(WPARAM)INVALID_HANDLE_VALUE,0);
+ }
+ break;
+ case IDC_NAMEORDER:
+ if (((LPNMHDR)lParam)->code == TVN_BEGINDRAGA) {
+ LPNMTREEVIEWA notify = (LPNMTREEVIEWA)lParam;
+ if ( notify->itemNew.lParam==0 || notify->itemNew.lParam == SIZEOF(nameOrderDescr)-1 )
+ break;
+ SetCapture(hwndDlg);
+ dat->dragging=1;
+ dat->hDragItem=((LPNMTREEVIEW)lParam)->itemNew.hItem;
+ TreeView_SelectItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),dat->hDragItem);
+ }
+ break;
+ }
+ break;
+ case WM_MOUSEMOVE:
+ if(!dat->dragging) break;
+ { TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ ClientToScreen(hwndDlg,&hti.pt);
+ ScreenToClient(GetDlgItem(hwndDlg,IDC_NAMEORDER),&hti.pt);
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_NAMEORDER),&hti);
+ if(hti.flags&(TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ hti.pt.y-=TreeView_GetItemHeight(GetDlgItem(hwndDlg,IDC_NAMEORDER))/2;
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_NAMEORDER),&hti);
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_NAMEORDER),hti.hItem,1);
+ }
+ else {
+ if(hti.flags&TVHT_ABOVE) SendDlgItemMessage(hwndDlg,IDC_NAMEORDER,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0);
+ if(hti.flags&TVHT_BELOW) SendDlgItemMessage(hwndDlg,IDC_NAMEORDER,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_NAMEORDER),NULL,0);
+ }
+ }
+ break;
+ case WM_LBUTTONUP:
+ if(!dat->dragging) break;
+ TreeView_SetInsertMark(GetDlgItem(hwndDlg,IDC_NAMEORDER),NULL,0);
+ dat->dragging=0;
+ ReleaseCapture();
+ { TVHITTESTINFO hti;
+ TVITEM tvi;
+ hti.pt.x=(short)LOWORD(lParam);
+ hti.pt.y=(short)HIWORD(lParam);
+ ClientToScreen(hwndDlg,&hti.pt);
+ ScreenToClient(GetDlgItem(hwndDlg,IDC_NAMEORDER),&hti.pt);
+ hti.pt.y-=TreeView_GetItemHeight(GetDlgItem(hwndDlg,IDC_NAMEORDER))/2;
+ TreeView_HitTest(GetDlgItem(hwndDlg,IDC_NAMEORDER),&hti);
+ if(dat->hDragItem==hti.hItem) break;
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem=hti.hItem;
+ TreeView_GetItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),&tvi);
+ if(tvi.lParam == SIZEOF(nameOrderDescr)-1) break;
+ if(hti.flags&(TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ TVINSERTSTRUCT tvis;
+ TCHAR name[128];
+ tvis.item.mask=TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_PARAM;
+ tvis.item.stateMask=0xFFFFFFFF;
+ tvis.item.pszText=name;
+ tvis.item.cchTextMax=SIZEOF(name);
+ tvis.item.hItem=dat->hDragItem;
+ TreeView_GetItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),&tvis.item);
+ TreeView_DeleteItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),dat->hDragItem);
+ tvis.hParent=NULL;
+ tvis.hInsertAfter=hti.hItem;
+ TreeView_SelectItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),TreeView_InsertItem(GetDlgItem(hwndDlg,IDC_NAMEORDER),&tvis));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+ case WM_DESTROY:
+ mir_free(dat);
+ break;
+ }
+ return FALSE;
+}
+
+static int ContactOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = -1000000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CONTACT);
+ odp.pszGroup = LPGEN("Customize");
+ odp.pszTitle = LPGEN("Contacts");
+ odp.pfnDlgProc = ContactOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
+int LoadContactsModule(void) {
+ {
+ // Load the name order
+ BYTE i;
+ DBVARIANT dbv;
+
+ for(i=0; i<NAMEORDERCOUNT; i++)
+ nameOrder[i]=i;
+
+ if(!DBGetContactSetting(NULL,"Contact","NameOrder",&dbv))
+ {
+ CopyMemory(nameOrder,dbv.pbVal,dbv.cpbVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ CreateServiceFunction(MS_CONTACT_GETCONTACTINFO,GetContactInfo);
+ HookEvent(ME_OPT_INITIALISE,ContactOptInit);
+ return 0;
+}
diff --git a/src/modules/database/database.cpp b/src/modules/database/database.cpp
new file mode 100644
index 0000000000..9c9aab8146
--- /dev/null
+++ b/src/modules/database/database.cpp
@@ -0,0 +1,563 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "profilemanager.h"
+#include "../srfile/file.h"
+
+// from the plugin loader, hate extern but the db frontend is pretty much tied
+extern PLUGINLINK pluginCoreLink;
+// contains the location of mirandaboot.ini
+extern TCHAR mirandabootini[MAX_PATH];
+bool dbCreated;
+TCHAR g_profileDir[MAX_PATH], g_profileName[MAX_PATH];
+
+bool fileExist(TCHAR* fname)
+{
+ if (fname[0] == 0) return false;
+
+ FILE* fp = _tfopen(fname, _T("r+"));
+ bool res = fp != NULL;
+ if (res) fclose(fp);
+ return res;
+}
+
+static void fillProfileName( const TCHAR* ptszFileName )
+{
+ const TCHAR* p = _tcsrchr( ptszFileName, '\\' );
+ if ( p == NULL )
+ p = ptszFileName;
+ else
+ p++;
+
+ _tcsncpy( g_profileName, p, SIZEOF(g_profileName));
+}
+
+bool IsInsideRootDir(TCHAR* profiledir, bool exact)
+{
+ int res;
+ TCHAR* pfd = Utils_ReplaceVarsT(_T("%miranda_path%"));
+ if (exact)
+ res = _tcsicmp(profiledir, pfd);
+ else
+ {
+ size_t len = _tcslen(pfd);
+ res = _tcsnicmp(profiledir, pfd, len);
+ }
+ mir_free(pfd);
+ return res == 0;
+}
+
+// returns 1 if the profile path was returned, without trailing slash
+int getProfilePath(TCHAR * buf, size_t cch)
+{
+ TCHAR profiledir[MAX_PATH];
+ GetPrivateProfileString(_T("Database"), _T("ProfileDir"), _T(""), profiledir, SIZEOF(profiledir), mirandabootini);
+
+ if (profiledir[0] == 0)
+ _tcscpy(profiledir, _T("%miranda_path%\\Profiles"));
+
+ TCHAR* exprofiledir = Utils_ReplaceVarsT(profiledir);
+ size_t len = pathToAbsoluteT(exprofiledir, buf, NULL);
+ mir_free(exprofiledir);
+
+ if (buf[len-1] == '/' || buf[len-1] == '\\')
+ buf[len-1] = 0;
+
+ return 0;
+}
+
+// returns 1 if *.dat spec is matched
+int isValidProfileName(const TCHAR *name)
+{
+ size_t len = _tcslen(name) - 4;
+ return len > 0 && _tcsicmp(&name[len], _T(".dat")) == 0;
+}
+
+// returns 1 if the profile manager should be shown
+static bool showProfileManager(void)
+{
+ TCHAR Mgr[32];
+ // is control pressed?
+ if (GetAsyncKeyState(VK_CONTROL)&0x8000)
+ return 1;
+
+ // wanna show it?
+ GetPrivateProfileString(_T("Database"), _T("ShowProfileMgr"), _T("never"), Mgr, SIZEOF(Mgr), mirandabootini);
+ return ( _tcsicmp(Mgr, _T("yes")) == 0 );
+}
+
+bool shouldAutoCreate(TCHAR *szProfile)
+{
+ if (szProfile[0] == 0)
+ return false;
+
+ TCHAR ac[32];
+ GetPrivateProfileString(_T("Database"), _T("AutoCreate"), _T(""), ac, SIZEOF(ac), mirandabootini);
+ return _tcsicmp(ac, _T("yes")) == 0;
+}
+
+static void getDefaultProfile(TCHAR * szProfile, size_t cch, TCHAR * profiledir)
+{
+ TCHAR defaultProfile[MAX_PATH];
+ GetPrivateProfileString(_T("Database"), _T("DefaultProfile"), _T(""), defaultProfile, SIZEOF(defaultProfile), mirandabootini);
+
+ if (defaultProfile[0] == 0)
+ return;
+
+ TCHAR* res = Utils_ReplaceVarsT(defaultProfile);
+ if (res) {
+ mir_sntprintf(szProfile, cch, _T("%s\\%s\\%s%s"), profiledir, res, res, isValidProfileName(res) ? _T("") : _T(".dat"));
+ mir_free(res);
+ }
+ else szProfile[0] = 0;
+}
+
+// returns 1 if something that looks like a profile is there
+static int getProfileCmdLineArgs(TCHAR * szProfile, size_t cch)
+{
+ TCHAR *szCmdLine = GetCommandLine();
+ TCHAR *szEndOfParam;
+ TCHAR szThisParam[1024];
+ int firstParam=1;
+
+ while(szCmdLine[0])
+ {
+ if(szCmdLine[0]=='"')
+ {
+ szEndOfParam = _tcschr(szCmdLine+1, '"');
+ if(szEndOfParam == NULL) break;
+ lstrcpyn(szThisParam, szCmdLine+1, min(SIZEOF(szThisParam), szEndOfParam - szCmdLine));
+ szCmdLine = szEndOfParam + 1;
+ }
+ else
+ {
+ szEndOfParam = szCmdLine + _tcscspn(szCmdLine, _T(" \t"));
+ lstrcpyn(szThisParam, szCmdLine, min(SIZEOF(szThisParam), szEndOfParam - szCmdLine+1));
+ szCmdLine = szEndOfParam;
+ }
+ while(*szCmdLine && *szCmdLine<=' ') szCmdLine++;
+ if (firstParam) { firstParam=0; continue; } //first param is executable name
+ if (szThisParam[0] == '/' || szThisParam[0] == '-') continue; //no switches supported
+
+ TCHAR* res = Utils_ReplaceVarsT(szThisParam);
+ if (res == NULL) return 0;
+ _tcsncpy(szProfile, res, cch); szProfile[cch-1] = 0;
+ mir_free(res);
+ return 1;
+ }
+ return 0;
+}
+
+void getProfileCmdLine(TCHAR * szProfile, size_t cch, TCHAR * profiledir)
+{
+ TCHAR buf[MAX_PATH];
+ if (getProfileCmdLineArgs(buf, SIZEOF(buf)))
+ {
+ TCHAR *p, profileName[MAX_PATH], newProfileDir[MAX_PATH];
+
+ p = _tcsrchr(buf, '\\'); if (p) ++p; else p = buf;
+
+ if (!isValidProfileName(buf) && *p)
+ _tcscat(buf, _T(".dat"));
+
+ _tcscpy(profileName, p);
+ p = _tcsrchr(profileName, '.'); if (p) *p = 0;
+
+ mir_sntprintf(newProfileDir, cch, _T("%s\\%s\\"), profiledir, profileName);
+ pathToAbsoluteT(buf, szProfile, newProfileDir);
+
+ if (_tcschr(buf, '\\'))
+ {
+ _tcscpy(profiledir, szProfile);
+ if (profileName[0])
+ {
+ p = _tcsrchr(profiledir, '\\'); *p = 0;
+ p = _tcsrchr(profiledir, '\\');
+ if (p && _tcsicmp(p + 1, profileName) == 0)
+ *p = 0;
+ }
+ else
+ szProfile[0] = 0;
+
+ }
+ }
+}
+
+// move profile from profile subdir
+static void moveProfileDirProfiles(TCHAR * profiledir, BOOL isRootDir = TRUE)
+{
+ TCHAR pfd[MAX_PATH];
+ if (isRootDir) {
+ TCHAR *path = Utils_ReplaceVarsT(_T("%miranda_path%\\*.dat"));
+ mir_sntprintf(pfd, SIZEOF(pfd), _T("%s"), path);
+ mir_free(path);
+ }
+ else
+ mir_sntprintf(pfd, SIZEOF(pfd), _T("%s\\*.dat"), profiledir);
+
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = FindFirstFile(pfd, &ffd);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ TCHAR *c =_tcsrchr(pfd, '\\'); if (c) *c = 0;
+ do
+ {
+ TCHAR path[MAX_PATH], path2[MAX_PATH];
+ TCHAR* profile = mir_tstrdup(ffd.cFileName);
+ TCHAR *c =_tcsrchr(profile, '.'); if (c) *c = 0;
+ mir_sntprintf(path, SIZEOF(path), _T("%s\\%s"), pfd, ffd.cFileName);
+ mir_sntprintf(path2, SIZEOF(path2), _T("%s\\%s"), profiledir, profile);
+ CreateDirectoryTreeT(path2);
+ mir_sntprintf(path2, SIZEOF(path2), _T("%s\\%s\\%s"), profiledir, profile, ffd.cFileName);
+ if (_taccess(path2, 0) == 0)
+ {
+ const TCHAR tszMoveMsg[] =
+ _T("Miranda is trying upgrade your profile structure.\n")
+ _T("It cannot move profile %s to the new location %s\n")
+ _T("Because profile with this name already exist. Please resolve the issue manually.");
+ TCHAR buf[512];
+
+ mir_sntprintf(buf, SIZEOF(buf), TranslateTS(tszMoveMsg), path, path2);
+ MessageBox(NULL, buf, _T("Miranda IM"), MB_ICONERROR | MB_OK);
+ }
+ else if (MoveFile(path, path2) == 0)
+ {
+ const TCHAR tszMoveMsg[] =
+ _T("Miranda is trying upgrade your profile structure.\n")
+ _T("It cannot move profile %s to the new location %s automatically\n")
+ _T("Most likely due to insufficient privileges. Please move profile manually.");
+ TCHAR buf[512];
+
+ mir_sntprintf(buf, SIZEOF(buf), TranslateTS(tszMoveMsg), path, path2);
+ MessageBox(NULL, buf, _T("Miranda IM"), MB_ICONERROR | MB_OK);
+ break;
+ }
+ mir_free(profile);
+ }
+ while(FindNextFile(hFind, &ffd));
+ }
+ FindClose(hFind);
+}
+
+// returns 1 if a single profile (full path) is found within the profile dir
+static int getProfile1(TCHAR * szProfile, size_t cch, TCHAR * profiledir, BOOL * noProfiles)
+{
+ unsigned int found = 0;
+
+ if (IsInsideRootDir(profiledir, false))
+ moveProfileDirProfiles(profiledir);
+ moveProfileDirProfiles(profiledir, FALSE);
+
+ bool nodprof = szProfile[0] == 0;
+ bool reqfd = !nodprof && (_taccess(szProfile, 0) == 0 || shouldAutoCreate(szProfile));
+ bool shpm = showProfileManager();
+
+ if (reqfd)
+ found++;
+
+ if (shpm || !reqfd) {
+ TCHAR searchspec[MAX_PATH];
+ mir_sntprintf(searchspec, SIZEOF(searchspec), _T("%s\\*.*"), profiledir);
+
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = FindFirstFile(searchspec, &ffd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ // make sure the first hit is actually a *.dat file
+ if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && _tcscmp(ffd.cFileName, _T(".")) && _tcscmp(ffd.cFileName, _T(".."))) {
+ TCHAR newProfile[MAX_PATH];
+ mir_sntprintf(newProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), profiledir, ffd.cFileName, ffd.cFileName);
+ if (_taccess(newProfile, 0) == 0)
+ if (++found == 1 && nodprof)
+ _tcscpy(szProfile, newProfile);
+ }
+ }
+ while (FindNextFile(hFind, &ffd));
+
+ FindClose(hFind);
+ }
+ reqfd = !shpm && found == 1 && nodprof;
+ }
+
+ if ( noProfiles )
+ *noProfiles = found == 0;
+
+ if ( nodprof && !reqfd )
+ szProfile[0] = 0;
+
+ return reqfd;
+}
+
+// returns 1 if a default profile should be selected instead of showing the manager.
+static int getProfileAutoRun(TCHAR * szProfile)
+{
+ TCHAR Mgr[32];
+ GetPrivateProfileString(_T("Database"), _T("ShowProfileMgr"), _T(""), Mgr, SIZEOF(Mgr), mirandabootini);
+ if (_tcsicmp(Mgr, _T("never")))
+ return 0;
+
+ return fileExist(szProfile) || shouldAutoCreate(szProfile);
+}
+
+// returns 1 if a profile was selected
+static int getProfile(TCHAR * szProfile, size_t cch)
+{
+ getProfilePath(g_profileDir, SIZEOF(g_profileDir));
+ if (IsInsideRootDir(g_profileDir, true))
+ {
+ if (WritePrivateProfileString(_T("Database"), _T("ProfileDir"), _T(""), mirandabootini))
+ getProfilePath(g_profileDir, SIZEOF(g_profileDir));
+ }
+
+ getDefaultProfile(szProfile, cch, g_profileDir);
+ getProfileCmdLine(szProfile, cch, g_profileDir);
+ if (IsInsideRootDir(g_profileDir, true))
+ {
+ MessageBox(NULL,
+ _T("Profile cannot be placed into Miranda root folder.\n")
+ _T("Please move Miranda profile to some other location."),
+ _T("Miranda IM"), MB_ICONERROR | MB_OK);
+ return 0;
+ }
+ if (getProfileAutoRun(szProfile))
+ return 1;
+
+ PROFILEMANAGERDATA pd = {0};
+ if (getProfile1(szProfile, cch, g_profileDir, &pd.noProfiles))
+ return 1;
+
+ pd.szProfile = szProfile;
+ pd.szProfileDir = g_profileDir;
+ return getProfileManager(&pd);
+}
+
+// carefully converts a file name from TCHAR* to char*
+char* makeFileName( const TCHAR* tszOriginalName )
+{
+ char* szResult = NULL;
+ char* szFileName = mir_t2a( tszOriginalName );
+ TCHAR* tszFileName = mir_a2t( szFileName );
+ if ( _tcscmp( tszOriginalName, tszFileName )) {
+ TCHAR tszProfile[MAX_PATH];
+ if ( GetShortPathName( tszOriginalName, tszProfile, MAX_PATH) != 0)
+ szResult = mir_t2a( tszProfile );
+ }
+
+ if ( !szResult )
+ szResult = szFileName;
+ else
+ mir_free(szFileName);
+ mir_free(tszFileName);
+
+ return szResult;
+}
+
+// called by the UI, return 1 on success, use link to create profile, set error if any
+int makeDatabase(TCHAR * profile, DATABASELINK * link, HWND hwndDlg)
+{
+ TCHAR buf[256];
+ int err=0;
+ // check if the file already exists
+ TCHAR * file = _tcsrchr(profile, '\\');
+ if (file) file++;
+ if (_taccess(profile, 0) == 0) {
+ // file already exists!
+ mir_sntprintf(buf, SIZEOF(buf), TranslateTS( _T("The profile '%s' already exists. Do you want to move it to the ")
+ _T("Recycle Bin? \n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\n")
+ _T("WARNING: A profile may contain confidential information and should be properly deleted.")), file );
+ if ( MessageBox(hwndDlg, buf, TranslateT("The profile already exists"), MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2) != IDYES )
+ return 0;
+
+ // move the file
+ SHFILEOPSTRUCT sf = {0};
+ sf.wFunc = FO_DELETE;
+ sf.pFrom = buf;
+ sf.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO;
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\0"), profile);
+ if ( SHFileOperation(&sf) != 0 ) {
+ mir_sntprintf(buf, SIZEOF(buf),TranslateT("Couldn't move '%s' to the Recycle Bin, Please select another profile name."),file);
+ MessageBox(0,buf,TranslateT("Problem moving profile"),MB_ICONINFORMATION|MB_OK);
+ return 0;
+ }
+ // now the file should be gone!
+ }
+ // ask the database to create the profile
+ CreatePathToFileT(profile);
+ char *prf = makeFileName(profile);
+ if (link->makeDatabase(prf, &err)) {
+ mir_sntprintf(buf, SIZEOF(buf),TranslateT("Unable to create the profile '%s', the error was %x"),file, err);
+ MessageBox(hwndDlg,buf,TranslateT("Problem creating profile"),MB_ICONERROR|MB_OK);
+ mir_free(prf);
+ return 0;
+ }
+ dbCreated = true;
+ // the profile has been created! woot
+ mir_free(prf);
+ return 1;
+}
+
+// enumerate all plugins that had valid DatabasePluginInfo()
+static int FindDbPluginForProfile(const char*, DATABASELINK * dblink, LPARAM lParam)
+{
+ TCHAR* tszProfile = ( TCHAR* )lParam;
+
+ int res = DBPE_CONT;
+ if ( dblink && dblink->cbSize == sizeof(DATABASELINK)) {
+ char* szProfile = makeFileName(tszProfile);
+ // liked the profile?
+ int err = 0;
+ if (dblink->grokHeader(szProfile, &err) == 0) {
+ // added APIs?
+ if ( !dblink->Load(szProfile, &pluginCoreLink)) {
+ fillProfileName( tszProfile );
+ res = DBPE_DONE;
+ }
+ else res = DBPE_HALT;
+ }
+ else {
+ res = DBPE_HALT;
+ switch ( err ) {
+ case EGROKPRF_CANTREAD:
+ case EGROKPRF_UNKHEADER:
+ // just not supported.
+ res = DBPE_CONT;
+
+ case EGROKPRF_VERNEWER:
+ case EGROKPRF_DAMAGED:
+ break;
+ }
+ } //if
+ mir_free(szProfile);
+ }
+ return res;
+}
+
+// enumerate all plugins that had valid DatabasePluginInfo()
+static int FindDbPluginAutoCreate(const char*, DATABASELINK * dblink, LPARAM lParam)
+{
+ TCHAR* tszProfile = ( TCHAR* )lParam;
+
+ int res = DBPE_CONT;
+ if (dblink && dblink->cbSize == sizeof(DATABASELINK)) {
+ CreatePathToFileT( tszProfile );
+
+ int err;
+ char *szProfile = makeFileName( tszProfile );
+ if (dblink->makeDatabase(szProfile, &err) == 0) {
+ dbCreated = true;
+ if ( !dblink->Load(szProfile, &pluginCoreLink)) {
+ fillProfileName( tszProfile );
+ res = DBPE_DONE;
+ }
+ else res = DBPE_HALT;
+ }
+ mir_free(szProfile);
+ }
+ return res;
+}
+
+typedef struct {
+ TCHAR * profile;
+ UINT msg;
+ ATOM aPath;
+ int found;
+} ENUMMIRANDAWINDOW;
+
+static BOOL CALLBACK EnumMirandaWindows(HWND hwnd, LPARAM lParam)
+{
+ TCHAR classname[256];
+ ENUMMIRANDAWINDOW * x = (ENUMMIRANDAWINDOW *)lParam;
+ DWORD_PTR res=0;
+ if ( GetClassName(hwnd,classname,SIZEOF(classname)) && lstrcmp( _T("Miranda"),classname)==0 ) {
+ if ( SendMessageTimeout(hwnd, x->msg, (WPARAM)x->aPath, 0, SMTO_ABORTIFHUNG, 100, &res) && res ) {
+ x->found++;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int FindMirandaForProfile(TCHAR * szProfile)
+{
+ ENUMMIRANDAWINDOW x={0};
+ x.profile=szProfile;
+ x.msg=RegisterWindowMessage( _T( "Miranda::ProcessProfile" ));
+ x.aPath=GlobalAddAtom(szProfile);
+ EnumWindows(EnumMirandaWindows, (LPARAM)&x);
+ GlobalDeleteAtom(x.aPath);
+ return x.found;
+}
+
+int LoadDatabaseModule(void)
+{
+ TCHAR szProfile[MAX_PATH];
+ pathToAbsoluteT(_T("."), szProfile, NULL);
+ _tchdir(szProfile);
+ szProfile[0] = 0;
+
+ // load the older basic services of the db
+ InitUtils();
+
+ // find out which profile to load
+ if ( !getProfile( szProfile, SIZEOF( szProfile )))
+ return 1;
+
+ PLUGIN_DB_ENUM dbe;
+ dbe.cbSize = sizeof(PLUGIN_DB_ENUM);
+ dbe.lParam = (LPARAM)szProfile;
+
+ if ( _taccess(szProfile, 0) && shouldAutoCreate( szProfile ))
+ dbe.pfnEnumCallback=( int(*) (const char*,void*,LPARAM) )FindDbPluginAutoCreate;
+ else
+ dbe.pfnEnumCallback=( int(*) (const char*,void*,LPARAM) )FindDbPluginForProfile;
+
+ // find a driver to support the given profile
+ int rc = CallService(MS_PLUGINS_ENUMDBPLUGINS, 0, (LPARAM)&dbe);
+ switch ( rc ) {
+ case -1: {
+ // no plugins at all
+ TCHAR buf[256];
+ TCHAR* p = _tcsrchr(szProfile,'\\');
+ mir_sntprintf(buf,SIZEOF(buf),TranslateT("Miranda is unable to open '%s' because you do not have any profile plugins installed.\nYou need to install dbx_3x.dll or equivalent."), p ? ++p : szProfile );
+ MessageBox(0,buf,TranslateT("No profile support installed!"),MB_OK | MB_ICONERROR);
+ break;
+ }
+ case 1:
+ // if there were drivers but they all failed cos the file is locked, try and find the miranda which locked it
+ if (fileExist(szProfile)) {
+ // file isn't locked, just no driver could open it.
+ TCHAR buf[256];
+ TCHAR* p = _tcsrchr(szProfile,'\\');
+ mir_sntprintf(buf,SIZEOF(buf),TranslateT("Miranda was unable to open '%s', it's in an unknown format.\nThis profile might also be damaged, please run DB-tool which should be installed."), p ? ++p : szProfile);
+ MessageBox(0,buf,TranslateT("Miranda can't understand that profile"),MB_OK | MB_ICONERROR);
+ }
+ else if (!FindMirandaForProfile(szProfile)) {
+ TCHAR buf[256];
+ TCHAR* p = _tcsrchr(szProfile,'\\');
+ mir_sntprintf(buf,SIZEOF(buf),TranslateT("Miranda was unable to open '%s'\nIt's inaccessible or used by other application or Miranda instance"), p ? ++p : szProfile);
+ MessageBox(0,buf,TranslateT("Miranda can't open that profile"),MB_OK | MB_ICONERROR);
+ }
+ break;
+ }
+ return (rc != 0);
+}
diff --git a/src/modules/database/dbini.cpp b/src/modules/database/dbini.cpp
new file mode 100644
index 0000000000..01e6645593
--- /dev/null
+++ b/src/modules/database/dbini.cpp
@@ -0,0 +1,490 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "../srfile/file.h"
+
+static bool bModuleInitialized = false;
+static HANDLE hIniChangeNotification;
+
+extern TCHAR mirandabootini[MAX_PATH];
+extern bool dbCreated;
+
+static INT_PTR CALLBACK InstallIniDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemText(hwndDlg, IDC_ININAME, (TCHAR*)lParam);
+ {
+ TCHAR szSecurity[11];
+ const TCHAR *pszSecurityInfo;
+
+ GetPrivateProfileString(_T("AutoExec"), _T("Warn"), _T("notsafe"), szSecurity, SIZEOF(szSecurity), mirandabootini);
+ if(!lstrcmpi(szSecurity, _T("all")))
+ pszSecurityInfo = LPGENT("Security systems to prevent malicious changes are in place and you will be warned before every change that is made.");
+ else if (!lstrcmpi(szSecurity, _T("onlyunsafe")))
+ pszSecurityInfo = LPGENT("Security systems to prevent malicious changes are in place and you will be warned before changes that are known to be unsafe.");
+ else if (!lstrcmpi(szSecurity, _T("none")))
+ pszSecurityInfo = LPGENT("Security systems to prevent malicious changes have been disabled. You will receive no further warnings.");
+ else pszSecurityInfo = NULL;
+ if (pszSecurityInfo) SetDlgItemText(hwndDlg, IDC_SECURITYINFO, TranslateTS(pszSecurityInfo));
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_VIEWINI:
+ { TCHAR szPath[MAX_PATH];
+ GetDlgItemText(hwndDlg, IDC_ININAME, szPath, SIZEOF(szPath));
+ ShellExecute(hwndDlg, _T("open"), szPath, NULL, NULL, SW_SHOW);
+ break;
+ }
+ case IDOK:
+ case IDCANCEL:
+ case IDC_NOTOALL:
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static bool IsInSpaceSeparatedList(const char *szWord,const char *szList)
+{
+ const char *szItem,*szEnd;
+ int wordLen = lstrlenA(szWord);
+
+ for(szItem = szList;;) {
+ szEnd = strchr(szItem,' ');
+ if (szEnd == NULL)
+ return !lstrcmpA( szItem, szWord );
+ if ( szEnd - szItem == wordLen ) {
+ if ( !strncmp( szItem, szWord, wordLen ))
+ return true;
+ }
+ szItem = szEnd+1;
+} }
+
+struct warnSettingChangeInfo_t {
+ TCHAR *szIniPath;
+ char *szSection;
+ char *szSafeSections;
+ char *szUnsafeSections;
+ char *szName;
+ char *szValue;
+ int warnNoMore,cancel;
+};
+
+static INT_PTR CALLBACK WarnIniChangeDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ static struct warnSettingChangeInfo_t *warnInfo;
+
+ switch(message) {
+ case WM_INITDIALOG:
+ { char szSettingName[256];
+ const TCHAR *pszSecurityInfo;
+ warnInfo = (warnSettingChangeInfo_t*)lParam;
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemText(hwndDlg, IDC_ININAME, warnInfo->szIniPath);
+ lstrcpyA(szSettingName, warnInfo->szSection);
+ lstrcatA(szSettingName," / ");
+ lstrcatA(szSettingName,warnInfo->szName);
+ SetDlgItemTextA(hwndDlg,IDC_SETTINGNAME,szSettingName);
+ SetDlgItemTextA(hwndDlg,IDC_NEWVALUE,warnInfo->szValue);
+ if(IsInSpaceSeparatedList(warnInfo->szSection,warnInfo->szSafeSections))
+ pszSecurityInfo=LPGENT("This change is known to be safe.");
+ else if(IsInSpaceSeparatedList(warnInfo->szSection,warnInfo->szUnsafeSections))
+ pszSecurityInfo=LPGENT("This change is known to be potentially hazardous.");
+ else
+ pszSecurityInfo=LPGENT("This change is not known to be safe.");
+ SetDlgItemText(hwndDlg,IDC_SECURITYINFO,TranslateTS(pszSecurityInfo));
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ warnInfo->cancel=1;
+ case IDYES:
+ case IDNO:
+ warnInfo->warnNoMore=IsDlgButtonChecked(hwndDlg,IDC_WARNNOMORE);
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK IniImportDoneDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemText(hwndDlg,IDC_ININAME,(TCHAR*)lParam);
+ SetDlgItemText(hwndDlg,IDC_NEWNAME,(TCHAR*)lParam);
+ return TRUE;
+ case WM_COMMAND:
+ { TCHAR szIniPath[MAX_PATH];
+ GetDlgItemText(hwndDlg,IDC_ININAME,szIniPath,SIZEOF(szIniPath));
+ switch(LOWORD(wParam)) {
+ case IDC_DELETE:
+ DeleteFile(szIniPath);
+ case IDC_LEAVE:
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ case IDC_RECYCLE:
+ { SHFILEOPSTRUCT shfo={0};
+ shfo.wFunc=FO_DELETE;
+ shfo.pFrom=szIniPath;
+ szIniPath[lstrlen(szIniPath)+1]='\0';
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO;
+ SHFileOperation(&shfo);
+ }
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ case IDC_MOVE:
+ { TCHAR szNewPath[MAX_PATH];
+ GetDlgItemText(hwndDlg,IDC_NEWNAME,szNewPath,SIZEOF(szNewPath));
+ MoveFile(szIniPath,szNewPath);
+ }
+ EndDialog(hwndDlg,LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+// settings:
+struct SettingsList
+{
+ char *name;
+ SettingsList *next;
+} *setting_items = NULL;
+
+int SettingsEnumProc(const char *szSetting, LPARAM lParam)
+{
+ SettingsList *newItem = (SettingsList *)mir_alloc(sizeof(SettingsList));
+ newItem->name = mir_strdup(szSetting);
+ newItem->next = setting_items;
+ setting_items = newItem;
+ return 0;
+}
+
+void ConvertBackslashes(char *, UINT);
+static void ProcessIniFile(TCHAR* szIniPath, char *szSafeSections, char *szUnsafeSections, int secur, bool secFN)
+{
+ FILE *fp = _tfopen(szIniPath, _T("rt"));
+ if ( fp == NULL )
+ return;
+
+ bool warnThisSection = false;
+ char szSection[128]; szSection[0] = 0;
+
+ while(!feof(fp)) {
+ char szLine[2048];
+ if (fgets(szLine,sizeof(szLine),fp) == NULL)
+ break;
+
+ int lineLength = lstrlenA(szLine);
+ while (lineLength && (BYTE)(szLine[lineLength-1])<=' ')
+ szLine[--lineLength]='\0';
+
+ if (szLine[0]==';' || szLine[0]<=' ')
+ continue;
+
+ if (szLine[0]=='[') {
+ char *szEnd = strchr(szLine+1,']');
+ if (szEnd == NULL)
+ continue;
+
+ if (szLine[1] == '!')
+ szSection[0] = '\0';
+ else {
+ lstrcpynA(szSection,szLine+1,min(sizeof(szSection),(int)(szEnd-szLine)));
+ switch (secur) {
+ case 0:
+ warnThisSection = false;
+ break;
+
+ case 1:
+ warnThisSection = !IsInSpaceSeparatedList(szSection, szSafeSections);
+ break;
+
+ case 2:
+ warnThisSection = IsInSpaceSeparatedList(szSection, szUnsafeSections);
+ break;
+
+ default:
+ warnThisSection = true;
+ break;
+ }
+ if (secFN) warnThisSection=0;
+ }
+ if (szLine[1] == '?') {
+ DBCONTACTENUMSETTINGS dbces;
+ dbces.pfnEnumProc=SettingsEnumProc;
+ lstrcpynA(szSection,szLine+2,min(sizeof(szSection),(int)(szEnd-szLine-1)));
+ dbces.szModule=szSection;
+ dbces.ofsSettings=0;
+ CallService(MS_DB_CONTACT_ENUMSETTINGS,0,(LPARAM)&dbces);
+ while (setting_items) {
+ SettingsList *next = setting_items->next;
+
+ DBCONTACTGETSETTING dbcgs;
+ dbcgs.szModule = szSection;
+ dbcgs.szSetting = setting_items->name;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, (LPARAM)&dbcgs);
+
+ mir_free(setting_items->name);
+ mir_free(setting_items);
+ setting_items = next;
+ }
+ }
+ continue;
+ }
+
+ if(szSection[0]=='\0')
+ continue;
+
+ char *szValue=strchr(szLine,'=');
+ if ( szValue == NULL )
+ continue;
+
+ char szName[128];
+ lstrcpynA(szName,szLine,min(sizeof(szName),(int)(szValue-szLine+1)));
+ szValue++;
+ {
+ warnSettingChangeInfo_t warnInfo;
+ warnInfo.szIniPath=szIniPath;
+ warnInfo.szName=szName;
+ warnInfo.szSafeSections=szSafeSections;
+ warnInfo.szSection=szSection;
+ warnInfo.szUnsafeSections=szUnsafeSections;
+ warnInfo.szValue=szValue;
+ warnInfo.warnNoMore=0;
+ warnInfo.cancel=0;
+ if(warnThisSection && IDNO==DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(IDD_WARNINICHANGE),NULL,WarnIniChangeDlgProc,(LPARAM)&warnInfo))
+ continue;
+ if(warnInfo.cancel)
+ break;
+ if(warnInfo.warnNoMore)
+ warnThisSection=0;
+ }
+
+ switch(szValue[0]) {
+ case 'b':
+ case 'B':
+ DBWriteContactSettingByte(NULL,szSection,szName,(BYTE)strtol(szValue+1,NULL,0));
+ break;
+ case 'w':
+ case 'W':
+ DBWriteContactSettingWord(NULL,szSection,szName,(WORD)strtol(szValue+1,NULL,0));
+ break;
+ case 'd':
+ case 'D':
+ DBWriteContactSettingDword(NULL,szSection,szName,(DWORD)strtoul(szValue+1,NULL,0));
+ break;
+ case 'l':
+ case 'L':
+ DBDeleteContactSetting(NULL,szSection,szName);
+ break;
+ case 'e':
+ case 'E':
+ ConvertBackslashes(szValue+1, LangPackGetDefaultCodePage());
+ case 's':
+ case 'S':
+ DBWriteContactSettingString(NULL,szSection,szName,szValue+1);
+ break;
+ case 'g':
+ case 'G':
+ { char *pstr;
+ for(pstr=szValue+1;*pstr;pstr++){
+ if(*pstr=='\\'){
+ switch(pstr[1]){
+ case 'n': *pstr='\n'; break;
+ case 't': *pstr='\t'; break;
+ case 'r': *pstr='\r'; break;
+ default: *pstr=pstr[1]; break;
+ }
+ MoveMemory(pstr+1,pstr+2,lstrlenA(pstr+2)+1);
+ } } }
+ case 'u':
+ case 'U':
+ DBWriteContactSettingStringUtf(NULL,szSection,szName,szValue+1);
+ break;
+ case 'n':
+ case 'h':
+ case 'N':
+ case 'H':
+ { PBYTE buf;
+ int len;
+ char *pszValue,*pszEnd;
+ DBCONTACTWRITESETTING cws;
+
+ buf=(PBYTE)mir_alloc(lstrlenA(szValue+1));
+ for(len=0,pszValue=szValue+1;;len++) {
+ buf[len]=(BYTE)strtol(pszValue,&pszEnd,0x10);
+ if(pszValue==pszEnd) break;
+ pszValue=pszEnd;
+ }
+ cws.szModule=szSection;
+ cws.szSetting=szName;
+ cws.value.type=DBVT_BLOB;
+ cws.value.pbVal=buf;
+ cws.value.cpbVal=len;
+ CallService(MS_DB_CONTACT_WRITESETTING,(WPARAM)(HANDLE)NULL,(LPARAM)&cws);
+ mir_free(buf);
+ }
+ break;
+ default:
+ MessageBox(NULL,TranslateT("Invalid setting type. The first character of every value must be b, w, d, l, s, e, u, g, h or n."),TranslateT("Install Database Settings"),MB_OK);
+ break;
+ }
+ }
+ fclose(fp);
+}
+
+static void DoAutoExec(void)
+{
+ TCHAR szUse[7], szIniPath[MAX_PATH], szFindPath[MAX_PATH];
+ TCHAR *str2;
+ TCHAR buf[2048], szSecurity[11], szOverrideSecurityFilename[MAX_PATH], szOnCreateFilename[MAX_PATH];
+ char *szSafeSections, *szUnsafeSections;
+ int secur;
+
+ GetPrivateProfileString(_T("AutoExec"),_T("Use"),_T("prompt"),szUse,SIZEOF(szUse),mirandabootini);
+ if(!lstrcmpi(szUse,_T("no"))) return;
+ GetPrivateProfileString(_T("AutoExec"),_T("Safe"),_T("CLC Icons CLUI CList SkinSounds"),buf,SIZEOF(buf),mirandabootini);
+ szSafeSections = mir_t2a(buf);
+ GetPrivateProfileString(_T("AutoExec"),_T("Unsafe"),_T("ICQ MSN"),buf,SIZEOF(buf),mirandabootini);
+ szUnsafeSections = mir_t2a(buf);
+ GetPrivateProfileString(_T("AutoExec"),_T("Warn"),_T("notsafe"),szSecurity,SIZEOF(szSecurity),mirandabootini);
+ if (!lstrcmpi(szSecurity,_T("none"))) secur = 0;
+ else if (!lstrcmpi(szSecurity,_T("notsafe"))) secur = 1;
+ else if (!lstrcmpi(szSecurity,_T("onlyunsafe"))) secur = 2;
+
+ GetPrivateProfileString(_T("AutoExec"),_T("OverrideSecurityFilename"),_T(""),szOverrideSecurityFilename,SIZEOF(szOverrideSecurityFilename),mirandabootini);
+ GetPrivateProfileString(_T("AutoExec"),_T("OnCreateFilename"),_T(""),szOnCreateFilename,SIZEOF(szOnCreateFilename),mirandabootini);
+ GetPrivateProfileString(_T("AutoExec"),_T("Glob"),_T("autoexec_*.ini"),szFindPath,SIZEOF(szFindPath),mirandabootini);
+
+ if (dbCreated && szOnCreateFilename[0]) {
+ str2 = Utils_ReplaceVarsT(szOnCreateFilename);
+ pathToAbsoluteT(str2, szIniPath, NULL);
+ mir_free(str2);
+
+ ProcessIniFile(szIniPath, szSafeSections, szUnsafeSections, 0, 1);
+ }
+
+ str2 = Utils_ReplaceVarsT(szFindPath);
+ pathToAbsoluteT(str2, szFindPath, NULL);
+ mir_free(str2);
+
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(szFindPath, &fd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ mir_free(szSafeSections);
+ mir_free(szUnsafeSections);
+ return;
+ }
+
+ str2 = _tcsrchr(szFindPath, '\\');
+ if (str2 == NULL) szFindPath[0] = 0;
+ else str2[1] = 0;
+
+ do {
+ bool secFN = lstrcmpi(fd.cFileName,szOverrideSecurityFilename) == 0;
+
+ mir_sntprintf(szIniPath, SIZEOF(szIniPath), _T("%s%s"), szFindPath, fd.cFileName);
+ if(!lstrcmpi(szUse,_T("prompt")) && !secFN) {
+ int result=DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(IDD_INSTALLINI),NULL,InstallIniDlgProc,(LPARAM)szIniPath);
+ if(result==IDC_NOTOALL) break;
+ if(result==IDCANCEL) continue;
+ }
+
+ ProcessIniFile(szIniPath, szSafeSections, szUnsafeSections, secur, secFN);
+
+ if(secFN)
+ DeleteFile(szIniPath);
+ else {
+ TCHAR szOnCompletion[8];
+ GetPrivateProfileString(_T("AutoExec"),_T("OnCompletion"),_T("recycle"),szOnCompletion,SIZEOF(szOnCompletion),mirandabootini);
+ if(!lstrcmpi(szOnCompletion,_T("delete")))
+ DeleteFile(szIniPath);
+ else if(!lstrcmpi(szOnCompletion,_T("recycle"))) {
+ SHFILEOPSTRUCT shfo={0};
+ shfo.wFunc=FO_DELETE;
+ shfo.pFrom=szIniPath;
+ szIniPath[lstrlen(szIniPath)+1]=0;
+ shfo.fFlags=FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO;
+ SHFileOperation(&shfo);
+ }
+ else if(!lstrcmpi(szOnCompletion,_T("rename"))) {
+ TCHAR szRenamePrefix[MAX_PATH];
+ TCHAR szNewPath[MAX_PATH];
+ GetPrivateProfileString(_T("AutoExec"),_T("RenamePrefix"),_T("done_"),szRenamePrefix,SIZEOF(szRenamePrefix),mirandabootini);
+ lstrcpy(szNewPath,szFindPath);
+ lstrcat(szNewPath,szRenamePrefix);
+ lstrcat(szNewPath,fd.cFileName);
+ MoveFile(szIniPath,szNewPath);
+ }
+ else if(!lstrcmpi(szOnCompletion,_T("ask")))
+ DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(IDD_INIIMPORTDONE),NULL,IniImportDoneDlgProc,(LPARAM)szIniPath);
+ }
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ mir_free(szSafeSections);
+ mir_free(szUnsafeSections);
+}
+
+static INT_PTR CheckIniImportNow(WPARAM, LPARAM)
+{
+ DoAutoExec();
+ FindNextChangeNotification(hIniChangeNotification);
+ return 0;
+}
+
+int InitIni(void)
+{
+ TCHAR szMirandaDir[MAX_PATH];
+
+ bModuleInitialized = true;
+
+ DoAutoExec();
+ pathToAbsoluteT(_T("."), szMirandaDir, NULL);
+ hIniChangeNotification=FindFirstChangeNotification(szMirandaDir, 0, FILE_NOTIFY_CHANGE_FILE_NAME);
+ if (hIniChangeNotification != INVALID_HANDLE_VALUE) {
+ CreateServiceFunction("DB/Ini/CheckImportNow", CheckIniImportNow);
+ CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hIniChangeNotification, (LPARAM)"DB/Ini/CheckImportNow");
+ }
+ return 0;
+}
+
+void UninitIni(void)
+{
+ if ( !bModuleInitialized ) return;
+ CallService(MS_SYSTEM_REMOVEWAIT,(WPARAM)hIniChangeNotification,0);
+ FindCloseChangeNotification(hIniChangeNotification);
+}
diff --git a/src/modules/database/dblists.cpp b/src/modules/database/dblists.cpp
new file mode 100644
index 0000000000..02332ab283
--- /dev/null
+++ b/src/modules/database/dblists.cpp
@@ -0,0 +1,282 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+/* a simple sorted list implementation */
+
+SortedList* List_Create( int p_limit, int p_increment )
+{
+ SortedList* result = ( SortedList* )mir_calloc( sizeof( SortedList ));
+ if ( result == NULL )
+ return(NULL);
+
+ result->increment = p_increment;
+ result->limit = p_limit;
+ return(result);
+}
+
+void List_Destroy( SortedList* p_list )
+{
+ if ( p_list == NULL )
+ return;
+
+ if ( p_list->items != NULL ) {
+ mir_free( p_list->items );
+ p_list->items = NULL;
+ }
+
+ p_list->realCount = p_list->limit = 0;
+}
+
+void* List_Find( SortedList* p_list, void* p_value )
+{
+ int index;
+
+ if ( !List_GetIndex( p_list, p_value, &index ))
+ return(NULL);
+
+ return(p_list->items[ index ]);
+}
+
+#ifdef _DEBUG
+#pragma optimize( "gt", on )
+#endif
+
+int List_GetIndex( SortedList* p_list, void* p_value, int* p_index )
+{
+ if (p_value == NULL)
+ {
+ *p_index = -1;
+ return 0;
+ }
+
+ switch ((INT_PTR)p_list->sortFunc)
+ {
+ case 0:
+ break;
+
+ case HandleKeySort:
+#ifdef _WIN64
+ {
+ const unsigned __int64 val = *(unsigned __int64 *)p_value;
+ int low = 0;
+ int high = p_list->realCount - 1;
+
+ while (low <= high)
+ {
+ int i = (low + high) / 2;
+ unsigned __int64 vali = *(unsigned __int64 *)p_list->items[i];
+ if (vali == val)
+ {
+ *p_index = i;
+ return 1;
+ }
+
+ if (vali < val)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+
+ *p_index = low;
+ }
+ break;
+#endif
+
+ case NumericKeySort:
+ {
+ const unsigned val = *(unsigned *)p_value;
+ int low = 0;
+ int high = p_list->realCount - 1;
+
+ while (low <= high)
+ {
+ int i = (low + high) / 2;
+ unsigned vali = *(unsigned *)p_list->items[i];
+ if (vali == val)
+ {
+ *p_index = i;
+ return 1;
+ }
+
+ if (vali < val)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+
+ *p_index = low;
+ }
+ break;
+
+ case PtrKeySort:
+ {
+ int low = 0;
+ int high = p_list->realCount - 1;
+
+ while (low <= high)
+ {
+ int i = (low + high) / 2;
+ const void* vali = p_list->items[i];
+ if (vali == p_value)
+ {
+ *p_index = i;
+ return 1;
+ }
+
+ if (vali < p_value)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+
+ *p_index = low;
+ }
+ break;
+
+ default:
+ {
+ int low = 0;
+ int high = p_list->realCount - 1;
+
+ while (low <= high)
+ {
+ int i = (low + high) / 2;
+ int result = p_list->sortFunc(p_list->items[i], p_value);
+ if (result == 0)
+ {
+ *p_index = i;
+ return 1;
+ }
+
+ if (result < 0)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+
+ *p_index = low;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int List_IndexOf( SortedList* p_list, void* p_value )
+{
+ if ( p_value == NULL )
+ return -1;
+
+ int i;
+ for ( i=0; i < p_list->realCount; i++ )
+ if ( p_list->items[i] == p_value )
+ return i;
+
+ return -1;
+}
+
+#ifdef _DEBUG
+#pragma optimize( "", on )
+#endif
+
+int List_Insert( SortedList* p_list, void* p_value, int p_index)
+{
+ if ( p_value == NULL || p_index > p_list->realCount )
+ return 0;
+
+ if ( p_list->realCount == p_list->limit )
+ {
+ p_list->items = ( void** )mir_realloc( p_list->items, sizeof( void* )*(p_list->realCount + p_list->increment));
+ p_list->limit += p_list->increment;
+ }
+
+ if ( p_index < p_list->realCount )
+ memmove( p_list->items+p_index+1, p_list->items+p_index, sizeof( void* )*( p_list->realCount-p_index ));
+
+ p_list->realCount++;
+
+ p_list->items[ p_index ] = p_value;
+ return 1;
+}
+
+int List_InsertPtr( SortedList* list, void* p )
+{
+ if ( p == NULL )
+ return -1;
+
+ int idx = list->realCount;
+ List_GetIndex( list, p, &idx );
+ return List_Insert( list, p, idx );
+}
+
+int List_Remove( SortedList* p_list, int index )
+{
+ if ( index < 0 || index > p_list->realCount )
+ return(0);
+
+ p_list->realCount--;
+ if ( p_list->realCount > index )
+ {
+ memmove( p_list->items+index, p_list->items+index+1, sizeof( void* )*( p_list->realCount-index ));
+ p_list->items[ p_list->realCount ] = NULL;
+ }
+
+ return 1;
+}
+
+int List_RemovePtr( SortedList* list, void* p )
+{
+ int idx = -1;
+ if ( List_GetIndex( list, p, &idx ))
+ List_Remove( list, idx );
+
+ return idx;
+}
+
+void List_Copy( SortedList* s, SortedList* d, size_t itemSize )
+{
+ int i;
+
+ d->increment = s->increment;
+ d->sortFunc = s->sortFunc;
+
+ for ( i = 0; i < s->realCount; i++ ) {
+ void* item = mir_alloc( itemSize );
+ memcpy( item, s->items[i], itemSize );
+ List_Insert( d, item, i );
+} }
+
+void List_ObjCopy( SortedList* s, SortedList* d, size_t itemSize )
+{
+ int i;
+
+ d->increment = s->increment;
+ d->sortFunc = s->sortFunc;
+
+ for ( i = 0; i < s->realCount; i++ ) {
+ void* item = new char[ itemSize ];
+ memcpy( item, s->items[i], itemSize );
+ List_Insert( d, item, i );
+} }
diff --git a/src/modules/database/dblists.h b/src/modules/database/dblists.h
new file mode 100644
index 0000000000..30886973a4
--- /dev/null
+++ b/src/modules/database/dblists.h
@@ -0,0 +1,39 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* a simple sorted list implementation */
+
+SortedList* List_Create( int, int );
+void List_Destroy( SortedList* );
+
+void* List_Find( SortedList*, void* );
+int List_GetIndex( SortedList*, void*, int* );
+int List_Insert( SortedList*, void*, int );
+int List_Remove( SortedList*, int );
+int List_IndexOf( SortedList*, void* );
+
+int List_InsertPtr( SortedList* list, void* p );
+int List_RemovePtr( SortedList* list, void* p );
+
+void List_Copy( SortedList*, SortedList*, size_t );
+void List_ObjCopy( SortedList*, SortedList*, size_t ); \ No newline at end of file
diff --git a/src/modules/database/dbutils.cpp b/src/modules/database/dbutils.cpp
new file mode 100644
index 0000000000..1e73ddd18e
--- /dev/null
+++ b/src/modules/database/dbutils.cpp
@@ -0,0 +1,365 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "profilemanager.h"
+
+static int CompareEventTypes( const DBEVENTTYPEDESCR* p1, const DBEVENTTYPEDESCR* p2 )
+{
+ int result = strcmp( p1->module, p2->module );
+ if ( result )
+ return result;
+
+ return p1->eventType - p2->eventType;
+}
+
+static LIST<DBEVENTTYPEDESCR> eventTypes( 10, CompareEventTypes );
+
+static BOOL bModuleInitialized = FALSE;
+
+static INT_PTR DbEventTypeRegister(WPARAM, LPARAM lParam)
+{
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )lParam;
+ if ( eventTypes.getIndex( et ) == -1 ) {
+ DBEVENTTYPEDESCR* p = ( DBEVENTTYPEDESCR* )mir_alloc( sizeof( DBEVENTTYPEDESCR ));
+ p->cbSize = DBEVENTTYPEDESCR_SIZE;
+ p->module = mir_strdup( et->module );
+ p->eventType = et->eventType;
+ p->descr = mir_strdup( et->descr );
+ p->textService = NULL;
+ p->iconService = NULL;
+ p->eventIcon = NULL;
+ p->flags = 0;
+ if ( et->cbSize == DBEVENTTYPEDESCR_SIZE ) {
+ if ( et->textService )
+ p->textService = mir_strdup( et->textService );
+ if ( et->iconService )
+ p->iconService = mir_strdup( et->iconService );
+ p->eventIcon = et->eventIcon;
+ p->flags = et->flags;
+ }
+ if ( !p->textService ) {
+ char szServiceName[100];
+ mir_snprintf( szServiceName, sizeof(szServiceName), "%s/GetEventText%d", p->module, p->eventType );
+ p->textService = mir_strdup( szServiceName );
+ }
+ if ( !p->iconService ) {
+ char szServiceName[100];
+ mir_snprintf( szServiceName, sizeof(szServiceName), "%s/GetEventIcon%d", p->module, p->eventType );
+ p->iconService = mir_strdup( szServiceName );
+ }
+ eventTypes.insert( p );
+ }
+
+ return 0;
+}
+
+static INT_PTR DbEventTypeGet(WPARAM wParam, LPARAM lParam)
+{
+ DBEVENTTYPEDESCR tmp;
+ int idx;
+
+ tmp.module = ( char* )wParam;
+ tmp.eventType = lParam;
+ if ( !List_GetIndex(( SortedList* )&eventTypes, &tmp, &idx ))
+ return 0;
+
+ return ( INT_PTR )eventTypes[idx];
+}
+
+static INT_PTR DbEventGetText(WPARAM wParam, LPARAM lParam)
+{
+ DBEVENTGETTEXT* egt = (DBEVENTGETTEXT*)lParam;
+ BOOL bIsDenyUnicode = (egt->datatype & DBVTF_DENYUNICODE);
+
+ DBEVENTINFO* dbei = egt->dbei;
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )DbEventTypeGet( ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+
+ if ( et && ServiceExists( et->textService ))
+ return CallService( et->textService, wParam, lParam );
+
+ if ( !dbei->pBlob ) return 0;
+
+ if ( dbei->eventType == EVENTTYPE_FILE ) {
+ char* filename = ((char *)dbei->pBlob) + sizeof(DWORD);
+ char* descr = filename + lstrlenA( filename ) + 1;
+ char* str = (*descr == 0) ? filename : descr;
+ switch ( egt->datatype ) {
+ case DBVT_WCHAR:
+ return ( INT_PTR )(( dbei->flags & DBEF_UTF ) ?
+ Utf8DecodeT( str ) : mir_a2t( str ));
+ case DBVT_ASCIIZ:
+ return ( INT_PTR )(( dbei->flags & DBEF_UTF ) ? Utf8Decode( mir_strdup( str ), NULL ) : mir_strdup( str ));
+ }
+ return 0;
+ }
+
+ // temporary fix for bug with event types conflict between jabber chat states notifications
+ // and srmm's status changes, must be commented out in future releases
+ if ( dbei->eventType == 25368 && dbei->cbBlob == 1 && dbei->pBlob[0] == 1 )
+ return 0;
+
+ egt->datatype &= ~DBVTF_DENYUNICODE;
+ if ( egt->datatype == DBVT_WCHAR )
+ {
+ WCHAR* msg = NULL;
+ if ( dbei->flags & DBEF_UTF ) {
+ char* str = (char*)alloca(dbei->cbBlob + 1);
+ if (str == NULL) return NULL;
+ memcpy(str, dbei->pBlob, dbei->cbBlob);
+ str[dbei->cbBlob] = 0;
+ Utf8DecodeCP( str, egt->codepage, &msg );
+ }
+ else {
+ size_t msglen = strlen(( char* )dbei->pBlob) + 1, msglenW = 0;
+ if ( msglen != dbei->cbBlob ) {
+ size_t i, count = (( dbei->cbBlob - msglen ) / sizeof( WCHAR ));
+ WCHAR* p = ( WCHAR* )&dbei->pBlob[ msglen ];
+ for ( i=0; i < count; i++ ) {
+ if ( p[i] == 0 ) {
+ msglenW = i;
+ break;
+ } } }
+
+ if ( msglenW > 0 && msglenW < msglen && !bIsDenyUnicode )
+ msg = mir_wstrdup(( WCHAR* )&dbei->pBlob[ msglen ] );
+ else {
+ msg = ( WCHAR* )mir_alloc( sizeof(WCHAR) * msglen );
+ MultiByteToWideChar( egt->codepage, 0, (char *) dbei->pBlob, -1, msg, (int)msglen );
+ } }
+ return ( INT_PTR )msg;
+ }
+ else if ( egt->datatype == DBVT_ASCIIZ ) {
+ char* msg = mir_strdup(( char* )dbei->pBlob );
+ if (dbei->flags & DBEF_UTF)
+ Utf8DecodeCP( msg, egt->codepage, NULL );
+
+ return ( INT_PTR )msg;
+ }
+ return 0;
+}
+
+static INT_PTR DbEventGetIcon( WPARAM wParam, LPARAM lParam )
+{
+ DBEVENTINFO* dbei = ( DBEVENTINFO* )lParam;
+ HICON icon = NULL;
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )DbEventTypeGet( ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+
+ if ( et && ServiceExists( et->iconService )) {
+ icon = ( HICON )CallService( et->iconService, wParam, lParam );
+ if ( icon )
+ return ( INT_PTR )icon;
+ }
+ if ( et && et->eventIcon )
+ icon = ( HICON )CallService( MS_SKIN2_GETICONBYHANDLE, 0, ( LPARAM )et->eventIcon );
+ if ( !icon ) {
+ char szName[100];
+ mir_snprintf( szName, sizeof( szName ), "eventicon_%s%d", dbei->szModule, dbei->eventType );
+ icon = ( HICON )CallService( MS_SKIN2_GETICON, 0, ( LPARAM )szName );
+ }
+
+ if ( !icon )
+ {
+ switch( dbei->eventType ) {
+ case EVENTTYPE_URL:
+ icon = LoadSkinIcon( SKINICON_EVENT_URL );
+ break;
+
+ case EVENTTYPE_FILE:
+ icon = LoadSkinIcon( SKINICON_EVENT_FILE );
+ break;
+
+ default: // EVENTTYPE_MESSAGE and unknown types
+ icon = LoadSkinIcon( SKINICON_EVENT_MESSAGE );
+ break;
+ }
+ }
+
+ if ( wParam & LR_SHARED )
+ return ( INT_PTR )icon;
+ else
+ return ( INT_PTR )CopyIcon( icon );
+}
+
+static INT_PTR DbEventGetStringT( WPARAM wParam, LPARAM lParam )
+{
+ DBEVENTINFO* dbei = ( DBEVENTINFO* )wParam;
+ char* string = ( char* )lParam;
+
+ #if defined( _UNICODE )
+ if ( dbei->flags & DBEF_UTF )
+ return ( INT_PTR )Utf8DecodeUcs2( string );
+
+ return ( INT_PTR )mir_a2t( string );
+ #else
+ char* res = mir_strdup( string );
+ if ( dbei->flags & DBEF_UTF )
+ Utf8Decode( res, NULL );
+ return ( INT_PTR )res;
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int sttEnumVars( const char* szVarName, LPARAM lParam )
+{
+ LIST<char>* vars = ( LIST<char>* )lParam;
+ vars->insert( mir_strdup( szVarName ));
+ return 0;
+}
+
+static INT_PTR DbDeleteModule( WPARAM, LPARAM lParam )
+{
+ LIST<char> vars( 20 );
+
+ DBCONTACTENUMSETTINGS dbces = { 0 };
+ dbces.pfnEnumProc = sttEnumVars;
+ dbces.lParam = ( LPARAM )&vars;
+ dbces.szModule = ( char* )lParam;
+ CallService( MS_DB_CONTACT_ENUMSETTINGS, NULL, (LPARAM)&dbces );
+
+ for ( int i=vars.getCount()-1; i >= 0; i-- ) {
+ DBDeleteContactSetting( NULL, ( char* )lParam, vars[i] );
+ mir_free( vars[i] );
+ }
+ vars.destroy();
+ return 0;
+}
+
+static INT_PTR GetProfilePath(WPARAM wParam, LPARAM lParam)
+{
+ if (!wParam || !lParam)
+ return 1;
+
+ char* dst = (char*)lParam;
+
+ #if defined( _UNICODE )
+ char* tmp = mir_t2a( g_profileDir );
+ strncpy( dst, tmp, wParam );
+ mir_free( tmp );
+ #else
+ strncpy( dst, g_profileDir, wParam );
+ #endif
+
+ if (wParam <= _tcslen(g_profileName))
+ {
+ dst[wParam - 1] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static INT_PTR GetProfileName(WPARAM wParam, LPARAM lParam)
+{
+ if (!wParam || !lParam)
+ return 1;
+
+ char* dst = (char*)lParam;
+
+ #if defined( _UNICODE )
+ char* tmp = makeFileName( g_profileName );
+ strncpy( dst, tmp, wParam );
+ mir_free( tmp );
+ #else
+ strncpy( dst, g_profileName, wParam );
+ #endif
+
+ if (wParam <= _tcslen(g_profileName))
+ {
+ dst[wParam - 1] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+#if defined( _UNICODE )
+
+static INT_PTR GetProfilePathW(WPARAM wParam, LPARAM lParam)
+{
+ if (!wParam || !lParam)
+ return 1;
+
+ wchar_t* dst = (wchar_t*)lParam;
+ wcsncpy(dst, g_profileDir, wParam);
+ if (wParam <= wcslen(g_profileDir))
+ {
+ dst[wParam - 1] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static INT_PTR GetProfileNameW(WPARAM wParam, LPARAM lParam)
+{
+ wchar_t* dst = (wchar_t*)lParam;
+ wcsncpy(dst, g_profileName, wParam );
+ if (wParam <= wcslen(g_profileName))
+ {
+ dst[wParam - 1] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int InitUtils()
+{
+ bModuleInitialized = TRUE;
+
+ CreateServiceFunction(MS_DB_EVENT_REGISTERTYPE, DbEventTypeRegister);
+ CreateServiceFunction(MS_DB_EVENT_GETTYPE, DbEventTypeGet);
+ CreateServiceFunction(MS_DB_EVENT_GETTEXT, DbEventGetText);
+ CreateServiceFunction(MS_DB_EVENT_GETICON, DbEventGetIcon);
+ CreateServiceFunction(MS_DB_EVENT_GETSTRINGT, DbEventGetStringT);
+
+ CreateServiceFunction(MS_DB_MODULE_DELETE, DbDeleteModule);
+
+ CreateServiceFunction(MS_DB_GETPROFILEPATH,GetProfilePath);
+ CreateServiceFunction(MS_DB_GETPROFILENAME,GetProfileName);
+ #if defined( _UNICODE )
+ CreateServiceFunction(MS_DB_GETPROFILEPATHW,GetProfilePathW);
+ CreateServiceFunction(MS_DB_GETPROFILENAMEW,GetProfileNameW);
+ #endif
+ return 0;
+}
+
+void UnloadEventsModule()
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ for ( i=0; i < eventTypes.getCount(); i++ ) {
+ DBEVENTTYPEDESCR* p = eventTypes[i];
+ mir_free( p->module );
+ mir_free( p->descr );
+ mir_free( p->textService );
+ mir_free( p->iconService );
+ mir_free( p );
+ }
+
+ eventTypes.destroy();
+}
diff --git a/src/modules/database/profilemanager.cpp b/src/modules/database/profilemanager.cpp
new file mode 100644
index 0000000000..06cd356d7c
--- /dev/null
+++ b/src/modules/database/profilemanager.cpp
@@ -0,0 +1,850 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "profilemanager.h"
+#include <sys/stat.h>
+
+#define WM_INPUTCHANGED (WM_USER + 0x3000)
+#define WM_FOCUSTEXTBOX (WM_USER + 0x3001)
+
+typedef BOOL (__cdecl *ENUMPROFILECALLBACK) (TCHAR * fullpath, TCHAR * profile, LPARAM lParam);
+
+struct DetailsPageInit {
+ int pageCount;
+ OPTIONSDIALOGPAGE *odp;
+};
+
+struct DetailsPageData {
+ DLGTEMPLATE *pTemplate;
+ HINSTANCE hInst;
+ DLGPROC dlgProc;
+ HWND hwnd;
+ int changed;
+};
+
+struct DlgProfData {
+ PROPSHEETHEADER * psh;
+ HWND hwndOK; // handle to OK button
+ PROFILEMANAGERDATA * pd;
+ HANDLE hFileNotify;
+};
+
+struct DetailsData {
+ HINSTANCE hInstIcmp;
+ HFONT hBoldFont;
+ int pageCount;
+ int currentPage;
+ struct DetailsPageData *opd;
+ RECT rcDisplay;
+ struct DlgProfData * prof;
+};
+
+struct ProfileEnumData {
+ HWND hwnd;
+ TCHAR* szProfile;
+};
+
+extern TCHAR mirandabootini[MAX_PATH];
+
+char **GetSeviceModePluginsList(void);
+void SetServiceModePlugin( int idx );
+
+static void ThemeDialogBackground(HWND hwnd)
+{
+ if (enableThemeDialogTexture)
+ enableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
+}
+
+static int findProfiles(TCHAR * szProfileDir, ENUMPROFILECALLBACK callback, LPARAM lParam)
+{
+ // find in Miranda IM profile subfolders
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA ffd;
+ TCHAR searchspec[MAX_PATH];
+ mir_sntprintf(searchspec, SIZEOF(searchspec), _T("%s\\*.*"), szProfileDir);
+ hFind = FindFirstFile(searchspec, &ffd);
+ if ( hFind == INVALID_HANDLE_VALUE )
+ return 0;
+
+ do {
+ // find all subfolders except "." and ".."
+ if ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && _tcscmp(ffd.cFileName, _T(".")) && _tcscmp(ffd.cFileName, _T("..")) ) {
+ TCHAR buf[MAX_PATH], profile[MAX_PATH];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\\%s\\%s.dat"), szProfileDir, ffd.cFileName, ffd.cFileName);
+ if (_taccess(buf, 0) == 0) {
+ mir_sntprintf(profile, SIZEOF(profile), _T("%s.dat"), ffd.cFileName);
+ if ( !callback(buf, profile, lParam ))
+ break;
+ }
+ }
+ }
+ while ( FindNextFile(hFind, &ffd) );
+ FindClose(hFind);
+
+ return 1;
+}
+
+static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if ( msg == WM_CHAR ) {
+ if ( _tcschr( _T(".?/\\#' "), (TCHAR)wParam) != 0 )
+ return 0;
+ PostMessage(GetParent(edit),WM_INPUTCHANGED,0,0);
+ }
+ return CallWindowProc((WNDPROC)GetWindowLongPtr(edit,GWLP_USERDATA),edit,msg,wParam,lParam);
+}
+
+static int FindDbProviders(const char*, DATABASELINK * dblink, LPARAM lParam)
+{
+ HWND hwndDlg = (HWND)lParam;
+ HWND hwndCombo = GetDlgItem(hwndDlg, IDC_PROFILEDRIVERS);
+ char szName[64];
+
+ if ( dblink->getFriendlyName(szName,SIZEOF(szName),1) == 0 ) {
+ // add to combo box
+ TCHAR* p = LangPackPcharToTchar( szName );
+ LRESULT index = SendMessage( hwndCombo, CB_ADDSTRING, 0, (LPARAM)p );
+ mir_free( p );
+ SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)dblink);
+ }
+ return DBPE_CONT;
+}
+
+static INT_PTR CALLBACK DlgProfileNew(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct DlgProfData * dat = (struct DlgProfData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault( hwndDlg );
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ dat = (struct DlgProfData *)lParam;
+ {
+ // fill in the db plugins present
+ PLUGIN_DB_ENUM dbe;
+ dbe.cbSize = sizeof(dbe);
+ dbe.pfnEnumCallback = (int(*)(const char*,void*,LPARAM))FindDbProviders;
+ dbe.lParam = (LPARAM)hwndDlg;
+ if ( CallService( MS_PLUGINS_ENUMDBPLUGINS, 0, ( LPARAM )&dbe ) == -1 ) {
+ // no plugins?!
+ EnableWindow( GetDlgItem(hwndDlg, IDC_PROFILEDRIVERS ), FALSE );
+ EnableWindow( GetDlgItem(hwndDlg, IDC_PROFILENAME ), FALSE );
+ ShowWindow( GetDlgItem(hwndDlg, IDC_NODBDRIVERS ), TRUE );
+ }
+ // default item
+ SendDlgItemMessage(hwndDlg, IDC_PROFILEDRIVERS, CB_SETCURSEL, 0, 0);
+ }
+ // subclass the profile name box
+ {
+ HWND hwndProfile = GetDlgItem(hwndDlg, IDC_PROFILENAME);
+ WNDPROC proc = (WNDPROC)GetWindowLongPtr(hwndProfile, GWLP_WNDPROC);
+ SetWindowLongPtr(hwndProfile,GWLP_USERDATA,(LONG_PTR)proc);
+ SetWindowLongPtr(hwndProfile,GWLP_WNDPROC,(LONG_PTR)ProfileNameValidate);
+ }
+
+ // decide if there is a default profile name given in the INI and if it should be used
+ if (dat->pd->noProfiles || (shouldAutoCreate(dat->pd->szProfile) && _taccess(dat->pd->szProfile, 0)))
+ {
+ TCHAR* profile = _tcsrchr(dat->pd->szProfile, '\\');
+ if (profile) ++profile;
+ else profile = dat->pd->szProfile;
+
+ TCHAR *p = _tcsrchr(profile, '.');
+ TCHAR c = 0;
+ if (p) { c = *p; *p = 0; }
+
+ SetDlgItemText(hwndDlg, IDC_PROFILENAME, profile);
+ if (c) *p = c;
+ }
+
+ // focus on the textbox
+ PostMessage( hwndDlg, WM_FOCUSTEXTBOX, 0, 0 );
+ return TRUE;
+
+ case WM_FOCUSTEXTBOX:
+ SetFocus( GetDlgItem( hwndDlg, IDC_PROFILENAME ));
+ break;
+
+ case WM_INPUTCHANGED: // when input in the edit box changes
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ EnableWindow( dat->hwndOK, GetWindowTextLength( GetDlgItem( hwndDlg, IDC_PROFILENAME )) > 0 );
+ break;
+
+ case WM_SHOWWINDOW:
+ if ( wParam ) {
+ SetWindowText( dat->hwndOK, TranslateT("&Create"));
+ SendMessage( hwndDlg, WM_INPUTCHANGED, 0, 0 );
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ NMHDR* hdr = ( NMHDR* )lParam;
+ if ( hdr && hdr->code == PSN_APPLY && dat && IsWindowVisible( hwndDlg )) {
+ TCHAR szName[MAX_PATH];
+ LRESULT curSel = SendDlgItemMessage(hwndDlg,IDC_PROFILEDRIVERS,CB_GETCURSEL,0,0);
+ if ( curSel == CB_ERR ) break; // should never happen
+ GetDlgItemText(hwndDlg, IDC_PROFILENAME, szName, SIZEOF( szName ));
+ if ( szName[0] == 0 )
+ break;
+
+ // profile placed in "profile_name" subfolder
+ mir_sntprintf( dat->pd->szProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->szProfileDir, szName, szName );
+ dat->pd->newProfile = 1;
+ dat->pd->dblink = (DATABASELINK *)SendDlgItemMessage( hwndDlg, IDC_PROFILEDRIVERS, CB_GETITEMDATA, ( WPARAM )curSel, 0 );
+
+ if ( makeDatabase( dat->pd->szProfile, dat->pd->dblink, hwndDlg ) == 0 ) {
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE );
+ } } }
+ break;
+ }
+
+ return FALSE;
+}
+
+static int DetectDbProvider(const char*, DATABASELINK * dblink, LPARAM lParam)
+{
+ int error;
+
+#ifdef _UNICODE
+ char* fullpath = makeFileName(( TCHAR* )lParam );
+#else
+ char* fullpath = (char*)lParam;
+#endif
+
+ int ret = dblink->grokHeader(fullpath, &error);
+#ifdef _UNICODE
+ mir_free( fullpath );
+#endif
+ if ( ret == 0) {
+#ifdef _UNICODE
+ char tmp[ MAX_PATH ];
+ dblink->getFriendlyName(tmp, SIZEOF(tmp), 1);
+ MultiByteToWideChar(CP_ACP, 0, tmp, -1, (TCHAR*)lParam, MAX_PATH);
+#else
+ dblink->getFriendlyName((TCHAR*)lParam, MAX_PATH, 1);
+#endif
+ return DBPE_HALT;
+ }
+
+ return DBPE_CONT;
+}
+
+BOOL EnumProfilesForList(TCHAR * fullpath, TCHAR * profile, LPARAM lParam)
+{
+ ProfileEnumData *ped = (ProfileEnumData*)lParam;
+ HWND hwndList = GetDlgItem(ped->hwnd, IDC_PROFILELIST);
+
+ TCHAR sizeBuf[64];
+ int iItem=0;
+ struct _stat statbuf;
+ bool bFileExists = false, bFileLocked = true;
+
+ TCHAR* p = _tcsrchr(profile, '.');
+ _tcscpy(sizeBuf, _T("0 KB"));
+ if ( p != NULL ) *p=0;
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_IMAGE;
+ item.pszText = profile;
+ item.iItem = 0;
+
+ if ( _tstat(fullpath, &statbuf) == 0) {
+ if ( statbuf.st_size > 1000000 ) {
+ mir_sntprintf(sizeBuf,SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1048576.0 );
+ _tcscpy(sizeBuf+5, _T(" MB"));
+ }
+ else {
+ mir_sntprintf(sizeBuf,SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1024.0 );
+ _tcscpy(sizeBuf+5, _T(" KB"));
+ }
+ bFileExists = TRUE;
+
+ bFileLocked = !fileExist(fullpath);
+ }
+
+ item.iImage = bFileLocked;
+
+ iItem = SendMessage( hwndList, LVM_INSERTITEM, 0, (LPARAM)&item );
+ if ( lstrcmpi(ped->szProfile, fullpath) == 0 )
+ ListView_SetItemState(hwndList, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+
+ item.iItem = iItem;
+ item.iSubItem = 2;
+ item.pszText = sizeBuf;
+ SendMessage( hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item );
+
+ if ( bFileExists ) {
+ PLUGIN_DB_ENUM dbe;
+ TCHAR szPath[MAX_PATH];
+
+ LVITEM item2;
+ item2.mask = LVIF_TEXT;
+ item2.iItem = iItem;
+
+ dbe.cbSize = sizeof(dbe);
+ dbe.pfnEnumCallback = (int(*)(const char*,void*,LPARAM))DetectDbProvider;
+ dbe.lParam = (LPARAM)szPath;
+ _tcscpy(szPath, fullpath);
+ if ( CallService( MS_PLUGINS_ENUMDBPLUGINS, 0, ( LPARAM )&dbe ) == 1 ) {
+ if (bFileLocked) {
+ // file locked
+ item2.pszText = TranslateT( "<In Use>" );
+ item2.iSubItem = 1;
+ SendMessage( hwndList, LVM_SETITEMTEXT, iItem, ( LPARAM )&item2 );
+ }
+ else {
+ item.pszText = szPath;
+ item.iSubItem = 1;
+ SendMessage( hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item );
+ } }
+
+ item2.iSubItem = 3;
+ item2.pszText = rtrim( _tctime( &statbuf.st_ctime ));
+ SendMessage( hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item2 );
+
+ item2.iSubItem = 4;
+ item2.pszText = rtrim( _tctime( &statbuf.st_mtime ));
+ SendMessage( hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item2 );
+ }
+ return TRUE;
+}
+
+void DeleteProfile(HWND hwndList, int iItem, DlgProfData* dat)
+{
+ if (iItem < 0)
+ return;
+
+ TCHAR profile[MAX_PATH], profilef[MAX_PATH*2];
+
+ LVITEM item = {0};
+ item.mask = LVIF_TEXT;
+ item.iItem = iItem;
+ item.pszText = profile;
+ item.cchTextMax = SIZEOF(profile);
+ if (!ListView_GetItem(hwndList, &item))
+ return;
+
+ mir_sntprintf(profilef, SIZEOF(profilef), TranslateT("Are you sure you want to remove profile \"%s\"?"), profile);
+
+ if (IDYES != MessageBox(NULL, profilef, _T("Miranda IM"), MB_YESNO | MB_TASKMODAL | MB_ICONWARNING))
+ return;
+
+ mir_sntprintf(profilef, SIZEOF(profilef), _T("%s\\%s%c"), dat->pd->szProfileDir, profile, 0);
+
+ SHFILEOPSTRUCT sf = {0};
+ sf.wFunc = FO_DELETE;
+ sf.pFrom = profilef;
+ sf.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO;
+ SHFileOperation(&sf);
+ ListView_DeleteItem(hwndList, item.iItem);
+}
+
+static INT_PTR CALLBACK DlgProfileSelect(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DlgProfData* dat = (struct DlgProfData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ HIMAGELIST hImgList;
+ LVCOLUMN col;
+
+ TranslateDialogDefault( hwndDlg );
+
+ dat = (DlgProfData*) lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ // set columns
+ col.mask = LVCF_TEXT | LVCF_WIDTH;
+ col.pszText = TranslateT("Profile");
+ col.cx=122;
+ ListView_InsertColumn( hwndList, 0, &col );
+
+ col.pszText = TranslateT("Driver");
+ col.cx=100;
+ ListView_InsertColumn( hwndList, 1, &col );
+
+ col.pszText = TranslateT("Size");
+ col.cx=60;
+ ListView_InsertColumn( hwndList, 2, &col );
+
+ col.pszText = TranslateT("Created");
+ col.cx=145;
+ ListView_InsertColumn( hwndList, 3, &col );
+
+ col.pszText = TranslateT("Modified");
+ col.cx=145;
+ ListView_InsertColumn( hwndList, 4, &col );
+
+ // icons
+ hImgList = ImageList_Create(16, 16, ILC_MASK | (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16), 2, 1);
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE));
+
+ // LV will destroy the image list
+ SetWindowLongPtr(hwndList, GWL_STYLE, GetWindowLongPtr(hwndList, GWL_STYLE) | LVS_SORTASCENDING);
+ ListView_SetImageList(hwndList, hImgList, LVSIL_SMALL);
+ ListView_SetExtendedListViewStyle(hwndList,
+ ListView_GetExtendedListViewStyle(hwndList) | LVS_EX_DOUBLEBUFFER | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT);
+
+ // find all the profiles
+ ProfileEnumData ped = { hwndDlg, dat->pd->szProfile };
+ findProfiles(dat->pd->szProfileDir, EnumProfilesForList, (LPARAM)&ped);
+ PostMessage(hwndDlg, WM_FOCUSTEXTBOX, 0, 0);
+
+ dat->hFileNotify = FindFirstChangeNotification(dat->pd->szProfileDir, TRUE,
+ FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (dat->hFileNotify != INVALID_HANDLE_VALUE)
+ SetTimer(hwndDlg, 0, 1200, NULL);
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 0);
+ FindCloseChangeNotification(dat->hFileNotify);
+ break;
+
+ case WM_TIMER:
+ if (WaitForSingleObject(dat->hFileNotify, 0) == WAIT_OBJECT_0)
+ {
+ ListView_DeleteAllItems(hwndList);
+ ProfileEnumData ped = { hwndDlg, dat->pd->szProfile };
+ findProfiles(dat->pd->szProfileDir, EnumProfilesForList, (LPARAM)&ped);
+ FindNextChangeNotification(dat->hFileNotify);
+ }
+ break;
+
+ case WM_FOCUSTEXTBOX:
+ SetFocus(hwndList);
+ if (dat->pd->szProfile[0] == 0 || ListView_GetSelectedCount(hwndList) == 0)
+ ListView_SetItemState(hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+ break;
+
+ case WM_SHOWWINDOW:
+ if ( wParam )
+ {
+ SetWindowText(dat->hwndOK, TranslateT("&Run"));
+ EnableWindow(dat->hwndOK, ListView_GetSelectedCount(hwndList)==1);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ LVHITTESTINFO lvht = {0};
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+ ScreenToClient(hwndList, &lvht.pt);
+ if (ListView_HitTest(hwndList, &lvht) < 0) break;
+
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run"));
+ AppendMenu(hMenu, MF_SEPARATOR, 2, NULL);
+ AppendMenu(hMenu, MF_STRING, 3, TranslateT("Delete"));
+ int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, hwndDlg, NULL);
+ switch (index) {
+ case 1:
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, IDOK, 0);
+ break;
+
+ case 3:
+ DeleteProfile(hwndList, lvht.iItem, dat);
+ break;
+ }
+ DestroyMenu(hMenu);
+ break;
+ }
+
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR hdr = (LPNMHDR) lParam;
+ if (hdr && hdr->code == PSN_INFOCHANGED)
+ break;
+
+ if (hdr && hdr->idFrom == IDC_PROFILELIST)
+ {
+ switch (hdr->code)
+ {
+ case LVN_ITEMCHANGED:
+ EnableWindow(dat->hwndOK, ListView_GetSelectedCount(hwndList) == 1);
+
+ case NM_DBLCLK:
+ {
+ LVITEM item = {0};
+ TCHAR profile[MAX_PATH];
+
+ if (dat == NULL) break;
+
+ item.mask = LVIF_TEXT;
+ item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL);
+ item.pszText = profile;
+ item.cchTextMax = SIZEOF(profile);
+
+ if (ListView_GetItem(hwndList, &item)) {
+ // profile is placed in "profile_name" subfolder
+ TCHAR tmpPath[MAX_PATH];
+ mir_sntprintf(tmpPath, SIZEOF(tmpPath), _T("%s\\%s.dat"), dat->pd->szProfileDir, profile);
+ HANDLE hFile = CreateFile(tmpPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ mir_sntprintf(dat->pd->szProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->szProfileDir, profile, profile);
+ else
+ _tcscpy(dat->pd->szProfile, tmpPath);
+ CloseHandle(hFile);
+ if (hdr->code == NM_DBLCLK) EndDialog(GetParent(hwndDlg), 1);
+ }
+ return TRUE;
+ }
+
+ case LVN_KEYDOWN:
+ {
+ LPNMLVKEYDOWN hdrk = (LPNMLVKEYDOWN) lParam;
+ if (hdrk->wVKey == VK_DELETE)
+ DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat);
+ break;
+ }
+ }
+ }
+ break;
+ } }
+
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProfileManager(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct DetailsData* dat = ( struct DetailsData* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ struct DlgProfData * prof = (struct DlgProfData *)lParam;
+ PROPSHEETHEADER *psh = prof->psh;
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(hMirandaInst, MAKEINTRESOURCE(IDI_USERDETAILS),IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0));
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(hMirandaInst, MAKEINTRESOURCE(IDI_USERDETAILS),IMAGE_ICON,GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),0));
+ dat = (struct DetailsData*)mir_alloc(sizeof(struct DetailsData));
+ dat->prof = prof;
+ prof->hwndOK = GetDlgItem( hwndDlg, IDOK );
+ EnableWindow( prof->hwndOK, FALSE );
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)dat );
+
+ {
+ TCHAR buf[512];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s: %s\n%s"), TranslateT("Miranda Profiles from"), prof->pd->szProfileDir,
+ TranslateT("Select or create your Miranda IM user profile"));
+ SetDlgItemText(hwndDlg, IDC_NAME, buf);
+ }
+
+ { OPTIONSDIALOGPAGE *odp;
+ int i;
+ TCITEM tci;
+
+ dat->currentPage = 0;
+ dat->pageCount = psh->nPages;
+ dat->opd = ( struct DetailsPageData* )mir_calloc( sizeof( struct DetailsPageData )*dat->pageCount );
+ odp = ( OPTIONSDIALOGPAGE* )psh->ppsp;
+
+ tci.mask = TCIF_TEXT;
+ for( i=0; i < dat->pageCount; i++ ) {
+ dat->opd[i].pTemplate = (DLGTEMPLATE *)LockResource(LoadResource(odp[i].hInstance,FindResourceA(odp[i].hInstance,odp[i].pszTemplate,MAKEINTRESOURCEA(5))));
+ dat->opd[i].dlgProc = odp[i].pfnDlgProc;
+ dat->opd[i].hInst = odp[i].hInstance;
+ dat->opd[i].hwnd = NULL;
+ dat->opd[i].changed = 0;
+ tci.pszText = ( TCHAR* )odp[i].ptszTitle;
+ if (dat->prof->pd->noProfiles || shouldAutoCreate(dat->prof->pd->szProfile))
+ dat->currentPage = 1;
+ TabCtrl_InsertItem( GetDlgItem(hwndDlg,IDC_TABS), i, &tci );
+ } }
+
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_TABS),&dat->rcDisplay);
+ TabCtrl_AdjustRect(GetDlgItem(hwndDlg,IDC_TABS),FALSE,&dat->rcDisplay);
+ {
+ POINT pt = {0,0};
+ ClientToScreen( hwndDlg, &pt );
+ OffsetRect( &dat->rcDisplay, -pt.x, -pt.y );
+ }
+
+ TabCtrl_SetCurSel( GetDlgItem( hwndDlg, IDC_TABS ), dat->currentPage );
+ dat->opd[dat->currentPage].hwnd = CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst,dat->opd[dat->currentPage].pTemplate,hwndDlg,dat->opd[dat->currentPage].dlgProc,(LPARAM)dat->prof);
+ ThemeDialogBackground( dat->opd[dat->currentPage].hwnd );
+ SetWindowPos( dat->opd[dat->currentPage].hwnd, HWND_TOP, dat->rcDisplay.left, dat->rcDisplay.top, 0, 0, SWP_NOSIZE );
+ { PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = ( LPARAM )0;
+ SendMessage( dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, ( LPARAM )&pshn );
+ }
+ // service mode combobox
+ {
+ char **list = GetSeviceModePluginsList();
+ if ( !list ) {
+ ShowWindow( GetDlgItem(hwndDlg, IDC_SM_LABEL ), FALSE );
+ ShowWindow( GetDlgItem(hwndDlg, IDC_SM_COMBO ), FALSE );
+ } else {
+ int i = 0;
+ LRESULT index;
+ HWND hwndCombo = GetDlgItem(hwndDlg, IDC_SM_COMBO );
+ index = SendMessage( hwndCombo, CB_ADDSTRING, 0, (LPARAM)_T("") );
+ SendMessage( hwndCombo, CB_SETITEMDATA, index, (LPARAM)-1 );
+ SendMessage( hwndCombo, CB_SETCURSEL, 0, 0);
+ while ( list[i] ) {
+ TCHAR *str = LangPackPcharToTchar( list[i] );
+ index = SendMessage( hwndCombo, CB_ADDSTRING, 0, (LPARAM)str );
+ mir_free(str);
+ SendMessage( hwndCombo, CB_SETITEMDATA, index, (LPARAM)i );
+ i++;
+ }
+ mir_free(list);
+ }
+ }
+ ShowWindow( dat->opd[dat->currentPage].hwnd, SW_SHOW );
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch ( GetDlgCtrlID(( HWND )lParam )) {
+ case IDC_WHITERECT:
+ SetBkColor(( HDC )wParam, GetSysColor( COLOR_WINDOW ));
+ return ( INT_PTR )GetSysColorBrush( COLOR_WINDOW );
+ }
+ break;
+
+ case PSM_CHANGED:
+ dat->opd[dat->currentPage].changed=1;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ { PSHNOTIFY pshn;
+ int i;
+
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)0;
+ for ( i=0; i < dat->pageCount; i++ ) {
+ pshn.hdr.hwndFrom = dat->opd[i].hwnd;
+ if ( dat->opd[i].hwnd != NULL )
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ switch(wParam) {
+ case IDC_TABS:
+ switch(((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGING:
+ { PSHNOTIFY pshn;
+ if ( dat->currentPage == -1 || dat->opd[dat->currentPage].hwnd == NULL )
+ break;
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ if ( SendMessage( dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, ( LPARAM )&pshn )) {
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, TRUE );
+ return TRUE;
+ }
+ break;
+ }
+ case TCN_SELCHANGE:
+ if ( dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL )
+ ShowWindow( dat->opd[ dat->currentPage ].hwnd, SW_HIDE );
+
+ dat->currentPage = TabCtrl_GetCurSel(GetDlgItem(hwndDlg,IDC_TABS));
+ if ( dat->currentPage != -1 ) {
+ if ( dat->opd[dat->currentPage].hwnd == NULL ) {
+ PSHNOTIFY pshn;
+ dat->opd[dat->currentPage].hwnd=CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst,dat->opd[dat->currentPage].pTemplate,hwndDlg,dat->opd[dat->currentPage].dlgProc,(LPARAM)dat->prof);
+ ThemeDialogBackground(dat->opd[dat->currentPage].hwnd);
+ SetWindowPos(dat->opd[dat->currentPage].hwnd,HWND_TOP,dat->rcDisplay.left,dat->rcDisplay.top,0,0,SWP_NOSIZE);
+ pshn.hdr.code=PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom=dat->opd[dat->currentPage].hwnd;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)0;
+ SendMessage(dat->opd[dat->currentPage].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ ShowWindow(dat->opd[dat->currentPage].hwnd,SW_SHOW);
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ { int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=0;
+ pshn.hdr.code=PSN_RESET;
+ for(i=0;i<dat->pageCount;i++) {
+ if (dat->opd[i].hwnd==NULL || !dat->opd[i].changed) continue;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ EndDialog(hwndDlg,0);
+ }
+ break;
+
+ case IDC_REMOVE:
+ if (!dat->prof->pd->noProfiles) {
+ HWND hwndList = GetDlgItem(dat->opd[0].hwnd, IDC_PROFILELIST);
+ DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat->prof);
+ }
+ break;
+
+ case IDOK:
+ {
+ int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)0;
+ if ( dat->currentPage != -1 ) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd;
+ if ( SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, ( LPARAM )&pshn ))
+ break;
+ }
+
+ pshn.hdr.code=PSN_APPLY;
+ for ( i=0; i < dat->pageCount; i++ ) {
+ if ( dat->opd[i].hwnd == NULL || !dat->opd[i].changed )
+ continue;
+
+ pshn.hdr.hwndFrom = dat->opd[i].hwnd;
+ SendMessage( dat->opd[i].hwnd, WM_NOTIFY, 0, ( LPARAM )&pshn );
+ if ( GetWindowLongPtr( dat->opd[i].hwnd, DWLP_MSGRESULT ) == PSNRET_INVALID_NOCHANGEPAGE) {
+ TabCtrl_SetCurSel( GetDlgItem( hwndDlg, IDC_TABS ), i );
+ if ( dat->currentPage != -1 )
+ ShowWindow( dat->opd[ dat->currentPage ].hwnd, SW_HIDE );
+ dat->currentPage = i;
+ ShowWindow( dat->opd[dat->currentPage].hwnd, SW_SHOW );
+ return 0;
+ } }
+ EndDialog(hwndDlg,1);
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ {
+ LRESULT curSel = SendDlgItemMessage(hwndDlg,IDC_SM_COMBO,CB_GETCURSEL,0,0);
+ if ( curSel != CB_ERR ) {
+ int idx = SendDlgItemMessage( hwndDlg, IDC_SM_COMBO, CB_GETITEMDATA, ( WPARAM )curSel, 0 );
+ SetServiceModePlugin(idx);
+ }
+ }
+ DestroyIcon(( HICON )SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, 0));
+ DestroyIcon(( HICON )SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0));
+ DeleteObject( dat->hBoldFont );
+ { int i;
+ for ( i=0; i < dat->pageCount; i++ )
+ if ( dat->opd[i].hwnd != NULL )
+ DestroyWindow( dat->opd[i].hwnd );
+ }
+ mir_free( dat->opd );
+ mir_free( dat );
+ break;
+ }
+ return FALSE;
+}
+
+static int AddProfileManagerPage(struct DetailsPageInit * opi, OPTIONSDIALOGPAGE * odp)
+{
+ if ( odp->cbSize != sizeof( OPTIONSDIALOGPAGE ))
+ return 1;
+
+ opi->odp = ( OPTIONSDIALOGPAGE* )mir_realloc( opi->odp, sizeof( OPTIONSDIALOGPAGE )*( opi->pageCount+1 ));
+ {
+ OPTIONSDIALOGPAGE* p = opi->odp + opi->pageCount++;
+ p->cbSize = sizeof(OPTIONSDIALOGPAGE);
+ p->hInstance = odp->hInstance;
+ p->pfnDlgProc = odp->pfnDlgProc;
+ p->position = odp->position;
+ p->ptszTitle = LangPackPcharToTchar(odp->pszTitle);
+ p->pszGroup = NULL;
+ p->groupPosition = odp->groupPosition;
+ p->hGroupIcon = odp->hGroupIcon;
+ p->hIcon = odp->hIcon;
+ if (( DWORD_PTR )odp->pszTemplate & 0xFFFF0000 )
+ p->pszTemplate = mir_strdup( odp->pszTemplate );
+ else
+ p->pszTemplate = odp->pszTemplate;
+ }
+ return 0;
+}
+
+int getProfileManager(PROFILEMANAGERDATA * pd)
+{
+ DetailsPageInit opi;
+ opi.pageCount=0;
+ opi.odp=NULL;
+
+ {
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.pszTitle = LPGEN("My Profiles");
+ odp.pfnDlgProc = DlgProfileSelect;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_SELECTION);
+ odp.hInstance = hMirandaInst;
+ AddProfileManagerPage(&opi, &odp);
+
+ odp.pszTitle = LPGEN("New Profile");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_NEW);
+ odp.pfnDlgProc = DlgProfileNew;
+ AddProfileManagerPage(&opi, &odp);
+ }
+
+ PROPSHEETHEADER psh = { 0 };
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_PROPSHEETPAGE|PSH_NOAPPLYNOW;
+ psh.hwndParent = NULL;
+ psh.nPages = opi.pageCount;
+ psh.pStartPage = 0;
+ psh.ppsp = (PROPSHEETPAGE*)opi.odp;
+
+ DlgProfData prof;
+ prof.pd = pd;
+ prof.psh = &psh;
+ int rc = DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(IDD_PROFILEMANAGER),NULL,DlgProfileManager,(LPARAM)&prof);
+
+ if ( rc != -1 )
+ for ( int i=0; i < opi.pageCount; i++ ) {
+ mir_free(( char* )opi.odp[i].pszTitle );
+ mir_free( opi.odp[i].pszGroup );
+ if (( DWORD_PTR )opi.odp[i].pszTemplate & 0xFFFF0000 )
+ mir_free(( char* )opi.odp[i].pszTemplate );
+ }
+
+ if ( opi.odp != NULL )
+ mir_free(opi.odp);
+
+ return rc;
+}
diff --git a/src/modules/database/profilemanager.h b/src/modules/database/profilemanager.h
new file mode 100644
index 0000000000..fc6bd56020
--- /dev/null
+++ b/src/modules/database/profilemanager.h
@@ -0,0 +1,43 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+typedef struct {
+ TCHAR * szProfile; // in/out
+ TCHAR * szProfileDir; // in/out
+ BOOL noProfiles; // in
+ BOOL newProfile; // out
+ DATABASELINK * dblink; // out
+} PROFILEMANAGERDATA;
+
+int InitUtils(void);
+
+char* makeFileName( const TCHAR* tszOriginalName );
+int makeDatabase(TCHAR * profile, DATABASELINK * link, HWND hwndDlg);
+int getProfileManager(PROFILEMANAGERDATA * pd);
+int getProfilePath(TCHAR * buf, size_t cch);
+int isValidProfileName(const TCHAR * name);
+bool fileExist(TCHAR* fname);
+bool shouldAutoCreate(TCHAR *szProfile);
+
+extern TCHAR g_profileDir[MAX_PATH];
+extern TCHAR g_profileName[MAX_PATH];
diff --git a/src/modules/findadd/findadd.cpp b/src/modules/findadd/findadd.cpp
new file mode 100644
index 0000000000..b0bef4fef4
--- /dev/null
+++ b/src/modules/findadd/findadd.cpp
@@ -0,0 +1,1033 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "findadd.h"
+
+#define TIMERID_THROBBER 111
+
+#define HM_SEARCHACK (WM_USER+10)
+#define M_SETGROUPVISIBILITIES (WM_USER+11)
+
+static HWND hwndFindAdd=NULL;
+static HANDLE hHookModulesLoaded = 0;
+static HANDLE hMainMenuItem = NULL;
+static int OnSystemModulesLoaded(WPARAM wParam,LPARAM lParam);
+
+static int FindAddDlgResizer(HWND,LPARAM lParam,UTILRESIZECONTROL *urc)
+{
+ static int y,nextY,oldTop;
+ struct FindAddDlgData *dat;
+
+ dat=(struct FindAddDlgData*)lParam;
+ switch(urc->wId) {
+ case IDC_RESULTS:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDOK:
+ dat->minDlgHeight=nextY+urc->rcItem.bottom-urc->rcItem.top;
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDC_ADD:
+ case IDC_MOREOPTIONS:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ case IDC_PROTOIDGROUP: //the resize is always processed in template order
+ nextY=y=urc->rcItem.top;
+ if(dat->showProtoId) nextY=y+urc->rcItem.bottom-urc->rcItem.top+7;
+ break;
+ case IDC_EMAILGROUP:
+ oldTop=urc->rcItem.top;
+ y=nextY;
+ if(dat->showEmail) nextY=y+urc->rcItem.bottom-urc->rcItem.top+7;
+ OffsetRect(&urc->rcItem,0,y-oldTop);
+ return RD_ANCHORX_LEFT|RD_ANCHORY_CUSTOM;
+ case IDC_NAMEGROUP:
+ oldTop=urc->rcItem.top;
+ y=nextY;
+ if(dat->showName) nextY=y+urc->rcItem.bottom-urc->rcItem.top+7;
+ OffsetRect(&urc->rcItem,0,y-oldTop);
+ return RD_ANCHORX_LEFT|RD_ANCHORY_CUSTOM;
+ case IDC_ADVANCEDGROUP:
+ oldTop=urc->rcItem.top;
+ y=nextY;
+ if(dat->showAdvanced) nextY=y+urc->rcItem.bottom-urc->rcItem.top+7;
+ OffsetRect(&urc->rcItem,0,y-oldTop);
+ return RD_ANCHORX_LEFT|RD_ANCHORY_CUSTOM;
+ case IDC_TINYEXTENDEDGROUP:
+ oldTop=urc->rcItem.top;
+ y=nextY;
+ if(dat->showTiny)
+ {
+ int height= urc->dlgNewSize.cy-y-(urc->dlgOriginalSize.cy-urc->rcItem.bottom);
+ nextY=y+200; //min height for custom dialog
+ urc->rcItem.top=urc->rcItem.bottom-height;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDC_BYEMAIL:
+ case IDC_EMAIL:
+ case IDC_BYNAME:
+ case IDC_STNAMENICK:
+ case IDC_STNAMEFIRST:
+ case IDC_STNAMELAST:
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ case IDC_BYADVANCED:
+ case IDC_BYCUSTOM:
+ case IDC_ADVANCED:
+ OffsetRect(&urc->rcItem,0,y-oldTop);
+ return RD_ANCHORX_LEFT|RD_ANCHORY_CUSTOM;
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+static void RenderThrobber(HDC hdc,RECT *rcItem,int *throbbing,int *pivot)
+{
+ HBRUSH hBr;
+ HDC hMemDC;
+ HBITMAP hBitmap;
+ HPEN hPen;
+ RECT rc;
+ int x,width,height,height2;
+
+ InflateRect(rcItem,-1,0);
+ width=rcItem->right-rcItem->left;
+ height=rcItem->bottom-rcItem->top;
+ height2=height/2;
+
+ if (*throbbing) {
+ /* create memdc */
+ hMemDC=CreateCompatibleDC(0);
+ hBitmap=( HBITMAP )SelectObject(hMemDC, CreateCompatibleBitmap(hdc,width,height));
+ /* flush it */
+ rc.left=rc.top=0;
+ rc.right=width;
+ rc.bottom=height;
+ hBr=GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hMemDC,&rc,hBr);
+ DeleteObject(hBr);
+ /* set up the pen */
+ hPen=(HPEN)SelectObject(hMemDC,CreatePen(PS_SOLID,4,GetSysColor(COLOR_BTNSHADOW)));
+ /* draw everything before the pivot */
+ x=*pivot;
+ while (x>(-height)) {
+ MoveToEx(hMemDC,x+height2,0,NULL);
+ LineTo(hMemDC,x-height2,height);
+ x-=12;
+ }
+
+ /* draw everything after the pivot */
+ x = *pivot;
+ while (x < width+height) {
+ MoveToEx(hMemDC,x+height2,0,NULL);
+ LineTo(hMemDC,x-height2,height);
+ x+=12;
+ }
+
+ /* move the pivot */
+ *pivot+=2;
+ /* reset the pivot point if it gets past the rect */
+ if (*pivot>width) *pivot=0;
+ /* put back the old pen and delete the new one */
+ DeleteObject(SelectObject(hMemDC,hPen));
+ /* cap the top and bottom */
+ hPen=(HPEN)SelectObject(hMemDC,CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNFACE)));
+ MoveToEx(hMemDC,0,0,NULL);
+ LineTo(hMemDC,width,0);
+ MoveToEx(hMemDC,0,height-1,NULL);
+ LineTo(hMemDC,width,height-1);
+ /* select in the old pen and delete the new pen */
+ DeleteObject(SelectObject(hMemDC,hPen));
+ /* paint to screen */
+ BitBlt(hdc,rcItem->left,rcItem->top,width,height,hMemDC,0,0,SRCCOPY);
+ /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */
+ hBitmap=( HBITMAP )SelectObject(hMemDC,hBitmap);
+ DeleteObject(hBitmap);
+ DeleteDC(hMemDC);
+ }
+ else {
+ /* just flush the DC */
+ hBr=GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hdc,rcItem,hBr);
+ DeleteObject(hBr);
+} }
+
+static void StartThrobber(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ dat->throbbing=1;
+ SetTimer(hwndDlg,TIMERID_THROBBER,25,NULL);
+}
+
+static void StopThrobber(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ KillTimer(hwndDlg,TIMERID_THROBBER);
+ dat->throbbing=0;
+ dat->pivot=0;
+ InvalidateRect(GetDlgItem(hwndDlg,IDC_STATUSBAR),NULL,FALSE);
+}
+
+static void ShowAdvancedSearchDlg(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ char *szProto=(char*)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),0);
+
+ if(szProto==NULL) return;
+ if(dat->hwndAdvSearch==NULL) {
+ RECT rc;
+ dat->hwndAdvSearch=(HWND)CallProtoService(szProto,PS_CREATEADVSEARCHUI,0,(LPARAM)hwndDlg);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_RESULTS),&rc);
+ SetWindowPos(dat->hwndAdvSearch,0,rc.left,rc.top,0,0,SWP_NOZORDER|SWP_NOSIZE);
+ }
+ if(animateWindow) {
+ animateWindow(dat->hwndAdvSearch,150,AW_ACTIVATE|AW_SLIDE|AW_HOR_POSITIVE);
+ RedrawWindow(dat->hwndAdvSearch,NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+ } else ShowWindow(dat->hwndAdvSearch,SW_SHOW);
+ CheckDlgButton(hwndDlg,IDC_ADVANCED,BST_CHECKED);
+}
+
+static void ReposTinySearchDlg(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ if ( dat->hwndTinySearch != NULL ) {
+ RECT rc;
+ RECT clientRect;
+ POINT pt={0,0};
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_TINYEXTENDEDGROUP),&rc);
+ GetWindowRect(dat->hwndTinySearch,&clientRect);
+ pt.x=rc.left;
+ pt.y=rc.top;
+ ScreenToClient(hwndDlg,&pt);
+ SetWindowPos(dat->hwndTinySearch,0,pt.x+5,pt.y+15,rc.right-rc.left-10,rc.bottom-rc.top-30,SWP_NOZORDER);
+ //SetWindowPos(GetDlgItem(hwndDlg,IDC_TINYEXTENDEDGROUP),0,0,0,rc.right-rc.left,clientRect.bottom-clientRect.top+20,SWP_NOMOVE|SWP_NOZORDER);
+} }
+
+static void ShowTinySearchDlg(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ char *szProto=(char*)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),0);
+ if(szProto==NULL) return;
+ if(dat->hwndTinySearch==NULL) {
+ dat->hwndTinySearch=(HWND)CallProtoService(szProto,PS_CREATEADVSEARCHUI,0,(LPARAM)/*GetDlgItem(*/hwndDlg/*,IDC_TINYEXTENDEDGROUP)*/);
+ if (dat->hwndTinySearch)
+ ReposTinySearchDlg(hwndDlg, dat);
+ else
+ dat->showTiny = false;
+ }
+ ShowWindow(dat->hwndTinySearch,SW_SHOW);
+}
+
+static void HideAdvancedSearchDlg(HWND hwndDlg,struct FindAddDlgData *dat)
+{
+ if(dat->hwndAdvSearch==NULL) return;
+ if(animateWindow && IsWinVerXPPlus()) //blending is quite slow on win2k
+ animateWindow(dat->hwndAdvSearch,150,AW_HIDE|AW_BLEND);
+ else ShowWindow(dat->hwndAdvSearch,SW_HIDE);
+ CheckDlgButton(hwndDlg,IDC_ADVANCED,BST_UNCHECKED);
+}
+
+void EnableResultButtons(HWND hwndDlg,int enable)
+{
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD), enable || IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID));
+ EnableWindow(GetDlgItem(hwndDlg,IDC_MOREOPTIONS),enable);
+}
+
+static void CheckSearchTypeRadioButton(HWND hwndDlg,int idControl)
+{
+ int i;
+ static const int controls[]={IDC_BYPROTOID,IDC_BYEMAIL,IDC_BYNAME,IDC_BYADVANCED,IDC_BYCUSTOM};
+ for( i=0; i < SIZEOF(controls); i++ )
+ CheckDlgButton(hwndDlg,controls[i],idControl==controls[i]?BST_CHECKED:BST_UNCHECKED);
+}
+
+#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.")
+#define sttErrTitle TranslateT("Search")
+
+static void SetListItemText( HWND hwndList, int idx, int col, TCHAR* szText )
+{
+ if ( szText && szText[0] )
+ {
+ ListView_SetItemText( hwndList, idx, col, szText );
+ }
+ else
+ {
+ ListView_SetItemText( hwndList, idx, col, TranslateT("<not specified>"));
+ }
+}
+
+static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct FindAddDlgData* dat = ( struct FindAddDlgData* )GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ int i,netProtoCount;
+ COMBOBOXEXITEM cbei;
+ HICON hIcon;
+
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER);
+ dat=(struct FindAddDlgData*)mir_calloc(sizeof(struct FindAddDlgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->notSearchedYet=1;
+ dat->iLastColumnSortIndex=1;
+ dat->bSortAscending=1;
+ dat->hBmpSortUp=(HBITMAP)LoadImage(hMirandaInst,MAKEINTRESOURCE(IDB_SORTCOLUP),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
+ dat->hBmpSortDown=(HBITMAP)LoadImage(hMirandaInst,MAKEINTRESOURCE(IDB_SORTCOLDOWN),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
+ SendDlgItemMessage(hwndDlg,IDC_MOREOPTIONS,BUTTONSETARROW,1,0);
+ ListView_SetExtendedListViewStyle(hwndList,LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP);
+
+ { LVCOLUMN lvc;
+ RECT rc;
+ LVITEM lvi;
+
+ GetClientRect(hwndList,&rc);
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvc.pszText = TranslateT("Results");
+ lvc.cx = rc.right-1;
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ lvi.mask=LVIF_TEXT;
+ lvi.iItem=0;
+ lvi.iSubItem=0;
+ lvi.pszText=TranslateT("There are no results to display.");
+ ListView_InsertItem(hwndList, &lvi);
+ }
+
+ // Allocate a reasonable amount of space in the status bar
+ { int partWidth[3];
+ SIZE textSize;
+ HDC hdc;
+
+ hdc=GetDC(GetDlgItem(hwndDlg,IDC_STATUSBAR));
+ SelectObject(hdc,(HFONT)SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,WM_GETFONT,0,0));
+ GetTextExtentPoint32(hdc,TranslateT("Searching"),lstrlen(TranslateT("Searching")),&textSize);
+ partWidth[0]=textSize.cx;
+ GetTextExtentPoint32(hdc,_T("01234567890123456789"), 20, &textSize );
+ partWidth[0]+=textSize.cx;
+ ReleaseDC(GetDlgItem(hwndDlg,IDC_STATUSBAR),hdc);
+ partWidth[1]=partWidth[0]+150;
+ partWidth[2]=-1;
+ SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,SB_SETPARTS,SIZEOF(partWidth),(LPARAM)partWidth);
+ SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,SB_SETTEXT,1|SBT_OWNERDRAW,0);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg,IDC_STATUSBAR),dat);
+ }
+ {
+ TCHAR *szProto = NULL;
+ int index = 0;
+ DBVARIANT dbv={0};
+ HDC hdc;
+ SIZE textSize;
+ RECT rect;
+ int cbwidth = 0;
+
+ if ( !DBGetContactSettingTString( NULL, "FindAdd", "LastSearched", &dbv ))
+ szProto = dbv.ptszVal;
+
+ for( i=0, netProtoCount=0; i < accounts.getCount(); i++ ) {
+ if (!Proto_IsAccountEnabled( accounts[i] )) continue;
+ DWORD caps = (DWORD)CallProtoService( accounts[i]->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0 );
+ if (caps & PF1_BASICSEARCH || caps & PF1_EXTSEARCH || caps & PF1_SEARCHBYEMAIL || caps & PF1_SEARCHBYNAME)
+ netProtoCount++;
+ }
+ dat->himlComboIcons=ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,netProtoCount+1,netProtoCount+1);
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CBEM_SETIMAGELIST,0,(LPARAM)dat->himlComboIcons);
+ cbei.mask=CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_TEXT|CBEIF_LPARAM;
+ cbei.iItem=0;
+ hdc=GetDC(hwndDlg);
+ SelectObject(hdc,(HFONT)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,WM_GETFONT,0,0));
+ if(netProtoCount>1) {
+ cbei.pszText=TranslateT("All Networks");
+ GetTextExtentPoint32(hdc,cbei.pszText,lstrlen(cbei.pszText),&textSize);
+ if (textSize.cx>cbwidth) cbwidth = textSize.cx;
+ cbei.iImage=cbei.iSelectedImage=ImageList_AddIcon_IconLibLoaded(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL);
+ cbei.lParam=0;
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CBEM_INSERTITEM,0,(LPARAM)&cbei);
+ cbei.iItem++;
+ }
+ for( i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if (!Proto_IsAccountEnabled(pa)) continue;
+ DWORD caps=(DWORD)CallProtoService( pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0 );
+ if ( !(caps&PF1_BASICSEARCH) && !(caps&PF1_EXTSEARCH) && !(caps&PF1_SEARCHBYEMAIL) && !(caps&PF1_SEARCHBYNAME))
+ continue;
+
+ cbei.pszText = pa->tszAccountName;
+ GetTextExtentPoint32(hdc,cbei.pszText,lstrlen(cbei.pszText),&textSize);
+ if (textSize.cx>cbwidth) cbwidth = textSize.cx;
+ hIcon=(HICON)CallProtoService( pa->szModuleName,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ cbei.iImage=cbei.iSelectedImage=ImageList_AddIcon(dat->himlComboIcons,hIcon);
+ DestroyIcon(hIcon);
+ cbei.lParam=(LPARAM)pa->szModuleName;
+ SendDlgItemMessageA(hwndDlg,IDC_PROTOLIST,CBEM_INSERTITEM,0,(LPARAM)&cbei);
+ if (szProto && cbei.pszText && !lstrcmp( szProto, pa->tszAccountName ))
+ index = cbei.iItem;
+ cbei.iItem++;
+ }
+ cbwidth+=32;
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETDROPPEDCONTROLRECT,0,(LPARAM)&rect);
+ if ((rect.right-rect.left)<cbwidth)
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_SETDROPPEDWIDTH,cbwidth,0);
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_SETCURSEL,index,0);
+ DBFreeVariant(&dbv); /* free string szProto was fetched with */
+ }
+ SendMessage(hwndDlg,M_SETGROUPVISIBILITIES,0,0);
+ Utils_RestoreWindowPosition(hwndDlg,NULL,"FindAdd","");
+
+ return TRUE;
+ }
+ case WM_SIZE:
+ { UTILRESIZEDIALOG urd={0};
+ urd.cbSize=sizeof(urd);
+ urd.hwndDlg=hwndDlg;
+ urd.hInstance=hMirandaInst;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_FINDADD);
+ urd.lParam=(LPARAM)dat;
+ urd.pfnResizer=FindAddDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd);
+ ReposTinySearchDlg(hwndDlg, dat);
+ SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,WM_SIZE,0,0);
+ if(dat->notSearchedYet) {
+ RECT rc;
+ GetClientRect(hwndList,&rc);
+ ListView_SetColumnWidth(hwndList,0,rc.right);
+ }
+ }
+ //fall through
+ case WM_MOVE:
+ if (dat && dat->hwndAdvSearch)
+ {
+ RECT rc;
+ GetWindowRect(hwndList,&rc);
+ SetWindowPos(dat->hwndAdvSearch,0,rc.left,rc.top,0,0,SWP_NOZORDER|SWP_NOSIZE);
+ }
+ break;
+ case WM_GETMINMAXINFO:
+ { MINMAXINFO *mmi=(MINMAXINFO*)lParam;
+ RECT rc,rc2;
+ GetWindowRect(hwndList,&rc);
+ GetWindowRect(hwndDlg,&rc2);
+ mmi->ptMinTrackSize.x=rc.left-rc2.left+10+GetSystemMetrics(SM_CXFRAME);
+ GetClientRect(GetDlgItem(hwndDlg,IDC_MOREOPTIONS),&rc);
+ mmi->ptMinTrackSize.x+=rc.right+5;
+ GetClientRect(GetDlgItem(hwndDlg,IDC_ADD),&rc);
+ mmi->ptMinTrackSize.x+=rc.right+5;
+ GetClientRect(GetDlgItem(hwndDlg,IDC_STATUSBAR),&rc);
+ mmi->ptMinTrackSize.y=dat->minDlgHeight+20+GetSystemMetrics(SM_CYCAPTION)+2*GetSystemMetrics(SM_CYFRAME);
+ GetClientRect(GetDlgItem(hwndDlg,IDC_STATUSBAR),&rc);
+ mmi->ptMinTrackSize.y+=rc.bottom;
+ return 0;
+ }
+ case M_SETGROUPVISIBILITIES:
+ { char *szProto;
+ int i;
+ DWORD protoCaps;
+ MINMAXINFO mmi;
+ RECT rc;
+ int checkmarkVisible;
+
+ dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0;
+ szProto=(char*)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),0);
+ if ( szProto == (char *)CB_ERR )
+ break;
+ if ( szProto == NULL ) {
+ for ( i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if (!Proto_IsAccountEnabled(pa)) continue;
+ protoCaps=(DWORD)CallProtoService(pa->szModuleName,PS_GETCAPS,PFLAGNUM_1,0);
+ if(protoCaps&PF1_SEARCHBYEMAIL) dat->showEmail=1;
+ if(protoCaps&PF1_SEARCHBYNAME) dat->showName=1;
+ }
+ }
+ else {
+ protoCaps=(DWORD)CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_1,0);
+ if(protoCaps&PF1_BASICSEARCH) dat->showProtoId=1;
+ if(protoCaps&PF1_SEARCHBYEMAIL) dat->showEmail=1;
+ if(protoCaps&PF1_SEARCHBYNAME) dat->showName=1;
+ if(protoCaps&PF1_EXTSEARCH && !(protoCaps&PF1_EXTSEARCHUI)) dat->showTiny=1;
+ if(protoCaps&PF1_EXTSEARCHUI) dat->showAdvanced=1;
+ if(protoCaps&PF1_USERIDISEMAIL && dat->showProtoId) {dat->showProtoId=0; dat->showEmail=1;}
+ if(dat->showProtoId) {
+ char *szUniqueId;
+ szUniqueId=(char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDTEXT,0);
+ if(szUniqueId) {
+ #if defined( _UNICODE )
+ TCHAR* p = mir_a2u(szUniqueId);
+ SetDlgItemText(hwndDlg,IDC_BYPROTOID,p);
+ mir_free(p);
+ #else
+ SetDlgItemTextA(hwndDlg,IDC_BYPROTOID,szUniqueId);
+ #endif
+ }
+ else SetDlgItemText(hwndDlg,IDC_BYPROTOID,TranslateT("Handle"));
+ if(protoCaps&PF1_NUMERICUSERID) SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROTOID),GWL_STYLE,GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROTOID),GWL_STYLE)|ES_NUMBER);
+ else SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROTOID),GWL_STYLE,GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROTOID),GWL_STYLE)&~ES_NUMBER);
+ }
+ }
+
+ if (dat->showTiny)
+ ShowTinySearchDlg(hwndDlg, dat);
+ else {
+ if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch=NULL;
+ } }
+
+#define en(id,t) ShowWindow(GetDlgItem(hwndDlg,IDC_##id),dat->show##t?SW_SHOW:SW_HIDE)
+ en(PROTOIDGROUP,ProtoId); en(BYPROTOID,ProtoId); en(PROTOID,ProtoId);
+ en(EMAILGROUP,Email); en(BYEMAIL,Email); en(EMAIL,Email);
+ en(NAMEGROUP,Name); en(BYNAME,Name);
+ en(STNAMENICK,Name); en(NAMENICK,Name);
+ en(STNAMEFIRST,Name); en(NAMEFIRST,Name);
+ en(STNAMELAST,Name); en(NAMELAST,Name);
+ en(ADVANCEDGROUP,Advanced); en(BYADVANCED,Advanced); en(ADVANCED,Advanced);
+ en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny);
+#undef en
+
+ checkmarkVisible=(dat->showAdvanced && IsDlgButtonChecked(hwndDlg,IDC_BYADVANCED)) ||
+ (dat->showEmail && IsDlgButtonChecked(hwndDlg,IDC_BYEMAIL)) ||
+ (dat->showTiny && IsDlgButtonChecked(hwndDlg,IDC_BYCUSTOM)) ||
+ (dat->showName && IsDlgButtonChecked(hwndDlg,IDC_BYNAME)) ||
+ (dat->showProtoId && IsDlgButtonChecked(hwndDlg,IDC_BYPROTOID));
+ if(!checkmarkVisible) {
+ if(dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg,IDC_BYPROTOID);
+ else if(dat->showEmail) CheckSearchTypeRadioButton(hwndDlg,IDC_BYEMAIL);
+ else if(dat->showName) CheckSearchTypeRadioButton(hwndDlg,IDC_BYNAME);
+ else if(dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg,IDC_BYADVANCED);
+ else if(dat->showTiny) CheckSearchTypeRadioButton(hwndDlg,IDC_BYCUSTOM);
+ }
+
+ SendMessage(hwndDlg,WM_SIZE,0,0);
+ SendMessage(hwndDlg,WM_GETMINMAXINFO,0,(LPARAM)&mmi);
+ GetWindowRect(hwndDlg,&rc);
+ if(rc.bottom-rc.top<mmi.ptMinTrackSize.y) SetWindowPos(hwndDlg,0,0,0,rc.right-rc.left,mmi.ptMinTrackSize.y,SWP_NOZORDER|SWP_NOMOVE);
+ break;
+ }
+ case WM_TIMER:
+ if(wParam==TIMERID_THROBBER) {
+ RECT rc;
+ HDC hdc;
+ int borders[3];
+ SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,SB_GETBORDERS,0,(LPARAM)borders);
+ SendDlgItemMessage(hwndDlg,IDC_STATUSBAR,SB_GETRECT,1,(LPARAM)&rc);
+ InflateRect(&rc,-borders[2]/2,-borders[1]/2);
+ hdc=GetDC(GetDlgItem(hwndDlg,IDC_STATUSBAR));
+ RenderThrobber(hdc,&rc,&dat->throbbing,&dat->pivot);
+ ReleaseDC(GetDlgItem(hwndDlg,IDC_STATUSBAR),hdc);
+ }
+ break;
+ case WM_DRAWITEM:
+ { DRAWITEMSTRUCT *dis=(DRAWITEMSTRUCT*)lParam;
+ if(dis->CtlID==IDC_STATUSBAR && dis->itemID==1) {
+ RenderThrobber(dis->hDC,&dis->rcItem,&dat->throbbing,&dat->pivot);
+ return TRUE;
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ if (wParam == IDC_RESULTS) {
+ switch(((LPNMHDR)lParam)->code) {
+ case LVN_ITEMCHANGED:
+ { int count=ListView_GetSelectedCount(hwndList);
+ if(dat->notSearchedYet) count=0;
+ EnableResultButtons(hwndDlg,count);
+ break;
+ }
+ case LVN_COLUMNCLICK:
+ {
+ LPNMLISTVIEW nmlv=(LPNMLISTVIEW)lParam;
+ HDITEM hdi;
+
+ hdi.mask=HDI_BITMAP|HDI_FORMAT;
+ hdi.fmt=HDF_LEFT|HDF_STRING;
+ Header_SetItem(ListView_GetHeader(hwndList),dat->iLastColumnSortIndex,&hdi);
+
+ if(nmlv->iSubItem!=dat->iLastColumnSortIndex)
+ {
+ dat->bSortAscending=TRUE;
+ dat->iLastColumnSortIndex=nmlv->iSubItem;
+ }
+ else dat->bSortAscending=!dat->bSortAscending;
+
+ hdi.fmt=HDF_LEFT|HDF_BITMAP|HDF_STRING|HDF_BITMAP_ON_RIGHT;
+ hdi.hbm=dat->bSortAscending?dat->hBmpSortDown:dat->hBmpSortUp;
+ Header_SetItem(ListView_GetHeader(hwndList),dat->iLastColumnSortIndex,&hdi);
+
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ }
+ break;
+ } }
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_PROTOLIST:
+ if(HIWORD(wParam)==CBN_SELCHANGE) {
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ if(dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch=NULL;
+ }
+ if(dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch=NULL;
+ }
+ SendMessage(hwndDlg,M_SETGROUPVISIBILITIES,0,0);
+ }
+ break;
+ case IDC_BYPROTOID:
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD),TRUE);
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ break;
+ case IDC_BYEMAIL:
+ case IDC_BYNAME:
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD), ListView_GetSelectedCount(hwndList) > 0);
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ break;
+ case IDC_PROTOID:
+ if(HIWORD(wParam)==EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ CheckSearchTypeRadioButton(hwndDlg,IDC_BYPROTOID);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD),TRUE);
+ }
+ break;
+ case IDC_EMAIL:
+ if(HIWORD(wParam)==EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ CheckSearchTypeRadioButton(hwndDlg,IDC_BYEMAIL);
+ }
+ break;
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ if(HIWORD(wParam)==EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ CheckSearchTypeRadioButton(hwndDlg,IDC_BYNAME);
+ }
+ break;
+ case IDC_ADVANCED:
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD), ListView_GetSelectedCount(hwndList) > 0);
+ if(IsDlgButtonChecked(hwndDlg,IDC_ADVANCED))
+ ShowAdvancedSearchDlg(hwndDlg,dat);
+ else
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ CheckSearchTypeRadioButton(hwndDlg,IDC_BYADVANCED);
+ break;
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ case IDOK:
+ {
+ HideAdvancedSearchDlg(hwndDlg,dat);
+ if(dat->searchCount) { //cancel search
+ SetDlgItemText(hwndDlg,IDOK,TranslateT("&Search"));
+ if(dat->hResultHook) {UnhookEvent(dat->hResultHook); dat->hResultHook=NULL;}
+ if(dat->search) {mir_free(dat->search); dat->search=NULL;}
+ dat->searchCount=0;
+ StopThrobber(hwndDlg,dat);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg,IDC_STATUSBAR),dat);
+ break;
+ }
+ char *szProto=(char*)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),0);
+ if(dat->search) {mir_free(dat->search); dat->search=NULL;}
+ dat->searchCount=0;
+ dat->hResultHook=HookEventMessage(ME_PROTO_ACK,hwndDlg,HM_SEARCHACK);
+ if (IsDlgButtonChecked(hwndDlg,IDC_BYCUSTOM))
+ {
+ BeginSearch(hwndDlg,dat,szProto,PS_SEARCHBYADVANCED,PF1_EXTSEARCHUI,dat->hwndTinySearch);
+ }
+ else if(IsDlgButtonChecked(hwndDlg,IDC_BYPROTOID)) {
+ TCHAR str[256];
+ GetDlgItemText(hwndDlg,IDC_PROTOID,str,SIZEOF(str));
+ rtrim(str);
+ if(str[0]==0)
+ MessageBox(hwndDlg,sttErrMsg,sttErrTitle,MB_OK);
+ else
+ BeginSearch(hwndDlg,dat,szProto,PS_BASICSEARCHT,PF1_BASICSEARCH,str);
+ }
+ else if(IsDlgButtonChecked(hwndDlg,IDC_BYEMAIL)) {
+ TCHAR str[256];
+ GetDlgItemText(hwndDlg,IDC_EMAIL,str,SIZEOF(str));
+ rtrim(str);
+ if(str[0]==0)
+ MessageBox(hwndDlg,sttErrMsg,sttErrTitle,MB_OK);
+ else
+ BeginSearch(hwndDlg,dat,szProto,PS_SEARCHBYEMAILT,PF1_SEARCHBYEMAIL,str);
+ }
+ else if(IsDlgButtonChecked(hwndDlg,IDC_BYNAME)) {
+ TCHAR nick[256],first[256],last[256];
+ PROTOSEARCHBYNAME psbn;
+ GetDlgItemText(hwndDlg,IDC_NAMENICK,nick,SIZEOF(nick));
+ GetDlgItemText(hwndDlg,IDC_NAMEFIRST,first,SIZEOF(first));
+ GetDlgItemText(hwndDlg,IDC_NAMELAST,last,SIZEOF(last));
+ psbn.pszFirstName = first;
+ psbn.pszLastName = last;
+ psbn.pszNick = nick;
+ if(nick[0]==0 && first[0]==0 && last[0]==0)
+ MessageBox(hwndDlg,sttErrMsg,sttErrTitle,MB_OK);
+ else
+ BeginSearch(hwndDlg,dat,szProto,PS_SEARCHBYNAMET,PF1_SEARCHBYNAME,&psbn);
+ }
+ else if(IsDlgButtonChecked(hwndDlg,IDC_BYADVANCED)) {
+ if(dat->hwndAdvSearch==NULL)
+ MessageBox(hwndDlg,sttErrMsg,sttErrTitle,MB_OK);
+ else
+ BeginSearch(hwndDlg,dat,szProto,PS_SEARCHBYADVANCED,PF1_EXTSEARCHUI,dat->hwndAdvSearch);
+ }
+
+ if(dat->searchCount==0) {
+ if(dat->hResultHook) {UnhookEvent(dat->hResultHook); dat->hResultHook=NULL;}
+ break;
+ }
+
+ dat->notSearchedYet=0;
+ FreeSearchResults(hwndList);
+
+ CreateResultsColumns(hwndList,dat,szProto);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg,IDC_STATUSBAR),dat);
+ SetStatusBarResultInfo(hwndDlg);
+ StartThrobber(hwndDlg,dat);
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel"));
+ break;
+ }
+ case IDC_ADD:
+ { LVITEM lvi;
+ struct ListSearchResult *lsr;
+ ADDCONTACTSTRUCT acs = {0};
+
+ if (ListView_GetSelectedCount(hwndList) == 1)
+ {
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED);
+ ListView_GetItem(hwndList, &lvi);
+ lsr = (struct ListSearchResult*)lvi.lParam;
+ acs.szProto = lsr->szProto;
+ acs.psr = &lsr->psr;
+ }
+ else
+ {
+ TCHAR str[256];
+ GetDlgItemText(hwndDlg, IDC_PROTOID, str, SIZEOF(str));
+ if (*rtrim(str) == 0) break;
+
+ PROTOSEARCHRESULT psr = {0};
+ psr.cbSize = sizeof(psr);
+ psr.flags = PSR_TCHAR;
+ psr.id = str;
+
+ acs.psr = &psr;
+ acs.szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ }
+
+ acs.handleType = HANDLE_SEARCHRESULT;
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs);
+ break;
+ }
+ case IDC_MOREOPTIONS:
+ { RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_MOREOPTIONS),&rc);
+ ShowMoreOptionsMenu(hwndDlg,rc.left,rc.bottom);
+ break;
+ }
+ }
+ if (lParam && dat->hwndTinySearch==(HWND)lParam
+ && HIWORD(wParam)==EN_SETFOCUS && LOWORD(wParam)==0
+ && !IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) {
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM);
+ }
+ break;
+ case WM_CONTEXTMENU:
+ { POINT pt;
+ LVHITTESTINFO lvhti;
+
+ pt.x=(short)LOWORD(lParam); pt.y=(short)HIWORD(lParam);
+ lvhti.pt=pt;
+ ScreenToClient(hwndDlg,&pt);
+ switch(GetDlgCtrlID(ChildWindowFromPoint(hwndDlg,pt))) {
+ case IDC_RESULTS:
+ if(dat->notSearchedYet) return TRUE;
+ ScreenToClient(hwndList,&lvhti.pt);
+ if(ListView_HitTest(hwndList,&lvhti)==-1) break;
+ ShowMoreOptionsMenu(hwndDlg,(short)LOWORD(lParam),(short)HIWORD(lParam));
+ return TRUE;
+ }
+ break;
+ }
+ case HM_SEARCHACK:
+ { ACKDATA *ack=(ACKDATA*)lParam;
+ int i;
+
+ if(ack->type!=ACKTYPE_SEARCH) break;
+ for(i=0;i<dat->searchCount;i++)
+ if(dat->search[i].hProcess==ack->hProcess && dat->search[i].hProcess != NULL && !lstrcmpA(dat->search[i].szProto,ack->szModule)) break;
+ if(i==dat->searchCount) break;
+ if(ack->result==ACKRESULT_SUCCESS || ack->result==ACKRESULT_FAILED) {
+ dat->searchCount--;
+ memmove(dat->search+i,dat->search+i+1,sizeof(struct ProtoSearchInfo)*(dat->searchCount-i));
+ if(dat->searchCount==0) {
+ mir_free(dat->search);
+ dat->search=NULL;
+ UnhookEvent(dat->hResultHook);
+ dat->hResultHook=NULL;
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search"));
+ StopThrobber(hwndDlg,dat);
+ }
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg,IDC_STATUSBAR),dat);
+ }
+ else if(ack->result==ACKRESULT_SEARCHRESULT && ack->lParam) {
+
+ PROTOSEARCHRESULT *psr;
+ CUSTOMSEARCHRESULTS * csr=(CUSTOMSEARCHRESULTS*)ack->lParam;
+ dat->bFlexSearchResult=TRUE;
+ psr=&(csr->psr);
+ // check if this is column names data (psr->cbSize==0)
+ if ( psr->cbSize==0 ){ // blob contain info about columns
+
+ int iColumn;
+ LVCOLUMN lvc={0};
+
+ //firstly remove all exist items
+ FreeSearchResults(hwndList);
+ ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too
+ //secondly remove all columns
+ while (ListView_DeleteColumn(hwndList,1)); //will delete fist column till it possible
+ //now will add columns and captions;
+ lvc.mask=LVCF_TEXT;
+ for (iColumn=0; iColumn<csr->nFieldCount; iColumn++)
+ {
+ lvc.pszText=TranslateTS(csr->pszFields[iColumn]);
+ ListView_InsertColumn (hwndList, iColumn+1, &lvc) ;
+ }
+ // Column inserting Done
+ } else { // blob contain info about found contacts
+
+ LVITEM lvi = {0};
+ int i, col;
+ struct ListSearchResult *lsr;
+ char *szComboProto;
+ COMBOBOXEXITEM cbei = {0};
+
+ lsr = (struct ListSearchResult*)mir_alloc(offsetof(struct ListSearchResult,psr)+psr->cbSize);
+ lsr->szProto = ack->szModule;
+ memcpy(&lsr->psr, psr, psr->cbSize);
+
+ /* Next block is not needed but behavior will be kept */
+ bool isUnicode = (psr->flags & PSR_UNICODE) != 0;
+ if (psr->id)
+ {
+ BOOL validPtr = isUnicode ? IsBadStringPtrW((wchar_t*)psr->id, 25) : IsBadStringPtrA((char*)psr->id, 25);
+ if (!validPtr)
+ {
+ isUnicode = false;
+ lsr->psr.id = NULL;
+ }
+ else
+ lsr->psr.id = isUnicode ? mir_u2t((wchar_t*)psr->id) : mir_a2t((char*)psr->id);
+ }
+
+ lsr->psr.nick = isUnicode ? mir_u2t((wchar_t*)psr->nick) : mir_a2t((char*)psr->nick);
+ lsr->psr.firstName = isUnicode ? mir_u2t((wchar_t*)psr->firstName) : mir_a2t((char*)psr->firstName);
+ lsr->psr.lastName = isUnicode ? mir_u2t((wchar_t*)psr->lastName) : mir_a2t((char*)psr->lastName);
+ lsr->psr.email = isUnicode ? mir_u2t((wchar_t*)psr->email) : mir_a2t((char*)psr->email);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_TCHAR;
+
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.lParam = (LPARAM)lsr;
+ for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--; )
+ {
+ szComboProto=(char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0);
+ if (szComboProto==NULL) continue;
+ if (!lstrcmpA(szComboProto,ack->szModule))
+ {
+ cbei.mask = CBEIF_IMAGE;
+ cbei.iItem = i;
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CBEM_GETITEM,0,(LPARAM)&cbei);
+ lvi.iImage = cbei.iImage;
+ }
+ }
+ i = ListView_InsertItem(hwndList, &lvi);
+ for (col=0; col<csr->nFieldCount; col++) {
+ SetListItemText(hwndList, i, col+1 , csr->pszFields[col] );
+ }
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ i=0;
+ while (ListView_SetColumnWidth(hwndList, i++, LVSCW_AUTOSIZE_USEHEADER));
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ break;
+ }
+ else if(ack->result==ACKRESULT_DATA) {
+ LVITEM lvi={0};
+ int i,col;
+ PROTOSEARCHRESULT *psr=(PROTOSEARCHRESULT*)ack->lParam;
+ struct ListSearchResult *lsr;
+ char *szComboProto;
+ COMBOBOXEXITEM cbei={0};
+ dat->bFlexSearchResult=FALSE;
+ lsr=(struct ListSearchResult*)mir_alloc(offsetof(struct ListSearchResult,psr)+psr->cbSize);
+ lsr->szProto=ack->szModule;
+
+ CopyMemory(&lsr->psr, psr, psr->cbSize);
+ lsr->psr.nick = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->nick) : mir_a2t((char*)psr->nick);
+ lsr->psr.firstName = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->firstName) : mir_a2t((char*)psr->firstName);
+ lsr->psr.lastName = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->lastName) : mir_a2t((char*)psr->lastName);
+ lsr->psr.email = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->email) : mir_a2t((char*)psr->email);
+ lsr->psr.id = psr->flags & PSR_UNICODE ? mir_u2t((wchar_t*)psr->id) : mir_a2t((char*)psr->id);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_TCHAR;
+
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ lvi.lParam=(LPARAM)lsr;
+ for(i = SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCOUNT,0,0); i--; )
+ {
+ szComboProto=(char*)SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETITEMDATA,i,0);
+ if(szComboProto==NULL) continue;
+ if(!lstrcmpA(szComboProto,ack->szModule)) {
+ cbei.mask=CBEIF_IMAGE;
+ cbei.iItem=i;
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CBEM_GETITEM,0,(LPARAM)&cbei);
+ lvi.iImage=cbei.iImage;
+ break;
+ }
+ }
+ i=ListView_InsertItem(hwndList, &lvi);
+ col=1;
+ SetListItemText(hwndList, i, col++, lsr->psr.id );
+ SetListItemText(hwndList, i, col++, lsr->psr.nick );
+ SetListItemText(hwndList, i, col++, lsr->psr.firstName );
+ SetListItemText(hwndList, i, col++, lsr->psr.lastName );
+ SetListItemText(hwndList, i, col++, lsr->psr.email );
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ break;
+ }
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ case WM_DESTROY:
+ {
+ TCHAR *szProto;
+ int len = SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETLBTEXTLEN,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),0);
+ szProto = ( TCHAR* )alloca( sizeof(TCHAR)*( len+1 ));
+ *szProto='\0';
+ SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETLBTEXT,SendDlgItemMessage(hwndDlg,IDC_PROTOLIST,CB_GETCURSEL,0,0),(LPARAM)szProto);
+ DBWriteContactSettingTString(NULL, "FindAdd", "LastSearched", szProto?szProto:_T(""));
+ }
+ SaveColumnSizes(hwndList);
+ if(dat->hResultHook!=NULL) UnhookEvent(dat->hResultHook);
+ FreeSearchResults(hwndList);
+ ImageList_Destroy(dat->himlComboIcons);
+ mir_free(dat->search);
+ if(dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch=NULL;
+ }
+ if(dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch=NULL;
+ }
+ DeleteObject(dat->hBmpSortDown);
+ DeleteObject(dat->hBmpSortUp);
+ mir_free(dat);
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg,NULL,"FindAdd","");
+
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR FindAddCommand(WPARAM, LPARAM)
+{
+ if(IsWindow(hwndFindAdd)) {
+ ShowWindow(hwndFindAdd,SW_SHOWNORMAL);
+ SetForegroundWindow(hwndFindAdd);
+ SetFocus(hwndFindAdd);
+ }
+ else {
+ int netProtoCount, i;
+
+ // Make sure we have some networks to search on. This is not ideal since
+ // this check will be repeated every time the dialog is requested, but it
+ // must be done since this service can be called from other places than the menu.
+ // One alternative would be to only create the service if we have network
+ // protocols loaded but that would delay the creation until MODULE_LOADED and
+ // that is not good either...
+ for ( i=0, netProtoCount=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if (!Proto_IsAccountEnabled(pa)) continue;
+ int protoCaps=CallProtoService( pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0 );
+ if ( protoCaps&PF1_BASICSEARCH || protoCaps&PF1_SEARCHBYEMAIL || protoCaps&PF1_SEARCHBYNAME
+ || protoCaps&PF1_EXTSEARCHUI ) netProtoCount++;
+ }
+ if (netProtoCount > 0)
+ hwndFindAdd=CreateDialog(hMirandaInst, MAKEINTRESOURCE(IDD_FINDADD), NULL, DlgProcFindAdd);
+ }
+ return 0;
+}
+
+int FindAddPreShutdown(WPARAM, LPARAM)
+{
+ if ( IsWindow( hwndFindAdd ))
+ DestroyWindow(hwndFindAdd);
+ hwndFindAdd = NULL;
+ return 0;
+}
+
+int LoadFindAddModule(void)
+{
+ CreateServiceFunction(MS_FINDADD_FINDADD,FindAddCommand);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,FindAddPreShutdown);
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = 500020000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_FINDUSER );
+ mi.pszName = LPGEN("&Find/Add Contacts...");
+ mi.pszService = MS_FINDADD_FINDADD;
+ hMainMenuItem = ( HANDLE )CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&mi);
+ return 0;
+}
+
+static int OnSystemModulesLoaded(WPARAM, LPARAM)
+{
+ int netProtoCount, i;
+
+ // Make sure we have some networks to search on.
+ for ( i=0, netProtoCount=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ int protoCaps = CallProtoService( pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0 );
+ if ( protoCaps & ( PF1_BASICSEARCH | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_EXTSEARCHUI ))
+ netProtoCount++;
+ }
+
+ CLISTMENUITEM cmi = { 0 };
+ cmi.cbSize = sizeof(cmi);
+ cmi.flags = CMIM_FLAGS;
+ if ( netProtoCount == 0 )
+ cmi.flags |= CMIF_HIDDEN;
+ CallService( MS_CLIST_MODIFYMENUITEM, (WPARAM)hMainMenuItem, (LPARAM)&cmi );
+ return 0;
+}
diff --git a/src/modules/findadd/findadd.h b/src/modules/findadd/findadd.h
new file mode 100644
index 0000000000..8e5fcb29e1
--- /dev/null
+++ b/src/modules/findadd/findadd.h
@@ -0,0 +1,59 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+struct ListSearchResult {
+ const char *szProto;
+ PROTOSEARCHRESULT psr;
+};
+
+struct ProtoSearchInfo {
+ const char *szProto;
+ HANDLE hProcess;
+};
+
+struct FindAddDlgData {
+ HANDLE hResultHook;
+ int bSortAscending;
+ int iLastColumnSortIndex;
+ HIMAGELIST himlComboIcons;
+ int showProtoId,showEmail,showName,showAdvanced,showTiny;
+ int minDlgHeight;
+ int notSearchedYet;
+ struct ProtoSearchInfo *search;
+ int searchCount;
+ HBITMAP hBmpSortUp,hBmpSortDown;
+ int throbbing;
+ int pivot;
+ HWND hwndAdvSearch;
+ HWND hwndTinySearch;
+ BOOL bFlexSearchResult;
+};
+
+int CALLBACK SearchResultsCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+void FreeSearchResults(HWND hwndResults);
+int BeginSearch(HWND hwndDlg,struct FindAddDlgData *dat,const char *szProto,const char *szSearchService,DWORD requiredCapability,void *pvSearchParams);
+void SetStatusBarSearchInfo(HWND hwndStatus,struct FindAddDlgData *dat);
+void SetStatusBarResultInfo(HWND hwndDlg);
+void CreateResultsColumns(HWND hwndResults,struct FindAddDlgData *dat,char *szProto);
+void EnableResultButtons(HWND hwndDlg,int enable);
+void ShowMoreOptionsMenu(HWND hwndDlg,int x,int y);
+void SaveColumnSizes(HWND hwndResults);
diff --git a/src/modules/findadd/searchresults.cpp b/src/modules/findadd/searchresults.cpp
new file mode 100644
index 0000000000..4c8b7cf46c
--- /dev/null
+++ b/src/modules/findadd/searchresults.cpp
@@ -0,0 +1,398 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "findadd.h"
+
+enum {
+ COLUMNID_PROTO,
+ COLUMNID_HANDLE,
+ COLUMNID_NICK,
+ COLUMNID_FIRST,
+ COLUMNID_LAST,
+ COLUMNID_EMAIL,
+ NUM_COLUMNID
+};
+
+void SaveColumnSizes(HWND hwndResults)
+{
+ int columnOrder[NUM_COLUMNID];
+ int columnCount;
+ char szSetting[32];
+ int i;
+ struct FindAddDlgData *dat;
+
+ dat=(struct FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults),GWLP_USERDATA);
+ columnCount=Header_GetItemCount(ListView_GetHeader(hwndResults));
+ if (columnCount != NUM_COLUMNID) return;
+ ListView_GetColumnOrderArray(hwndResults,columnCount,columnOrder);
+ for(i=0; i < NUM_COLUMNID; i++) {
+ mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i);
+ DBWriteContactSettingByte(NULL,"FindAdd",szSetting,(BYTE)columnOrder[i]);
+ if(i>=columnCount) continue;
+ mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i);
+ DBWriteContactSettingWord(NULL,"FindAdd",szSetting,(WORD)ListView_GetColumnWidth(hwndResults,i));
+ }
+ DBWriteContactSettingByte(NULL,"FindAdd","SortColumn",(BYTE)dat->iLastColumnSortIndex);
+ DBWriteContactSettingByte(NULL,"FindAdd","SortAscending",(BYTE)dat->bSortAscending);
+}
+
+static const TCHAR *szColumnNames[] = { NULL, NULL, _T("Nick"), _T("First Name"), _T("Last Name"), _T("E-mail") };
+static int defaultColumnSizes[]={0,90,100,100,100,2000};
+void LoadColumnSizes(HWND hwndResults,const char *szProto)
+{
+ HDITEM hdi;
+ int columnOrder[NUM_COLUMNID];
+ int columnCount;
+ char szSetting[32];
+ int i;
+ FindAddDlgData *dat;
+ bool colOrdersValid;
+
+ defaultColumnSizes[COLUMNID_PROTO] = GetSystemMetrics(SM_CXSMICON) + 4;
+ dat = (FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults), GWLP_USERDATA);
+
+ columnCount = NUM_COLUMNID;
+ colOrdersValid = true;
+ for(i=0; i < NUM_COLUMNID; i++)
+ {
+ LVCOLUMN lvc;
+ if( i < columnCount )
+ {
+ int bNeedsFree = FALSE;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ if( szColumnNames[i] != NULL )
+ lvc.pszText = TranslateTS( szColumnNames[i] );
+ else if( i == COLUMNID_HANDLE )
+ {
+ if (szProto)
+ {
+ #if defined( _UNICODE )
+ bNeedsFree = TRUE;
+ lvc.pszText = mir_a2t((char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDTEXT,0));
+ #else
+ lvc.pszText = (char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDTEXT,0);
+ #endif
+ }
+ else
+ lvc.pszText = _T("ID");
+ }
+ else lvc.mask &= ~LVCF_TEXT;
+ mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i);
+ lvc.cx = DBGetContactSettingWord(NULL, "FindAdd", szSetting, defaultColumnSizes[i]);
+ ListView_InsertColumn(hwndResults, i, (LPARAM)&lvc);
+ #if defined( _UNICODE )
+ if (bNeedsFree)
+ mir_free(lvc.pszText);
+ #endif
+ }
+ mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i);
+ columnOrder[i] = DBGetContactSettingByte(NULL, "FindAdd", szSetting, -1);
+ if (columnOrder[i] == -1 || columnOrder[i] >= NUM_COLUMNID) colOrdersValid = false;
+ }
+
+ if (colOrdersValid)
+ ListView_SetColumnOrderArray(hwndResults, columnCount, columnOrder);
+
+ dat->iLastColumnSortIndex = DBGetContactSettingByte(NULL, "FindAdd", "SortColumn", COLUMNID_NICK);
+ if (dat->iLastColumnSortIndex >= columnCount) dat->iLastColumnSortIndex = COLUMNID_NICK;
+ dat->bSortAscending = DBGetContactSettingByte(NULL, "FindAdd", "SortAscending", TRUE);
+
+ hdi.mask = HDI_BITMAP | HDI_FORMAT;
+ hdi.fmt = HDF_LEFT | HDF_BITMAP | HDF_STRING | HDF_BITMAP_ON_RIGHT;
+ hdi.hbm = dat->bSortAscending ? dat->hBmpSortDown : dat->hBmpSortUp;
+ Header_SetItem(ListView_GetHeader(hwndResults), dat->iLastColumnSortIndex, &hdi);
+}
+
+static LPARAM ListView_GetItemLParam(HWND hwndList, int idx)
+{
+ LVITEM lv;
+ lv.iItem = idx;
+ lv.mask = LVIF_PARAM;
+ ListView_GetItem(hwndList,&lv);
+ return lv.lParam;
+}
+
+int CALLBACK SearchResultsCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ struct FindAddDlgData *dat=(struct FindAddDlgData*)GetWindowLongPtr((HWND) lParamSort, GWLP_USERDATA);
+ int sortMultiplier;
+ int sortCol;
+ struct ListSearchResult *lsr1, *lsr2;
+ HWND hList=GetDlgItem((HWND) lParamSort, IDC_RESULTS);
+
+ sortMultiplier=dat->bSortAscending?1:-1;
+ sortCol=dat->iLastColumnSortIndex;
+ if (!dat->bFlexSearchResult)
+ {
+ lsr1=(struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam1);
+ lsr2=(struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam2);
+
+ if ( lsr1 == NULL || lsr2 == NULL ) return 0;
+ switch(sortCol)
+ {
+ case COLUMNID_PROTO:
+ return lstrcmpA(lsr1->szProto, lsr2->szProto)*sortMultiplier;
+ case COLUMNID_HANDLE:
+ return lstrcmpi(lsr1->psr.id, lsr2->psr.id)*sortMultiplier;
+ case COLUMNID_NICK:
+ return lstrcmpi(lsr1->psr.nick, lsr2->psr.nick)*sortMultiplier;
+ case COLUMNID_FIRST:
+ return lstrcmpi(lsr1->psr.firstName, lsr2->psr.firstName)*sortMultiplier;
+ case COLUMNID_LAST:
+ return lstrcmpi(lsr1->psr.lastName, lsr2->psr.lastName)*sortMultiplier;
+ case COLUMNID_EMAIL:
+ return lstrcmpi(lsr1->psr.email, lsr2->psr.email)*sortMultiplier;
+ }
+ }
+ else
+ {
+ TCHAR szText1[100];
+ TCHAR szText2[100];
+ ListView_GetItemText(hList,(int)lParam1,sortCol,szText1,SIZEOF(szText1));
+ ListView_GetItemText(hList,(int)lParam2,sortCol,szText2,SIZEOF(szText2));
+ return _tcsicmp(szText1, szText2)*sortMultiplier;
+ }
+ return 0;
+}
+
+void FreeSearchResults(HWND hwndResults)
+{
+ LV_ITEM lvi;
+ struct ListSearchResult *lsr;
+ for(lvi.iItem=ListView_GetItemCount(hwndResults)-1;lvi.iItem>=0;lvi.iItem--) {
+ lvi.mask=LVIF_PARAM;
+ ListView_GetItem(hwndResults,&lvi);
+ lsr=(struct ListSearchResult*)lvi.lParam;
+ if(lsr==NULL) continue;
+ mir_free(lsr->psr.id);
+ mir_free(lsr->psr.email);
+ mir_free(lsr->psr.nick);
+ mir_free(lsr->psr.firstName);
+ mir_free(lsr->psr.lastName);
+ mir_free(lsr);
+ }
+ ListView_DeleteAllItems(hwndResults);
+ EnableResultButtons(GetParent(hwndResults),0);
+}
+
+// on its own thread
+static void BeginSearchFailed(void * arg)
+{
+ TCHAR buf[128];
+ if ( arg != NULL ) {
+ const TCHAR* protoName = (TCHAR*)arg;
+ mir_sntprintf(buf,SIZEOF(buf),
+ TranslateT("Could not start a search on '%s', there was a problem - is %s connected?"),
+ protoName,protoName);
+ mir_free((char*)arg);
+ }
+ else lstrcpyn(buf,TranslateT("Could not search on any of the protocols, are you online?"),SIZEOF(buf));
+ MessageBox(0,buf,TranslateT("Problem with search"),MB_OK | MB_ICONERROR);
+}
+
+int BeginSearch(HWND,struct FindAddDlgData *dat,const char *szProto,const char *szSearchService,DWORD requiredCapability,void *pvSearchParams)
+{
+ int i;
+ if ( szProto == NULL ) {
+ int failures = 0;
+ dat->searchCount = 0;
+ dat->search = (struct ProtoSearchInfo*)mir_calloc(sizeof(struct ProtoSearchInfo) * accounts.getCount());
+ for( i=0; i < accounts.getCount();i++) {
+ PROTOACCOUNT* pa = accounts[i];
+ if (!Proto_IsAccountEnabled(pa)) continue;
+ DWORD caps=(DWORD)CallProtoService(pa->szModuleName,PS_GETCAPS,PFLAGNUM_1,0);
+ if(!(caps&requiredCapability)) continue;
+ dat->search[dat->searchCount].hProcess = (HANDLE)CallProtoService(pa->szModuleName,szSearchService,0,(LPARAM)pvSearchParams);
+ dat->search[dat->searchCount].szProto = pa->szModuleName;
+ if ( dat->search[dat->searchCount].hProcess == NULL ) failures++;
+ else dat->searchCount++;
+ }
+ if(failures) {
+ //infuriatingly vague error message. fixme.
+ if(dat->searchCount==0) {
+ forkthread(BeginSearchFailed,0,NULL);
+ mir_free(dat->search);
+ dat->search=NULL;
+ return 1;
+ } }
+ }
+ else {
+ dat->search=(struct ProtoSearchInfo*)mir_alloc(sizeof(struct ProtoSearchInfo));
+ dat->searchCount=1;
+ dat->search[0].hProcess=(HANDLE)CallProtoService(szProto,szSearchService,0,(LPARAM)pvSearchParams);
+ dat->search[0].szProto=szProto;
+ if(dat->search[0].hProcess==NULL) {
+ //infuriatingly vague error message. fixme.
+ PROTOACCOUNT* pa = Proto_GetAccount(szProto);
+ forkthread(BeginSearchFailed, 0, mir_tstrdup(pa->tszAccountName));
+ mir_free(dat->search);
+ dat->search=NULL;
+ dat->searchCount=0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// !!!!!!!! this code is dangerous like a hell
+void SetStatusBarSearchInfo(HWND hwndStatus,struct FindAddDlgData *dat)
+{
+ TCHAR str[256];
+
+ if (dat->searchCount != 0 ) {
+ int i;
+
+ lstrcpy( str, TranslateT("Searching"));
+ for( i=0; i < dat->searchCount; i++ ) {
+ PROTOACCOUNT* pa = Proto_GetAccount( dat->search[i].szProto );
+ if ( !pa )
+ continue;
+
+ lstrcat(str, i ? _T(",") : _T( " " ));
+ lstrcat(str, pa->tszAccountName );
+ } }
+ else lstrcpy(str, TranslateT("Idle"));
+
+ SendMessage( hwndStatus, SB_SETTEXT, 0, (LPARAM)str );
+}
+
+struct ProtoResultsSummary {
+ const char *szProto;
+ int count;
+};
+void SetStatusBarResultInfo(HWND hwndDlg)
+{
+ HWND hwndStatus=GetDlgItem(hwndDlg,IDC_STATUSBAR);
+ HWND hwndResults=GetDlgItem(hwndDlg,IDC_RESULTS);
+ LV_ITEM lvi;
+ struct ListSearchResult *lsr;
+ struct ProtoResultsSummary *subtotal=NULL;
+ int subtotalCount=0;
+ int i,total;
+ TCHAR str[256];
+
+ total=ListView_GetItemCount(hwndResults);
+ for(lvi.iItem=total-1;lvi.iItem>=0;lvi.iItem--) {
+ lvi.mask=LVIF_PARAM;
+ ListView_GetItem(hwndResults,&lvi);
+ lsr=(struct ListSearchResult*)lvi.lParam;
+ if(lsr==NULL) continue;
+ for(i=0;i<subtotalCount;i++) {
+ if(subtotal[i].szProto==lsr->szProto) {
+ subtotal[i].count++;
+ break;
+ }
+ }
+ if(i==subtotalCount) {
+ subtotal=(struct ProtoResultsSummary*)mir_realloc(subtotal,sizeof(struct ProtoResultsSummary)*(subtotalCount+1));
+ subtotal[subtotalCount].szProto=lsr->szProto;
+ subtotal[subtotalCount++].count=1;
+ }
+ }
+ if ( total != 0 ) {
+ TCHAR substr[64];
+ PROTOACCOUNT* pa = Proto_GetAccount( subtotal[0].szProto );
+ if ( pa == NULL )
+ return;
+
+ if ( subtotalCount == 1 ) {
+ if(total==1) mir_sntprintf( str, SIZEOF(str), TranslateT("1 %s user found"), pa->tszAccountName );
+ else mir_sntprintf( str, SIZEOF(str), TranslateT("%d %s users found"), total, pa->tszAccountName );
+ }
+ else {
+ mir_sntprintf( str, SIZEOF(str), TranslateT("%d users found ("),total);
+ for( i=0; i < subtotalCount; i++ ) {
+ if ( i ) {
+ if (( pa = Proto_GetAccount( subtotal[i].szProto )) == NULL )
+ return;
+ lstrcat( str, _T(", "));
+ }
+ mir_sntprintf( substr, SIZEOF(substr), _T("%d %s"), subtotal[i].count, pa->tszAccountName );
+ lstrcat( str, substr );
+ }
+ lstrcat( str, _T(")"));
+ }
+ mir_free(subtotal);
+ }
+ else lstrcpy(str, TranslateT("No users found"));
+ SendMessage(hwndStatus, SB_SETTEXT, 2, (LPARAM)str );
+}
+
+void CreateResultsColumns(HWND hwndResults,struct FindAddDlgData *dat,char *szProto)
+{
+ SaveColumnSizes(hwndResults);
+ while(ListView_DeleteColumn(hwndResults,0));
+ ListView_SetImageList(hwndResults,dat->himlComboIcons,LVSIL_SMALL);
+ LoadColumnSizes(hwndResults,szProto);
+}
+
+void ShowMoreOptionsMenu(HWND hwndDlg,int x,int y)
+{
+ struct FindAddDlgData *dat;
+ HMENU hPopupMenu,hMenu;
+ int commandId;
+ struct ListSearchResult *lsr;
+
+ dat=(struct FindAddDlgData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+
+ { LVITEM lvi;
+ if(ListView_GetSelectedCount(GetDlgItem(hwndDlg,IDC_RESULTS))!=1) return;
+ lvi.mask=LVIF_PARAM;
+ lvi.iItem=ListView_GetNextItem(GetDlgItem(hwndDlg,IDC_RESULTS),-1,LVNI_ALL|LVNI_SELECTED);
+ ListView_GetItem(GetDlgItem(hwndDlg,IDC_RESULTS),&lvi);
+ lsr=(struct ListSearchResult*)lvi.lParam;
+ }
+
+ hMenu=LoadMenu(hMirandaInst,MAKEINTRESOURCE(IDR_CONTEXT));
+ hPopupMenu=GetSubMenu(hMenu,4);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)hPopupMenu,0);
+ commandId=TrackPopupMenu(hPopupMenu,TPM_RIGHTBUTTON|TPM_RETURNCMD,x,y,0,hwndDlg,NULL);
+ switch(commandId) {
+ case IDC_ADD:
+ { ADDCONTACTSTRUCT acs;
+
+ acs.handle=NULL;
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=lsr->szProto;
+ acs.psr=&lsr->psr;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)hwndDlg,(LPARAM)&acs);
+ break;
+ }
+ case IDC_DETAILS:
+ { HANDLE hContact;
+ hContact=(HANDLE)CallProtoService(lsr->szProto,PS_ADDTOLIST,PALF_TEMPORARY,(LPARAM)&lsr->psr);
+ CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)hContact,0);
+ break;
+ }
+ case IDC_SENDMESSAGE:
+ { HANDLE hContact;
+ hContact=(HANDLE)CallProtoService(lsr->szProto,PS_ADDTOLIST,PALF_TEMPORARY,(LPARAM)&lsr->psr);
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)(const char*)NULL);
+ break;
+ }
+ }
+ DestroyMenu(hPopupMenu);
+ DestroyMenu(hMenu);
+}
+
+
diff --git a/src/modules/fonts/FontOptions.cpp b/src/modules/fonts/FontOptions.cpp
new file mode 100644
index 0000000000..cbd19d23de
--- /dev/null
+++ b/src/modules/fonts/FontOptions.cpp
@@ -0,0 +1,1523 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "FontService.h"
+
+// *_w2 is working copy of list
+// *_w3 is stores initial configuration
+
+static int sttCompareFont( const TFontID* p1, const TFontID* p2 )
+{
+ int result = _tcscmp( p1->group, p2->group );
+ if ( result != 0 )
+ return result;
+ result = p1->order - p2->order;
+ if ( result != 0 )
+ return result;
+ return _tcscmp( TranslateTS(p1->name), TranslateTS(p2->name) );
+}
+
+OBJLIST<TFontID> font_id_list( 20, sttCompareFont ), font_id_list_w2( 20, sttCompareFont ), font_id_list_w3( 20, sttCompareFont );
+
+static int sttCompareColour( const TColourID* p1, const TColourID* p2 )
+{
+ int result = _tcscmp( p1->group, p2->group );
+ if ( result != 0 )
+ return result;
+ result = p1->order - p2->order;
+ if ( result != 0 )
+ return result;
+
+ return _tcscmp( TranslateTS(p1->name), TranslateTS(p2->name) );
+}
+
+OBJLIST<TColourID> colour_id_list( 10, sttCompareColour ), colour_id_list_w2( 10, sttCompareColour ), colour_id_list_w3( 10, sttCompareColour );
+
+
+static int sttCompareEffect( const TEffectID* p1, const TEffectID* p2 )
+{
+ int result = _tcscmp( p1->group, p2->group );
+ if ( result != 0 )
+ return result;
+ result = p1->order - p2->order;
+ if ( result != 0 )
+ return result;
+
+ return _tcscmp( TranslateTS(p1->name), TranslateTS(p2->name) );
+}
+
+OBJLIST<TEffectID> effect_id_list( 10, sttCompareEffect ), effect_id_list_w2( 10, sttCompareEffect ), effect_id_list_w3( 10, sttCompareEffect );
+
+typedef struct DrawTextWithEffectParam_tag
+{
+ int cbSize;
+ HDC hdc; // handle to DC
+ LPCTSTR lpchText; // text to draw
+ int cchText; // length of text to draw
+ LPRECT lprc; // rectangle coordinates
+ UINT dwDTFormat; // formatting options
+ FONTEFFECT * pEffect; // effect to be drawn on
+
+} DrawTextWithEffectParam;
+
+#define MS_DRAW_TEXT_WITH_EFFECTA "Modern/SkinEngine/DrawTextWithEffectA"
+#define MS_DRAW_TEXT_WITH_EFFECTW "Modern/SkinEngine/DrawTextWithEffectW"
+
+#ifdef _UNICODE
+ #define MS_DRAW_TEXT_WITH_EFFECT MS_DRAW_TEXT_WITH_EFFECTW
+#else
+ #define MS_DRAW_TEXT_WITH_EFFECT MS_DRAW_TEXT_WITH_EFFECTA
+#endif
+
+// Helper
+int __inline DrawTextWithEffect( HDC hdc, LPCTSTR lpchText, int cchText, RECT * lprc, UINT dwDTFormat, FONTEFFECT * pEffect )
+{
+ DrawTextWithEffectParam params;
+ static BYTE bIfServiceExists = ServiceExists( MS_DRAW_TEXT_WITH_EFFECT ) ? 1 : 0;
+
+ if ( pEffect == NULL || pEffect->effectIndex == 0 )
+ return DrawText ( hdc, lpchText, cchText, lprc, dwDTFormat ); // If no effect specified draw by GDI it just more careful with ClearType
+
+ if ( bIfServiceExists == 0)
+ return DrawText ( hdc, lpchText, cchText, lprc, dwDTFormat );
+
+ // else
+ params.cbSize = sizeof( DrawTextWithEffectParam );
+ params.hdc = hdc;
+ params.lpchText = lpchText;
+ params.cchText = cchText;
+ params.lprc = lprc;
+ params.dwDTFormat = dwDTFormat;
+ params.pEffect = pEffect;
+ return CallService( MS_DRAW_TEXT_WITH_EFFECT, (WPARAM)&params, 0 );
+}
+
+
+#define UM_SETFONTGROUP (WM_USER + 101)
+#define TIMER_ID 11015
+
+#define FSUI_COLORBOXWIDTH 50
+#define FSUI_COLORBOXLEFT 5
+#define FSUI_FONTFRAMEHORZ 5
+#define FSUI_FONTFRAMEVERT 4
+#define FSUI_FONTLEFT (FSUI_COLORBOXLEFT+FSUI_COLORBOXWIDTH+5)
+
+extern void UpdateFontSettings(TFontID *font_id, TFontSettings *fontsettings);
+extern void UpdateColourSettings(TColourID *colour_id, COLORREF *colour);
+extern void UpdateEffectSettings(TEffectID* effect_id, TEffectSettings* effectsettings);
+
+void WriteLine(HANDLE fhand, char *line)
+{
+ DWORD wrote;
+ strcat(line, "\r\n");
+ WriteFile(fhand, line, (DWORD)strlen(line), &wrote, 0);
+}
+
+BOOL ExportSettings(HWND hwndDlg, TCHAR *filename, OBJLIST<TFontID>& flist, OBJLIST<TColourID>& clist, OBJLIST<TEffectID>& elist)
+{
+ int i;
+ char header[512], buff[1024], abuff[1024];
+
+ HANDLE fhand = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ if(fhand == INVALID_HANDLE_VALUE) {
+ MessageBox(hwndDlg, filename, TranslateT("Failed to create file"), MB_ICONWARNING | MB_OK);
+ return FALSE;
+ }
+
+ header[0] = 0;
+
+ strcpy(buff, "SETTINGS:\r\n");
+ WriteLine(fhand, buff);
+
+ for ( i = 0; i < flist.getCount(); i++ ) {
+ TFontID& F = flist[i];
+
+ mir_snprintf(buff, SIZEOF(buff), "\r\n[%s]", F.dbSettingsGroup);
+ if ( strcmp( buff, header ) != 0) {
+ strcpy(header, buff);
+ WriteLine(fhand, buff);
+ }
+
+ if ( F.flags & FIDF_APPENDNAME )
+ mir_snprintf( buff, SIZEOF(buff), "%sName=s", F.prefix );
+ else
+ mir_snprintf( buff, SIZEOF(buff), "%s=s", F.prefix );
+
+ #if defined( _UNICODE )
+ WideCharToMultiByte(code_page, 0, F.value.szFace, -1, abuff, 1024, 0, 0);
+ abuff[1023]=0;
+ strcat( buff, abuff );
+ #else
+ strcat( buff, F.value.szFace );
+ #endif
+ WriteLine(fhand, buff);
+
+ mir_snprintf(buff, SIZEOF(buff), "%sSize=b", F.prefix);
+ if ( F.flags & FIDF_SAVEACTUALHEIGHT ) {
+ HDC hdc;
+ SIZE size;
+ HFONT hFont, hOldFont;
+ LOGFONT lf;
+ CreateFromFontSettings( &F.value, &lf );
+ hFont = CreateFontIndirect(&lf);
+
+ hdc = GetDC(hwndDlg);
+ hOldFont = (HFONT)SelectObject(hdc, hFont);
+ GetTextExtentPoint32(hdc, _T("_W"), 2, &size);
+ ReleaseDC(hwndDlg, hdc);
+ SelectObject(hdc, hOldFont);
+ DeleteObject(hFont);
+
+ strcat(buff, _itoa((BYTE)size.cy, abuff, 10));
+ }
+ else if(F.flags & FIDF_SAVEPOINTSIZE) {
+ HDC hdc = GetDC(hwndDlg);
+ strcat(buff, _itoa((BYTE)-MulDiv(F.value.size, 72, GetDeviceCaps(hdc, LOGPIXELSY)), abuff, 10));
+ ReleaseDC(hwndDlg, hdc);
+ }
+ else strcat(buff, _itoa((BYTE)F.value.size, abuff, 10));
+
+ WriteLine(fhand, buff);
+
+ mir_snprintf(buff, SIZEOF(buff), "%sSty=b%d", F.prefix, (BYTE)F.value.style);
+ WriteLine(fhand, buff);
+ mir_snprintf(buff, SIZEOF(buff), "%sSet=b%d", F.prefix, (BYTE)F.value.charset);
+ WriteLine(fhand, buff);
+ mir_snprintf(buff, SIZEOF(buff), "%sCol=d%d", F.prefix, (DWORD)F.value.colour);
+ WriteLine(fhand, buff);
+ if(F.flags & FIDF_NOAS) {
+ mir_snprintf(buff, SIZEOF(buff), "%sAs=w%d", F.prefix, (WORD)0x00FF);
+ WriteLine(fhand, buff);
+ }
+ mir_snprintf(buff, SIZEOF(buff), "%sFlags=w%d", F.prefix, (WORD)F.flags);
+ WriteLine(fhand, buff);
+ }
+
+ header[0] = 0;
+ for ( i=0; i < clist.getCount(); i++ ) {
+ TColourID& C = clist[i];
+
+ mir_snprintf(buff, SIZEOF(buff), "\r\n[%s]", C.dbSettingsGroup );
+ if(strcmp(buff, header) != 0) {
+ strcpy(header, buff);
+ WriteLine(fhand, buff);
+ }
+ mir_snprintf(buff, SIZEOF(buff), "%s=d%d", C.setting, (DWORD)C.value );
+ WriteLine(fhand, buff);
+ }
+
+ header[0] = 0;
+ for ( i=0; i < elist.getCount(); i++ ) {
+ TEffectID& E = elist[i];
+
+ mir_snprintf(buff, SIZEOF(buff), "\r\n[%s]", E.dbSettingsGroup );
+ if(strcmp(buff, header) != 0) {
+ strcpy(header, buff);
+ WriteLine(fhand, buff);
+ }
+ mir_snprintf(buff, SIZEOF(buff), "%sEffect=b%d", E.setting, E.value.effectIndex );
+ WriteLine(fhand, buff);
+ mir_snprintf(buff, SIZEOF(buff), "%sEffectCol1=d%d", E.setting, E.value.baseColour );
+ WriteLine(fhand, buff);
+ mir_snprintf(buff, SIZEOF(buff), "%sEffectCol2=d%d", E.setting, E.value.secondaryColour );
+ WriteLine(fhand, buff);
+ }
+
+
+ CloseHandle(fhand);
+ return TRUE;
+}
+
+void OptionsChanged()
+{
+ NotifyEventHooks(hFontReloadEvent, 0, 0);
+ NotifyEventHooks(hColourReloadEvent, 0, 0);
+}
+
+TOOLINFO ti;
+int x, y;
+
+UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
+ switch(uiMsg) {
+ case WM_INITDIALOG: {
+ CHOOSEFONT* cf = (CHOOSEFONT *)lParam;
+
+ TranslateDialogDefault(hdlg);
+ ShowWindow(GetDlgItem(hdlg, 1095), SW_HIDE);
+ if(cf && (cf->lCustData & FIDF_DISABLESTYLES)) {
+ EnableWindow(GetDlgItem(hdlg, 1137), FALSE);
+ ShowWindow(GetDlgItem(hdlg, 1137), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, 1095), SW_SHOW);
+ }
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+struct FSUIListItemData
+{
+ int font_id;
+ int colour_id;
+ int effect_id;
+};
+
+
+static BOOL sttFsuiBindColourIdToFonts(HWND hwndList, const TCHAR *name, const TCHAR *backgroundGroup, const TCHAR *backgroundName, int colourId)
+{
+ int i;
+ BOOL res = FALSE;
+ for (i = SendMessage(hwndList, LB_GETCOUNT, 0, 0); i--; )
+ {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendMessage(hwndList, LB_GETITEMDATA, i, 0);
+ if ( itemData && itemData->font_id >= 0) {
+ TFontID& F = font_id_list_w2[itemData->font_id];
+
+ if ( name && !_tcscmp( F.name, name )) {
+ itemData->colour_id = colourId;
+ res = TRUE;
+ }
+
+ if ( backgroundGroup && backgroundName && !_tcscmp( F.backgroundGroup, backgroundGroup) && !_tcscmp( F.backgroundName, backgroundName)) {
+ itemData->colour_id = colourId;
+ res = TRUE;
+ } } }
+
+ return res;
+}
+
+static BOOL sttFsuiBindEffectIdToFonts(HWND hwndList, const TCHAR *name, int effectId)
+{
+ int i;
+ BOOL res = FALSE;
+ for (i = SendMessage(hwndList, LB_GETCOUNT, 0, 0); i--; )
+ {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendMessage(hwndList, LB_GETITEMDATA, i, 0);
+ if ( itemData && itemData->font_id >= 0) {
+ TFontID& F = font_id_list_w2[itemData->font_id];
+
+ if ( name && !_tcscmp( F.name, name )) {
+ itemData->effect_id = effectId;
+ res = TRUE;
+ }
+
+ } }
+
+ return res;
+}
+
+static HTREEITEM sttFindNamedTreeItemAt(HWND hwndTree, HTREEITEM hItem, const TCHAR *name)
+{
+ TVITEM tvi = {0};
+ TCHAR str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = TreeView_GetChild(hwndTree, hItem);
+ else
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = MAX_PATH;
+
+ while (tvi.hItem)
+ {
+ TreeView_GetItem(hwndTree, &tvi);
+
+ if (!lstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return NULL;
+}
+
+static void sttFsuiCreateSettingsTreeNode(HWND hwndTree, const TCHAR *groupName)
+{
+ TCHAR itemName[1024];
+ TCHAR* sectionName;
+ int sectionLevel = 0;
+
+ HTREEITEM hSection = NULL;
+ lstrcpy(itemName, groupName);
+ sectionName = itemName;
+
+ while (sectionName) {
+ // allow multi-level tree
+ TCHAR* pItemName = sectionName;
+ HTREEITEM hItem;
+
+ if (sectionName = _tcschr(sectionName, '/')) {
+ // one level deeper
+ *sectionName = 0;
+ }
+
+ pItemName = TranslateTS( pItemName );
+
+ hItem = sttFindNamedTreeItemAt(hwndTree, hSection, pItemName);
+ if (!sectionName || !hItem) {
+ if (!hItem) {
+ TVINSERTSTRUCT tvis = {0};
+ TreeItem *treeItem = (TreeItem *)mir_alloc(sizeof(TreeItem));
+ treeItem->groupName = sectionName ? NULL : mir_tstrdup(groupName);
+ treeItem->paramName = mir_t2a(itemName);
+
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_SORT;//TVI_LAST;
+ tvis.item.mask = TVIF_TEXT|TVIF_PARAM;
+ tvis.item.pszText = pItemName;
+ tvis.item.lParam = (LPARAM)treeItem;
+
+ hItem = TreeView_InsertItem(hwndTree, &tvis);
+
+ ZeroMemory(&tvis.item, sizeof(tvis.item));
+ tvis.item.hItem = hItem;
+ tvis.item.mask = TVIF_HANDLE|TVIF_STATE;
+ tvis.item.state = tvis.item.stateMask = DBGetContactSettingByte(NULL, "FontServiceUI", treeItem->paramName, TVIS_EXPANDED );
+ TreeView_SetItem(hwndTree, &tvis.item);
+ } }
+
+ if (sectionName) {
+ *sectionName = '/';
+ sectionName++;
+ }
+
+ sectionLevel++;
+
+ hSection = hItem;
+ }
+}
+
+static void sttSaveCollapseState( HWND hwndTree )
+{
+ HTREEITEM hti;
+ TVITEM tvi;
+
+ hti = TreeView_GetRoot( hwndTree );
+ while( hti != NULL ) {
+ HTREEITEM ht;
+ TreeItem *treeItem;
+
+ tvi.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM;
+ tvi.hItem = hti;
+ tvi.stateMask = (DWORD)-1;
+ TreeView_GetItem( hwndTree, &tvi );
+
+ if( tvi.cChildren > 0 ) {
+ treeItem = (TreeItem *)tvi.lParam;
+ if ( tvi.state & TVIS_EXPANDED )
+ DBWriteContactSettingByte(NULL, "FontServiceUI", treeItem->paramName, TVIS_EXPANDED );
+ else
+ DBWriteContactSettingByte(NULL, "FontServiceUI", treeItem->paramName, 0 );
+ }
+
+ ht = TreeView_GetChild( hwndTree, hti );
+ if( ht == NULL ) {
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ while( ht == NULL ) {
+ hti = TreeView_GetParent( hwndTree, hti );
+ if( hti == NULL ) break;
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ } }
+
+ hti = ht;
+} }
+
+static void sttFreeListItems(HWND hList)
+{
+ int idx = 0;
+ LRESULT res;
+ FSUIListItemData *itemData;
+ int count = SendMessage( hList, LB_GETCOUNT, 0, 0 );
+ if ( count > 0 ) {
+ while ( idx < count) {
+ res = SendMessage( hList, LB_GETITEMDATA, idx++, 0 );
+ itemData = (FSUIListItemData *)res;
+ if ( itemData && res != LB_ERR )
+ mir_free( itemData );
+ }
+ }
+}
+
+static BOOL ShowEffectButton( HWND hwndDlg, BOOL bShow )
+{
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BKGCOLOUR), bShow ? SW_HIDE : SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BKGCOLOUR_STATIC), bShow ? SW_HIDE : SW_SHOW);
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EFFECT), bShow ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EFFECT_STATIC), bShow ? SW_SHOW : SW_HIDE);
+ return TRUE;
+}
+
+static INT_PTR CALLBACK ChooseEffectDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ static TEffectSettings * pEffect = NULL;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ pEffect = ( TEffectSettings*) lParam;
+ {
+ int i;
+ TCHAR * ModernEffectNames[]=
+ {
+ _T("<none>"),
+ _T("Shadow at left"),
+ _T("Shadow at right"),
+ _T("Outline"),
+ _T("Outline smooth"),
+ _T("Smooth bump"),
+ _T("Contour thin"),
+ _T("Contour heavy"),
+ };
+
+ for ( i=0; i<SIZEOF(ModernEffectNames) ; i++ )
+ {
+ int itemid = SendDlgItemMessage(hwndDlg, IDC_EFFECT_COMBO, CB_ADDSTRING,0,(LPARAM)TranslateTS(ModernEffectNames[i]));
+ SendDlgItemMessage(hwndDlg, IDC_EFFECT_COMBO, CB_SETITEMDATA, itemid, i );
+ SendDlgItemMessage(hwndDlg, IDC_EFFECT_COMBO, CB_SETCURSEL, 0, 0 );
+ }
+
+ int cnt=SendDlgItemMessage(hwndDlg, IDC_EFFECT_COMBO, CB_GETCOUNT, 0, 0 );
+ for ( i = 0; i < cnt; i++ ) {
+ if (SendDlgItemMessage(hwndDlg,IDC_EFFECT_COMBO,CB_GETITEMDATA,i,0)==pEffect->effectIndex ) {
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COMBO,CB_SETCURSEL, i, 0 );
+ break;
+ } } }
+
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR1,CPM_SETCOLOUR,0,pEffect->baseColour&0x00FFFFFF);
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR2,CPM_SETCOLOUR,0,pEffect->secondaryColour&0x00FFFFFF);
+
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN1,UDM_SETRANGE,0,MAKELONG(255,0));
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN2,UDM_SETRANGE,0,MAKELONG(255,0));
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN1,UDM_SETPOS,0,MAKELONG((BYTE)~((BYTE)((pEffect->baseColour&0xFF000000)>>24)),0));
+ SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN2,UDM_SETPOS,0,MAKELONG((BYTE)~((BYTE)((pEffect->secondaryColour&0xFF000000)>>24)),0));
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ {
+ int i = SendDlgItemMessage(hwndDlg,IDC_EFFECT_COMBO,CB_GETCURSEL, 0, 0 );
+ pEffect->effectIndex=(BYTE)SendDlgItemMessage(hwndDlg,IDC_EFFECT_COMBO,CB_GETITEMDATA,i,0);
+ pEffect->baseColour=SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR1,CPM_GETCOLOUR,0,0)|((~(BYTE)SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN1,UDM_GETPOS,0,0))<<24);
+ pEffect->secondaryColour=SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR2,CPM_GETCOLOUR,0,0)|((~(BYTE)SendDlgItemMessage(hwndDlg,IDC_EFFECT_COLOUR_SPIN2,UDM_GETPOS,0,0))<<24);;
+ }
+ EndDialog( hwndDlg, IDOK );
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog( hwndDlg, IDCANCEL );
+ return TRUE;
+ }
+ break;
+ case WM_DESTROY:
+ pEffect = NULL;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static BOOL ChooseEffectDialog( HWND hwndParent, TEffectSettings * es)
+{
+ return ( DialogBoxParam( hMirandaInst, MAKEINTRESOURCE(IDD_CHOOSE_FONT_EFFECT), hwndParent, ChooseEffectDlgProc, (LPARAM) es ) == IDOK );
+}
+
+static void sttSaveFontData(HWND hwndDlg, TFontID &F)
+{
+ LOGFONT lf;
+ char str[128];
+
+ if ( F.flags & FIDF_APPENDNAME )
+ mir_snprintf(str, SIZEOF(str), "%sName", F.prefix);
+ else
+ mir_snprintf(str, SIZEOF(str), "%s", F.prefix);
+
+ if ( DBWriteContactSettingTString( NULL, F.dbSettingsGroup, str, F.value.szFace )) {
+ #if defined( _UNICODE )
+ char buff[1024];
+ WideCharToMultiByte(code_page, 0, F.value.szFace, -1, buff, 1024, 0, 0);
+ DBWriteContactSettingString(NULL, F.dbSettingsGroup, str, buff);
+ #endif
+ }
+
+ mir_snprintf(str, SIZEOF(str), "%sSize", F.prefix);
+ if ( F.flags & FIDF_SAVEACTUALHEIGHT ) {
+ HDC hdc;
+ SIZE size;
+ HFONT hFont, hOldFont;
+ CreateFromFontSettings( &F.value, &lf );
+ hFont = CreateFontIndirect( &lf );
+ hdc = GetDC(hwndDlg);
+ hOldFont = (HFONT)SelectObject( hdc, hFont );
+ GetTextExtentPoint32( hdc, _T("_W"), 2, &size);
+ ReleaseDC(hwndDlg, hdc);
+ SelectObject(hdc, hOldFont);
+ DeleteObject(hFont);
+
+ DBWriteContactSettingByte(NULL, F.dbSettingsGroup, str, (char)size.cy);
+ }
+ else if ( F.flags & FIDF_SAVEPOINTSIZE ) {
+ HDC hdc = GetDC(hwndDlg);
+ DBWriteContactSettingByte(NULL, F.dbSettingsGroup, str, (BYTE)-MulDiv(F.value.size, 72, GetDeviceCaps(hdc, LOGPIXELSY)));
+ ReleaseDC(hwndDlg, hdc);
+ }
+ else DBWriteContactSettingByte(NULL, F.dbSettingsGroup, str, F.value.size);
+
+ mir_snprintf(str, SIZEOF(str), "%sSty", F.prefix);
+ DBWriteContactSettingByte(NULL, F.dbSettingsGroup, str, F.value.style);
+ mir_snprintf(str, SIZEOF(str), "%sSet", F.prefix);
+ DBWriteContactSettingByte(NULL, F.dbSettingsGroup, str, F.value.charset);
+ mir_snprintf(str, SIZEOF(str), "%sCol", F.prefix);
+ DBWriteContactSettingDword(NULL, F.dbSettingsGroup, str, F.value.colour);
+ if ( F.flags & FIDF_NOAS ) {
+ mir_snprintf(str, SIZEOF(str), "%sAs", F.prefix);
+ DBWriteContactSettingWord(NULL, F.dbSettingsGroup, str, (WORD)0x00FF);
+ }
+ mir_snprintf(str, SIZEOF(str), "%sFlags", F.prefix);
+ DBWriteContactSettingWord(NULL, F.dbSettingsGroup, str, (WORD)F.flags);
+}
+
+static INT_PTR CALLBACK DlgProcLogOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ LOGFONT lf;
+
+ static HBRUSH hBkgColourBrush = 0;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ font_id_list_w2 = font_id_list;
+ font_id_list_w3 = font_id_list;
+
+ colour_id_list_w2 = colour_id_list;
+ colour_id_list_w3 = colour_id_list;
+
+ effect_id_list_w2 = effect_id_list;
+ effect_id_list_w3 = effect_id_list;
+
+ for ( i = 0; i < font_id_list_w2.getCount(); i++ ) {
+ TFontID& F = font_id_list_w2[i];
+ // sync settings with database
+ UpdateFontSettings( &F, &F.value );
+ sttFsuiCreateSettingsTreeNode(GetDlgItem(hwndDlg, IDC_FONTGROUP), F.group);
+ }
+
+ for ( i = 0; i < colour_id_list_w2.getCount(); i++ ) {
+ TColourID& C = colour_id_list_w2[i];
+
+ // sync settings with database
+ UpdateColourSettings( &C, &C.value );
+ sttFsuiCreateSettingsTreeNode(GetDlgItem(hwndDlg, IDC_FONTGROUP), C.group);
+ }
+
+ for ( i = 0; i < effect_id_list_w2.getCount(); i++ ) {
+ TEffectID& E = effect_id_list_w2[i];
+
+ // sync settings with database
+ UpdateEffectSettings( &E, &E.value );
+ sttFsuiCreateSettingsTreeNode(GetDlgItem(hwndDlg, IDC_FONTGROUP), E.group);
+ }
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_BKGCOLOUR, CPM_SETDEFAULTCOLOUR, 0, (LPARAM)GetSysColor(COLOR_WINDOW));
+ return TRUE;
+
+ case UM_SETFONTGROUP:
+ {
+ TreeItem *treeItem;
+ TCHAR *group_buff = NULL;
+ TVITEM tvi = {0};
+ tvi.hItem = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_FONTGROUP));
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_FONTGROUP), &tvi);
+ treeItem = (TreeItem *)tvi.lParam;
+ group_buff = treeItem->groupName;
+
+ sttFreeListItems(GetDlgItem(hwndDlg, IDC_FONTLIST));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_RESETCONTENT, 0, 0);
+
+ if (group_buff) {
+ BOOL need_restart = FALSE;
+ int fontId = 0, itemId;
+ int first_font_index = -1;
+ int colourId = 0;
+ int first_colour_index = -1;
+ int effectId = 0;
+ int first_effect_index = -1;
+
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, WM_SETREDRAW, FALSE, 0);
+
+ for ( fontId = 0; fontId < font_id_list_w2.getCount(); fontId++ ) {
+ TFontID& F = font_id_list_w2[fontId];
+ if ( _tcsncmp( F.group, group_buff, 64 ) == 0 ) {
+ FSUIListItemData *itemData = ( FSUIListItemData* )mir_alloc(sizeof(FSUIListItemData));
+ itemData->colour_id = -1;
+ itemData->effect_id = -1;
+ itemData->font_id = fontId;
+
+ if ( first_font_index == -1 )
+ first_font_index = fontId;
+
+ itemId = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_ADDSTRING, (WPARAM)-1, (LPARAM)itemData);
+ need_restart |= (F.flags & FIDF_NEEDRESTART);
+ } }
+
+// ShowWindow( GetDlgItem(hwndDlg, IDC_STAT_RESTART), (need_restart ? SW_SHOW : SW_HIDE));
+
+ if ( hBkgColourBrush ) {
+ DeleteObject( hBkgColourBrush );
+ hBkgColourBrush = 0;
+ }
+
+ for ( colourId = 0; colourId < colour_id_list_w2.getCount(); colourId++ ) {
+ TColourID& C = colour_id_list_w2[colourId];
+ if ( _tcsncmp( C.group, group_buff, 64 ) == 0 ) {
+ FSUIListItemData *itemData = NULL;
+ if ( first_colour_index == -1 )
+ first_colour_index = colourId;
+
+ if (!sttFsuiBindColourIdToFonts(GetDlgItem(hwndDlg, IDC_FONTLIST), C.name, C.group, C.name, colourId)) {
+ itemData = ( FSUIListItemData* )mir_alloc(sizeof(FSUIListItemData));
+ itemData->colour_id = colourId;
+ itemData->font_id = -1;
+ itemData->effect_id = -1;
+
+ itemId = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_ADDSTRING, (WPARAM)-1, (LPARAM)itemData);
+ }
+
+ if ( _tcscmp( C.name, _T("Background") ) == 0 )
+ hBkgColourBrush = CreateSolidBrush( C.value );
+ } }
+
+ if ( !hBkgColourBrush )
+ hBkgColourBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
+
+ for ( effectId = 0; effectId < effect_id_list_w2.getCount(); effectId++ ) {
+ TEffectID& E = effect_id_list_w2[effectId];
+ if ( _tcsncmp( E.group, group_buff, 64 ) == 0 ) {
+ FSUIListItemData *itemData = NULL;
+ if ( first_effect_index == -1 )
+ first_effect_index = effectId;
+
+ if (!sttFsuiBindEffectIdToFonts(GetDlgItem(hwndDlg, IDC_FONTLIST), E.name, effectId)) {
+ itemData = ( FSUIListItemData* )mir_alloc(sizeof(FSUIListItemData));
+ itemData->effect_id = effectId;
+ itemData->font_id = -1;
+ itemData->colour_id = -1;
+
+ itemId = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_ADDSTRING, (WPARAM)-1, (LPARAM)itemData);
+ } } }
+
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, WM_SETREDRAW, TRUE, 0);
+ UpdateWindow(GetDlgItem(hwndDlg, IDC_FONTLIST));
+
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_SETSEL, TRUE, 0);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_FONTLIST, LBN_SELCHANGE), 0);
+ }
+ else {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKGCOLOUR), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FONTCOLOUR), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHOOSEFONT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_RESET), FALSE);
+ ShowEffectButton(hwndDlg, FALSE);
+ }
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam;
+ HFONT hFont = NULL, hoFont = NULL;
+ SIZE fontSize;
+ BOOL bIsFont = FALSE;
+ FSUIListItemData *itemData = (FSUIListItemData *)mis->itemData;
+ TCHAR *itemName = NULL;
+ HDC hdc;
+
+ if ((mis->CtlID != IDC_FONTLIST) || (mis->itemID == -1))
+ break;
+
+ if (!itemData) return FALSE;
+
+ if (itemData->font_id >= 0) {
+ int iItem = itemData->font_id;
+ bIsFont = TRUE;
+ CreateFromFontSettings( &font_id_list_w2[iItem].value, &lf );
+ hFont = CreateFontIndirect(&lf);
+ itemName = TranslateTS(font_id_list_w2[iItem].name);
+ }
+
+ if (itemData->colour_id >= 0) {
+ int iItem = itemData->colour_id;
+ if ( !itemName )
+ itemName = TranslateTS( colour_id_list_w2[iItem].name );
+ }
+
+ hdc = GetDC(GetDlgItem(hwndDlg, mis->CtlID));
+ if ( hFont )
+ hoFont = (HFONT) SelectObject(hdc, hFont);
+ else
+ hoFont = (HFONT) SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, mis->CtlID, WM_GETFONT, 0, 0));
+
+ GetTextExtentPoint32(hdc, itemName, lstrlen(itemName), &fontSize);
+ if (hoFont) SelectObject(hdc, hoFont);
+ if (hFont) DeleteObject(hFont);
+ ReleaseDC(GetDlgItem(hwndDlg, mis->CtlID), hdc);
+ mis->itemWidth = fontSize.cx + 2*FSUI_FONTFRAMEHORZ + 4;
+ mis->itemHeight = fontSize.cy + 2*FSUI_FONTFRAMEVERT + 4;
+ return TRUE;
+ }
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *) lParam;
+ HFONT hFont = NULL, hoFont = NULL;
+ COLORREF clBack = (COLORREF)-1;
+ COLORREF clText = GetSysColor(COLOR_WINDOWTEXT);
+ BOOL bIsFont = FALSE;
+ TCHAR *itemName = NULL;
+
+ FSUIListItemData *itemData = (FSUIListItemData *)dis->itemData;
+
+ FONTEFFECT Effect;
+ FONTEFFECT * pEffect = NULL;
+
+ if(dis->CtlID != IDC_FONTLIST)
+ break;
+
+ if (!itemData) return FALSE;
+
+ if ( itemData->font_id >= 0 ) {
+ int iItem = itemData->font_id;
+ bIsFont = TRUE;
+ CreateFromFontSettings(&font_id_list_w2[iItem].value, &lf );
+ hFont = CreateFontIndirect(&lf);
+ itemName = TranslateTS(font_id_list_w2[iItem].name);
+ clText = font_id_list_w2[iItem].value.colour;
+ }
+
+ if ( itemData->colour_id >= 0 ) {
+ int iItem = itemData->colour_id;
+ if (bIsFont)
+ clBack = colour_id_list_w2[iItem].value;
+ else {
+ clText = colour_id_list_w2[iItem].value;
+ itemName = TranslateTS(colour_id_list_w2[iItem].name);
+
+ } }
+
+ if ( itemData->effect_id >= 0 ) {
+ int iItem = itemData->effect_id;
+
+ Effect.effectIndex = effect_id_list_w2[iItem].value.effectIndex;
+ Effect.baseColour = effect_id_list_w2[iItem].value.baseColour;
+ Effect.secondaryColour = effect_id_list_w2[iItem].value.secondaryColour;
+ pEffect = &Effect;
+
+ if (!bIsFont)
+ itemName = TranslateTS(effect_id_list_w2[iItem].name);
+ }
+
+ if (hFont)
+ hoFont = (HFONT) SelectObject(dis->hDC, hFont);
+ else
+ hoFont = (HFONT) SelectObject(dis->hDC, (HFONT)SendDlgItemMessage(hwndDlg, dis->CtlID, WM_GETFONT, 0, 0));
+
+ SetBkMode(dis->hDC, TRANSPARENT);
+
+ if (dis->itemState & ODS_SELECTED) {
+ SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+ else {
+ SetTextColor(dis->hDC, bIsFont?clText:GetSysColor(COLOR_WINDOWTEXT));
+ if (bIsFont && (clBack != (COLORREF)-1)) {
+ HBRUSH hbrTmp = CreateSolidBrush(clBack);
+ FillRect(dis->hDC, &dis->rcItem, hbrTmp);
+ DeleteObject(hbrTmp);
+ }
+ else FillRect(dis->hDC, &dis->rcItem, bIsFont ? hBkgColourBrush : GetSysColorBrush(COLOR_WINDOW));
+ }
+
+ if ( bIsFont ) {
+ HBRUSH hbrBack;
+ RECT rc;
+
+ if (clBack != (COLORREF)-1)
+ hbrBack = CreateSolidBrush(clBack);
+ else {
+ LOGBRUSH lb;
+ GetObject(hBkgColourBrush, sizeof(lf), &lb);
+ hbrBack = CreateBrushIndirect(&lb);
+ }
+
+ SetRect(&rc,
+ dis->rcItem.left+FSUI_COLORBOXLEFT,
+ dis->rcItem.top+FSUI_FONTFRAMEVERT,
+ dis->rcItem.left+FSUI_COLORBOXLEFT+FSUI_COLORBOXWIDTH,
+ dis->rcItem.bottom-FSUI_FONTFRAMEVERT);
+
+ FillRect(dis->hDC, &rc, hbrBack);
+ DeleteObject(hbrBack);
+
+ FrameRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ rc.left += 1;
+ rc.top += 1;
+ rc.right -= 1;
+ rc.bottom -= 1;
+ FrameRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHTTEXT));
+
+ SetTextColor(dis->hDC, clText);
+
+ DrawTextWithEffect(dis->hDC, _T("abc"), 3, &rc, DT_CENTER|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS, pEffect );
+
+ if (dis->itemState & ODS_SELECTED) {
+ SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ pEffect = NULL; // Do not draw effect on selected item name text
+ }
+ rc = dis->rcItem;
+ rc.left += FSUI_FONTLEFT;
+ DrawTextWithEffect(dis->hDC, itemName, (int)_tcslen(itemName), &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS, pEffect );
+ } else
+ {
+ RECT rc;
+ HBRUSH hbrTmp;
+ SetRect(&rc,
+ dis->rcItem.left+FSUI_COLORBOXLEFT,
+ dis->rcItem.top+FSUI_FONTFRAMEVERT,
+ dis->rcItem.left+FSUI_COLORBOXLEFT+FSUI_COLORBOXWIDTH,
+ dis->rcItem.bottom-FSUI_FONTFRAMEVERT);
+
+ hbrTmp = CreateSolidBrush(clText);
+ FillRect(dis->hDC, &rc, hbrTmp);
+ DeleteObject(hbrTmp);
+
+ FrameRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ rc.left += 1;
+ rc.top += 1;
+ rc.right -= 1;
+ rc.bottom -= 1;
+ FrameRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHTTEXT));
+
+ rc = dis->rcItem;
+ rc.left += FSUI_FONTLEFT;
+
+ DrawTextWithEffect(dis->hDC, itemName, (int)_tcslen(itemName), &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS, pEffect );
+ }
+ if (hoFont) SelectObject(dis->hDC, hoFont);
+ if (hFont) DeleteObject(hFont);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_FONTLIST:
+ if (HIWORD(wParam) == LBN_SELCHANGE) {
+ int selCount, i;
+
+ char bEnableFont = 1;
+ char bEnableClText = 1;
+ char bEnableClBack = 1;
+ char bEnableEffect = 1;
+ char bEnableReset = 1;
+
+ COLORREF clBack = 0xffffffff;
+ COLORREF clText = 0xffffffff;
+
+ if (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, (WPARAM)0, (LPARAM)0)) {
+ int *selItems = (int *)mir_alloc(font_id_list_w2.getCount() * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM)selItems);
+ for (i = 0; i < selCount; ++i) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (IsBadReadPtr(itemData, sizeof(FSUIListItemData))) continue; // prevent possible problems with corrupted itemData
+
+ if (bEnableClBack && (itemData->colour_id < 0))
+ bEnableClBack = 0;
+ if (bEnableEffect && (itemData->effect_id < 0))
+ bEnableEffect = 0;
+ if (bEnableFont && (itemData->font_id < 0))
+ bEnableFont = 0;
+ if (!bEnableFont || bEnableClText && (itemData->font_id < 0))
+ bEnableClText = 0;
+ if (bEnableReset && (itemData->font_id >= 0) && !(font_id_list_w2[itemData->font_id].flags&FIDF_DEFAULTVALID))
+ bEnableReset = 0;
+
+ if (bEnableClBack && (itemData->colour_id >= 0) && (clBack == 0xffffffff))
+ clBack = colour_id_list_w2[itemData->colour_id].value;
+ if (bEnableClText && (itemData->font_id >= 0) && (clText == 0xffffffff))
+ clText = font_id_list_w2[itemData->font_id].value.colour;
+ }
+ mir_free(selItems);
+ }
+ else {
+ bEnableFont = 0;
+ bEnableClText = 0;
+ bEnableClBack = 0;
+ bEnableReset = 0;
+ bEnableEffect = 0;
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKGCOLOUR), bEnableClBack);
+ ShowEffectButton( hwndDlg, bEnableEffect && !bEnableClBack );
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FONTCOLOUR), bEnableClText);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHOOSEFONT), bEnableFont);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_RESET), bEnableReset);
+
+ if (bEnableClBack) SendDlgItemMessage(hwndDlg, IDC_BKGCOLOUR, CPM_SETCOLOUR, 0, clBack);
+ if (bEnableClText) SendDlgItemMessage(hwndDlg, IDC_FONTCOLOUR, CPM_SETCOLOUR, 0, clText);
+
+ return TRUE;
+ }
+
+ if (HIWORD(wParam) != LBN_DBLCLK)
+ return TRUE;
+
+ //fall through
+
+ case IDC_CHOOSEFONT:
+ {
+ int selCount;
+ if (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, 0, 0)) {
+ FSUIListItemData *itemData;
+ CHOOSEFONT cf = { 0 };
+ int i;
+ int *selItems = (int *)mir_alloc(selCount * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM) selItems);
+ itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[0], 0);
+ if (itemData->font_id < 0) {
+ mir_free(selItems);
+ if (itemData->colour_id >= 0)
+ SendDlgItemMessage(hwndDlg, IDC_BKGCOLOUR, WM_LBUTTONUP, 0, 0);
+ return TRUE;
+ }
+
+ TFontID& F = font_id_list_w2[itemData->font_id];
+
+ CreateFromFontSettings(&F.value, &lf );
+
+ cf.lStructSize = sizeof(cf);
+ cf.hwndOwner = hwndDlg;
+ cf.lpLogFont = &lf;
+ cf.lCustData = 0;
+
+ if ( F.flags & FIDF_ALLOWEFFECTS )
+ {
+ cf.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS | CF_ENABLETEMPLATE | CF_ENABLEHOOK;
+ // use custom font dialog to disable colour selection
+ cf.hInstance = hMirandaInst;
+ cf.lpTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_FONT);
+ cf.lpfnHook = CFHookProc;
+ }
+ else if ( F.flags & FIDF_DISABLESTYLES ) { // no style selection, mutually exclusive with FIDF_ALLOWEFFECTS
+ cf.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLETEMPLATE | CF_ENABLEHOOK | CF_TTONLY | CF_NOOEMFONTS;
+ cf.lCustData = F.flags;
+ cf.hInstance = hMirandaInst;
+ cf.lpTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_FONT);
+ cf.lpfnHook = CFHookProc;
+ lf.lfWeight = FW_NORMAL;
+ lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE;
+ }
+ else cf.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
+
+ if (ChooseFont(&cf)) {
+ for (i = 0; i < selCount; ++i) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (itemData->font_id < 0)
+ continue;
+
+ TFontID& F1 = font_id_list_w2[itemData->font_id];
+ F1.value.size = (char)lf.lfHeight;
+ F1.value.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0);
+ F1.value.charset = lf.lfCharSet;
+ _tcscpy(F1.value.szFace, lf.lfFaceName);
+
+ MEASUREITEMSTRUCT mis = { 0 };
+ mis.CtlID = IDC_FONTLIST;
+ mis.itemID = selItems[i];
+ mis.itemData = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ SendMessage(hwndDlg, WM_MEASUREITEM, 0, (LPARAM) & mis);
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_SETITEMHEIGHT, selItems[i], mis.itemHeight);
+ }
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_FONTLIST), NULL, TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ mir_free(selItems);
+ }
+ return TRUE;
+ }
+ case IDC_EFFECT:
+ {
+ int selCount;
+ if (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, 0, 0)) {
+ FSUIListItemData *itemData;
+ TEffectSettings es = { 0 };
+ int i;
+ int *selItems = (int *)mir_alloc(selCount * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM) selItems);
+ itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[0], 0);
+ TEffectID& E = effect_id_list_w2[itemData->effect_id];
+ es = E.value;
+ if ( ChooseEffectDialog(hwndDlg, &es) ) {
+ for (i = 0; i < selCount; ++i) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (itemData->effect_id < 0)
+ continue;
+
+ TEffectID& E1 = effect_id_list_w2[itemData->effect_id];
+ E1.value = es;
+ }
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_FONTLIST), NULL, TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ mir_free(selItems);
+ }
+ break;
+ }
+ case IDC_FONTCOLOUR:
+ {
+ int selCount, i;
+ if (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, 0, 0)) {
+ int *selItems = (int *)mir_alloc(selCount * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM) selItems);
+ for (i = 0; i < selCount; i++) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (itemData->font_id < 0) continue;
+ font_id_list_w2[itemData->font_id].value.colour = SendDlgItemMessage(hwndDlg, IDC_FONTCOLOUR, CPM_GETCOLOUR, 0, 0);
+ }
+ mir_free(selItems);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_FONTLIST), NULL, FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE);
+ }
+ break;
+ }
+ case IDC_BKGCOLOUR:
+ {
+ int selCount, i;
+ if (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, 0, 0)) {
+ int *selItems = (int *)mir_alloc(selCount * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM) selItems);
+ for (i = 0; i < selCount; i++) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (itemData->colour_id < 0) continue;
+ colour_id_list_w2[itemData->colour_id].value = SendDlgItemMessage(hwndDlg, IDC_BKGCOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ if ( _tcscmp( colour_id_list_w2[itemData->colour_id].name, _T("Background") ) == 0 )
+ {
+ if ( hBkgColourBrush ) DeleteObject( hBkgColourBrush );
+ hBkgColourBrush = CreateSolidBrush( colour_id_list_w2[itemData->colour_id].value );
+ }
+ }
+ mir_free(selItems);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_FONTLIST), NULL, FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE);
+ }
+ break;
+ }
+ case IDC_BTN_RESET:
+ {
+ int selCount;
+ if (font_id_list_w2.getCount() && (selCount = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELCOUNT, (WPARAM)0, (LPARAM)0))) {
+ int *selItems = (int *)mir_alloc(font_id_list_w2.getCount() * sizeof(int));
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETSELITEMS, (WPARAM)selCount, (LPARAM)selItems);
+ for (i = 0; i < selCount; ++i) {
+ FSUIListItemData *itemData = (FSUIListItemData *)SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ if (IsBadReadPtr(itemData, sizeof(FSUIListItemData))) continue; // prevent possible problems with corrupted itemData
+
+ if((itemData->font_id >= 0) && (font_id_list_w2[itemData->font_id].flags & FIDF_DEFAULTVALID)) {
+ font_id_list_w2[itemData->font_id].value = font_id_list_w2[itemData->font_id].deffontsettings;
+
+ MEASUREITEMSTRUCT mis = { 0 };
+ mis.CtlID = IDC_FONTLIST;
+ mis.itemID = selItems[i];
+ mis.itemData = SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_GETITEMDATA, selItems[i], 0);
+ SendMessage(hwndDlg, WM_MEASUREITEM, 0, (LPARAM) & mis);
+ SendDlgItemMessage(hwndDlg, IDC_FONTLIST, LB_SETITEMHEIGHT, selItems[i], mis.itemHeight);
+ }
+
+ if (itemData->colour_id >= 0)
+ colour_id_list_w2[itemData->colour_id].value = colour_id_list_w2[itemData->colour_id].defcolour;
+
+ if (itemData->effect_id >= 0)
+ effect_id_list_w2[itemData->effect_id].value = effect_id_list_w2[itemData->effect_id].defeffect;
+
+ }
+ mir_free(selItems);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_FONTLIST), NULL, TRUE);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_FONTLIST, LBN_SELCHANGE), 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE);
+ }
+ break;
+ }
+ case IDC_BTN_EXPORT:
+ {
+ TCHAR fname_buff[MAX_PATH], filter[MAX_PATH];
+ mir_sntprintf(filter, SIZEOF(filter), _T("%s (*.ini)%c*.ini%c%s (*.txt)%c*.TXT%c%s (*.*)%c*.*%c"), TranslateT("Configuration Files"), 0, 0, TranslateT("Text Files"), 0, 0, TranslateT("All Files"), 0, 0);
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(ofn);
+ ofn.lpstrFile = fname_buff;
+ ofn.lpstrFile[0] = '\0';
+ ofn.nMaxFile = MAX_PATH;
+ ofn.hwndOwner = hwndDlg;
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
+ ofn.lpstrFilter = filter;
+ ofn.nFilterIndex = 1;
+
+ ofn.lpstrDefExt = _T("ini");
+
+ if ( GetSaveFileName( &ofn ) == TRUE )
+ if ( !ExportSettings( hwndDlg, ofn.lpstrFile, font_id_list, colour_id_list, effect_id_list ))
+ MessageBox(hwndDlg, TranslateT("Error writing file"), TranslateT("Error"), MB_ICONWARNING | MB_OK);
+ return TRUE;
+ }
+ case IDC_BTN_UNDO:
+ font_id_list_w2 = font_id_list_w3;
+ colour_id_list_w2 = colour_id_list_w3;
+ effect_id_list_w2 = effect_id_list_w3;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNDO), FALSE);
+
+ SendMessage(hwndDlg, UM_SETFONTGROUP, 0, 0);
+ break;
+
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->idFrom == 0 && ((LPNMHDR) lParam)->code == PSN_APPLY ) {
+ char str[32];
+
+ font_id_list_w3 = font_id_list;
+ colour_id_list_w3 = colour_id_list;
+ effect_id_list_w3 = effect_id_list;
+
+ EnableWindow( GetDlgItem(hwndDlg, IDC_BTN_UNDO), TRUE );
+
+ font_id_list = font_id_list_w2;
+ colour_id_list = colour_id_list_w2;
+ effect_id_list = effect_id_list_w2;
+
+ for ( i=0; i < font_id_list_w2.getCount(); i++ ) {
+ TFontID& F = font_id_list_w2[i];
+ sttSaveFontData(hwndDlg, F);
+ }
+
+ for ( i=0; i < colour_id_list_w2.getCount(); i++ ) {
+ TColourID& C = colour_id_list_w2[i];
+
+ mir_snprintf(str, SIZEOF(str), "%s", C.setting);
+ DBWriteContactSettingDword(NULL, C.dbSettingsGroup, str, C.value);
+ }
+
+ for ( i=0; i < effect_id_list_w2.getCount(); i++ ) {
+ TEffectID& E = effect_id_list_w2[i];
+
+ mir_snprintf(str, SIZEOF(str), "%sEffect", E.setting);
+ DBWriteContactSettingByte(NULL, E.dbSettingsGroup, str, E.value.effectIndex);
+
+ mir_snprintf(str, SIZEOF(str), "%sEffectCol1", E.setting);
+ DBWriteContactSettingDword(NULL, E.dbSettingsGroup, str, E.value.baseColour);
+
+ mir_snprintf(str, SIZEOF(str), "%sEffectCol2", E.setting);
+ DBWriteContactSettingDword(NULL, E.dbSettingsGroup, str, E.value.secondaryColour);
+ }
+
+ OptionsChanged();
+ return TRUE;
+ }
+
+ if (((LPNMHDR) lParam)->idFrom == IDC_FONTGROUP) {
+ switch(((NMHDR*)lParam)->code) {
+ case TVN_SELCHANGEDA: // !!!! This needs to be here - both !!
+ case TVN_SELCHANGEDW:
+ SendMessage(hwndDlg, UM_SETFONTGROUP, 0, 0);
+ break;
+
+ case TVN_DELETEITEMA: // no idea why both TVN_SELCHANGEDA/W should be there but let's keep this both too...
+ case TVN_DELETEITEMW:
+ {
+ TreeItem *treeItem = (TreeItem *)(((LPNMTREEVIEW)lParam)->itemOld.lParam);
+ if (treeItem) {
+ mir_free(treeItem->groupName);
+ mir_free(treeItem->paramName);
+ mir_free(treeItem);
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, TIMER_ID);
+ sttSaveCollapseState(GetDlgItem(hwndDlg, IDC_FONTGROUP));
+ DeleteObject(hBkgColourBrush);
+ font_id_list_w2.destroy();
+ font_id_list_w3.destroy();
+ colour_id_list_w2.destroy();
+ colour_id_list_w3.destroy();
+ effect_id_list_w2.destroy();
+ effect_id_list_w3.destroy();
+ sttFreeListItems(GetDlgItem(hwndDlg, IDC_FONTLIST));
+ break;
+ }
+ return FALSE;
+}
+
+int OptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+
+ odp.cbSize = sizeof(odp);
+ odp.cbSize = OPTIONPAGE_OLD_SIZE2;
+ odp.position = -790000000;
+ odp.hInstance = hMirandaInst;;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_FONTS);
+ odp.pszTitle = LPGEN("Fonts & Colors");
+ odp.pszGroup = LPGEN("Customize");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.nIDBottomSimpleControl = 0;
+ odp.pfnDlgProc = DlgProcLogOptions;
+ CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static TFontID *sttFindFont(OBJLIST<TFontID> &fonts, char *module, char *prefix)
+{
+ for ( int i = 0; i < fonts.getCount(); i++ )
+ {
+ TFontID& F = fonts[i];
+ if ( !lstrcmpA(F.dbSettingsGroup, module) && !lstrcmpA(F.prefix, prefix) )
+ return &F;
+ }
+
+ return 0;
+}
+
+static INT_PTR CALLBACK DlgProcModernOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ LOGFONT lf;
+
+ static TFontID fntHeader={0}, fntGeneral={0}, fntSmall={0};
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ fntHeader = *sttFindFont(font_id_list, "Fonts", "Header");
+ UpdateFontSettings(&fntHeader, &fntHeader.value);
+ fntGeneral = *sttFindFont(font_id_list, "Fonts", "Generic");
+ UpdateFontSettings(&fntGeneral, &fntGeneral.value);
+ fntSmall = *sttFindFont(font_id_list, "Fonts", "Small");
+ UpdateFontSettings(&fntSmall, &fntSmall.value);
+
+ return TRUE;
+ }
+
+ case WM_DRAWITEM:
+ {
+ TFontID *pf = 0;
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *) lParam;
+ switch (dis->CtlID)
+ {
+ case IDC_PREVIEWHEADER:
+ pf = &fntHeader;
+ break;
+ case IDC_PREVIEWGENERAL:
+ pf = &fntGeneral;
+ break;
+ case IDC_PREVIEWSMALL:
+ pf = &fntSmall;
+ break;
+ }
+
+ if (!pf) break;
+
+ HFONT hFont = NULL, hoFont = NULL;
+ COLORREF clText = GetSysColor(COLOR_WINDOWTEXT);
+ CreateFromFontSettings(&pf->value, &lf);
+ hFont = CreateFontIndirect(&lf);
+ hoFont = (HFONT) SelectObject(dis->hDC, hFont);
+ SetBkMode(dis->hDC, TRANSPARENT);
+ SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_BTNFACE));
+ DrawText(dis->hDC, TranslateT("Sample Text"), (int)_tcslen(TranslateT("Sample Text")), &dis->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS|DT_CENTER);
+ if (hoFont) SelectObject(dis->hDC, hoFont);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_CHOOSEFONTHEADER:
+ case IDC_CHOOSEFONTGENERAL:
+ case IDC_CHOOSEFONTSMALL:
+ {
+ CHOOSEFONT cf = { 0 };
+ TFontID *pf = NULL;
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHOOSEFONTHEADER:
+ pf = &fntHeader;
+ break;
+ case IDC_CHOOSEFONTGENERAL:
+ pf = &fntGeneral;
+ break;
+ case IDC_CHOOSEFONTSMALL:
+ pf = &fntSmall;
+ break;
+ };
+
+ CreateFromFontSettings(&pf->value, &lf);
+
+ cf.lStructSize = sizeof(cf);
+ cf.hwndOwner = hwndDlg;
+ cf.lpLogFont = &lf;
+ cf.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
+ if ( pf->flags & FIDF_ALLOWEFFECTS )
+ {
+ cf.Flags |= CF_EFFECTS | CF_ENABLETEMPLATE | CF_ENABLEHOOK;
+ // use custom font dialog to disable colour selection
+ cf.hInstance = hMirandaInst;
+ cf.lpTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_FONT);
+ cf.lpfnHook = CFHookProc;
+ }
+
+ if (ChooseFont(&cf))
+ {
+ pf->value.size = (char)lf.lfHeight;
+ pf->value.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0);
+ pf->value.charset = lf.lfCharSet;
+ _tcscpy(pf->value.szFace, lf.lfFaceName);
+
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEWHEADER), NULL, TRUE);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEWGENERAL), NULL, TRUE);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEWSMALL), NULL, TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->idFrom == 0 && ((LPNMHDR) lParam)->code == PSN_APPLY ) {
+ for ( i=0; i < font_id_list.getCount(); i++ )
+ {
+ TFontID &F = font_id_list[i];
+ if (F.deffontsettings.charset == SYMBOL_CHARSET) continue;
+
+ COLORREF cl = F.value.colour;
+ if ((F.flags&FIDF_CLASSMASK) == FIDF_CLASSHEADER ||
+ (F.flags&FIDF_CLASSMASK) == 0 &&
+ (_tcsstr(F.name, _T("Incoming nick")) ||
+ _tcsstr(F.name, _T("Outgoing nick")) ||
+ _tcsstr(F.name, _T("Incoming timestamp")) ||
+ _tcsstr(F.name, _T("Outgoing timestamp")))
+ )
+ {
+ F.value = fntHeader.value;
+ } else
+ if ((F.flags&FIDF_CLASSMASK) == FIDF_CLASSSMALL)
+ {
+ F.value = fntSmall.value;
+ } else
+ {
+ F.value = fntGeneral.value;
+ }
+ F.value.colour = cl;
+ sttSaveFontData(hwndDlg, F);
+ }
+
+ OptionsChanged();
+ }
+ break;
+ }
+ return FALSE;
+}
+
+INT_PTR CALLBACK AccMgrDlgProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK DlgPluginOpt(HWND, UINT, WPARAM, LPARAM);
+
+int FontsModernOptInit(WPARAM wParam, LPARAM lParam)
+{
+ static int iBoldControls[] =
+ {
+ IDC_TXT_TITLE1, IDC_TXT_TITLE2, IDC_TXT_TITLE3,
+ MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+ obj.cbSize = sizeof(obj);
+ obj.dwFlags = MODEROPT_FLG_TCHAR|MODEROPT_FLG_NORESIZE;
+ obj.hIcon = LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ obj.hInstance = hMirandaInst;
+ obj.iSection = MODERNOPT_PAGE_SKINS;
+ obj.iType = MODERNOPT_TYPE_SUBSECTIONPAGE;
+ obj.iBoldControls = iBoldControls;
+ obj.lptzSubsection = LPGENT("Fonts");
+ obj.lpzClassicGroup = "Customize";
+ obj.lpzClassicPage = "Fonts";
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_FONTS);
+ obj.pfnDlgProc = DlgProcModernOptions;
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+
+ obj.iSection = MODERNOPT_PAGE_ACCOUNTS;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_ACCOUNTS);
+ obj.pfnDlgProc = AccMgrDlgProc;
+ obj.lpzClassicGroup = NULL;
+ obj.lpzClassicPage = "Network";
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+
+ obj.iSection = MODERNOPT_PAGE_MODULES;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+// obj.lptzSubsection = LPGENT("Installed Plugins");
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_MODULES);
+ obj.pfnDlgProc = DlgPluginOpt;
+ obj.iBoldControls = iBoldControls;
+ obj.lpzClassicGroup = NULL;
+ obj.lpzClassicPage = NULL;
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ return 0;
+}
diff --git a/src/modules/fonts/FontService.cpp b/src/modules/fonts/FontService.cpp
new file mode 100644
index 0000000000..f0f1afae16
--- /dev/null
+++ b/src/modules/fonts/FontService.cpp
@@ -0,0 +1,126 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "m_fontservice.h"
+
+#include "FontService.h"
+
+int code_page = CP_ACP;
+HANDLE hFontReloadEvent, hColourReloadEvent;
+
+int OptInit( WPARAM, LPARAM );
+int FontsModernOptInit(WPARAM wParam, LPARAM lParam);
+
+INT_PTR RegisterFont(WPARAM wParam, LPARAM lParam);
+INT_PTR RegisterFontW(WPARAM wParam, LPARAM lParam);
+
+INT_PTR GetFont(WPARAM wParam, LPARAM lParam);
+INT_PTR GetFontW(WPARAM wParam, LPARAM lParam);
+
+INT_PTR RegisterColour(WPARAM wParam, LPARAM lParam);
+INT_PTR RegisterColourW(WPARAM wParam, LPARAM lParam);
+
+INT_PTR GetColour(WPARAM wParam, LPARAM lParam);
+INT_PTR GetColourW(WPARAM wParam, LPARAM lParam);
+
+INT_PTR RegisterEffect(WPARAM wParam, LPARAM lParam);
+INT_PTR RegisterEffectW(WPARAM wParam, LPARAM lParam);
+
+INT_PTR GetEffect(WPARAM wParam, LPARAM lParam);
+INT_PTR GetEffectW(WPARAM wParam, LPARAM lParam);
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_OPT_INITIALISE, OptInit);
+ HookEvent(ME_MODERNOPT_INITIALIZE, FontsModernOptInit);
+ return 0;
+}
+
+static int OnPreShutdown(WPARAM, LPARAM)
+{
+ DestroyHookableEvent(hFontReloadEvent);
+ DestroyHookableEvent(hColourReloadEvent);
+
+ font_id_list.destroy();
+ colour_id_list.destroy();
+ return 0;
+}
+
+int LoadFontserviceModule( void )
+{
+ code_page = LangPackGetDefaultCodePage();
+
+ CreateServiceFunction(MS_FONT_REGISTER, RegisterFont);
+ CreateServiceFunction(MS_FONT_GET, GetFont);
+
+ CreateServiceFunction(MS_COLOUR_REGISTER, RegisterColour);
+ CreateServiceFunction(MS_COLOUR_GET, GetColour);
+
+ CreateServiceFunction(MS_EFFECT_REGISTER, RegisterEffect);
+ CreateServiceFunction(MS_EFFECT_GET, GetEffect);
+
+#if defined( _UNICODE )
+ CreateServiceFunction(MS_FONT_REGISTERW, RegisterFontW);
+ CreateServiceFunction(MS_FONT_GETW, GetFontW);
+
+ CreateServiceFunction(MS_COLOUR_REGISTERW, RegisterColourW);
+ CreateServiceFunction(MS_COLOUR_GETW, GetColourW);
+
+ CreateServiceFunction(MS_EFFECT_REGISTERW, RegisterEffectW);
+ CreateServiceFunction(MS_EFFECT_GETW, GetEffectW);
+#endif
+
+ hFontReloadEvent = CreateHookableEvent(ME_FONT_RELOAD);
+ hColourReloadEvent = CreateHookableEvent(ME_COLOUR_RELOAD);
+
+ // cretae generic fonts
+ FontIDT fontid = {0};
+
+ fontid.cbSize = sizeof(FontID);
+ strncpy(fontid.dbSettingsGroup, "Fonts", sizeof(fontid.dbSettingsGroup));
+ _tcsncpy(fontid.group, _T("General"), SIZEOF(fontid.group));
+
+ _tcsncpy(fontid.name, _T("Headers"), SIZEOF(fontid.name));
+ fontid.flags = FIDF_APPENDNAME | FIDF_NOAS | FIDF_SAVEPOINTSIZE | FIDF_ALLOWEFFECTS | FIDF_CLASSHEADER;
+ strncpy(fontid.prefix, "Header", SIZEOF(fontid.prefix));
+ fontid.order = 0;
+ FontRegisterT( &fontid );
+
+ _tcsncpy(fontid.name, _T("Generic text"), SIZEOF(fontid.name));
+ fontid.flags = FIDF_APPENDNAME | FIDF_NOAS | FIDF_SAVEPOINTSIZE | FIDF_ALLOWEFFECTS | FIDF_CLASSGENERAL;
+ strncpy(fontid.prefix, "Generic", SIZEOF(fontid.prefix));
+ fontid.order = 0;
+ FontRegisterT( &fontid );
+
+ _tcsncpy(fontid.name, _T("Small text"), SIZEOF(fontid.name));
+ fontid.flags = FIDF_APPENDNAME | FIDF_NOAS | FIDF_SAVEPOINTSIZE | FIDF_ALLOWEFFECTS | FIDF_CLASSSMALL;
+ strncpy(fontid.prefix, "Small", SIZEOF(fontid.prefix));
+ fontid.order = 0;
+ FontRegisterT( &fontid );
+
+ // do last for silly dyna plugin
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
+ return 0;
+}
diff --git a/src/modules/fonts/FontService.h b/src/modules/fonts/FontService.h
new file mode 100644
index 0000000000..4f665ae6d3
--- /dev/null
+++ b/src/modules/fonts/FontService.h
@@ -0,0 +1,112 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "m_fontservice.h"
+
+
+// settings to be used for the value of 'deffontsettings' in the FontID structure below - i.e. defaults
+typedef struct TFontSettings_tag
+{
+ COLORREF colour;
+ char size;
+ BYTE style; // see the DBFONTF_* flags above
+ BYTE charset;
+ TCHAR szFace[LF_FACESIZE];
+}
+ TFontSettings;
+
+// a font identifier structure - used for registering a font, and getting one out again
+
+struct TFontID
+{
+ int cbSize;
+ TCHAR group[64]; // group the font belongs to - this is the 'Font Group' list in the options page
+ TCHAR name[64]; // this is the name of the font setting - e.g. 'contacts' in the 'contact list' group
+ char dbSettingsGroup[32]; // the 'module' in the database where the font data is stored
+ char prefix[32]; // this is prepended to the settings used to store this font's data in the db
+ DWORD flags; // bitwise OR of the FIDF_* flags above
+ TFontSettings deffontsettings; // defaults, valid if flags & FIDF_DEFAULTVALID
+ int order; // controls the order in the font group in which the fonts are listed in the UI (if order fields are equal,
+ // they will be ordered alphabetically by name)
+ TCHAR backgroundGroup[64];
+ TCHAR backgroundName[64];
+ TFontSettings value;
+};
+
+struct TColourID
+{
+ int cbSize;
+ TCHAR group[64];
+ TCHAR name[64];
+ char dbSettingsGroup[32];
+ char setting[32];
+ DWORD flags;
+ COLORREF defcolour;
+ int order;
+
+ COLORREF value;
+};
+
+// clist_modern related tune-up, adding clist_modern effects to FontService
+
+typedef struct TEffectSettings_tag
+{
+ BYTE effectIndex;
+ DWORD baseColour; // ARGB
+ DWORD secondaryColour; // ARGB
+}
+TEffectSettings;
+
+
+struct TEffectID
+{
+ int cbSize;
+ TCHAR group[64];
+ TCHAR name[64];
+ char dbSettingsGroup[32];
+ char setting[32];
+ DWORD flags;
+ TEffectSettings defeffect;
+ int order;
+
+ TEffectSettings value;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// global data & functions
+
+typedef struct
+{
+ char *paramName;
+ TCHAR *groupName;
+}
+ TreeItem;
+
+extern OBJLIST<TFontID> font_id_list;
+extern OBJLIST<TColourID> colour_id_list;
+extern OBJLIST<TEffectID> effect_id_list;
+
+extern int code_page;
+extern HANDLE hFontReloadEvent, hColourReloadEvent;
+
+int CreateFromFontSettings(TFontSettings *fs, LOGFONT *lf );
diff --git a/src/modules/fonts/services.cpp b/src/modules/fonts/services.cpp
new file mode 100644
index 0000000000..3106060d8b
--- /dev/null
+++ b/src/modules/fonts/services.cpp
@@ -0,0 +1,534 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "FontService.h"
+
+COLORREF GetColorFromDefault(COLORREF cl);
+
+#if defined( _UNICODE )
+void ConvertFontSettings( FontSettings* fs, TFontSettings* fsw)
+{
+ fsw->colour = fs->colour;
+ fsw->size = fs->size;
+ fsw->style = fs->style;
+ fsw->charset = fs->charset;
+
+ MultiByteToWideChar( code_page, 0, fs->szFace, -1, fsw->szFace, LF_FACESIZE);
+}
+
+void ConvertFontID( FontID *fid, TFontID* fidw )
+{
+ memset(fidw, 0, sizeof(TFontID));
+ fidw->cbSize = sizeof(TFontID);
+ strcpy(fidw->dbSettingsGroup, fid->dbSettingsGroup);
+ strcpy(fidw->prefix, fid->prefix);
+ fidw->flags = fid->flags;
+ fidw->order = fid->order;
+ ConvertFontSettings(&fid->deffontsettings, &fidw->deffontsettings);
+
+ MultiByteToWideChar( code_page, 0, fid->group, -1, fidw->group, 64);
+ MultiByteToWideChar( code_page, 0, fid->name, -1, fidw->name, 64);
+ if (fid->cbSize >= FontID_SIZEOF_V2A) {
+ MultiByteToWideChar( code_page, 0, fid->backgroundGroup, -1, fidw->backgroundGroup, 64);
+ MultiByteToWideChar( code_page, 0, fid->backgroundName, -1, fidw->backgroundName, 64);
+ }
+}
+
+void ConvertColourID(ColourID *cid, TColourID* cidw)
+{
+ cidw->cbSize = sizeof(TColourID);
+
+ strcpy(cidw->dbSettingsGroup, cid->dbSettingsGroup);
+ strcpy(cidw->setting, cid->setting);
+ cidw->flags = cid->flags;
+ cidw->defcolour = cid->defcolour;
+ cidw->order = cid->order;
+
+ MultiByteToWideChar( code_page, 0, cid->group, -1, cidw->group, 64);
+ MultiByteToWideChar( code_page, 0, cid->name, -1, cidw->name, 64);
+}
+
+void ConvertEffectID(EffectID *eid, TEffectID* eidw)
+{
+ eidw->cbSize = sizeof(TEffectID);
+
+ strcpy(eidw->dbSettingsGroup, eid->dbSettingsGroup);
+ strcpy(eidw->setting, eid->setting);
+ eidw->flags = eid->flags;
+ eidw->defeffect.effectIndex = eid->defeffect.effectIndex;
+ eidw->defeffect.baseColour = eid->defeffect.baseColour;
+ eidw->defeffect.secondaryColour = eid->defeffect.secondaryColour;
+ eidw->order = eid->order;
+
+ MultiByteToWideChar( code_page, 0, eid->group, -1, eidw->group, 64);
+ MultiByteToWideChar( code_page, 0, eid->name, -1, eidw->name, 64);
+}
+
+
+void ConvertLOGFONT(LOGFONTW *lfw, LOGFONTA *lfa)
+{
+ lfa->lfHeight = lfw->lfHeight;
+ lfa->lfWidth = lfw->lfWidth;
+ lfa->lfEscapement = lfw->lfEscapement;
+ lfa->lfOrientation = lfw->lfOrientation;
+ lfa->lfWeight = lfw->lfWeight;
+ lfa->lfItalic = lfw->lfItalic;
+ lfa->lfUnderline = lfw->lfUnderline;
+ lfa->lfStrikeOut = lfw->lfStrikeOut;
+ lfa->lfCharSet = lfw->lfCharSet;
+ lfa->lfOutPrecision = lfw->lfOutPrecision;
+ lfa->lfClipPrecision = lfw->lfClipPrecision;
+ lfa->lfQuality = lfw->lfQuality;
+ lfa->lfPitchAndFamily = lfw->lfPitchAndFamily;
+
+ WideCharToMultiByte( code_page, 0, lfw->lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, 0, 0);
+}
+#endif
+
+static void GetDefaultFontSetting(LOGFONT* lf, COLORREF* colour)
+{
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), lf, FALSE);
+ if ( colour )
+ *colour = GetSysColor(COLOR_WINDOWTEXT);
+
+ lf->lfHeight = 10;
+
+ HDC hdc = GetDC(0);
+ lf->lfHeight = -MulDiv(lf->lfHeight,GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(0, hdc);
+}
+
+int GetFontSettingFromDB(char *settings_group, char *prefix, LOGFONT* lf, COLORREF * colour, DWORD flags)
+{
+ DBVARIANT dbv;
+ char idstr[256];
+ BYTE style;
+ int retval = 0;
+
+ GetDefaultFontSetting(lf, colour);
+
+ if(flags & FIDF_APPENDNAME) mir_snprintf(idstr, SIZEOF(idstr), "%sName", prefix);
+ else mir_snprintf(idstr, SIZEOF(idstr), "%s", prefix);
+
+ if ( !DBGetContactSettingTString(NULL, settings_group, idstr, &dbv )) {
+ _tcscpy(lf->lfFaceName, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else retval = 1;
+
+ if (colour) {
+ mir_snprintf(idstr, SIZEOF(idstr), "%sCol", prefix);
+ *colour = DBGetContactSettingDword(NULL, settings_group, idstr, *colour);
+ }
+
+ mir_snprintf(idstr, SIZEOF(idstr), "%sSize", prefix);
+ lf->lfHeight = (char)DBGetContactSettingByte(NULL, settings_group, idstr, lf->lfHeight);
+
+
+ //wsprintf(idstr, "%sFlags", prefix);
+ //if(DBGetContactSettingDword(NULL, settings_group, idstr, 0) & FIDF_SAVEACTUALHEIGHT) {
+ // HDC hdc = GetDC(0);
+ // lf->lfHeight = -lf->lfHeight;
+ // ReleaseDC(0, hdc);
+ //}
+
+ mir_snprintf(idstr, SIZEOF(idstr), "%sSty", prefix);
+ style = (BYTE) DBGetContactSettingByte(NULL, settings_group, idstr,
+ (lf->lfWeight == FW_NORMAL ? 0 : DBFONTF_BOLD) | (lf->lfItalic ? DBFONTF_ITALIC : 0) | (lf->lfUnderline ? DBFONTF_UNDERLINE : 0) | lf->lfStrikeOut ? DBFONTF_STRIKEOUT : 0);
+
+ lf->lfWidth = lf->lfEscapement = lf->lfOrientation = 0;
+ lf->lfWeight = style & DBFONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = (style & DBFONTF_ITALIC) != 0;
+ lf->lfUnderline = (style & DBFONTF_UNDERLINE) != 0;
+ lf->lfStrikeOut = (style & DBFONTF_STRIKEOUT) != 0;
+
+ mir_snprintf(idstr, SIZEOF(idstr), "%sSet", prefix);
+ lf->lfCharSet = DBGetContactSettingByte(NULL, settings_group, idstr, lf->lfCharSet);
+
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ if(lf->lfHeight > 0) {
+ HDC hdc = GetDC(0);
+ if(flags & FIDF_SAVEPOINTSIZE) {
+ lf->lfHeight = -MulDiv(lf->lfHeight,GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ } else { // assume SAVEACTUALHEIGHT
+ TEXTMETRIC tm;
+ HFONT hFont = CreateFontIndirect(lf);
+ HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
+
+ GetTextMetrics(hdc, &tm);
+
+ lf->lfHeight = -(lf->lfHeight - tm.tmInternalLeading);
+
+ SelectObject(hdc, hOldFont);
+ DeleteObject(hFont);
+ }
+ //lf->lfHeight = -MulDiv(lf->lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(0, hdc);
+ }
+
+ return retval;
+}
+
+int CreateFromFontSettings(TFontSettings* fs, LOGFONT* lf )
+{
+ GetDefaultFontSetting(lf, 0);
+
+ _tcscpy(lf->lfFaceName, fs->szFace);
+
+ lf->lfWidth = lf->lfEscapement = lf->lfOrientation = 0;
+ lf->lfWeight = fs->style & DBFONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = (fs->style & DBFONTF_ITALIC) != 0;
+ lf->lfUnderline = (fs->style & DBFONTF_UNDERLINE) != 0;
+ lf->lfStrikeOut = (fs->style & DBFONTF_STRIKEOUT) != 0;;
+ lf->lfCharSet = fs->charset;
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ lf->lfHeight = fs->size;
+ return 0;
+}
+
+void UpdateFontSettings(TFontID* font_id, TFontSettings* fontsettings)
+{
+ LOGFONT lf;
+ COLORREF colour;
+ if ( GetFontSettingFromDB(font_id->dbSettingsGroup, font_id->prefix, &lf, &colour, font_id->flags) && (font_id->flags & FIDF_DEFAULTVALID)) {
+ CreateFromFontSettings(&font_id->deffontsettings, &lf );
+ colour = GetColorFromDefault(font_id->deffontsettings.colour);
+ }
+
+ fontsettings->style =
+ (lf.lfWeight == FW_NORMAL ? 0 : DBFONTF_BOLD) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0);
+
+ fontsettings->size = (char)lf.lfHeight;
+ fontsettings->charset = lf.lfCharSet;
+ fontsettings->colour = colour;
+ _tcscpy(fontsettings->szFace, lf.lfFaceName);
+}
+
+static COLORREF sttMixColor(COLORREF cl1, COLORREF cl2, int q)
+{
+ return RGB(
+ (GetRValue(cl1) * q + GetRValue(cl2) * (255 - q)) / 255,
+ (GetGValue(cl1) * q + GetGValue(cl2) * (255 - q)) / 255,
+ (GetBValue(cl1) * q + GetBValue(cl2) * (255 - q)) / 255
+ );
+}
+
+COLORREF GetColorFromDefault(COLORREF cl)
+{
+/*
+ if (cl & 0x80000000)
+ return GetSysColor(cl & 0x7fffffff);
+
+ if (cl & 0x40000000)
+ {
+ switch (cl)
+ {
+ case MIRCOLOR_BTNHALF: return sttMixColor(GetSysColor(COLOR_BTNFACE), GetSysColor(COLOR_BTNTEXT), 128);
+ case MIRCOLOR_WNDHALF: return sttMixColor(GetSysColor(COLOR_WINDOW), GetSysColor(COLOR_WINDOWTEXT), 128);
+ case MIRCOLOR_SELHALF: return sttMixColor(GetSysColor(COLOR_HIGHLIGHT), GetSysColor(COLOR_HIGHLIGHTTEXT), 128);
+ case MIRCOLOR_INBACK: return sttMixColor(GetSysColor(COLOR_WINDOW), RGB(0,0,255), 245);
+ case MIRCOLOR_INTEXT: return GetSysColor(COLOR_WINDOWTEXT);
+ case MIRCOLOR_INHALF: return sttMixColor(GetColorFromDefault(MIRCOLOR_INBACK), GetColorFromDefault(MIRCOLOR_INTEXT), 128);
+ case MIRCOLOR_OUTBACK: return sttMixColor(GetSysColor(COLOR_WINDOW), RGB(0,255,0), 245);
+ case MIRCOLOR_OUTTEXT: return GetSysColor(COLOR_WINDOWTEXT);
+ case MIRCOLOR_OUTHALF: return sttMixColor(GetColorFromDefault(MIRCOLOR_OUTBACK), GetColorFromDefault(MIRCOLOR_OUTTEXT), 128);
+ }
+ }
+*/
+ return cl;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// RegisterFont service
+
+static int sttRegisterFontWorker( TFontID* font_id )
+{
+ for ( int i = 0; i < font_id_list.getCount(); i++ ) {
+ TFontID& F = font_id_list[i];
+ if ( !lstrcmp( F.group, font_id->group ) && !lstrcmp( F.name, font_id->name ) && !( F.flags & FIDF_ALLOWREREGISTER ))
+ return 1;
+ }
+
+ char idstr[256];
+ mir_snprintf(idstr, SIZEOF(idstr), "%sFlags", font_id->prefix);
+ DBWriteContactSettingDword(0, font_id->dbSettingsGroup, idstr, font_id->flags);
+ {
+ TFontID* newItem = new TFontID;
+ memset( newItem, 0, sizeof( TFontID ));
+ memcpy( newItem, font_id, font_id->cbSize);
+
+ if (!lstrcmp(newItem->deffontsettings.szFace, _T("MS Shell Dlg")))
+ {
+ LOGFONT lf;
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE);
+ lstrcpyn(newItem->deffontsettings.szFace, lf.lfFaceName, SIZEOF(newItem->deffontsettings.szFace));
+ if (!newItem->deffontsettings.size)
+ newItem->deffontsettings.size = lf.lfHeight;
+ }
+
+ UpdateFontSettings( font_id, &newItem->value );
+ font_id_list.insert( newItem );
+ }
+ return 0;
+}
+
+#if defined( _UNICODE )
+INT_PTR RegisterFontW(WPARAM wParam, LPARAM )
+{
+ return sttRegisterFontWorker(( TFontID* )wParam );
+}
+#endif
+
+INT_PTR RegisterFont(WPARAM wParam, LPARAM)
+{
+ #if defined( _UNICODE )
+ TFontID temp;
+ ConvertFontID( ( FontID* )wParam, &temp );
+ return sttRegisterFontWorker( &temp );
+ #else
+ return sttRegisterFontWorker(( TFontID* )wParam );
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GetFont service
+
+static int sttGetFontWorker( TFontID* font_id, LOGFONT* lf )
+{
+ COLORREF colour;
+
+ for ( int i = 0; i < font_id_list.getCount(); i++ ) {
+ TFontID& F = font_id_list[i];
+ if ( !_tcsncmp( F.name, font_id->name, SIZEOF(F.name)) && !_tcsncmp( F.group, font_id->group, SIZEOF(F.group))) {
+ if ( GetFontSettingFromDB( F.dbSettingsGroup, F.prefix, lf, &colour, F.flags) && ( F.flags & FIDF_DEFAULTVALID )) {
+ CreateFromFontSettings( &F.deffontsettings, lf );
+ colour = GetColorFromDefault(F.deffontsettings.colour);
+ }
+
+ return (int)colour;
+ } }
+
+ GetDefaultFontSetting( lf, &colour );
+ return (int)colour;
+}
+
+#if defined( _UNICODE )
+INT_PTR GetFontW(WPARAM wParam, LPARAM lParam)
+{
+ return sttGetFontWorker(( TFontID* )wParam, ( LOGFONT* )lParam );
+}
+#endif
+
+INT_PTR GetFont(WPARAM wParam, LPARAM lParam)
+{
+ #if defined( _UNICODE )
+ TFontID temp;
+ LOGFONT lftemp;
+ ConvertFontID((FontID *)wParam, &temp);
+ { int ret = sttGetFontWorker( &temp, &lftemp );
+ ConvertLOGFONT( &lftemp, ( LOGFONTA* )lParam );
+ return ret;
+ }
+ #else
+ return sttGetFontWorker(( TFontID* )wParam, ( LOGFONT* )lParam );
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// RegisterColour service
+
+void UpdateColourSettings( TColourID* colour_id, COLORREF *colour)
+{
+ *colour = ( COLORREF )DBGetContactSettingDword(NULL, colour_id->dbSettingsGroup, colour_id->setting, GetColorFromDefault(colour_id->defcolour) );
+}
+
+static int sttRegisterColourWorker( TColourID* colour_id )
+{
+ for ( int i = 0; i < colour_id_list.getCount(); i++ ) {
+ TColourID& C = colour_id_list[i];
+ if ( !_tcscmp( C.group, colour_id->group ) && !_tcscmp( C.name, colour_id->name ))
+ return 1;
+ }
+
+ TColourID* newItem = new TColourID;
+ memcpy( newItem, colour_id, sizeof( TColourID ));
+ UpdateColourSettings( colour_id, &newItem->value );
+ colour_id_list.insert( newItem );
+ return 0;
+}
+
+#if defined( _UNICODE )
+INT_PTR RegisterColourW(WPARAM wParam, LPARAM)
+{
+ return sttRegisterColourWorker(( TColourID* )wParam );
+}
+#endif
+
+INT_PTR RegisterColour(WPARAM wParam, LPARAM)
+{
+ #if defined( _UNICODE )
+ TColourID temp;
+ ConvertColourID(( ColourID* )wParam, &temp );
+ return sttRegisterColourWorker( &temp );
+ #else
+ return sttRegisterColourWorker(( TColourID* )wParam );
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GetColour service
+
+static int sttGetColourWorker( TColourID* colour_id )
+{
+ int i;
+
+ for ( i = 0; i < colour_id_list.getCount(); i++ ) {
+ TColourID& C = colour_id_list[i];
+ if ( !_tcscmp( C.group, colour_id->group ) && !_tcscmp( C.name, colour_id->name ))
+ return (int)DBGetContactSettingDword(NULL, C.dbSettingsGroup, C.setting, GetColorFromDefault(C.defcolour));
+ }
+
+ return -1;
+}
+
+#if defined( _UNICODE )
+INT_PTR GetColourW(WPARAM wParam, LPARAM)
+{
+ return sttGetColourWorker(( TColourID* )wParam );
+}
+#endif
+
+INT_PTR GetColour(WPARAM wParam, LPARAM)
+{
+ #if defined( _UNICODE )
+ TColourID temp;
+ ConvertColourID(( ColourID* )wParam, &temp );
+ return sttGetColourWorker( &temp );
+ #else
+ return sttGetColourWorker(( TColourID* )wParam );
+ #endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Effects
+
+void UpdateEffectSettings(TEffectID* effect_id, TEffectSettings* effectsettings)
+{
+ char str[256];
+
+ mir_snprintf(str, SIZEOF(str), "%sEffect", effect_id->setting);
+ effectsettings->effectIndex = DBGetContactSettingByte(NULL, effect_id->dbSettingsGroup, str, effect_id->defeffect.effectIndex);
+
+ mir_snprintf(str, SIZEOF(str), "%sEffectCol1", effect_id->setting);
+ effectsettings->baseColour = DBGetContactSettingDword(NULL, effect_id->dbSettingsGroup, str, effect_id->defeffect.baseColour);
+
+ mir_snprintf(str, SIZEOF(str), "%sEffectCol2", effect_id->setting);
+ effectsettings->secondaryColour = DBGetContactSettingDword(NULL, effect_id->dbSettingsGroup, str, effect_id->defeffect.secondaryColour);
+
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// RegisterFont service
+
+static int sttRegisterEffectWorker( TEffectID* effect_id )
+{
+ for ( int i = 0; i < effect_id_list.getCount(); i++ ) {
+ TEffectID& E = effect_id_list[i];
+ if ( !_tcscmp( E.group, effect_id->group ) && !_tcscmp( E.name, effect_id->name ))
+ return 1;
+ }
+
+ TEffectID* newItem = new TEffectID;
+ memcpy( newItem, effect_id, sizeof( TEffectID ));
+ UpdateEffectSettings( effect_id, &newItem->value );
+ effect_id_list.insert( newItem );
+ return 0;
+}
+
+#if defined( _UNICODE )
+INT_PTR RegisterEffectW(WPARAM wParam, LPARAM lParam)
+{
+ return sttRegisterEffectWorker(( TEffectID* )wParam );
+}
+#endif
+
+INT_PTR RegisterEffect(WPARAM wParam, LPARAM lParam)
+{
+#if defined( _UNICODE )
+ TEffectID temp;
+ ConvertEffectID( ( EffectID* )wParam, &temp );
+ return sttRegisterEffectWorker( &temp );
+#else
+ return sttRegisterEffectWorker(( TEffectID* )wParam );
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GetEffect service
+
+static int sttGetEffectWorker( TEffectID* effect_id, FONTEFFECT* effect )
+{
+ for ( int i = 0; i < effect_id_list.getCount(); i++ ) {
+ TEffectID& E = effect_id_list[i];
+ if ( !_tcsncmp( E.name, effect_id->name, SIZEOF(E.name)) && !_tcsncmp( E.group, effect_id->group, SIZEOF(E.group)))
+ {
+ TEffectSettings temp;
+ UpdateEffectSettings( effect_id, &temp );
+
+ effect->effectIndex = temp.effectIndex;
+ effect->baseColour = temp.baseColour;
+ effect->secondaryColour = temp.secondaryColour;
+
+ return (int) TRUE;
+ } }
+
+ return (int)FALSE;
+}
+
+#if defined( _UNICODE )
+INT_PTR GetEffectW(WPARAM wParam, LPARAM lParam)
+{
+ return sttGetEffectWorker(( TEffectID* )wParam, ( FONTEFFECT* )lParam );
+}
+#endif
+
+INT_PTR GetEffect(WPARAM wParam, LPARAM lParam)
+{
+#if defined( _UNICODE )
+ TEffectID temp;
+ ConvertEffectID((EffectID *)wParam, &temp);
+ return sttGetEffectWorker( &temp, ( FONTEFFECT* )lParam );
+#else
+ return sttGetEffectWorker(( TEffectID* )wParam, ( FONTEFFECT* )lParam );
+#endif
+}
diff --git a/src/modules/help/about.cpp b/src/modules/help/about.cpp
new file mode 100644
index 0000000000..37c949260a
--- /dev/null
+++ b/src/modules/help/about.cpp
@@ -0,0 +1,148 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#if defined( _UNICODE )
+ #define STR_VERSION_FORMAT "%s%S%S"
+#else
+ #define STR_VERSION_FORMAT "%s%s%s"
+#endif
+
+INT_PTR CALLBACK DlgProcAbout(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static int iState = 0;
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { TCHAR filename[MAX_PATH], *productCopyright;
+ DWORD unused;
+ DWORD verInfoSize;
+ UINT blockSize;
+ PVOID pVerInfo;
+
+ GetModuleFileName(NULL,filename,SIZEOF(filename));
+ verInfoSize=GetFileVersionInfoSize(filename,&unused);
+ pVerInfo=mir_alloc(verInfoSize);
+ GetFileVersionInfo(filename,0,verInfoSize,pVerInfo);
+ VerQueryValue(pVerInfo,_T("\\StringFileInfo\\000004b0\\LegalCopyright"),(LPVOID*)&productCopyright,&blockSize);
+ SetDlgItemText(hwndDlg,IDC_DEVS,productCopyright);
+ mir_free(pVerInfo);
+ }
+ { char productVersion[56], *p;
+ int isAnsi = 0;
+ TCHAR str[64];
+ CallService(MS_SYSTEM_GETVERSIONTEXT,SIZEOF(productVersion),(LPARAM)productVersion);
+ // Hide Unicode from version text as it is assumed at this point
+ p = strstr(productVersion, " Unicode");
+ if (p)
+ *p = '\0';
+ else
+ isAnsi = 1;
+ mir_sntprintf(str,SIZEOF(str),_T(STR_VERSION_FORMAT), TranslateT("v"), productVersion, isAnsi?" ANSI":"");
+ {
+ TCHAR oldTitle[256], newTitle[256];
+ GetDlgItemText( hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF( oldTitle ));
+ mir_sntprintf( newTitle, SIZEOF(newTitle), oldTitle, str );
+ SetDlgItemText( hwndDlg, IDC_HEADERBAR, newTitle );
+ }
+
+ mir_sntprintf(str,SIZEOF(str),TranslateT("Built %s %s"),_T(__DATE__),_T(__TIME__));
+ SetDlgItemText(hwndDlg,IDC_BUILDTIME,str);
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREDITSFILE), SW_HIDE);
+ {
+ HRSRC hResInfo = FindResource(hMirandaInst,MAKEINTRESOURCE(IDR_CREDITS),_T("TEXT"));
+ DWORD ResSize = SizeofResource(hMirandaInst,hResInfo);
+ HGLOBAL hRes = LoadResource(hMirandaInst,hResInfo);
+ char* pszMsg = (char*)LockResource(hRes);
+ if (pszMsg)
+ {
+ char* pszMsgt = (char*)alloca(ResSize + 1);
+ memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0;
+
+ TCHAR *ptszMsg;
+ if (ResSize >=3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf')
+ ptszMsg = Utf8DecodeT(pszMsgt + 3);
+ else
+ ptszMsg = mir_a2t_cp(pszMsgt, 1252);
+
+ SetDlgItemText(hwndDlg, IDC_CREDITSFILE, ptszMsg);
+ UnlockResource(pszMsg);
+ mir_free(ptszMsg);
+ }
+ FreeResource(hRes);
+ }
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_OTHER_MIRANDA);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch( LOWORD( wParam )) {
+ case IDOK:
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ case IDC_CONTRIBLINK:
+ if (iState) {
+ iState = 0;
+ SetDlgItemText(hwndDlg, IDC_CONTRIBLINK, TranslateT("Credits >"));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_DEVS), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUILDTIME), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREDITSFILE), SW_HIDE);
+ }
+ else {
+ iState = 1;
+ SetDlgItemText(hwndDlg, IDC_CONTRIBLINK, TranslateT("< Copyright"));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_DEVS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUILDTIME), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREDITSFILE), SW_SHOW);
+ }
+ break;
+ }
+ break;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ switch ( GetWindowLongPtr(( HWND )lParam, GWL_ID )) {
+ case IDC_WHITERECT:
+ case IDC_BUILDTIME:
+ case IDC_CREDITSFILE:
+ case IDC_DEVS:
+ SetTextColor((HDC)wParam,GetSysColor(COLOR_WINDOWTEXT));
+ break;
+ default:
+ return FALSE;
+ }
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib( hwndDlg );
+ {
+ HFONT hFont=(HFONT)SendDlgItemMessage(hwndDlg,IDC_VERSION,WM_GETFONT,0,0);
+ SendDlgItemMessage(hwndDlg,IDC_VERSION,WM_SETFONT,SendDlgItemMessage(hwndDlg,IDOK,WM_GETFONT,0,0),0);
+ DeleteObject(hFont);
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/help/help.cpp b/src/modules/help/help.cpp
new file mode 100644
index 0000000000..432049e2d1
--- /dev/null
+++ b/src/modules/help/help.cpp
@@ -0,0 +1,117 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+INT_PTR CALLBACK DlgProcAbout(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+HWND hAboutDlg=NULL;
+static HANDLE hBugEvent = NULL;
+
+static INT_PTR AboutCommand(WPARAM wParam,LPARAM)
+{
+ if (IsWindow(hAboutDlg)) {
+ SetForegroundWindow(hAboutDlg);
+ SetFocus(hAboutDlg);
+ return 0;
+ }
+ hAboutDlg=CreateDialog(hMirandaInst,MAKEINTRESOURCE(IDD_ABOUT),(HWND)wParam,DlgProcAbout);
+ return 0;
+}
+
+static INT_PTR IndexCommand(WPARAM, LPARAM)
+{
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)Translate("http://wiki.miranda-im.org/"));
+ return 0;
+}
+
+static INT_PTR WebsiteCommand(WPARAM, LPARAM)
+{
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)"http://www.miranda-im.org");
+ return 0;
+}
+
+static int BugCommandEvent(WPARAM wParam, LPARAM lParam) {
+ char *szUrl = (char*)lParam;
+
+ if (szUrl) {
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)szUrl);
+ }
+ return 0;
+}
+
+static INT_PTR BugCommand(WPARAM, LPARAM)
+{
+ NotifyEventHooks(hBugEvent, 0, (LPARAM)"http://code.google.com/p/miranda/issues/list");
+ return 0;
+}
+
+
+int ShutdownHelpModule(WPARAM, LPARAM)
+{
+ if (IsWindow(hAboutDlg)) DestroyWindow(hAboutDlg);
+ hAboutDlg=NULL;
+ return 0;
+}
+
+int LoadHelpModule(void)
+{
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,ShutdownHelpModule);
+
+ CreateServiceFunction("Help/AboutCommand",AboutCommand);
+ CreateServiceFunction("Help/IndexCommand",IndexCommand);
+ CreateServiceFunction("Help/WebsiteCommand",WebsiteCommand);
+ CreateServiceFunction("Help/BugCommand",BugCommand);
+
+ hBugEvent = CreateHookableEvent(ME_HELP_BUGREPORT);
+ SetHookDefaultForHookableEvent(hBugEvent, BugCommandEvent);
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.pszPopupName = LPGEN("&Help");
+ mi.popupPosition = 2000090000;
+ mi.position = 2000090000;
+ mi.pszName = LPGEN("&About...");
+ mi.pszService = "Help/AboutCommand";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_HELP);
+ mi.position = -500050000;
+ mi.pszName = LPGEN("&Support");
+ mi.pszService = "Help/IndexCommand";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_MIRANDAWEB);
+ mi.position = 2000050000;
+ mi.pszName = LPGEN("&Miranda IM Homepage");
+ mi.pszService = "Help/WebsiteCommand";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ mi.icolibItem = GetSkinIconHandle(SKINICON_EVENT_URL);
+ mi.position = 2000040000;
+ mi.pszName = LPGEN("&Report Bug");
+ mi.pszService = "Help/BugCommand";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+ return 0;
+}
diff --git a/src/modules/history/history.cpp b/src/modules/history/history.cpp
new file mode 100644
index 0000000000..3b6b805a7f
--- /dev/null
+++ b/src/modules/history/history.cpp
@@ -0,0 +1,434 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define SUMMARY 0
+#define DETAIL 1
+#define DM_FINDNEXT (WM_USER+10)
+#define DM_HREBUILD (WM_USER+11)
+
+static INT_PTR CALLBACK DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK DlgProcHistoryFind(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static HANDLE hWindowList=0;
+
+static INT_PTR UserHistoryCommand(WPARAM wParam, LPARAM)
+{
+ HWND hwnd = WindowList_Find( hWindowList,( HANDLE )wParam );
+ if ( hwnd ) {
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ return 0;
+ }
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_HISTORY),NULL,DlgProcHistory,wParam);
+ return 0;
+}
+
+static int HistoryContactDelete(WPARAM wParam, LPARAM)
+{
+ HWND hwnd = WindowList_Find(hWindowList,(HANDLE)wParam);
+ if ( hwnd != NULL )
+ DestroyWindow(hwnd);
+ return 0;
+}
+
+int PreShutdownHistoryModule(WPARAM, LPARAM)
+{
+ if (hWindowList)
+ WindowList_BroadcastAsync(hWindowList,WM_DESTROY,0,0);
+ return 0;
+}
+
+int LoadHistoryModule(void)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = 1000090000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_HISTORY );
+ mi.pszName = LPGEN("View &History");
+ mi.pszService = MS_HISTORY_SHOWCONTACTHISTORY;
+ CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+
+ CreateServiceFunction(MS_HISTORY_SHOWCONTACTHISTORY,UserHistoryCommand);
+ hWindowList=(HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST,0,0);
+ HookEvent(ME_DB_CONTACT_DELETED,HistoryContactDelete);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,PreShutdownHistoryModule);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Fills the events list
+
+static void GetMessageDescription( DBEVENTINFO *dbei, TCHAR* buf, int cbBuf )
+{
+ TCHAR* msg = DbGetEventTextT( dbei, CP_ACP );
+ _tcsncpy( buf, msg ? msg : TranslateT("Invalid Message"), cbBuf );
+ buf[ cbBuf-1 ] = 0;
+ mir_free( msg );
+}
+
+static void GetUrlDescription( DBEVENTINFO *dbei, TCHAR* buf, int cbBuf )
+{
+ int len = dbei->cbBlob;
+ if ( len >= cbBuf )
+ len = cbBuf-1;
+
+ #if !defined( _UNICODE )
+ memcpy( buf, dbei->pBlob, len );
+ #else
+ MultiByteToWideChar( CP_ACP, 0, ( LPCSTR )dbei->pBlob, len, buf, cbBuf );
+ #endif
+ buf[ len ] = 0;
+
+ if ( len < cbBuf-3 )
+ _tcscat( buf, _T( "\r\n" ));
+}
+
+static void GetFileDescription( DBEVENTINFO *dbei, TCHAR* buf, int cbBuf )
+{
+ int len = dbei->cbBlob - sizeof( DWORD );
+ if ( len >= cbBuf )
+ len = cbBuf-1;
+
+ #if !defined( _UNICODE )
+ memcpy( buf, dbei->pBlob + sizeof( DWORD ), len );
+ #else
+ MultiByteToWideChar( CP_ACP, 0, ( LPCSTR )dbei->pBlob + sizeof( DWORD ), len, buf, cbBuf );
+ #endif
+ buf[ len ] = 0;
+
+ if ( len < cbBuf-3 )
+ _tcscat( buf, _T( "\r\n" ));
+}
+
+static void GetObjectDescription( DBEVENTINFO *dbei, TCHAR* str, int cbStr )
+{
+ switch( dbei->eventType ) {
+ case EVENTTYPE_MESSAGE:
+ GetMessageDescription( dbei, str, cbStr );
+ break;
+
+ case EVENTTYPE_URL:
+ GetUrlDescription( dbei, str, cbStr );
+ break;
+
+ case EVENTTYPE_FILE:
+ GetFileDescription( dbei, str, cbStr );
+ break;
+
+ default:
+ {
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )CallService( MS_DB_EVENT_GETTYPE, ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+ if ( et && ( et->flags & DETF_HISTORY )) {
+ GetMessageDescription( dbei, str, cbStr );
+ }
+ else
+ str[ 0 ] = 0;
+} } }
+
+static void GetObjectSummary( DBEVENTINFO *dbei, TCHAR* str, int cbStr )
+{
+ TCHAR* pszSrc, *pszTmp = NULL;
+
+ switch( dbei->eventType ) {
+ case EVENTTYPE_MESSAGE:
+ if ( dbei->flags & DBEF_SENT ) pszSrc = TranslateT( "Outgoing Message" );
+ else pszSrc = TranslateT( "Incoming Message" );
+ break;
+
+ case EVENTTYPE_URL:
+ if ( dbei->flags & DBEF_SENT ) pszSrc = TranslateT( "Outgoing URL" );
+ else pszSrc = TranslateT( "Incoming URL" );
+ break;
+
+ case EVENTTYPE_FILE:
+ if ( dbei->flags & DBEF_SENT ) pszSrc = TranslateT( "Outgoing File" );
+ else pszSrc = TranslateT( "Incoming File" );
+ break;
+
+ default:
+ {
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )CallService( MS_DB_EVENT_GETTYPE, ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+ if ( et && ( et->flags & DETF_HISTORY )) {
+ pszTmp = mir_a2t( et->descr );
+ pszSrc = TranslateTS( pszTmp );
+ break;
+ }
+ else {
+ str[ 0 ] = 0;
+ return;
+ } } }
+
+ _tcsncpy( str, ( const TCHAR* )pszSrc, cbStr );
+ str[ cbStr-1 ] = 0;
+
+ mir_free( pszTmp );
+}
+
+typedef struct {
+ HANDLE hContact;
+ HWND hwnd;
+} THistoryThread;
+
+static void FillHistoryThread(void* param)
+{
+ TCHAR str[200], eventText[256], strdatetime[64];
+ HANDLE hDbEvent;
+ DBEVENTINFO dbei;
+ int newBlobSize,oldBlobSize,i;
+ HWND hwndList;
+ THistoryThread *hInfo = ( THistoryThread* )param;
+
+ SendDlgItemMessage(hInfo->hwnd,IDC_LIST,LB_RESETCONTENT,0,0);
+ i=CallService(MS_DB_EVENT_GETCOUNT,(WPARAM)hInfo->hContact,0);
+ SendDlgItemMessage(hInfo->hwnd,IDC_LIST,LB_INITSTORAGE,i,i*40);
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ oldBlobSize=0;
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDLAST,(WPARAM)hInfo->hContact,0);
+ hwndList = GetDlgItem(hInfo->hwnd,IDC_LIST);
+ while ( hDbEvent != NULL ) {
+ if ( !IsWindow( hInfo->hwnd ))
+ break;
+ newBlobSize=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)hDbEvent,0);
+ if(newBlobSize>oldBlobSize) {
+ dbei.pBlob=(PBYTE)mir_realloc(dbei.pBlob,newBlobSize);
+ oldBlobSize=newBlobSize;
+ }
+ dbei.cbBlob = oldBlobSize;
+ CallService( MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei );
+ GetObjectSummary(&dbei,str,SIZEOF(str));
+ if(str[0]) {
+ tmi.printTimeStamp(NULL, dbei.timestamp, _T("d t"), strdatetime, SIZEOF(strdatetime), 0);
+ mir_sntprintf( eventText, SIZEOF(eventText), _T("%s: %s"), strdatetime, str );
+ i = SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)eventText );
+ SendMessage(hwndList, LB_SETITEMDATA, i, (LPARAM)hDbEvent);
+ }
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDPREV,(WPARAM)hDbEvent,0);
+ }
+ mir_free(dbei.pBlob);
+
+ SendDlgItemMessage(hInfo->hwnd,IDC_LIST,LB_SETCURSEL,0,0);
+ SendMessage(hInfo->hwnd,WM_COMMAND,MAKEWPARAM(IDC_LIST,LBN_SELCHANGE),0);
+ EnableWindow(GetDlgItem(hInfo->hwnd, IDC_LIST), TRUE);
+ mir_free(hInfo);
+}
+
+static int HistoryDlgResizer(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch(urc->wId) {
+ case IDC_LIST:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ case IDC_EDIT:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ case IDC_FIND:
+ case IDC_DELETEHISTORY:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM;
+ case IDOK:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+static INT_PTR CALLBACK DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact;
+
+ hContact=(HANDLE)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)lParam);
+ hContact = (HANDLE)lParam;
+ WindowList_Add(hWindowList,hwndDlg,hContact);
+ Utils_RestoreWindowPosition(hwndDlg,hContact,"History","");
+ {
+ TCHAR* contactName, str[200];
+ contactName = cli.pfnGetContactDisplayName( hContact, 0 );
+ mir_sntprintf(str,SIZEOF(str),TranslateT("History for %s"),contactName);
+ SetWindowText(hwndDlg,str);
+ }
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_OTHER_HISTORY);
+ SendMessage(hwndDlg,DM_HREBUILD,0,0);
+ return TRUE;
+
+ case DM_HREBUILD:
+ {
+ THistoryThread* hInfo = (THistoryThread*)mir_alloc(sizeof(THistoryThread));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LIST), FALSE);
+ hInfo->hContact = hContact;
+ hInfo->hwnd = hwndDlg;
+ forkthread(FillHistoryThread, 0, hInfo);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg,hContact,"History","");
+ WindowList_Remove(hWindowList,hwndDlg);
+ return TRUE;
+
+ case WM_GETMINMAXINFO:
+ ((MINMAXINFO*)lParam)->ptMinTrackSize.x=300;
+ ((MINMAXINFO*)lParam)->ptMinTrackSize.y=230;
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd={0};
+ urd.cbSize=sizeof(urd);
+ urd.hwndDlg=hwndDlg;
+ urd.hInstance=hMirandaInst;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_HISTORY);
+ urd.lParam=(LPARAM)NULL;
+ urd.pfnResizer=HistoryDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+
+ case IDC_FIND:
+ ShowWindow(CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_HISTORY_FIND), hwndDlg, DlgProcHistoryFind, (LPARAM)hwndDlg), SW_SHOW);
+ return TRUE;
+
+ case IDC_DELETEHISTORY:
+ {
+ HANDLE hDbevent;
+ int index = SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETCURSEL,0,0);
+ if ( index == LB_ERR )
+ break;
+
+ if ( MessageBox(hwndDlg,TranslateT("Are you sure you want to delete this history item?"),TranslateT("Delete History"),MB_YESNO|MB_ICONQUESTION)==IDYES) {
+ hDbevent = (HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETITEMDATA,index,0);
+ CallService(MS_DB_EVENT_DELETE,(WPARAM)hContact,(LPARAM)hDbevent);
+ SendMessage(hwndDlg,DM_HREBUILD,0,0);
+ }
+ return TRUE;
+ }
+ case IDC_LIST:
+ if ( HIWORD(wParam) == LBN_SELCHANGE ) {
+ TCHAR str[8192],*contactName;
+ HANDLE hDbEvent;
+ DBEVENTINFO dbei;
+ int sel;
+ sel=SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETCURSEL,0,0);
+ if(sel==LB_ERR) { EnableWindow(GetDlgItem(hwndDlg,IDC_DELETEHISTORY),FALSE); break; }
+ EnableWindow(GetDlgItem(hwndDlg,IDC_DELETEHISTORY),TRUE);
+ contactName = cli.pfnGetContactDisplayName( hContact, 0 );
+ hDbEvent=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETITEMDATA,sel,0);
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)hDbEvent,0);
+ if ((int)dbei.cbBlob != -1)
+ {
+ dbei.pBlob=(PBYTE)mir_alloc(dbei.cbBlob);
+ if (CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei) == 0)
+ {
+ GetObjectDescription(&dbei,str,SIZEOF(str));
+ if ( str[0] )
+ SetDlgItemText(hwndDlg, IDC_EDIT, str);
+ }
+ mir_free(dbei.pBlob);
+ }
+ }
+ return TRUE;
+ }
+ break;
+ case DM_FINDNEXT:
+ {
+ TCHAR str[1024];
+ HANDLE hDbEvent,hDbEventStart;
+ DBEVENTINFO dbei;
+ int newBlobSize,oldBlobSize;
+
+ int index = SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETCURSEL,0,0);
+ if ( index == LB_ERR )
+ break;
+
+ hDbEventStart=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETITEMDATA,index,0);
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.pBlob=NULL;
+ oldBlobSize=0;
+ for(;;) {
+ hDbEvent = (HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,LB_GETITEMDATA,++index,0);
+ if(hDbEvent == ( HANDLE )LB_ERR) {
+ index = -1;
+ continue;
+ }
+ if(hDbEvent==hDbEventStart) break;
+ newBlobSize=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)hDbEvent,0);
+ if(newBlobSize>oldBlobSize) {
+ dbei.pBlob=(PBYTE)mir_realloc(dbei.pBlob,newBlobSize);
+ oldBlobSize=newBlobSize;
+ }
+ dbei.cbBlob=oldBlobSize;
+ CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei);
+ GetObjectDescription(&dbei,str,SIZEOF(str));
+ if(str[0]) {
+ CharUpperBuff(str,lstrlen(str));
+ if( _tcsstr(str,(const TCHAR*)lParam)!=NULL) {
+ SendDlgItemMessage(hwndDlg,IDC_LIST,LB_SETCURSEL,index,0);
+ SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_LIST,LBN_SELCHANGE),0);
+ break;
+ } } }
+
+ mir_free(dbei.pBlob);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProcHistoryFind(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK://find Next
+ {
+ TCHAR str[128];
+ HWND hwndParent = ( HWND )GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ GetDlgItemText(hwndDlg, IDC_FINDWHAT, str, SIZEOF(str));
+ CharUpperBuff(str,lstrlen(str));
+ SendMessage(hwndParent,DM_FINDNEXT,0,(LPARAM)str);
+ return TRUE;
+ }
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/icolib/IcoLib.h b/src/modules/icolib/IcoLib.h
new file mode 100644
index 0000000000..72e3aa4122
--- /dev/null
+++ b/src/modules/icolib/IcoLib.h
@@ -0,0 +1,83 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+typedef struct
+{
+ TCHAR* name;
+ int flags;
+ int maxOrder;
+ int ref_count;
+}
+ SectionItem;
+
+typedef struct
+{
+ TCHAR* file;
+ int ref_count;
+}
+ IconSourceFile;
+
+typedef struct
+{
+ IconSourceFile* file;
+ int indx;
+ int cx, cy;
+
+ int ref_count;
+
+ HICON icon;
+ int icon_ref_count;
+
+ BYTE* icon_data;
+ int icon_size;
+}
+ IconSourceItem;
+
+typedef struct
+{
+ char* name;
+ SectionItem* section;
+ int orderID;
+ TCHAR* description;
+ TCHAR* default_file;
+ int default_indx;
+ int cx, cy;
+
+ IconSourceItem* source_small;
+ IconSourceItem* source_big;
+ IconSourceItem* default_icon;
+
+ TCHAR* temp_file;
+ HICON temp_icon;
+ BOOL temp_reset;
+}
+ IconItem;
+
+typedef struct
+{
+ char *paramName;
+ DWORD value;
+}
+ TreeItem;
+
+// extracticon.c
+UINT _ExtractIconEx(LPCTSTR lpszFile, int iconIndex, int cxIcon, int cyIcon, HICON *phicon, UINT flags);
diff --git a/src/modules/icolib/extracticon.cpp b/src/modules/icolib/extracticon.cpp
new file mode 100644
index 0000000000..a886d6510d
--- /dev/null
+++ b/src/modules/icolib/extracticon.cpp
@@ -0,0 +1,280 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#ifdef _ASSERT
+#undef _ASSERT
+#endif
+#define _ASSERT(n)
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/introductiontoresources/resourcereference/resourcestructures/newheader.asp
+typedef struct
+{
+ WORD Reserved;
+ WORD ResType;
+ WORD ResCount;
+}
+ NEWHEADER;
+
+#define MAGIC_ICON 0
+#define MAGIC_ICO1 1
+#define MAGIC_CUR 2
+#define MAGIC_BMP ((WORD)'B'+((WORD)'M'<<8))
+
+#define MAGIC_ANI1 ((WORD)'R'+((WORD)'I'<<8))
+#define MAGIC_ANI2 ((WORD)'F'+((WORD)'F'<<8))
+#define MAGIC_ANI3 ((WORD)'A'+((WORD)'C'<<8))
+#define MAGIC_ANI4 ((WORD)'O'+((WORD)'N'<<8))
+
+#define VER30 0x00030000
+
+void* _RelativeVirtualAddresstoPtr(IMAGE_DOS_HEADER* pDosHeader, DWORD rva)
+{
+ IMAGE_NT_HEADERS* pPE = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader + pDosHeader->e_lfanew);
+ IMAGE_SECTION_HEADER* pSection = IMAGE_FIRST_SECTION( pPE );
+ int i;
+
+ for (i = 0; i < pPE->FileHeader.NumberOfSections; i++) {
+ IMAGE_SECTION_HEADER* cSection = &pSection[i];
+ DWORD size = cSection->Misc.VirtualSize ? cSection->Misc.VirtualSize : cSection->SizeOfRawData;
+
+ if (rva >= cSection->VirtualAddress && rva < cSection->VirtualAddress + size)
+ return (LPBYTE)pDosHeader + cSection->PointerToRawData + (rva - cSection->VirtualAddress);
+ }
+
+ return NULL;
+}
+
+void* _GetResourceTable(IMAGE_DOS_HEADER* pDosHeader)
+{
+ IMAGE_NT_HEADERS* pPE = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader + pDosHeader->e_lfanew);
+
+ if (pPE->Signature != IMAGE_NT_SIGNATURE)
+ return NULL;
+ if (pPE->FileHeader.SizeOfOptionalHeader < 2)
+ return NULL;
+
+ // The DataDirectory is an array of 16 structures.
+ // Each array entry has a predefined meaning for what it refers to.
+
+ switch (pPE->OptionalHeader.Magic)
+ {
+ case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ if (pPE->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER32))
+ return _RelativeVirtualAddresstoPtr(pDosHeader, ((PIMAGE_NT_HEADERS32)pPE)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ break;
+
+ case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ if (pPE->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER64))
+ return _RelativeVirtualAddresstoPtr(pDosHeader, ((PIMAGE_NT_HEADERS64)pPE)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ break;
+ }
+
+ return NULL;
+}
+
+IMAGE_RESOURCE_DIRECTORY_ENTRY* _FindResourceBase(void* prt, int resType, int* pCount)
+{
+ IMAGE_RESOURCE_DIRECTORY* pDir = (IMAGE_RESOURCE_DIRECTORY*)prt;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pRes;
+ int i, count;
+
+ *pCount = 0;
+
+ count = pDir->NumberOfIdEntries + pDir->NumberOfNamedEntries;
+ pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1);
+
+ for(i = 0; i < count; i++)
+ if (pRes[i].Name == (DWORD)resType) break;
+
+ if (i == count) return NULL;
+
+ pDir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt +
+ (pRes[i].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY));
+
+ count = pDir->NumberOfIdEntries + pDir->NumberOfNamedEntries;
+ *pCount = count;
+ pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1);
+
+ return pRes;
+}
+
+int _FindResourceCount(void* prt, int resType)
+{
+ int count;
+
+ _FindResourceBase(prt, resType, &count);
+ return count;
+}
+
+void* _FindResource(IMAGE_DOS_HEADER* pDosHeader, void* prt, int resIndex, int resType, DWORD* pcbSize)
+{
+ int count, index = 0;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pRes;
+ IMAGE_RESOURCE_DATA_ENTRY* pEntry;
+
+ pRes = _FindResourceBase(prt, resType, &count);
+ if (resIndex < 0) {
+ for(index = 0; index < count; index++)
+ if (pRes[index].Name == (DWORD)(-resIndex))
+ break;
+ }
+ else index = resIndex;
+
+ if (index >= count)
+ return NULL;
+
+ if (pRes[index].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) {
+ IMAGE_RESOURCE_DIRECTORY* pDir;
+ pDir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt + (pRes[index].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY) );
+ pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1);
+ index = 0;
+ }
+
+ if ( pRes[index].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY )
+ return NULL;
+
+ pEntry = (IMAGE_RESOURCE_DATA_ENTRY*)((LPBYTE)prt + pRes[index].OffsetToData);
+ *pcbSize = pEntry->Size;
+ return _RelativeVirtualAddresstoPtr(pDosHeader, pEntry->OffsetToData);
+}
+
+UINT _ExtractFromExe(HANDLE hFile, int iconIndex, int cxIconSize, int cyIconSize, HICON *phicon, UINT flags)
+{
+ int retval = 0;
+ DWORD fileLen = GetFileSize(hFile, NULL);
+ HANDLE hFileMap = INVALID_HANDLE_VALUE, pFile = NULL;
+ IMAGE_DOS_HEADER* pDosHeader;
+ void* pRes;
+ DWORD cbSize = 0;
+ NEWHEADER* pIconDir;
+ int idIcon;
+ LPBITMAPINFOHEADER pIcon;
+ // UINT res = 0;
+
+ hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hFileMap == NULL) goto cleanup;
+
+ pFile = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
+ if (pFile == NULL) goto cleanup;
+
+ pDosHeader = (IMAGE_DOS_HEADER*)(void*)pFile;
+ if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) goto cleanup;
+ if (pDosHeader->e_lfanew <= 0) goto cleanup;
+ if ((DWORD)(pDosHeader->e_lfanew) >= fileLen) goto cleanup;
+
+ pRes = _GetResourceTable(pDosHeader);
+ if (!pRes) goto cleanup;
+ if (!phicon) {
+ retval = _FindResourceCount(pRes, (int)RT_GROUP_ICON);
+ goto cleanup;
+ }
+
+ pIconDir = (NEWHEADER*)_FindResource(pDosHeader, pRes, iconIndex, (int)RT_GROUP_ICON, &cbSize);
+ if (!pIconDir) goto cleanup;
+ if (pIconDir->Reserved || pIconDir->ResType != RES_ICON) goto cleanup;
+
+ idIcon = LookupIconIdFromDirectoryEx((LPBYTE)pIconDir, TRUE, cxIconSize, cyIconSize, flags);
+ pIcon = (LPBITMAPINFOHEADER)_FindResource(pDosHeader, pRes, -idIcon, (int)RT_ICON, &cbSize);
+ if (!pIcon) goto cleanup;
+
+ if ( pIcon->biSize != sizeof(BITMAPINFOHEADER) && pIcon->biSize != sizeof(BITMAPCOREHEADER)) {
+ _ASSERT(0);
+ goto cleanup;
+ }
+
+ *phicon = CreateIconFromResourceEx((LPBYTE)pIcon, cbSize, TRUE, VER30, cxIconSize, cyIconSize, flags);
+ retval = 1;
+
+cleanup:
+ if (pFile) UnmapViewOfFile(pFile);
+ if (hFileMap != INVALID_HANDLE_VALUE) CloseHandle(hFileMap);
+
+ return retval;
+}
+
+UINT _ExtractFromICO( LPCTSTR pFileName, int iconIndex, int cxIcon, int cyIcon, HICON* phicon, UINT flags )
+{
+ HICON hicon;
+
+ if ( iconIndex >= 1 )
+ return 0;
+
+ // do we just want a count?
+ if (!phicon)
+ return 1;
+
+ flags |= LR_LOADFROMFILE;
+ hicon = (HICON)LoadImage( NULL, pFileName, IMAGE_ICON, cxIcon, cyIcon, flags );
+ if (!hicon)
+ return 0;
+
+ *phicon = hicon;
+ return 1;
+}
+
+UINT _ExtractIconEx(LPCTSTR lpszFile, int iconIndex, int cxIcon, int cyIcon, HICON *phicon, UINT flags)
+{
+ HANDLE hFile;
+ WORD magic[6];
+ DWORD read = 0;
+ UINT res = 0;
+
+ if (cxIcon == GetSystemMetrics(SM_CXICON) && cyIcon == GetSystemMetrics(SM_CYICON))
+ res = ExtractIconEx(lpszFile, iconIndex, phicon, NULL, 1);
+ else if (cxIcon == GetSystemMetrics(SM_CXSMICON) && cyIcon == GetSystemMetrics(SM_CYSMICON))
+ res = ExtractIconEx(lpszFile, iconIndex, NULL, phicon, 1);
+ else if (cxIcon == 0 || cyIcon == 0)
+ res = ExtractIconEx(lpszFile, iconIndex, NULL, phicon, 1);
+ // check if the api succeded, if not try our method too
+ if (res) return res;
+
+ hFile = CreateFile(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return 0;
+
+ // failed to read file signature
+ if ( !ReadFile(hFile,&magic, sizeof(magic), &read, NULL ) || (read != sizeof(magic))) {
+ CloseHandle(hFile);
+ return 0;
+ }
+
+ switch ( magic[0] ) {
+ case IMAGE_DOS_SIGNATURE:
+ res = _ExtractFromExe(hFile, iconIndex, cxIcon, cyIcon, phicon, flags);
+ break;
+
+ case MAGIC_ANI1: // ani cursors are RIFF file of type 'ACON'
+ if (magic[1] == MAGIC_ANI2 && magic[4] == MAGIC_ANI3 && magic[5] == MAGIC_ANI4)
+ res = _ExtractFromICO(lpszFile, iconIndex, cxIcon, cyIcon, phicon, flags);
+ break;
+
+ case MAGIC_ICON:
+ if ((magic[1] == MAGIC_ICO1 || magic[1] == MAGIC_CUR ) && magic[2] >= 1 )
+ res = _ExtractFromICO(lpszFile, iconIndex, cxIcon, cyIcon, phicon, flags);
+
+ break;
+ }
+
+ CloseHandle(hFile);
+ return res;
+}
diff --git a/src/modules/icolib/skin2icons.cpp b/src/modules/icolib/skin2icons.cpp
new file mode 100644
index 0000000000..a1a3e670b2
--- /dev/null
+++ b/src/modules/icolib/skin2icons.cpp
@@ -0,0 +1,1992 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+#include "IcoLib.h"
+
+static BOOL bModuleInitialized = FALSE;
+static HANDLE hIcons2ChangedEvent, hIconsChangedEvent;
+static HICON hIconBlank = NULL;
+
+HANDLE hIcoLib_AddNewIcon, hIcoLib_RemoveIcon, hIcoLib_GetIcon, hIcoLib_GetIcon2,
+ hIcoLib_GetIconHandle, hIcoLib_IsManaged, hIcoLib_AddRef, hIcoLib_ReleaseIcon;
+
+static int iconEventActive = 0;
+
+static BOOL bNeedRebuild = FALSE;
+
+struct IcoLibOptsData {
+ HWND hwndIndex;
+};
+
+CRITICAL_SECTION csIconList;
+
+#define SECTIONPARAM_MAKE(index, level, flags) MAKELONG( (index)&0xFFFF, MAKEWORD( level, flags ) )
+#define SECTIONPARAM_INDEX(lparam) LOWORD( lparam )
+#define SECTIONPARAM_LEVEL(lparam) LOBYTE( HIWORD(lparam) )
+#define SECTIONPARAM_FLAGS(lparam) HIBYTE( HIWORD(lparam) )
+#define SECTIONPARAM_HAVEPAGE 0x0001
+
+static int sttCompareSections( const SectionItem* p1, const SectionItem* p2 )
+{ return _tcscmp( p1->name, p2->name );
+}
+
+static LIST<SectionItem> sectionList( 20, sttCompareSections );
+
+static int sttCompareIconSourceFiles( const IconSourceFile* p1, const IconSourceFile* p2 )
+{ return _tcsicmp( p1->file, p2->file );
+}
+
+static LIST<IconSourceFile> iconSourceFileList( 10, sttCompareIconSourceFiles );
+
+static int sttCompareIconSourceItems( const IconSourceItem* p1, const IconSourceItem* p2 )
+{ if (p1->indx < p2->indx)
+ return -1;
+
+ if (p1->indx > p2->indx)
+ return 1;
+
+ if (p1->cx < p2->cx)
+ return -1;
+
+ if (p1->cx > p2->cx)
+ return 1;
+
+ if (p1->cy < p2->cy)
+ return -1;
+
+ if (p1->cy > p2->cy)
+ return 1;
+
+ if ( p1->file == p2->file )
+ return 0;
+
+ return ( p1->file > p2->file ) ? 1 : -1;
+}
+
+static LIST<IconSourceItem> iconSourceList( 20, sttCompareIconSourceItems );
+
+static int sttCompareIcons( const IconItem* p1, const IconItem* p2 )
+{ return strcmp( p1->name, p2->name );
+}
+
+static LIST<IconItem> iconList( 20, sttCompareIcons );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utility functions
+
+static void __fastcall MySetCursor(TCHAR* nCursor)
+{ SetCursor( LoadCursor( NULL, nCursor ));
+}
+
+static void __fastcall SAFE_FREE(void** p)
+{
+ if ( *p ) {
+ mir_free( *p );
+ *p = NULL;
+} }
+
+static void __fastcall SafeDestroyIcon( HICON* icon )
+{
+ if ( *icon ) {
+ DestroyIcon( *icon );
+ *icon = NULL;
+} }
+
+
+// Helper functions to manage Icon resources
+
+IconSourceFile* IconSourceFile_Get( const TCHAR* file, bool isPath )
+{
+ TCHAR fileFull[ MAX_PATH ];
+
+ if ( !file )
+ return NULL;
+
+ if (isPath)
+ pathToAbsoluteT( file, fileFull, NULL );
+ /// TODO: convert path to long - eliminate duplicate items
+ else
+ _tcscpy( fileFull, file );
+
+ IconSourceFile key = { fileFull };
+ int ix;
+ if (( ix = iconSourceFileList.getIndex( &key )) != -1 ) {
+ iconSourceFileList[ ix ]->ref_count++;
+ return iconSourceFileList[ ix ];
+ }
+
+ IconSourceFile* newItem = (IconSourceFile*)mir_calloc( sizeof( IconSourceFile ));
+ newItem->file = mir_tstrdup( fileFull );
+ newItem->ref_count = 1;
+ iconSourceFileList.insert( newItem );
+
+ return newItem;
+}
+
+int IconSourceFile_Release( IconSourceFile** pitem )
+{
+ if ( pitem && *pitem && (*pitem)->ref_count ) {
+ IconSourceFile* item = *pitem;
+ if ( --item->ref_count <= 0 ) {
+ int indx;
+ if (( indx = iconSourceFileList.getIndex( item )) != -1 ) {
+ SAFE_FREE(( void** )&item->file );
+ iconSourceFileList.remove( indx );
+ SAFE_FREE(( void** )&item );
+ }
+ }
+ *pitem = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+static int BytesPerScanLine(int PixelsPerScanline, int BitsPerPixel, int Alignment)
+{ Alignment--;
+ int bytes = ((PixelsPerScanline * BitsPerPixel) + Alignment) & ~Alignment;
+ return bytes / 8;
+}
+
+static int InitializeBitmapInfoHeader( HBITMAP bitmap, BITMAPINFOHEADER* bi )
+{
+ DIBSECTION DS;
+ int bytes;
+
+ DS.dsBmih.biSize = 0;
+ bytes = GetObject( bitmap, sizeof(DS), &DS );
+ if ( bytes == 0 ) return 1; // Failure
+ else if (( bytes >= (sizeof(DS.dsBm) + sizeof(DS.dsBmih))) &&
+ (DS.dsBmih.biSize >= DWORD(sizeof(DS.dsBmih))))
+ *bi = DS.dsBmih;
+ else {
+ memset(bi, 0, sizeof(BITMAPINFOHEADER));
+ bi->biSize = sizeof(BITMAPINFOHEADER);
+ bi->biWidth = DS.dsBm.bmWidth;
+ bi->biHeight = DS.dsBm.bmHeight;
+ }
+ bi->biBitCount = DS.dsBm.bmBitsPixel * DS.dsBm.bmPlanes;
+ bi->biPlanes = 1;
+ if ( bi->biClrImportant > bi->biClrUsed )
+ bi->biClrImportant = bi->biClrUsed;
+ if ( !bi->biSizeImage )
+ bi->biSizeImage = BytesPerScanLine( bi->biWidth, bi->biBitCount, 32 ) * abs( bi->biHeight );
+ return 0; // Success
+}
+
+static int InternalGetDIBSizes( HBITMAP bitmap, int* InfoHeaderSize, int* ImageSize )
+{
+ BITMAPINFOHEADER bi;
+
+ if ( InitializeBitmapInfoHeader( bitmap, &bi )) return 1; // Failure
+ if ( bi.biBitCount > 8 ) {
+ *InfoHeaderSize = sizeof(BITMAPINFOHEADER);
+ if ((bi.biCompression & BI_BITFIELDS) != 0 )
+ *InfoHeaderSize += 12;
+ }
+ else {
+ if ( bi.biClrUsed == 0 )
+ *InfoHeaderSize = sizeof(BITMAPINFOHEADER) +
+ sizeof(RGBQUAD) * (int)(1 << bi.biBitCount);
+ else
+ *InfoHeaderSize = sizeof(BITMAPINFOHEADER) +
+ sizeof(RGBQUAD) * bi.biClrUsed;
+ }
+ *ImageSize = bi.biSizeImage;
+ return 0; // Success
+}
+
+static int InternalGetDIB( HBITMAP bitmap, HPALETTE palette, void* bitmapInfo, void* Bits )
+{
+ HPALETTE oldPal = 0;
+
+ if ( InitializeBitmapInfoHeader( bitmap, (BITMAPINFOHEADER*)bitmapInfo )) return 1; // Failure
+
+ HDC DC = CreateCompatibleDC(0);
+ if ( palette ) {
+ oldPal = SelectPalette( DC, palette, FALSE );
+ RealizePalette( DC );
+ }
+ int result = GetDIBits( DC, bitmap, 0, ((BITMAPINFOHEADER*)bitmapInfo)->biHeight, Bits, (BITMAPINFO*)bitmapInfo, DIB_RGB_COLORS) == 0;
+
+ if ( oldPal ) SelectPalette( DC, oldPal, FALSE );
+ DeleteDC( DC );
+ return result;
+}
+
+static int GetIconData( HICON icon, BYTE** data, int* size )
+{
+ ICONINFO iconInfo;
+ int MonoInfoSize, ColorInfoSize;
+ int MonoBitsSize, ColorBitsSize;
+
+ if ( !data || !size ) return 1; // Failure
+
+ if ( !GetIconInfo( icon, &iconInfo )) return 1; // Failure
+
+ if ( InternalGetDIBSizes( iconInfo.hbmMask, &MonoInfoSize, &MonoBitsSize ) ||
+ InternalGetDIBSizes( iconInfo.hbmColor, &ColorInfoSize, &ColorBitsSize )) {
+ DeleteObject( iconInfo.hbmColor );
+ DeleteObject( iconInfo.hbmMask );
+ return 1; // Failure
+ }
+ void* MonoInfo = mir_alloc( MonoInfoSize );
+ void* MonoBits = mir_alloc( MonoBitsSize );
+ void* ColorInfo = mir_alloc( ColorInfoSize );
+ void* ColorBits = mir_alloc( ColorBitsSize );
+
+ if ( InternalGetDIB( iconInfo.hbmMask, 0, MonoInfo, MonoBits ) ||
+ InternalGetDIB( iconInfo.hbmColor, 0, ColorInfo, ColorBits )) {
+ SAFE_FREE( &MonoInfo );
+ SAFE_FREE( &MonoBits );
+ SAFE_FREE( &ColorInfo );
+ SAFE_FREE( &ColorBits );
+ DeleteObject( iconInfo.hbmColor );
+ DeleteObject( iconInfo.hbmMask );
+ return 1; // Failure
+ }
+
+ *size = ColorInfoSize + ColorBitsSize + MonoBitsSize;
+ *data = (BYTE*)mir_alloc(*size);
+
+ BYTE* buf = *data;
+ ((BITMAPINFOHEADER*)ColorInfo)->biHeight *= 2; // color height includes mono bits
+ memcpy( buf, ColorInfo, ColorInfoSize );
+ buf += ColorInfoSize;
+ memcpy( buf, ColorBits, ColorBitsSize );
+ buf += ColorBitsSize;
+ memcpy( buf, MonoBits, MonoBitsSize );
+
+ SAFE_FREE( &MonoInfo );
+ SAFE_FREE( &MonoBits );
+ SAFE_FREE( &ColorInfo );
+ SAFE_FREE( &ColorBits );
+ DeleteObject( iconInfo.hbmColor );
+ DeleteObject( iconInfo.hbmMask );
+ return 0; // Success
+}
+
+#define VER30 0x00030000
+
+static HICON IconSourceItem_GetIcon( IconSourceItem* item )
+{
+ if ( item->icon ) {
+ item->icon_ref_count++;
+ return item->icon;
+ }
+
+ if ( item->icon_size ) {
+ item->icon = CreateIconFromResourceEx( item->icon_data, item->icon_size, TRUE, VER30, item->cx, item->cy, LR_COLOR );
+ if ( item->icon ) {
+ item->icon_ref_count++;
+ return item->icon;
+ }
+ }
+ //SHOULD BE REPLACED WITH GOOD ENOUGH FUNCTION
+ _ExtractIconEx( item->file->file, item->indx, item->cx, item->cy, &item->icon, LR_COLOR );
+
+ if ( item->icon )
+ item->icon_ref_count++;
+
+ return item->icon;
+}
+
+static int IconSourceItem_ReleaseIcon( IconSourceItem* item )
+{
+ if ( item && item->icon_ref_count )
+ {
+ item->icon_ref_count--;
+ if ( !item->icon_ref_count ) {
+ if ( !item->icon_size )
+ if ( GetIconData( item->icon, &item->icon_data, &item->icon_size ))
+ item->icon_size = 0; // Failure
+ SafeDestroyIcon( &item->icon );
+ }
+ return 0; // Success
+ }
+ return 1; // Failure
+}
+
+IconSourceItem* GetIconSourceItem( const TCHAR* file, int indx, int cxIcon, int cyIcon )
+{
+ if ( !file )
+ return NULL;
+
+ IconSourceFile* r_file = IconSourceFile_Get( file, true );
+ IconSourceItem key = { r_file, indx, cxIcon, cyIcon };
+ int ix;
+ if (( ix = iconSourceList.getIndex( &key )) != -1 ) {
+ IconSourceFile_Release( &r_file );
+ iconSourceList[ ix ]->ref_count++;
+ return iconSourceList[ ix ];
+ }
+
+ IconSourceItem* newItem = (IconSourceItem*)mir_calloc( sizeof( IconSourceItem ));
+ newItem->file = r_file;
+ newItem->indx = indx;
+ newItem->ref_count = 1;
+ newItem->cx = cxIcon;
+ newItem->cy = cyIcon;
+ iconSourceList.insert( newItem );
+
+ return newItem;
+}
+
+IconSourceItem* GetIconSourceItemFromPath( const TCHAR* path, int cxIcon, int cyIcon )
+{
+ TCHAR *comma;
+ TCHAR file[ MAX_PATH ];
+ int n;
+
+ if ( !path )
+ return NULL;
+
+ lstrcpyn( file, path, SIZEOF( file ));
+ comma = _tcsrchr( file, ',' );
+ if ( !comma )
+ n = 0;
+ else {
+ n = _ttoi( comma+1 );
+ *comma = 0;
+ }
+ return GetIconSourceItem( file, n, cxIcon, cyIcon );
+}
+
+IconSourceItem* CreateStaticIconSourceItem( int cxIcon, int cyIcon )
+{
+ TCHAR sourceName[ MAX_PATH ];
+ IconSourceFile key = { sourceName };
+
+ int i = 0;
+ do { // find new unique name
+ mir_sntprintf( sourceName, SIZEOF( sourceName ), _T("*StaticIcon_%d"), ++i);
+ } while ( iconSourceFileList.getIndex( &key ) != -1 );
+
+ IconSourceItem* newItem = (IconSourceItem*)mir_calloc( sizeof( IconSourceItem ));
+ newItem->file = IconSourceFile_Get( sourceName, false );
+ newItem->indx = 0;
+ newItem->ref_count = 1;
+ newItem->cx = cxIcon;
+ newItem->cy = cyIcon;
+ iconSourceList.insert( newItem );
+
+ return newItem;
+}
+
+static int IconSourceItem_Release( IconSourceItem** pitem )
+{
+ if ( pitem && *pitem && (*pitem)->ref_count ) {
+ IconSourceItem* item = *pitem;
+ item->ref_count--;
+ if ( !item->ref_count ) {
+ int indx;
+ if (( indx = iconSourceList.getIndex( item )) != -1 ) {
+ IconSourceFile_Release( &item->file );
+ SafeDestroyIcon( &item->icon );
+ SAFE_FREE(( void** )&item->icon_data );
+ iconSourceList.remove( indx );
+ SAFE_FREE(( void** )&item );
+ }
+ }
+ *pitem = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Service functions
+
+static HICON ExtractIconFromPath( const TCHAR *path, int cxIcon, int cyIcon )
+{
+ TCHAR *comma;
+ TCHAR file[ MAX_PATH ], fileFull[ MAX_PATH ];
+ int n;
+ HICON hIcon;
+
+ if ( !path )
+ return (HICON)NULL;
+
+ lstrcpyn( file, path, SIZEOF( file ));
+ comma = _tcsrchr( file, ',' );
+ if ( !comma )
+ n = 0;
+ else {
+ n = _ttoi( comma+1 );
+ *comma = 0;
+ }
+ pathToAbsoluteT( file, fileFull, NULL );
+ hIcon = NULL;
+
+ //SHOULD BE REPLACED WITH GOOD ENOUGH FUNCTION
+ _ExtractIconEx( fileFull, n, cxIcon, cyIcon, &hIcon, LR_COLOR );
+ return hIcon;
+}
+
+static SectionItem* IcoLib_AddSection(TCHAR *sectionName, BOOL create_new)
+{
+ if ( !sectionName )
+ return NULL;
+
+ int indx;
+ SectionItem key = { sectionName, 0 };
+ if (( indx = sectionList.getIndex( &key )) != -1 )
+ return sectionList[ indx ];
+
+ if ( create_new ) {
+ SectionItem* newItem = ( SectionItem* )mir_calloc( sizeof( SectionItem ));
+ newItem->name = mir_tstrdup( sectionName );
+ newItem->flags = 0;
+ sectionList.insert( newItem );
+ bNeedRebuild = TRUE;
+ return newItem;
+ }
+
+ return NULL;
+}
+
+static void IcoLib_RemoveSection(SectionItem* section)
+{
+ if ( !section )
+ return;
+
+ int indx;
+
+ if (( indx = sectionList.getIndex( section )) != -1 ) {
+ sectionList.remove( indx );
+ SAFE_FREE(( void** )&section->name);
+ SAFE_FREE(( void** )&section );
+ bNeedRebuild = TRUE;
+ }
+}
+
+static IconItem* IcoLib_FindIcon(const char* pszIconName)
+{
+ int indx;
+ IconItem key = { (char*)pszIconName };
+ if (( indx = iconList.getIndex( &key )) != -1 )
+ return iconList[ indx ];
+
+ return NULL;
+}
+
+static IconItem* IcoLib_FindHIcon(HICON hIcon, bool &big)
+{
+ IconItem* item = NULL;
+ int indx;
+
+ for ( indx = 0; indx < iconList.getCount(); indx++ ) {
+ if ( iconList[ indx ]->source_small && iconList[ indx ]->source_small->icon == hIcon) {
+ item = iconList[ indx ];
+ big = false;
+ break;
+ }
+ else if ( iconList[ indx ]->source_big && iconList[ indx ]->source_big->icon == hIcon) {
+ item = iconList[ indx ];
+ big = true;
+ break;
+ }
+ }
+
+ return item;
+}
+
+static void IcoLib_FreeIcon(IconItem* icon)
+{
+ if ( !icon) return;
+
+ SAFE_FREE(( void** )&icon->name );
+ SAFE_FREE(( void** )&icon->description );
+ SAFE_FREE(( void** )&icon->default_file );
+ SAFE_FREE(( void** )&icon->temp_file );
+ if ( icon->section) {
+ if ( !--icon->section->ref_count)
+ IcoLib_RemoveSection( icon->section );
+ icon->section = NULL;
+ }
+ IconSourceItem_Release( &icon->source_small );
+ IconSourceItem_Release( &icon->source_big );
+ IconSourceItem_Release( &icon->default_icon );
+ SafeDestroyIcon( &icon->temp_icon );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_AddNewIcon
+
+HANDLE IcoLib_AddNewIcon( SKINICONDESC* sid )
+{
+ int utf = 0, utf_path = 0;
+ IconItem* item;
+
+ if ( !sid->cbSize )
+ return NULL;
+
+ if ( sid->cbSize < SKINICONDESC_SIZE_V1 )
+ return NULL;
+
+ if ( sid->cbSize >= SKINICONDESC_SIZE ) {
+ utf = sid->flags & SIDF_UNICODE ? 1 : 0;
+ utf_path = sid->flags & SIDF_PATH_UNICODE ? 1 : 0;
+ }
+
+ EnterCriticalSection( &csIconList );
+
+ item = IcoLib_FindIcon( sid->pszName );
+ if ( !item ) {
+ item = ( IconItem* )mir_alloc( sizeof( IconItem ));
+ item->name = sid->pszName;
+ iconList.insert( item );
+ }
+ else IcoLib_FreeIcon( item );
+
+ ZeroMemory( item, sizeof( *item ));
+ item->name = mir_strdup( sid->pszName );
+ if ( utf ) {
+ item->description = mir_u2t( sid->pwszDescription );
+ #ifdef _UNICODE
+ item->section = IcoLib_AddSection( sid->pwszSection, TRUE );
+ #else
+ char* pszSection = sid->pwszSection ? mir_u2a( sid->pwszSection ) : NULL;
+ item->section = IcoLib_AddSection( pszSection, TRUE );
+ SAFE_FREE(( void** )&pszSection );
+ #endif
+ }
+ else {
+ item->description = mir_a2t( sid->pszDescription );
+ #ifdef _UNICODE
+ WCHAR* pwszSection = sid->pszSection ? mir_a2u( sid->pszSection ) : NULL;
+
+ item->section = IcoLib_AddSection( pwszSection, TRUE );
+ SAFE_FREE(( void** )&pwszSection );
+ #else
+ item->section = IcoLib_AddSection( sid->pszSection, TRUE );
+ #endif
+ }
+ if ( item->section ) {
+ item->section->ref_count++;
+ item->orderID = ++item->section->maxOrder;
+ }
+ else
+ item->orderID = 0;
+
+ if ( sid->pszDefaultFile ) {
+ if ( utf_path ) {
+ #ifdef _UNICODE
+ WCHAR fileFull[ MAX_PATH ];
+
+ pathToAbsoluteW( sid->pwszDefaultFile, fileFull, NULL );
+ item->default_file = mir_wstrdup( fileFull );
+ #else
+ char* file = mir_u2a( sid->pwszDefaultFile );
+ char fileFull[ MAX_PATH ];
+
+ pathToAbsolute( file, fileFull, NULL );
+ SAFE_FREE(( void** )&file );
+ item->default_file = mir_strdup( fileFull );
+ #endif
+ }
+ else {
+ #ifdef _UNICODE
+ WCHAR *file = mir_a2u( sid->pszDefaultFile );
+ WCHAR fileFull[ MAX_PATH ];
+
+ pathToAbsoluteW( file, fileFull, NULL );
+ SAFE_FREE(( void** )&file );
+ item->default_file = mir_wstrdup( fileFull );
+ #else
+ char fileFull[ MAX_PATH ];
+
+ pathToAbsolute( sid->pszDefaultFile, fileFull, NULL );
+ item->default_file = mir_strdup( fileFull );
+ #endif
+ } }
+ item->default_indx = sid->iDefaultIndex;
+
+ if ( sid->cbSize >= SKINICONDESC_SIZE_V3 ) {
+ item->cx = sid->cx;
+ item->cy = sid->cy;
+ }
+
+ if ( sid->cbSize >= SKINICONDESC_SIZE_V2 && sid->hDefaultIcon ) {
+ bool big;
+ IconItem* def_item = IcoLib_FindHIcon( sid->hDefaultIcon, big );
+ if ( def_item ) {
+ item->default_icon = big ? def_item->source_big : def_item->source_small;
+ item->default_icon->ref_count++;
+ }
+ else {
+ int cx = item->cx ? item->cx : GetSystemMetrics(SM_CXSMICON);
+ int cy = item->cy ? item->cy : GetSystemMetrics(SM_CYSMICON);
+ item->default_icon = CreateStaticIconSourceItem( cx, cy );
+ if ( GetIconData( sid->hDefaultIcon, &item->default_icon->icon_data, &item->default_icon->icon_size )) {
+ IconSourceItem_Release( &item->default_icon );
+ }
+ }
+ }
+
+ if ( sid->cbSize >= SKINICONDESC_SIZE && item->section )
+ item->section->flags = sid->flags & SIDF_SORTED;
+
+ LeaveCriticalSection( &csIconList );
+ return item;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_RemoveIcon
+
+static INT_PTR IcoLib_RemoveIcon( WPARAM, LPARAM lParam )
+{
+ if ( lParam ) {
+ int indx;
+ EnterCriticalSection( &csIconList );
+
+ if (( indx = iconList.getIndex(( IconItem* )&lParam )) != -1 ) {
+ IconItem *item = iconList[ indx ];
+ IcoLib_FreeIcon( item );
+ iconList.remove( indx );
+ SAFE_FREE(( void** )&item );
+ }
+
+ LeaveCriticalSection( &csIconList );
+ return ( indx == -1 ) ? 1 : 0;
+ }
+ return 1; // Failed
+}
+
+HICON IconItem_GetDefaultIcon( IconItem* item, bool big )
+{
+ HICON hIcon = NULL;
+
+ if ( item->default_icon && !big ) {
+ IconSourceItem_Release( &item->source_small );
+ item->source_small = item->default_icon;
+ item->source_small->ref_count++;
+ hIcon = IconSourceItem_GetIcon( item->source_small );
+ }
+
+ if ( !hIcon && item->default_file ) {
+ int cx = item->cx ? item->cx : GetSystemMetrics(big ? SM_CXICON : SM_CXSMICON);
+ int cy = item->cy ? item->cy : GetSystemMetrics(big ? SM_CYICON : SM_CYSMICON);
+ IconSourceItem* def_icon = GetIconSourceItem( item->default_file, item->default_indx, cx, cy );
+ if ( big ) {
+ if ( def_icon != item->source_big ) {
+ IconSourceItem_Release( &item->source_big );
+ item->source_big = def_icon;
+ if ( def_icon ) {
+ def_icon->ref_count++;
+ hIcon = IconSourceItem_GetIcon( def_icon );
+ }
+ }
+ else
+ IconSourceItem_Release( &def_icon );
+ }
+ else {
+ if ( def_icon != item->default_icon ) {
+ IconSourceItem_Release( &item->default_icon );
+ item->default_icon = def_icon;
+ if ( def_icon ) {
+ IconSourceItem_Release( &item->source_small );
+ item->source_small = def_icon;
+ def_icon->ref_count++;
+ hIcon = IconSourceItem_GetIcon( def_icon );
+ }
+ }
+ else
+ IconSourceItem_Release( &def_icon );
+ }
+ }
+ return hIcon;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IconItem_GetIcon
+
+HICON IconItem_GetIcon( IconItem* item, bool big )
+{
+ DBVARIANT dbv = {0};
+ HICON hIcon = NULL;
+
+ big = big && !item->cx;
+ IconSourceItem* &source = big ? item->source_big : item->source_small;
+
+ if ( !source && !DBGetContactSettingTString( NULL, "SkinIcons", item->name, &dbv )) {
+ int cx = item->cx ? item->cx : GetSystemMetrics(big ? SM_CXICON : SM_CXSMICON);
+ int cy = item->cy ? item->cy : GetSystemMetrics(big ? SM_CYICON : SM_CYSMICON);
+ source = GetIconSourceItemFromPath( dbv.ptszVal, cx, cy );
+ DBFreeVariant( &dbv );
+ }
+
+ if ( source )
+ hIcon = IconSourceItem_GetIcon( source );
+
+ if ( !hIcon )
+ hIcon = IconItem_GetDefaultIcon( item, big );
+
+ if ( !hIcon )
+ return hIconBlank;
+
+ return hIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IconItem_GetIcon_Preview
+
+HICON IconItem_GetIcon_Preview( IconItem* item )
+{
+ HICON hIcon = NULL;
+
+ if ( !item->temp_reset ) {
+ HICON hRefIcon = IconItem_GetIcon( item, false );
+ hIcon = CopyIcon( hRefIcon );
+ if ( item->source_small && item->source_small->icon == hRefIcon )
+ IconSourceItem_ReleaseIcon( item->source_small );
+ }
+ else {
+ if ( item->default_icon ) {
+ HICON hRefIcon = IconSourceItem_GetIcon( item->default_icon );
+ if ( hRefIcon ) {
+ hIcon = CopyIcon( hRefIcon );
+ if ( item->default_icon->icon == hRefIcon )
+ IconSourceItem_ReleaseIcon( item->default_icon );
+ } }
+
+ if ( !hIcon && item->default_file ) {
+ IconSourceItem_Release( &item->default_icon );
+ item->default_icon = GetIconSourceItem( item->default_file, item->default_indx, item->cx, item->cy );
+ if ( item->default_icon ) {
+ HICON hRefIcon = IconSourceItem_GetIcon( item->default_icon );
+ if ( hRefIcon ) {
+ hIcon = CopyIcon( hRefIcon );
+ if ( item->default_icon->icon == hRefIcon )
+ IconSourceItem_ReleaseIcon( item->default_icon );
+ } } }
+
+ if ( !hIcon )
+ return CopyIcon(hIconBlank);
+ }
+ return hIcon;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_GetIcon
+// lParam: pszIconName
+// wParam: PLOADIMAGEPARAM or NULL.
+// if wParam == NULL, default is used:
+// uType = IMAGE_ICON
+// cx/cyDesired = GetSystemMetrics(SM_CX/CYSMICON)
+// fuLoad = 0
+
+HICON IcoLib_GetIcon( const char* pszIconName, bool big )
+{
+ IconItem* item;
+ HICON result = NULL;
+
+ if ( !pszIconName )
+ return hIconBlank;
+
+ EnterCriticalSection( &csIconList );
+
+ item = IcoLib_FindIcon( pszIconName );
+ if ( item ) {
+ result = IconItem_GetIcon( item, big );
+ }
+ LeaveCriticalSection( &csIconList );
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_GetIconHandle
+// lParam: pszIconName
+
+HANDLE IcoLib_GetIconHandle( const char* pszIconName )
+{
+ IconItem* item;
+
+ if ( !pszIconName )
+ return NULL;
+
+ EnterCriticalSection( &csIconList );
+ item = IcoLib_FindIcon( pszIconName );
+ LeaveCriticalSection( &csIconList );
+
+ return (HANDLE)item;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_GetIconByHandle
+// lParam: icolib item handle
+// wParam: 0
+
+HICON IcoLib_GetIconByHandle( HANDLE hItem, bool big )
+{
+ if ( hItem == NULL )
+ return NULL;
+
+ HICON result = hIconBlank;
+ IconItem* pi = ( IconItem* )hItem;
+
+ EnterCriticalSection( &csIconList );
+
+ // we can get any junk here... but getIndex() is MUCH faster than indexOf().
+ __try
+ {
+ if ( iconList.getIndex( pi ) != -1 )
+ result = IconItem_GetIcon( pi, big );
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+
+ LeaveCriticalSection( &csIconList );
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_IsManaged
+// lParam: NULL
+// wParam: HICON
+
+HANDLE IcoLib_IsManaged( HICON hIcon )
+{
+ IconItem* item;
+
+ EnterCriticalSection( &csIconList );
+
+ bool big;
+ item = IcoLib_FindHIcon( hIcon, big );
+ if ( item ) {
+ IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small;
+ if ( source->icon_ref_count == 0 )
+ item = NULL;
+ }
+
+ LeaveCriticalSection( &csIconList );
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_AddRef
+// lParam: NULL
+// wParam: HICON
+
+static INT_PTR IcoLib_AddRef( WPARAM wParam, LPARAM )
+{
+ EnterCriticalSection( &csIconList );
+
+ bool big;
+ IconItem *item = IcoLib_FindHIcon(( HICON )wParam, big);
+
+ INT_PTR res = 1;
+ if ( item ) {
+ IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small;
+ if ( source->icon_ref_count ) {
+ source->icon_ref_count++;
+ res = 0;
+ }
+ }
+
+ LeaveCriticalSection( &csIconList );
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib_ReleaseIcon
+// lParam: pszIconName or NULL
+// wParam: HICON or NULL
+
+int IcoLib_ReleaseIcon( HICON hIcon, char* szIconName, bool big )
+{
+ IconItem *item = NULL;
+
+ EnterCriticalSection(&csIconList);
+
+ if ( szIconName )
+ item = IcoLib_FindIcon( szIconName );
+
+ if ( !item && hIcon ) // find by HICON
+ item = IcoLib_FindHIcon( hIcon, big );
+
+ int res = 1;
+ if ( item ) {
+ IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small;
+ if ( source && source->icon_ref_count ) {
+ if ( iconEventActive )
+ source->icon_ref_count--;
+ else
+ IconSourceItem_ReleaseIcon( source );
+ res = 0;
+ }
+ }
+
+ LeaveCriticalSection( &csIconList );
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib GUI service routines
+
+static void LoadSectionIcons(TCHAR *filename, SectionItem* sectionActive)
+{
+ TCHAR path[ MAX_PATH ];
+ int suffIndx;
+ HICON hIcon;
+ int indx;
+
+ mir_sntprintf( path, SIZEOF(path), _T("%s,"), filename );
+ suffIndx = lstrlen( path );
+
+ EnterCriticalSection( &csIconList );
+
+ for ( indx = 0; indx < iconList.getCount(); indx++ ) {
+ IconItem *item = iconList[ indx ];
+
+ if ( item->default_file && item->section == sectionActive ) {
+ _itot( item->default_indx, path + suffIndx, 10 );
+ hIcon = ExtractIconFromPath( path, item->cx, item->cy );
+ if ( hIcon ) {
+ SAFE_FREE(( void** )&item->temp_file );
+ SafeDestroyIcon( &item->temp_icon );
+
+ item->temp_file = mir_tstrdup( path );
+ item->temp_icon = hIcon;
+ item->temp_reset = FALSE;
+ } } }
+
+ LeaveCriticalSection( &csIconList );
+}
+
+void LoadSubIcons(HWND htv, TCHAR *filename, HTREEITEM hItem)
+{
+ TVITEM tvi;
+ TreeItem *treeItem;
+ SectionItem* sectionActive;
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+
+ TreeView_GetItem( htv, &tvi );
+ treeItem = (TreeItem *)tvi.lParam;
+ sectionActive = sectionList[ SECTIONPARAM_INDEX(treeItem->value) ];
+
+ tvi.hItem = TreeView_GetChild( htv, tvi.hItem );
+ while ( tvi.hItem ) {
+ LoadSubIcons( htv, filename, tvi.hItem );
+ tvi.hItem = TreeView_GetNextSibling( htv, tvi.hItem );
+ }
+
+ if ( SECTIONPARAM_FLAGS(treeItem->value) & SECTIONPARAM_HAVEPAGE )
+ LoadSectionIcons( filename, sectionActive );
+}
+
+static void UndoChanges( int iconIndx, int cmd )
+{
+ IconItem *item = iconList[ iconIndx ];
+
+ if ( !item->temp_file && !item->temp_icon && item->temp_reset && cmd == ID_CANCELCHANGE )
+ item->temp_reset = FALSE;
+ else
+ {
+ SAFE_FREE(( void** )&item->temp_file );
+ SafeDestroyIcon( &item->temp_icon );
+ }
+
+ if ( cmd == ID_RESET )
+ item->temp_reset = TRUE;
+}
+
+void UndoSubItemChanges( HWND htv, HTREEITEM hItem, int cmd )
+{
+ TVITEM tvi = {0};
+ TreeItem *treeItem;
+ int indx;
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ TreeView_GetItem( htv, &tvi );
+ treeItem = (TreeItem *)tvi.lParam;
+
+ if ( SECTIONPARAM_FLAGS( treeItem->value ) & SECTIONPARAM_HAVEPAGE ) {
+ EnterCriticalSection( &csIconList );
+
+ for ( indx = 0; indx < iconList.getCount(); indx++ )
+ if ( iconList[ indx ]->section == sectionList[ SECTIONPARAM_INDEX(treeItem->value) ])
+ UndoChanges( indx, cmd );
+
+ LeaveCriticalSection( &csIconList );
+ }
+
+ tvi.hItem = TreeView_GetChild( htv, tvi.hItem );
+ while ( tvi.hItem ) {
+ UndoSubItemChanges( htv, tvi.hItem, cmd );
+ tvi.hItem = TreeView_GetNextSibling( htv, tvi.hItem );
+} }
+
+static void OpenIconsPage()
+{
+ CallService( MS_UTILS_OPENURL, 1, (LPARAM)"http://addons.miranda-im.org/index.php?action=display&id=35" );
+}
+
+static int OpenPopupMenu(HWND hwndDlg)
+{
+ HMENU hMenu, hPopup;
+ POINT pt;
+ int cmd;
+
+ GetCursorPos(&pt);
+ hMenu = LoadMenu( hMirandaInst, MAKEINTRESOURCE( IDR_ICOLIB_CONTEXT ));
+ hPopup = GetSubMenu( hMenu, 0 );
+ CallService( MS_LANGPACK_TRANSLATEMENU, ( WPARAM )hPopup, 0 );
+ cmd = TrackPopupMenu( hPopup, TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL );
+ DestroyMenu( hMenu );
+ return cmd;
+}
+
+static TCHAR* OpenFileDlg( HWND hParent, const TCHAR* szFile, BOOL bAll )
+{
+ OPENFILENAME ofn = {0};
+ TCHAR filter[512],*pfilter,file[MAX_PATH*2];
+
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hParent;
+
+ lstrcpy(filter,TranslateT("Icon Sets"));
+ if (bAll)
+ lstrcat(filter,_T(" (*.dll;*.icl;*.exe;*.ico)"));
+ else
+ lstrcat(filter,_T(" (*.dll)"));
+
+ pfilter=filter+lstrlen(filter)+1;
+ if (bAll)
+ lstrcpy(pfilter,_T("*.DLL;*.ICL;*.EXE;*.ICO"));
+ else
+ lstrcpy(pfilter,_T("*.DLL"));
+
+ pfilter += lstrlen(pfilter) + 1;
+ lstrcpy(pfilter, TranslateT("All Files"));
+ lstrcat(pfilter,_T(" (*)"));
+ pfilter += lstrlen(pfilter) + 1;
+ lstrcpy(pfilter,_T("*"));
+ pfilter += lstrlen(pfilter) + 1;
+ *pfilter='\0';
+
+ ofn.lpstrFilter = filter;
+ ofn.lpstrDefExt = _T("dll");
+ lstrcpyn(file, szFile, SIZEOF(file));
+ ofn.lpstrFile = file;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ ofn.nMaxFile = MAX_PATH*2;
+
+ if (!GetOpenFileName(&ofn))
+ return NULL;
+
+ return mir_tstrdup(file);
+}
+
+//
+// User interface
+//
+
+#define DM_REBUILDICONSPREVIEW (WM_USER+10)
+#define DM_CHANGEICON (WM_USER+11)
+#define DM_CHANGESPECIFICICON (WM_USER+12)
+#define DM_UPDATEICONSPREVIEW (WM_USER+13)
+#define DM_REBUILD_CTREE (WM_USER+14)
+
+INT_PTR CALLBACK DlgProcIconImport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void DoOptionsChanged(HWND hwndDlg)
+{
+ SendMessage(hwndDlg, DM_UPDATEICONSPREVIEW, 0, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+}
+
+void DoIconsChanged(HWND hwndDlg)
+{
+ SendMessage(hwndDlg, DM_UPDATEICONSPREVIEW, 0, 0);
+
+ iconEventActive = 1; // Disable icon destroying - performance boost
+ NotifyEventHooks(hIconsChangedEvent, 0, 0);
+ NotifyEventHooks(hIcons2ChangedEvent, 0, 0);
+ iconEventActive = 0;
+
+ EnterCriticalSection(&csIconList); // Destroy unused icons
+ for (int indx = 0; indx < iconList.getCount(); indx++) {
+ IconItem *item = iconList[indx];
+ if ( item->source_small && !item->source_small->icon_ref_count) {
+ item->source_small->icon_ref_count++;
+ IconSourceItem_ReleaseIcon( item->source_small );
+ }
+ if ( item->source_big && !item->source_big->icon_ref_count) {
+ item->source_big->icon_ref_count++;
+ IconSourceItem_ReleaseIcon( item->source_big );
+ }
+ }
+ LeaveCriticalSection(&csIconList);
+}
+
+static HTREEITEM FindNamedTreeItemAt(HWND hwndTree, HTREEITEM hItem, const TCHAR *name)
+{
+ TVITEM tvi = {0};
+ TCHAR str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = TreeView_GetChild(hwndTree, hItem);
+ else
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = MAX_PATH;
+
+ while (tvi.hItem)
+ {
+ TreeView_GetItem(hwndTree, &tvi);
+
+ if (!lstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// icon import dialog's window procedure
+
+static int IconDlg_Resize(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_ICONSET:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_BROWSE:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_PREVIEW:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDC_GETMORE:
+ return RD_ANCHORX_CENTRE | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; // default
+}
+
+INT_PTR CALLBACK DlgProcIconImport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HWND hwndParent,hwndDragOver;
+ static int dragging;
+ static int dragItem,dropHiLite;
+ static HWND hPreview = NULL;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ hwndParent = (HWND)lParam;
+ hPreview = GetDlgItem(hwndDlg, IDC_PREVIEW);
+ dragging = dragItem = 0;
+ ListView_SetImageList(hPreview, ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),ILC_COLOR32|ILC_MASK,0,100), LVSIL_NORMAL);
+ ListView_SetIconSpacing(hPreview, 56, 67);
+ {
+ RECT rcThis, rcParent;
+ int cxScreen = GetSystemMetrics(SM_CXSCREEN);
+
+ GetWindowRect(hwndDlg,&rcThis);
+ GetWindowRect(hwndParent,&rcParent);
+ OffsetRect(&rcThis,rcParent.right-rcThis.left,0);
+ OffsetRect(&rcThis,0,rcParent.top-rcThis.top);
+ GetWindowRect(GetParent(hwndParent),&rcParent);
+ if (rcThis.right > cxScreen) {
+ OffsetRect(&rcParent,cxScreen-rcThis.right,0);
+ OffsetRect(&rcThis,cxScreen-rcThis.right,0);
+ MoveWindow(GetParent(hwndParent),rcParent.left,rcParent.top,rcParent.right-rcParent.left,rcParent.bottom-rcParent.top,TRUE);
+ }
+ MoveWindow(hwndDlg,rcThis.left,rcThis.top,rcThis.right-rcThis.left,rcThis.bottom-rcThis.top,FALSE);
+ GetClientRect(hwndDlg, &rcThis);
+ SendMessage(hwndDlg, WM_SIZE, 0, MAKELPARAM(rcThis.right-rcThis.left, rcThis.bottom-rcThis.top));
+ }
+
+ if (shAutoComplete) shAutoComplete(GetDlgItem(hwndDlg,IDC_ICONSET), 1);
+ SetDlgItemText(hwndDlg,IDC_ICONSET,_T("icons.dll"));
+ return TRUE;
+
+ case DM_REBUILDICONSPREVIEW:
+ {
+ LVITEM lvi;
+ TCHAR filename[MAX_PATH],caption[64];
+ HIMAGELIST hIml;
+ int count,i;
+ HICON hIcon;
+
+ MySetCursor(IDC_WAIT);
+ ListView_DeleteAllItems(hPreview);
+ hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL);
+ ImageList_RemoveAll(hIml);
+ GetDlgItemText(hwndDlg, IDC_ICONSET, filename, SIZEOF(filename));
+ {
+ RECT rcPreview,rcGroup;
+
+ GetWindowRect(hPreview, &rcPreview);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_IMPORTMULTI),&rcGroup);
+ //SetWindowPos(hPreview,0,0,0,rcPreview.right-rcPreview.left,rcGroup.bottom-rcPreview.top,SWP_NOZORDER|SWP_NOMOVE);
+ }
+
+ if (_taccess(filename,0) != 0) {
+ MySetCursor(IDC_ARROW);
+ break;
+ }
+
+ lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = 0;
+ count = (int)_ExtractIconEx( filename, -1, 16,16, NULL, LR_DEFAULTCOLOR );
+ for (i = 0; i < count; lvi.iItem++, i++) {
+ mir_sntprintf(caption, SIZEOF(caption), _T("%d"), i+1);
+ lvi.pszText = caption;
+ //hIcon = ExtractIcon(hMirandaInst, filename, i);
+ _ExtractIconEx( filename, i, 16,16, &hIcon, LR_DEFAULTCOLOR );
+ lvi.iImage = ImageList_AddIcon(hIml, hIcon);
+ DestroyIcon(hIcon);
+ lvi.lParam = i;
+ ListView_InsertItem(hPreview, &lvi);
+ }
+ MySetCursor(IDC_ARROW);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch( LOWORD( wParam )) {
+ case IDC_BROWSE:
+ {
+ TCHAR str[MAX_PATH];
+ TCHAR *file;
+
+ GetDlgItemText(hwndDlg,IDC_ICONSET,str,SIZEOF(str));
+ if (!(file = OpenFileDlg(GetParent(hwndDlg), str, TRUE))) break;
+ SetDlgItemText(hwndDlg,IDC_ICONSET,file);
+ SAFE_FREE(( void** )&file );
+ }
+ break;
+
+ case IDC_GETMORE:
+ OpenIconsPage();
+ break;
+
+ case IDC_ICONSET:
+ if (HIWORD(wParam) == EN_CHANGE)
+ SendMessage(hwndDlg, DM_REBUILDICONSPREVIEW, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (dragging) {
+ LVHITTESTINFO lvhti;
+ int onItem=0;
+ HWND hwndOver;
+ RECT rc;
+ POINT ptDrag;
+ HWND hPPreview = GetDlgItem(hwndParent, IDC_PREVIEW);
+
+ lvhti.pt.x = (short)LOWORD(lParam); lvhti.pt.y = (short)HIWORD(lParam);
+ ClientToScreen(hwndDlg, &lvhti.pt);
+ hwndOver = WindowFromPoint(lvhti.pt);
+ GetWindowRect(hwndOver, &rc);
+ ptDrag.x = lvhti.pt.x - rc.left; ptDrag.y = lvhti.pt.y - rc.top;
+ if (hwndOver != hwndDragOver) {
+ ImageList_DragLeave(hwndDragOver);
+ hwndDragOver = hwndOver;
+ ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y);
+ }
+
+ ImageList_DragMove(ptDrag.x, ptDrag.y);
+ if (hwndOver == hPPreview) {
+ ScreenToClient(hPPreview, &lvhti.pt);
+
+ if (ListView_HitTest(hPPreview, &lvhti) != -1) {
+ if (lvhti.iItem != dropHiLite) {
+ ImageList_DragLeave(hwndDragOver);
+ if (dropHiLite != -1)
+ ListView_SetItemState(hPPreview, dropHiLite, 0, LVIS_DROPHILITED);
+ dropHiLite = lvhti.iItem;
+ ListView_SetItemState(hPPreview, dropHiLite, LVIS_DROPHILITED, LVIS_DROPHILITED);
+ UpdateWindow(hPPreview);
+ ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y);
+ }
+ onItem = 1;
+ } }
+
+ if (!onItem && dropHiLite != -1) {
+ ImageList_DragLeave(hwndDragOver);
+ ListView_SetItemState(hPPreview, dropHiLite, 0, LVIS_DROPHILITED);
+ UpdateWindow(hPPreview);
+ ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y);
+ dropHiLite = -1;
+ }
+ MySetCursor(onItem ? IDC_ARROW : IDC_NO);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (dragging) {
+ ReleaseCapture();
+ ImageList_EndDrag();
+ dragging = 0;
+ if (dropHiLite != -1) {
+ TCHAR path[MAX_PATH],fullPath[MAX_PATH],filename[MAX_PATH];
+ LVITEM lvi;
+
+ GetDlgItemText(hwndDlg, IDC_ICONSET, fullPath, SIZEOF(fullPath));
+ CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)fullPath, (LPARAM)filename);
+ lvi.mask=LVIF_PARAM;
+ lvi.iItem = dragItem; lvi.iSubItem = 0;
+ ListView_GetItem(hPreview, &lvi);
+ mir_sntprintf(path, MAX_PATH, _T("%s,%d"), filename, (int)lvi.lParam);
+ SendMessage(hwndParent, DM_CHANGEICON, dropHiLite, (LPARAM)path);
+ ListView_SetItemState(GetDlgItem(hwndParent, IDC_PREVIEW), dropHiLite, 0, LVIS_DROPHILITED);
+ } }
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_PREVIEW:
+ switch (((LPNMHDR)lParam)->code) {
+ case LVN_BEGINDRAG:
+ SetCapture(hwndDlg);
+ dragging = 1;
+ dragItem = ((LPNMLISTVIEW)lParam)->iItem;
+ dropHiLite = -1;
+ ImageList_BeginDrag(ListView_GetImageList(hPreview, LVSIL_NORMAL), dragItem, GetSystemMetrics(SM_CXICON)/2, GetSystemMetrics(SM_CYICON)/2);
+ {
+ POINT pt;
+ RECT rc;
+
+ GetCursorPos(&pt);
+ GetWindowRect(hwndDlg, &rc);
+ ImageList_DragEnter(hwndDlg, pt.x - rc.left, pt.y - rc.top);
+ hwndDragOver = hwndDlg;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ EnableWindow(GetDlgItem(hwndParent,IDC_IMPORT),TRUE);
+ break;
+
+ case WM_SIZE:
+ { // make the dlg resizeable
+ UTILRESIZEDIALOG urd = {0};
+
+ if (IsIconic(hwndDlg)) break;
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hMirandaInst;
+ urd.hwndDlg = hwndDlg;
+ urd.lParam = 0; // user-defined
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_ICOLIB_IMPORT);
+ urd.pfnResizer = IconDlg_Resize;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ break;
+ } }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// IcoLib options window procedure
+
+static int CALLBACK DoSortIconsFunc(LPARAM lParam1, LPARAM lParam2, LPARAM )
+{ return lstrcmpi(TranslateTS(iconList[lParam1]->description), TranslateTS(iconList[lParam2]->description));
+}
+
+static int CALLBACK DoSortIconsFuncByOrder(LPARAM lParam1, LPARAM lParam2, LPARAM )
+{ return iconList[lParam1]->orderID - iconList[lParam2]->orderID;
+}
+
+static void SaveCollapseState( HWND hwndTree )
+{
+ HTREEITEM hti;
+ TVITEM tvi;
+
+ hti = TreeView_GetRoot( hwndTree );
+ while( hti != NULL ) {
+ HTREEITEM ht;
+ TreeItem *treeItem;
+
+ tvi.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM;
+ tvi.hItem = hti;
+ tvi.stateMask = (DWORD)-1;
+ TreeView_GetItem( hwndTree, &tvi );
+
+ if( tvi.cChildren > 0 ) {
+ treeItem = (TreeItem *)tvi.lParam;
+ if ( tvi.state & TVIS_EXPANDED )
+ DBWriteContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, TVIS_EXPANDED );
+ else
+ DBWriteContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, 0 );
+ }
+
+ ht = TreeView_GetChild( hwndTree, hti );
+ if( ht == NULL ) {
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ while( ht == NULL ) {
+ hti = TreeView_GetParent( hwndTree, hti );
+ if( hti == NULL ) break;
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ } }
+
+ hti = ht;
+} }
+
+INT_PTR CALLBACK DlgProcIcoLibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct IcoLibOptsData *dat;
+ static HTREEITEM prevItem = 0;
+ static HWND hPreview = NULL;
+
+ dat = (struct IcoLibOptsData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ hPreview = GetDlgItem(hwndDlg, IDC_PREVIEW);
+ dat = (struct IcoLibOptsData*)mir_alloc(sizeof(struct IcoLibOptsData));
+ dat->hwndIndex = NULL;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ //
+ // Reset temporary data & upload sections list
+ //
+ EnterCriticalSection(&csIconList);
+ {
+ int indx;
+ for (indx = 0; indx < iconList.getCount(); indx++) {
+ iconList[indx]->temp_file = NULL;
+ iconList[indx]->temp_icon = NULL;
+ iconList[indx]->temp_reset = FALSE;
+ }
+ bNeedRebuild = FALSE;
+ }
+ LeaveCriticalSection(&csIconList);
+ //
+ // Setup preview listview
+ //
+ ListView_SetUnicodeFormat(hPreview, TRUE);
+ ListView_SetExtendedListViewStyleEx(hPreview, LVS_EX_INFOTIP, LVS_EX_INFOTIP);
+ ListView_SetImageList(hPreview, ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),ILC_COLOR32|ILC_MASK,0,30), LVSIL_NORMAL);
+ ListView_SetIconSpacing(hPreview, 56, 67);
+
+ SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0);
+ return TRUE;
+
+ case DM_REBUILD_CTREE:
+ {
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_CATEGORYLIST);
+ int indx;
+ TCHAR itemName[1024];
+ HTREEITEM hSection;
+
+ if (!hwndTree) break;
+
+ TreeView_SelectItem(hwndTree, NULL);
+ TreeView_DeleteAllItems(hwndTree);
+
+ for (indx = 0; indx < sectionList.getCount(); indx++) {
+ TCHAR* sectionName;
+ int sectionLevel = 0;
+
+ hSection = NULL;
+ lstrcpy(itemName, sectionList[indx]->name);
+ sectionName = itemName;
+
+ while (sectionName) {
+ // allow multi-level tree
+ TCHAR* pItemName = sectionName;
+ HTREEITEM hItem;
+
+ if (sectionName = _tcschr(sectionName, '/')) {
+ // one level deeper
+ *sectionName = 0;
+ }
+
+ pItemName = TranslateTS( pItemName );
+
+ hItem = FindNamedTreeItemAt(hwndTree, hSection, pItemName);
+ if (!sectionName || !hItem) {
+ if (!hItem) {
+ TVINSERTSTRUCT tvis = {0};
+ TreeItem *treeItem = (TreeItem *)mir_alloc(sizeof(TreeItem));
+ treeItem->value = SECTIONPARAM_MAKE( indx, sectionLevel, sectionName?0:SECTIONPARAM_HAVEPAGE );
+ treeItem->paramName = mir_t2a(itemName);
+
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_SORT; //TVI_LAST;
+ tvis.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE;
+ tvis.item.pszText = pItemName;
+ tvis.item.lParam = (LPARAM) treeItem;
+
+ tvis.item.state = tvis.item.stateMask = DBGetContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, TVIS_EXPANDED );
+ hItem = TreeView_InsertItem(hwndTree, &tvis);
+ }
+ else {
+ TVITEM tvi = {0};
+ TreeItem *treeItem;
+ tvi.hItem = hItem;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem( hwndTree, &tvi );
+ treeItem = (TreeItem *)tvi.lParam;
+ treeItem->value = SECTIONPARAM_MAKE( indx, sectionLevel, SECTIONPARAM_HAVEPAGE );
+ } }
+
+ if (sectionName) {
+ *sectionName = '/';
+ sectionName++;
+ }
+ sectionLevel++;
+
+ hSection = hItem;
+ } }
+
+ ShowWindow(hwndTree, SW_SHOW);
+
+ TreeView_SelectItem(hwndTree, FindNamedTreeItemAt(hwndTree, 0, NULL));
+ }
+ break;
+
+ // Rebuild preview to new section
+ case DM_REBUILDICONSPREVIEW:
+ {
+ LVITEM lvi = {0};
+ HIMAGELIST hIml;
+ HICON hIcon;
+ SectionItem* sectionActive = ( SectionItem* )lParam;
+ int indx;
+
+ EnableWindow(hPreview, sectionActive != NULL );
+
+ ListView_DeleteAllItems(hPreview);
+ hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL);
+ ImageList_RemoveAll(hIml);
+
+ if (sectionActive == NULL)
+ break;
+
+ lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
+
+ EnterCriticalSection(&csIconList);
+
+ for (indx = 0; indx < iconList.getCount(); indx++) {
+ IconItem *item = iconList[indx];
+
+ if (item->section == sectionActive) {
+ lvi.pszText = TranslateTS(item->description);
+ hIcon = item->temp_icon;
+ if ( !hIcon )
+ hIcon = IconItem_GetIcon_Preview( item );
+ lvi.iImage = ImageList_AddIcon(hIml, hIcon);
+ lvi.lParam = indx;
+ ListView_InsertItem(hPreview, &lvi);
+ if (hIcon != item->temp_icon) SafeDestroyIcon( &hIcon );
+ } }
+
+ LeaveCriticalSection(&csIconList);
+
+ if ( sectionActive->flags & SIDF_SORTED )
+ ListView_SortItems(hPreview, DoSortIconsFunc, 0);
+ else
+ ListView_SortItems(hPreview, DoSortIconsFuncByOrder, 0);
+ }
+ break;
+
+ // Refresh preview to new section
+ case DM_UPDATEICONSPREVIEW:
+ {
+ LVITEM lvi = {0};
+ HICON hIcon;
+ int indx, count;
+ HIMAGELIST hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL);
+
+ lvi.mask = LVIF_IMAGE|LVIF_PARAM;
+ count = ListView_GetItemCount(hPreview);
+
+ for (indx = 0; indx < count; indx++) {
+ lvi.iItem = indx;
+ ListView_GetItem(hPreview, &lvi);
+ EnterCriticalSection(&csIconList);
+ hIcon = iconList[lvi.lParam]->temp_icon;
+ if (!hIcon)
+ hIcon = IconItem_GetIcon_Preview( iconList[lvi.lParam] );
+ LeaveCriticalSection(&csIconList);
+
+ if (hIcon)
+ ImageList_ReplaceIcon(hIml, lvi.iImage, hIcon);
+ if (hIcon != iconList[lvi.lParam]->temp_icon) SafeDestroyIcon( &hIcon );
+ }
+ ListView_RedrawItems(hPreview, 0, count);
+ }
+ break;
+
+ // Temporary change icon - only inside options dialog
+ case DM_CHANGEICON:
+ {
+ TCHAR *path=(TCHAR*)lParam;
+ LVITEM lvi = {0};
+ IconItem *item;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = wParam;
+ ListView_GetItem( hPreview, &lvi );
+
+ EnterCriticalSection( &csIconList );
+ item = iconList[ lvi.lParam ];
+
+ SAFE_FREE(( void** )&item->temp_file );
+ SafeDestroyIcon( &item->temp_icon );
+ item->temp_file = mir_tstrdup( path );
+ item->temp_icon = ( HICON )ExtractIconFromPath( path, item->cx, item->cy );
+ item->temp_reset = FALSE;
+
+ LeaveCriticalSection( &csIconList );
+ DoOptionsChanged( hwndDlg );
+ }
+ break;
+
+ case WM_COMMAND:
+ if ( LOWORD(wParam) == IDC_IMPORT ) {
+ dat->hwndIndex = CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_ICOLIB_IMPORT), GetParent(hwndDlg), DlgProcIconImport, (LPARAM)hwndDlg);
+ EnableWindow((HWND)lParam, FALSE);
+ }
+ else if ( LOWORD(wParam) == IDC_GETMORE ) {
+ OpenIconsPage();
+ break;
+ }
+ else if (LOWORD(wParam) == IDC_LOADICONS ) {
+ TCHAR filetmp[1] = {0};
+ TCHAR *file;
+
+ if ( file = OpenFileDlg( hwndDlg, filetmp, FALSE )) {
+ HWND htv = GetDlgItem( hwndDlg, IDC_CATEGORYLIST );
+ TCHAR filename[ MAX_PATH ];
+
+ CallService( MS_UTILS_PATHTORELATIVET, ( WPARAM )file, ( LPARAM )filename );
+ SAFE_FREE(( void** )&file );
+
+ MySetCursor( IDC_WAIT );
+ LoadSubIcons( htv, filename, TreeView_GetSelection( htv ));
+ MySetCursor( IDC_ARROW );
+
+ DoOptionsChanged( hwndDlg );
+ } }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (( HWND )wParam == hPreview ) {
+ UINT count = ListView_GetSelectedCount( hPreview );
+
+ if ( count > 0 ) {
+ int cmd = OpenPopupMenu( hwndDlg );
+ switch( cmd ) {
+ case ID_CANCELCHANGE:
+ case ID_RESET:
+ {
+ LVITEM lvi = {0};
+ int itemIndx = -1;
+
+ while (( itemIndx = ListView_GetNextItem( hPreview, itemIndx, LVNI_SELECTED )) != -1 ) {
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = itemIndx; //lvhti.iItem;
+ ListView_GetItem( hPreview, &lvi );
+
+ UndoChanges( lvi.lParam, cmd );
+ }
+
+ DoOptionsChanged( hwndDlg );
+ break;
+ } } }
+ }
+ else {
+ HWND htv = GetDlgItem( hwndDlg, IDC_CATEGORYLIST );
+ if (( HWND )wParam == htv ) {
+ int cmd = OpenPopupMenu( hwndDlg );
+
+ switch( cmd ) {
+ case ID_CANCELCHANGE:
+ case ID_RESET:
+ UndoSubItemChanges( htv, TreeView_GetSelection( htv ), cmd );
+ DoOptionsChanged( hwndDlg );
+ break;
+ } } }
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch(((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ {
+ int indx;
+
+ EnterCriticalSection(&csIconList);
+
+ for (indx = 0; indx < iconList.getCount(); indx++) {
+ IconItem *item = iconList[indx];
+ if (item->temp_reset) {
+ DBDeleteContactSetting(NULL, "SkinIcons", item->name);
+ if (item->source_small != item->default_icon) {
+ IconSourceItem_Release( &item->source_small );
+ }
+ }
+ else if (item->temp_file) {
+ DBWriteContactSettingTString(NULL, "SkinIcons", item->name, item->temp_file);
+ IconSourceItem_Release( &item->source_small );
+ SafeDestroyIcon( &item->temp_icon );
+ }
+ }
+ LeaveCriticalSection(&csIconList);
+
+ DoIconsChanged(hwndDlg);
+ return TRUE;
+ } }
+ break;
+
+ case IDC_PREVIEW:
+ if(((LPNMHDR)lParam)->code == LVN_GETINFOTIP)
+ {
+ IconItem *item;
+ NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam;
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = pInfoTip->iItem;
+ ListView_GetItem(pInfoTip->hdr.hwndFrom, &lvi);
+
+ if( lvi.lParam < iconList.getCount() ) {
+ item = iconList[lvi.lParam];
+ if( item->temp_file )
+ _tcsncpy( pInfoTip->pszText, item->temp_file, pInfoTip->cchTextMax );
+ else if( item->default_file )
+ mir_sntprintf( pInfoTip->pszText, pInfoTip->cchTextMax, _T("%s,%d"), item->default_file, item->default_indx );
+ }
+ }
+ if ( bNeedRebuild ) {
+ EnterCriticalSection(&csIconList);
+ bNeedRebuild = FALSE;
+ LeaveCriticalSection(&csIconList);
+ SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0);
+ }
+ break;
+
+ case IDC_CATEGORYLIST:
+ switch(((NMHDR*)lParam)->code) {
+ case TVN_SELCHANGEDA: // !!!! This needs to be here - both !!
+ case TVN_SELCHANGEDW:
+ {
+ NMTREEVIEW *pnmtv = (NMTREEVIEW*)lParam;
+ TVITEM tvi = pnmtv->itemNew;
+ TreeItem *treeItem = (TreeItem *)tvi.lParam;
+ if ( treeItem )
+ SendMessage(hwndDlg, DM_REBUILDICONSPREVIEW, 0, ( LPARAM )(
+ (SECTIONPARAM_FLAGS(treeItem->value)&SECTIONPARAM_HAVEPAGE)?
+ sectionList[ SECTIONPARAM_INDEX(treeItem->value) ] : NULL ) );
+ break;
+ }
+ case TVN_DELETEITEMA: // no idea why both TVN_SELCHANGEDA/W should be there but let's keep this both too...
+ case TVN_DELETEITEMW:
+ {
+ TreeItem *treeItem = (TreeItem *)(((LPNMTREEVIEW)lParam)->itemOld.lParam);
+ if (treeItem) {
+ mir_free(treeItem->paramName);
+ mir_free(treeItem);
+ }
+ break;
+ } }
+ if ( bNeedRebuild ) {
+ EnterCriticalSection(&csIconList);
+ bNeedRebuild = FALSE;
+ LeaveCriticalSection(&csIconList);
+ SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0);
+ } }
+ break;
+ case WM_DESTROY:
+ {
+ int indx;
+
+ SaveCollapseState( GetDlgItem(hwndDlg, IDC_CATEGORYLIST) );
+ DestroyWindow(dat->hwndIndex);
+
+ EnterCriticalSection(&csIconList);
+ for (indx = 0; indx < iconList.getCount(); indx++) {
+ IconItem *item = iconList[indx];
+
+ SAFE_FREE(( void** )&item->temp_file);
+ SafeDestroyIcon(&item->temp_icon);
+ }
+ LeaveCriticalSection(&csIconList);
+
+ SAFE_FREE(( void** )&dat);
+ break;
+ } }
+
+ return FALSE;
+}
+
+static UINT iconsExpertOnlyControls[]={IDC_IMPORT};
+
+static int SkinOptionsInit( WPARAM wParam,LPARAM )
+{
+ OPTIONSDIALOGPAGE odp = {0};
+
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hMirandaInst;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = -180000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ICOLIB);
+ odp.pszTitle = LPGEN("Icons");
+ odp.pszGroup = LPGEN("Customize");
+ odp.pfnDlgProc = DlgProcIcoLibOpts;
+ odp.expertOnlyControls = iconsExpertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF(iconsExpertOnlyControls);
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static int SkinSystemModulesLoaded( WPARAM, LPARAM )
+{
+ HookEvent(ME_OPT_INITIALISE, SkinOptionsInit);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module initialization and finalization procedure
+
+static INT_PTR sttIcoLib_AddNewIcon( WPARAM, LPARAM lParam )
+{ return (INT_PTR)IcoLib_AddNewIcon(( SKINICONDESC* )lParam );
+}
+
+static INT_PTR sttIcoLib_GetIcon( WPARAM wParam, LPARAM lParam )
+{ return (INT_PTR)IcoLib_GetIcon(( const char* )lParam, wParam != 0 );
+}
+
+static INT_PTR sttIcoLib_GetIconHandle( WPARAM, LPARAM lParam )
+{ return (INT_PTR)IcoLib_GetIconHandle(( const char* )lParam );
+}
+
+static INT_PTR sttIcoLib_GetIconByHandle( WPARAM wParam, LPARAM lParam )
+{ return (INT_PTR)IcoLib_GetIconByHandle(( HANDLE )lParam, wParam != 0 );
+}
+
+static INT_PTR sttIcoLib_ReleaseIcon( WPARAM wParam, LPARAM lParam )
+{ return (INT_PTR)IcoLib_ReleaseIcon(( HICON )wParam, ( char* )lParam, false );
+}
+
+static INT_PTR sttIcoLib_ReleaseIconBig( WPARAM wParam, LPARAM lParam )
+{ return (INT_PTR)IcoLib_ReleaseIcon(( HICON )wParam, ( char* )lParam, true );
+}
+
+static INT_PTR sttIcoLib_IsManaged( WPARAM wParam, LPARAM )
+{ return (INT_PTR)IcoLib_IsManaged(( HICON )wParam );
+}
+
+int LoadIcoLibModule(void)
+{
+ bModuleInitialized = TRUE;
+
+ hIconBlank = LoadIconEx(NULL, MAKEINTRESOURCE(IDI_BLANK),0);
+
+ InitializeCriticalSection(&csIconList);
+ hIcoLib_AddNewIcon = CreateServiceFunction(MS_SKIN2_ADDICON, sttIcoLib_AddNewIcon);
+ hIcoLib_RemoveIcon = CreateServiceFunction(MS_SKIN2_REMOVEICON, IcoLib_RemoveIcon);
+ hIcoLib_GetIcon = CreateServiceFunction(MS_SKIN2_GETICON, sttIcoLib_GetIcon);
+ hIcoLib_GetIconHandle = CreateServiceFunction(MS_SKIN2_GETICONHANDLE, sttIcoLib_GetIconHandle);
+ hIcoLib_GetIcon2 = CreateServiceFunction(MS_SKIN2_GETICONBYHANDLE, sttIcoLib_GetIconByHandle);
+ hIcoLib_IsManaged = CreateServiceFunction(MS_SKIN2_ISMANAGEDICON, sttIcoLib_IsManaged);
+ hIcoLib_AddRef = CreateServiceFunction(MS_SKIN2_ADDREFICON, IcoLib_AddRef);
+ hIcoLib_ReleaseIcon = CreateServiceFunction(MS_SKIN2_RELEASEICON, sttIcoLib_ReleaseIcon);
+ hIcoLib_ReleaseIcon = CreateServiceFunction(MS_SKIN2_RELEASEICONBIG, sttIcoLib_ReleaseIconBig);
+
+ hIcons2ChangedEvent = CreateHookableEvent(ME_SKIN2_ICONSCHANGED);
+ hIconsChangedEvent = CreateHookableEvent(ME_SKIN_ICONSCHANGED);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, SkinSystemModulesLoaded);
+
+ return 0;
+}
+
+void UnloadIcoLibModule(void)
+{
+ int indx;
+
+ if ( !bModuleInitialized ) return;
+
+ DestroyHookableEvent(hIconsChangedEvent);
+ DestroyHookableEvent(hIcons2ChangedEvent);
+
+ DestroyServiceFunction(hIcoLib_AddNewIcon);
+ DestroyServiceFunction(hIcoLib_RemoveIcon);
+ DestroyServiceFunction(hIcoLib_GetIcon);
+ DestroyServiceFunction(hIcoLib_GetIconHandle);
+ DestroyServiceFunction(hIcoLib_GetIcon2);
+ DestroyServiceFunction(hIcoLib_IsManaged);
+ DestroyServiceFunction(hIcoLib_AddRef);
+ DestroyServiceFunction(hIcoLib_ReleaseIcon);
+ DeleteCriticalSection(&csIconList);
+
+ for (indx = iconList.getCount()-1; indx >= 0; indx-- ) {
+ IconItem* I = iconList[indx];
+ iconList.remove( indx );
+ IcoLib_FreeIcon( I );
+ mir_free( I );
+ }
+ iconList.destroy();
+
+ for (indx = iconSourceList.getCount()-1; indx >= 0; indx-- ) {
+ IconSourceItem* I = iconSourceList[indx];
+ iconSourceList.remove( indx );
+ IconSourceFile_Release( &I->file );
+ SafeDestroyIcon( &I->icon );
+ SAFE_FREE(( void** )&I->icon_data );
+ SAFE_FREE(( void** )&I );
+ }
+ iconSourceList.destroy();
+
+ for (indx = iconSourceFileList.getCount()-1; indx >= 0; indx-- ) {
+ IconSourceFile* I = iconSourceFileList[indx];
+ iconSourceFileList.remove( indx );
+ SAFE_FREE(( void** )&I->file );
+ SAFE_FREE(( void** )&I );
+ }
+ iconSourceFileList.destroy();
+
+ for (indx = 0; indx < sectionList.getCount(); indx++) {
+ SAFE_FREE(( void** )&sectionList[indx]->name );
+ mir_free( sectionList[indx] );
+ }
+ sectionList.destroy();
+
+ SafeDestroyIcon(&hIconBlank);
+}
diff --git a/src/modules/idle/idle.cpp b/src/modules/idle/idle.cpp
new file mode 100644
index 0000000000..bc8b3ec4ef
--- /dev/null
+++ b/src/modules/idle/idle.cpp
@@ -0,0 +1,520 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2005 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+#define IDLEMOD "Idle"
+#define IDL_USERIDLECHECK "UserIdleCheck"
+#define IDL_IDLEMETHOD "IdleMethod"
+#define IDL_IDLETIME1ST "IdleTime1st"
+#define IDL_IDLEONSAVER "IdleOnSaver" // IDC_SCREENSAVER
+#define IDL_IDLEONFULLSCR "IdleOnFullScr" // IDC_FULLSCREEN
+#define IDL_IDLEONLOCK "IdleOnLock" // IDC_LOCKED
+#define IDL_IDLEONTSDC "IdleOnTerminalDisconnect" //
+#define IDL_IDLEPRIVATE "IdlePrivate" // IDC_IDLEPRIVATE
+#define IDL_IDLESTATUSLOCK "IdleStatusLock" // IDC_IDLESTATUSLOCK
+#define IDL_AAENABLE "AAEnable"
+#define IDL_AASTATUS "AAStatus"
+
+#define IdleObject_IsIdle(obj) (obj->state&0x1)
+#define IdleObject_SetIdle(obj) (obj->state|=0x1)
+#define IdleObject_ClearIdle(obj) (obj->state&=~0x1)
+
+// either use meth 0,1 or figure out which one
+#define IdleObject_UseMethod0(obj) (obj->state&=~0x2)
+#define IdleObject_UseMethod1(obj) (obj->state|=0x2)
+#define IdleObject_GetMethod(obj) (obj->state&0x2)
+
+#define IdleObject_IdleCheckSaver(obj) (obj->state&0x4)
+#define IdleObject_SetSaverCheck(obj) (obj->state|=0x4)
+
+#define IdleObject_IdleCheckWorkstation(obj) (obj->state&0x8)
+#define IdleObject_SetWorkstationCheck(obj) (obj->state|=0x8)
+
+#define IdleObject_IsPrivacy(obj) (obj->state&0x10)
+#define IdleObject_SetPrivacy(obj) (obj->state|=0x10)
+
+#define IdleObject_SetStatusLock(obj) (obj->state|=0x20)
+
+#define IdleObject_IdleCheckTerminal(obj) (obj->state&0x40)
+#define IdleObject_SetTerminalCheck(obj) (obj->state|=0x40)
+
+#define IdleObject_IdleCheckFullScr(obj) (obj->state&0x80)
+#define IdleObject_SetFullScrCheck(obj) (obj->state|=0x80)
+
+//#include <Wtsapi32.h>
+
+#ifndef _INC_WTSAPI
+
+#define WTS_CURRENT_SERVER_HANDLE ((HANDLE)NULL)
+#define WTS_CURRENT_SESSION ((DWORD)-1)
+
+typedef enum _WTS_CONNECTSTATE_CLASS {
+ WTSActive, // User logged on to WinStation
+ WTSConnected, // WinStation connected to client
+ WTSConnectQuery, // In the process of connecting to client
+ WTSShadow, // Shadowing another WinStation
+ WTSDisconnected, // WinStation logged on without client
+ WTSIdle, // Waiting for client to connect
+ WTSListen, // WinStation is listening for connection
+ WTSReset, // WinStation is being reset
+ WTSDown, // WinStation is down due to error
+ WTSInit, // WinStation in initialization
+} WTS_CONNECTSTATE_CLASS;
+
+
+typedef enum _WTS_INFO_CLASS {
+ WTSInitialProgram,
+ WTSApplicationName,
+ WTSWorkingDirectory,
+ WTSOEMId,
+ WTSSessionId,
+ WTSUserName,
+ WTSWinStationName,
+ WTSDomainName,
+ WTSConnectState,
+ WTSClientBuildNumber,
+ WTSClientName,
+ WTSClientDirectory,
+ WTSClientProductId,
+ WTSClientHardwareId,
+ WTSClientAddress,
+ WTSClientDisplay,
+ WTSClientProtocolType,
+} WTS_INFO_CLASS;
+
+#endif
+
+VOID (WINAPI *_WTSFreeMemory)(PVOID);
+BOOL (WINAPI *_WTSQuerySessionInformation)(HANDLE, DWORD, WTS_INFO_CLASS, PVOID, DWORD*);
+
+BOOL bIsWTSApiPresent = FALSE;
+
+static BOOL bModuleInitialized = FALSE;
+
+BOOL InitWTSAPI()
+{
+ HMODULE hDll = LoadLibraryA("wtsapi32.dll");
+ if (hDll) {
+ _WTSFreeMemory = (VOID (WINAPI *)(PVOID))GetProcAddress(hDll, "WTSFreeMemory");
+ #ifdef UNICODE
+ _WTSQuerySessionInformation = (BOOL (WINAPI *)(HANDLE, DWORD, WTS_INFO_CLASS, PVOID, DWORD*))GetProcAddress(hDll, "WTSQuerySessionInformationW");
+ #else
+ _WTSQuerySessionInformation = (BOOL (WINAPI *)(HANDLE, DWORD, WTS_INFO_CLASS, PVOID, DWORD*))GetProcAddress(hDll, "WTSQuerySessionInformationA");
+ #endif
+
+ if (_WTSFreeMemory && _WTSQuerySessionInformation) return 1;
+ }
+ return 0;
+}
+
+BOOL IsTerminalDisconnected()
+{
+ PVOID pBuffer = NULL;
+ DWORD pBytesReturned = 0;
+ BOOL result = FALSE;
+
+ if ( !bIsWTSApiPresent )
+ return FALSE;
+
+ if ( _WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, &pBuffer, &pBytesReturned)) {
+ if ( *( PDWORD )pBuffer == WTSDisconnected)
+ result = TRUE;
+ }
+ else bIsWTSApiPresent = FALSE;
+
+ if ( pBuffer )
+ _WTSFreeMemory( pBuffer );
+ return result;
+}
+
+typedef struct {
+ UINT_PTR hTimer;
+ unsigned int useridlecheck;
+ unsigned int state;
+ unsigned int minutes; // user setting, number of minutes of inactivity to wait for
+ POINT mousepos;
+ unsigned int mouseidle;
+ int aastatus;
+ int idleType;
+}
+ IdleObject;
+
+static const WORD aa_Status[] = {ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND, ID_STATUS_ONTHEPHONE, ID_STATUS_OUTTOLUNCH};
+
+static IdleObject gIdleObject;
+static HANDLE hIdleEvent;
+static BOOL (WINAPI * MyGetLastInputInfo)(PLASTINPUTINFO);
+
+void CALLBACK IdleTimer(HWND hwnd, UINT umsg, UINT_PTR idEvent, DWORD dwTime);
+
+static void IdleObject_ReadSettings(IdleObject * obj)
+{
+ obj->useridlecheck = DBGetContactSettingByte(NULL, IDLEMOD, IDL_USERIDLECHECK, 0);
+ obj->minutes = DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLETIME1ST, 10);
+ obj->aastatus = !DBGetContactSettingByte(NULL, IDLEMOD, IDL_AAENABLE, 0) ? 0 : DBGetContactSettingWord(NULL, IDLEMOD, IDL_AASTATUS, 0);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEMETHOD, 0) ) IdleObject_UseMethod1(obj);
+ else IdleObject_UseMethod0(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEONSAVER, 0) ) IdleObject_SetSaverCheck(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEONFULLSCR, 0) ) IdleObject_SetFullScrCheck(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEONLOCK, 0 ) ) IdleObject_SetWorkstationCheck(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEPRIVATE, 0) ) IdleObject_SetPrivacy(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLESTATUSLOCK, 0) ) IdleObject_SetStatusLock(obj);
+ if ( DBGetContactSettingByte(NULL, IDLEMOD, IDL_IDLEONTSDC, 0) ) IdleObject_SetTerminalCheck(obj);
+}
+
+static void IdleObject_Create(IdleObject * obj)
+{
+ ZeroMemory(obj, sizeof(IdleObject));
+ obj->hTimer=SetTimer(NULL, 0, 2000, IdleTimer);
+ IdleObject_ReadSettings(obj);
+}
+
+static void IdleObject_Destroy(IdleObject * obj)
+{
+ if (IdleObject_IsIdle(obj))
+ NotifyEventHooks(hIdleEvent, 0, 0);
+ IdleObject_ClearIdle(obj);
+ KillTimer(NULL, obj->hTimer);
+}
+
+static int IdleObject_IsUserIdle(IdleObject * obj)
+{
+ DWORD dwTick;
+ if ( IdleObject_GetMethod(obj) ) {
+ CallService(MS_SYSTEM_GETIDLE, 0, (LPARAM)&dwTick);
+ return GetTickCount() - dwTick > (obj->minutes * 60 * 1000);
+ }
+
+ if ( MyGetLastInputInfo != NULL ) {
+ LASTINPUTINFO ii;
+ ZeroMemory(&ii,sizeof(ii));
+ ii.cbSize=sizeof(ii);
+ if ( MyGetLastInputInfo(&ii) )
+ return GetTickCount() - ii.dwTime > (obj->minutes * 60 * 1000);
+ }
+ else {
+ POINT pt;
+ GetCursorPos(&pt);
+ if ( pt.x != obj->mousepos.x || pt.y != obj->mousepos.y ) {
+ obj->mousepos=pt;
+ obj->mouseidle=0;
+ }
+ else obj->mouseidle += 2;
+
+ if ( obj->mouseidle )
+ return obj->mouseidle * 1000 >= (obj->minutes * 60 * 1000);
+ }
+ return FALSE;
+}
+
+static bool IsWorkstationLocked (void)
+{
+ bool rc = false;
+
+ if (openInputDesktop != NULL) {
+ HDESK hDesk = openInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
+ if (hDesk == NULL)
+ rc = true;
+ else if (closeDesktop != NULL)
+ closeDesktop(hDesk);
+ }
+ return rc;
+}
+
+static bool IsScreenSaverRunning(void)
+{
+ BOOL rc = FALSE;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE);
+ return rc != 0;
+}
+
+bool IsFullScreen(void)
+{
+ RECT rcScreen = {0};
+
+ rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
+ rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
+
+ if (MyMonitorFromWindow)
+ {
+ HMONITOR hMon = MyMonitorFromWindow(cli.hwndContactList, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (MyGetMonitorInfo(hMon, &mi))
+ rcScreen = mi.rcMonitor;
+ }
+
+ HWND hWndDesktop = GetDesktopWindow();
+ HWND hWndShell = GetShellWindow();
+
+ // check foregroundwindow
+ HWND hWnd = GetForegroundWindow();
+ if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell)
+ {
+ TCHAR tszClassName[128] = _T("");
+ GetClassName(hWnd, tszClassName, SIZEOF(tszClassName));
+ if (_tcscmp(tszClassName, _T("WorkerW")))
+ {
+ RECT rect, rectw, recti;
+ GetWindowRect(hWnd, &rectw);
+
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect.right);
+
+ if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) &&
+ EqualRect(&recti, &rcScreen))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void IdleObject_Tick(IdleObject * obj)
+{
+ bool idle = false;
+ int idleType = 0, flags = 0;
+
+ if ( obj->useridlecheck && IdleObject_IsUserIdle(obj)) {
+ idleType = 1; idle = true;
+ }
+ else if ( IdleObject_IdleCheckSaver(obj) && IsScreenSaverRunning()) {
+ idleType = 2; idle = true;
+ }
+ else if ( IdleObject_IdleCheckFullScr(obj) && IsFullScreen()) {
+ idleType = 5; idle = true;
+ }
+ else if ( IdleObject_IdleCheckWorkstation(obj) && IsWorkstationLocked()) {
+ idleType = 3; idle = true;
+ }
+ else if ( IdleObject_IdleCheckTerminal(obj) && IsTerminalDisconnected()) {
+ idleType = 4; idle = true;
+ }
+
+ if ( IdleObject_IsPrivacy(obj))
+ flags |= IDF_PRIVACY;
+
+ if ( !IdleObject_IsIdle(obj) && idle ) {
+ IdleObject_SetIdle(obj);
+ obj->idleType = idleType;
+ NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags);
+ }
+ if ( IdleObject_IsIdle(obj) && !idle ) {
+ IdleObject_ClearIdle(obj);
+ obj->idleType = 0;
+ NotifyEventHooks(hIdleEvent, 0, flags);
+} }
+
+void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD)
+{
+ if ( gIdleObject.hTimer == idEvent )
+ IdleObject_Tick( &gIdleObject );
+}
+
+int IdleGetStatusIndex(WORD status)
+{
+ int j;
+ for (j = 0; j < SIZEOF(aa_Status); j++ )
+ if ( aa_Status[j] == status )
+ return j;
+
+ return 0;
+}
+
+static INT_PTR CALLBACK IdleOptsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ int j;
+ int method = DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEMETHOD, 0);
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_IDLESHORT, DBGetContactSettingByte(NULL,IDLEMOD,IDL_USERIDLECHECK,0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_IDLEONWINDOWS, method == 0 ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_IDLEONMIRANDA, method ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SCREENSAVER, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEONSAVER,0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_FULLSCREEN, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEONFULLSCR,0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_LOCKED, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEONLOCK,0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_IDLEPRIVATE, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEPRIVATE,0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_IDLESTATUSLOCK, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLESTATUSLOCK,0) ? BST_CHECKED : BST_UNCHECKED);
+ if ( !bIsWTSApiPresent )
+ EnableWindow( GetDlgItem( hwndDlg, IDC_IDLETERMINAL ), FALSE );
+ else
+ CheckDlgButton(hwndDlg, IDC_IDLETERMINAL, DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLEONTSDC,0) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_IDLESPIN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, IDC_IDLE1STTIME), 0);
+ SendDlgItemMessage(hwndDlg, IDC_IDLESPIN, UDM_SETRANGE32, 1, 60);
+ SendDlgItemMessage(hwndDlg, IDC_IDLESPIN, UDM_SETPOS, 0, MAKELONG((short) DBGetContactSettingByte(NULL,IDLEMOD,IDL_IDLETIME1ST, 10), 0));
+ SendDlgItemMessage(hwndDlg, IDC_IDLE1STTIME, EM_LIMITTEXT, (WPARAM)2, 0);
+
+ CheckDlgButton(hwndDlg, IDC_AASHORTIDLE, DBGetContactSettingByte(NULL, IDLEMOD, IDL_AAENABLE, 0) ? BST_CHECKED:BST_UNCHECKED);
+ for ( j = 0; j < SIZEOF(aa_Status); j++ )
+ SendDlgItemMessage(hwndDlg, IDC_AASTATUS, CB_ADDSTRING, 0, ( LPARAM )cli.pfnGetStatusModeDescription( aa_Status[j], 0 ));
+
+ j = IdleGetStatusIndex((WORD)(DBGetContactSettingWord(NULL, IDLEMOD, IDL_AASTATUS, 0)));
+ SendDlgItemMessage(hwndDlg, IDC_AASTATUS, CB_SETCURSEL, j, 0);
+ SendMessage(hwndDlg, WM_USER+2, 0, 0);
+ return TRUE;
+ }
+ case WM_USER+2:
+ {
+ BOOL checked = IsDlgButtonChecked(hwndDlg, IDC_IDLESHORT) == BST_CHECKED;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IDLEONWINDOWS), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IDLEONMIRANDA), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IDLE1STTIME), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AASTATUS), IsDlgButtonChecked(hwndDlg, IDC_AASHORTIDLE)==BST_CHECKED?1:0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IDLESTATUSLOCK), IsDlgButtonChecked(hwndDlg, IDC_AASHORTIDLE)==BST_CHECKED?1:0);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ NMHDR * hdr = (NMHDR *)lParam;
+ if ( hdr && hdr->code == PSN_APPLY ) {
+ int method = IsDlgButtonChecked(hwndDlg, IDC_IDLEONWINDOWS) == BST_CHECKED;
+ int mins = SendDlgItemMessage(hwndDlg, IDC_IDLESPIN, UDM_GETPOS, 0, 0);
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLETIME1ST, (BYTE)(HIWORD(mins) == 0 ? LOWORD(mins) : 10));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_USERIDLECHECK, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_IDLESHORT) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEMETHOD, (BYTE)(method ? 0 : 1));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEONSAVER, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_SCREENSAVER) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEONFULLSCR, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_FULLSCREEN) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEONLOCK, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_LOCKED) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEONTSDC, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_IDLETERMINAL) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLEPRIVATE, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_IDLEPRIVATE) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_AAENABLE, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_AASHORTIDLE)==BST_CHECKED?1:0));
+ DBWriteContactSettingByte(NULL, IDLEMOD, IDL_IDLESTATUSLOCK, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_IDLESTATUSLOCK)==BST_CHECKED?1:0));
+ {
+ int curSel = SendDlgItemMessage(hwndDlg, IDC_AASTATUS, CB_GETCURSEL, 0, 0);
+ if ( curSel != CB_ERR ) {
+ DBWriteContactSettingWord(NULL, IDLEMOD, IDL_AASTATUS, (WORD)(aa_Status[curSel]));
+ }
+ }
+ // destroy any current idle and reset settings.
+ IdleObject_Destroy(&gIdleObject);
+ IdleObject_Create(&gIdleObject);
+ }
+ break;
+ }
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDC_IDLE1STTIME:
+ {
+ int min;
+ if ( (HWND)lParam != GetFocus() || HIWORD(wParam) != EN_CHANGE ) return FALSE;
+ min=GetDlgItemInt(hwndDlg, IDC_IDLE1STTIME, NULL, FALSE);
+ if ( min == 0 && GetWindowTextLength(GetDlgItem(hwndDlg, IDC_IDLE1STTIME)) )
+ SendDlgItemMessage(hwndDlg, IDC_IDLESPIN, UDM_SETPOS, 0, MAKELONG((short) 1, 0));
+ break;
+ }
+ case IDC_IDLESHORT:
+ case IDC_AASHORTIDLE:
+ SendMessage(hwndDlg, WM_USER+2, 0, 0);
+ break;
+
+ case IDC_AASTATUS:
+ if ( HIWORD(wParam) != CBN_SELCHANGE )
+ return TRUE;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ return FALSE;
+}
+
+static int IdleOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_IDLE);
+ odp.pszGroup = LPGEN("Status");
+ odp.pszTitle = LPGEN("Idle");
+ odp.pfnDlgProc = IdleOptsDlgProc;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
+static INT_PTR IdleGetInfo(WPARAM, LPARAM lParam)
+{
+ MIRANDA_IDLE_INFO *mii = ( MIRANDA_IDLE_INFO* )lParam;
+ if ( !mii || ( mii->cbSize != sizeof(MIRANDA_IDLE_INFO) && mii->cbSize != MIRANDA_IDLE_INFO_SIZE_1 ))
+ return 1;
+
+ mii->idleTime = gIdleObject.minutes;
+ mii->privacy = gIdleObject.state&0x10;
+ mii->aaStatus = gIdleObject.aastatus;
+ mii->aaLock = gIdleObject.state&0x20;
+
+ if ( mii->cbSize == sizeof(MIRANDA_IDLE_INFO))
+ mii->idleType = gIdleObject.idleType;
+ return 0;
+}
+
+static int IdleModernOptInit(WPARAM wParam, LPARAM)
+{
+ static const int iBoldControls[] =
+ {
+ IDC_TXT_TITLE1, IDC_TXT_TITLE2, IDC_TXT_TITLE3,
+ MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+ obj.cbSize = sizeof(obj);
+ obj.hInstance = hMirandaInst;
+ obj.dwFlags = MODEROPT_FLG_TCHAR | MODEROPT_FLG_NORESIZE;
+ obj.iSection = MODERNOPT_PAGE_STATUS;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+ obj.iBoldControls = (int*)iBoldControls;
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_IDLE);
+ obj.pfnDlgProc = IdleOptsDlgProc;
+// obj.lpzClassicGroup = "Status";
+// obj.lpzClassicPage = "Messages";
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ return 0;
+}
+
+int LoadIdleModule(void)
+{
+ bModuleInitialized = TRUE;
+
+ bIsWTSApiPresent = InitWTSAPI();
+ MyGetLastInputInfo=(BOOL (WINAPI *)(LASTINPUTINFO*))GetProcAddress(GetModuleHandleA("user32"), "GetLastInputInfo");
+ hIdleEvent=CreateHookableEvent(ME_IDLE_CHANGED);
+ IdleObject_Create(&gIdleObject);
+ CreateServiceFunction(MS_IDLE_GETIDLEINFO, IdleGetInfo);
+ HookEvent(ME_OPT_INITIALISE, IdleOptInit);
+ HookEvent(ME_MODERNOPT_INITIALIZE, IdleModernOptInit);
+ return 0;
+}
+
+void UnloadIdleModule()
+{
+ if ( !bModuleInitialized ) return;
+
+ IdleObject_Destroy(&gIdleObject);
+ DestroyHookableEvent(hIdleEvent);
+ hIdleEvent=NULL;
+}
diff --git a/src/modules/ignore/ignore.cpp b/src/modules/ignore/ignore.cpp
new file mode 100644
index 0000000000..18ded9d972
--- /dev/null
+++ b/src/modules/ignore/ignore.cpp
@@ -0,0 +1,481 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define IGNOREEVENT_MAX 7
+
+static const DWORD ignoreIdToPf1[IGNOREEVENT_MAX]={PF1_IMRECV,PF1_URLRECV,PF1_FILERECV,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+static const DWORD ignoreIdToPf4[IGNOREEVENT_MAX]={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,PF4_SUPPORTTYPING};
+
+static DWORD GetMask(HANDLE hContact)
+{
+ DWORD mask=DBGetContactSettingDword(hContact,"Ignore","Mask1",(DWORD)(-1));
+ if(mask==(DWORD)(-1)) {
+ if(hContact==NULL) mask=0;
+ else {
+ if(DBGetContactSettingByte(hContact,"CList","Hidden",0) || DBGetContactSettingByte(hContact,"CList","NotOnList",0))
+ mask=DBGetContactSettingDword(NULL,"Ignore","Mask1",0);
+ else
+ mask=DBGetContactSettingDword(NULL,"Ignore","Default1",0);
+ }
+ }
+ return mask;
+}
+
+static void SetListGroupIcons(HWND hwndList,HANDLE hFirstItem,HANDLE hParentItem,int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[IGNOREEVENT_MAX]={1,1,1,1,1,1,1};
+ int childCount[IGNOREEVENT_MAX]={0,0,0,0,0,0,0},i;
+ int iImage;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetListGroupIcons(hwndList,hChildItem,hItem,childCount);
+ for(i=0; i < SIZEOF(iconOn); i++)
+ if(iconOn[i] && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i)==0) iconOn[i]=0;
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ for( i=0; i < SIZEOF(iconOn); i++ ) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i);
+ if(iconOn[i] && iImage==0) iconOn[i]=0;
+ if(iImage!=0xFF) childCount[i]++;
+ }
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+ //set icons
+ for( i=0; i < SIZEOF(iconOn); i++ ) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(i,childCount[i]?(iconOn[i]?i+3:0):0xFF));
+ if(groupChildCount) groupChildCount[i]+=childCount[i];
+ }
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(IGNOREEVENT_MAX,1));
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(IGNOREEVENT_MAX+1,2));
+}
+
+static void SetAllChildIcons(HWND hwndList,HANDLE hFirstItem,int iColumn,int iImage)
+{
+ int typeOfFirst,iOldIcon;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetAllChildIcons(hwndList,hChildItem,iColumn,iImage);
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ iOldIcon=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if(iOldIcon!=0xFF && iOldIcon!=iImage) SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+}
+
+static void ResetListOptions(HWND hwndList)
+{
+ int i;
+
+ SendMessage(hwndList,CLM_SETBKBITMAP,0,(LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList,CLM_SETBKCOLOR,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN,4,0);
+ SendMessage(hwndList,CLM_SETINDENT,10,0);
+ SendMessage(hwndList,CLM_SETHIDEEMPTYGROUPS,1,0);
+ for(i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList,CLM_SETTEXTCOLOR,i,GetSysColor(COLOR_WINDOWTEXT));
+}
+
+static void SetIconsForColumn(HWND hwndList,HANDLE hItem,HANDLE hItemAll,int iColumn,int iImage)
+{
+ int itemType;
+
+ itemType=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hItem,0);
+ if(itemType==CLCIT_CONTACT) {
+ int oldiImage = SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if (oldiImage!=0xFF&&oldiImage!=iImage)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ }
+ else if(itemType==CLCIT_INFO) {
+ if(hItem==hItemAll) SetAllChildIcons(hwndList,hItem,iColumn,iImage);
+ else SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage)); //hItemUnknown
+ }
+ else if(itemType==CLCIT_GROUP) {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hItem) SetAllChildIcons(hwndList,hItem,iColumn,iImage);
+ }
+}
+
+static void InitialiseItem(HWND hwndList,HANDLE hContact,HANDLE hItem,DWORD proto1Caps,DWORD proto4Caps)
+{
+ DWORD mask;
+ int i;
+
+ mask=GetMask(hContact);
+ for(i=0;i<IGNOREEVENT_MAX;i++)
+ if((ignoreIdToPf1[i]==0xFFFFFFFF&&ignoreIdToPf4[i]==0xFFFFFFFF) || (proto1Caps&ignoreIdToPf1[i]||proto4Caps&ignoreIdToPf4[i]))
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,mask&(1<<i)?i+3:0));
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(IGNOREEVENT_MAX,1));
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(IGNOREEVENT_MAX+1,2));
+}
+
+static void SaveItemMask(HWND hwndList,HANDLE hContact,HANDLE hItem,const char *pszSetting)
+{
+ DWORD mask;
+ int i,iImage;
+
+ mask=0;
+ for(i=0;i<IGNOREEVENT_MAX;i++) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,0));
+ if(iImage && iImage!=0xFF) mask|=1<<i;
+ }
+ DBWriteContactSettingDword(hContact,"Ignore",pszSetting,mask);
+}
+
+static void SetAllContactIcons(HWND hwndList)
+{
+ HANDLE hContact,hItem;
+ DWORD proto1Caps, proto4Caps;
+ char *szProto;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(IGNOREEVENT_MAX,0))==0xFF) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if(szProto==NULL) proto1Caps=proto4Caps=0;
+ else {
+ proto1Caps=CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_1,0);
+ proto4Caps=CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_4,0);
+ }
+ InitialiseItem(hwndList,hContact,hItem,proto1Caps,proto4Caps);
+ if(!DBGetContactSettingByte(hContact,"CList","Hidden",0))
+ SendMessage(hwndList,CLM_SETCHECKMARK,(WPARAM)hItem,1);
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+}
+
+static INT_PTR CALLBACK DlgProcIgnoreOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ static HICON hIcons[IGNOREEVENT_MAX+2];
+ static HANDLE hItemAll,hItemUnknown;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { HIMAGELIST hIml;
+ int i;
+ hIml=ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,3+IGNOREEVENT_MAX,3+IGNOREEVENT_MAX);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_SMALLDOT);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_FILLEDBLOB);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_EMPTYBLOB);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_EVENT_MESSAGE);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_EVENT_URL);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_EVENT_FILE);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_USERONLINE);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_MIRANDA);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_ADDCONTACT);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_TYPING);
+
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRAIMAGELIST,0,(LPARAM)hIml);
+ for( i=0; i < SIZEOF(hIcons); i++ )
+ hIcons[i]=ImageList_GetIcon(hIml,1+i,ILD_NORMAL);
+ }
+
+ SendDlgItemMessage(hwndDlg,IDC_ALLICON,STM_SETICON,(WPARAM)hIcons[0],0);
+ SendDlgItemMessage(hwndDlg,IDC_NONEICON,STM_SETICON,(WPARAM)hIcons[1],0);
+ SendDlgItemMessage(hwndDlg,IDC_MSGICON,STM_SETICON,(WPARAM)hIcons[2],0);
+ SendDlgItemMessage(hwndDlg,IDC_URLICON,STM_SETICON,(WPARAM)hIcons[3],0);
+ SendDlgItemMessage(hwndDlg,IDC_FILEICON,STM_SETICON,(WPARAM)hIcons[4],0);
+ SendDlgItemMessage(hwndDlg,IDC_ONLINEICON,STM_SETICON,(WPARAM)hIcons[5],0);
+ SendDlgItemMessage(hwndDlg,IDC_AUTHICON,STM_SETICON,(WPARAM)hIcons[6],0);
+ SendDlgItemMessage(hwndDlg,IDC_ADDED,STM_SETICON,(WPARAM)hIcons[7],0);
+ SendDlgItemMessage(hwndDlg,IDC_TYPINGICON,STM_SETICON,(WPARAM)hIcons[8],0);
+
+ if(!SendMessage(GetParent(hwndDlg),PSM_ISEXPERT,0,0)) {
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_LIST),GWL_STYLE,GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_LIST),GWL_STYLE)&~(CLS_CHECKBOXES|CLS_GROUPCHECKBOXES|CLS_SHOWHIDDEN));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_AUTOREBUILD,0,0);
+ }
+
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRACOLUMNS,IGNOREEVENT_MAX+2,0);
+
+ { CLCINFOITEM cii={0};
+ cii.cbSize=sizeof(cii);
+ cii.flags=CLCIIF_GROUPFONT;
+ cii.pszText=TranslateT("** All contacts **");
+ hItemAll=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_ADDINFOITEM,0,(LPARAM)&cii);
+
+ cii.pszText=TranslateT("** Unknown contacts **");
+ hItemUnknown=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_ADDINFOITEM,0,(LPARAM)&cii);
+ InitialiseItem(GetDlgItem(hwndDlg,IDC_LIST),NULL,hItemUnknown,0xFFFFFFFF,0xFFFFFFFF);
+ }
+
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ return TRUE;
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ //fall through
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ break;
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case CLN_CHECKCHANGED:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case NM_CLICK:
+ { HANDLE hItem;
+ NMCLISTCONTROL *nm=(NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+ int iImage;
+
+ if(nm->iColumn==-1) break;
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_HITTEST,(WPARAM)&hitFlags,MAKELPARAM(nm->pt.x,nm->pt.y));
+ if(hItem==NULL) break;
+ if(!(hitFlags&CLCHT_ONITEMEXTRA)) break;
+ if(nm->iColumn==IGNOREEVENT_MAX) { //ignore all
+ for(iImage=0;iImage<IGNOREEVENT_MAX;iImage++)
+ SetIconsForColumn(GetDlgItem(hwndDlg,IDC_LIST),hItem,hItemAll,iImage,iImage+3);
+ }
+ else if(nm->iColumn==IGNOREEVENT_MAX+1) { //ignore none
+ for(iImage=0;iImage<IGNOREEVENT_MAX;iImage++)
+ SetIconsForColumn(GetDlgItem(hwndDlg,IDC_LIST),hItem,hItemAll,iImage,0);
+ }
+ else {
+ iImage=SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(nm->iColumn,0));
+ if(iImage==0) iImage=nm->iColumn+3;
+ else if(iImage!=0xFF) iImage=0;
+ SetIconsForColumn(GetDlgItem(hwndDlg,IDC_LIST),hItem,hItemAll,nm->iColumn,iImage);
+ }
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { HANDLE hContact,hItem;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem) SaveItemMask(GetDlgItem(hwndDlg,IDC_LIST),hContact,hItem,"Mask1");
+ if(SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETCHECKMARK,(WPARAM)hItem,0))
+ DBDeleteContactSetting(hContact,"CList","Hidden");
+ else
+ DBWriteContactSettingByte(hContact,"CList","Hidden",1);
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+ SaveItemMask(GetDlgItem(hwndDlg,IDC_LIST),NULL,hItemAll,"Default1");
+ SaveItemMask(GetDlgItem(hwndDlg,IDC_LIST),NULL,hItemUnknown,"Mask1");
+ return TRUE;
+ }
+ case PSN_EXPERTCHANGED:
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_LIST),GWL_STYLE,((PSHNOTIFY*)lParam)->lParam?GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_LIST),GWL_STYLE)|CLS_CHECKBOXES|CLS_GROUPCHECKBOXES|CLS_SHOWHIDDEN:GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_LIST),GWL_STYLE)&~(CLS_CHECKBOXES|CLS_GROUPCHECKBOXES|CLS_SHOWHIDDEN));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_AUTOREBUILD,0,0);
+ break;
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ { int i;
+ HIMAGELIST hIml;
+ for( i=0; i < SIZEOF(hIcons); i++ )
+ DestroyIcon(hIcons[i]);
+ hIml=(HIMAGELIST)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGELIST,0,0);
+ ImageList_Destroy(hIml);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static UINT expertOnlyControls[]={IDC_STCHECKMARKS};
+static int IgnoreOptInitialise(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = 900000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_IGNORE);
+ odp.pszTitle = LPGEN("Ignore");
+ odp.pszGroup = LPGEN("Events");
+ odp.pfnDlgProc = DlgProcIgnoreOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.expertOnlyControls = expertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF( expertOnlyControls );
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp);
+ return 0;
+}
+
+static INT_PTR IsIgnored(WPARAM wParam,LPARAM lParam)
+{
+ DWORD mask=GetMask((HANDLE)wParam);
+ if(lParam<1 || lParam>IGNOREEVENT_MAX) return 1;
+ return (mask>>(lParam-1))&1;
+}
+
+static INT_PTR Ignore(WPARAM wParam,LPARAM lParam)
+{
+ DWORD mask=GetMask((HANDLE)wParam);
+ if((lParam<1 || lParam>IGNOREEVENT_MAX) && lParam!=IGNOREEVENT_ALL) return 1;
+ if(lParam==IGNOREEVENT_ALL) mask=(1<<IGNOREEVENT_MAX)-1;
+ else mask|=1<<(lParam-1);
+ DBWriteContactSettingDword((HANDLE)wParam,"Ignore","Mask1",mask);
+ return 0;
+}
+
+static INT_PTR Unignore(WPARAM wParam,LPARAM lParam)
+{
+ DWORD mask=GetMask((HANDLE)wParam);
+ if((lParam<1 || lParam>IGNOREEVENT_MAX) && lParam!=IGNOREEVENT_ALL) return 1;
+ if(lParam==IGNOREEVENT_ALL) mask=0;
+ else mask&=~(1<<(lParam-1));
+ DBWriteContactSettingDword((HANDLE)wParam,"Ignore","Mask1",mask);
+ return 0;
+}
+
+static int IgnoreContactAdded(WPARAM wParam, LPARAM)
+{
+ CallService(MS_PROTO_ADDTOCONTACT,wParam,(LPARAM)"Ignore");
+ return 0;
+}
+
+static INT_PTR IgnoreRecvMessage(WPARAM wParam,LPARAM lParam)
+{
+ if(IsIgnored((WPARAM)((CCSDATA*)lParam)->hContact,IGNOREEVENT_MESSAGE)) return 1;
+ return CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+}
+
+static INT_PTR IgnoreRecvUrl(WPARAM wParam,LPARAM lParam)
+{
+ if(IsIgnored((WPARAM)((CCSDATA*)lParam)->hContact,IGNOREEVENT_URL)) return 1;
+ return CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+}
+
+static INT_PTR IgnoreRecvFile(WPARAM wParam,LPARAM lParam)
+{
+ if(IsIgnored((WPARAM)((CCSDATA*)lParam)->hContact,IGNOREEVENT_FILE)) return 1;
+ return CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+}
+
+static INT_PTR IgnoreRecvAuth(WPARAM wParam,LPARAM lParam)
+{
+ if(IsIgnored((WPARAM)((CCSDATA*)lParam)->hContact,IGNOREEVENT_AUTHORIZATION)) return 1;
+ return CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+}
+
+static int IgnoreAddedNotify(WPARAM, LPARAM lParam)
+{
+ DBEVENTINFO *dbei=(DBEVENTINFO*)lParam;
+ if (dbei && dbei->eventType==EVENTTYPE_ADDED && dbei->pBlob!=NULL) {
+ HANDLE hContact;
+
+ hContact=*((PHANDLE)(dbei->pBlob+sizeof(DWORD)));
+ if (CallService(MS_DB_CONTACT_IS,(WPARAM)hContact,0) && IsIgnored((WPARAM)hContact,IGNOREEVENT_YOUWEREADDED))
+ return 1;
+ }
+ return 0;
+}
+
+static int IgnoreModernOptInit(WPARAM wParam, LPARAM)
+{
+ static int iBoldControls[] =
+ {
+ IDC_TXT_TITLE1, IDC_TXT_TITLE2, IDC_TXT_TITLE3,
+ MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+ obj.cbSize = sizeof(obj);
+ obj.hInstance = hMirandaInst;
+ obj.dwFlags = MODEROPT_FLG_TCHAR;
+ obj.iSection = MODERNOPT_PAGE_IGNORE;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+ obj.iBoldControls = iBoldControls;
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_IGNORE);
+ obj.pfnDlgProc = DlgProcIgnoreOpts;
+// obj.lpzClassicGroup = "Events";
+// obj.lpzClassicPage = "Ignore";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ return 0;
+}
+
+int LoadIgnoreModule(void)
+{
+ PROTOCOLDESCRIPTOR pd = { 0 };
+ pd.cbSize=sizeof(pd);
+ pd.szName="Ignore";
+ pd.type=PROTOTYPE_IGNORE;
+ CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd);
+
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while ( hContact != NULL ) {
+ if (!CallService(MS_PROTO_ISPROTOONCONTACT,(WPARAM)hContact,(LPARAM)"Ignore"))
+ CallService(MS_PROTO_ADDTOCONTACT,(WPARAM)hContact,(LPARAM)"Ignore");
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
+ }
+
+ CreateServiceFunction("Ignore"PSR_MESSAGE,IgnoreRecvMessage);
+ CreateServiceFunction("Ignore"PSR_URL,IgnoreRecvUrl);
+ CreateServiceFunction("Ignore"PSR_FILE,IgnoreRecvFile);
+ CreateServiceFunction("Ignore"PSR_AUTH,IgnoreRecvAuth);
+ CreateServiceFunction(MS_IGNORE_ISIGNORED,IsIgnored);
+ CreateServiceFunction(MS_IGNORE_IGNORE,Ignore);
+ CreateServiceFunction(MS_IGNORE_UNIGNORE,Unignore);
+
+ HookEvent(ME_DB_CONTACT_ADDED,IgnoreContactAdded);
+ HookEvent(ME_DB_EVENT_FILTER_ADD,IgnoreAddedNotify);
+ HookEvent(ME_MODERNOPT_INITIALIZE, IgnoreModernOptInit);
+ HookEvent(ME_OPT_INITIALISE,IgnoreOptInitialise);
+ return 0;
+}
diff --git a/src/modules/keybindings/keybindings.cpp b/src/modules/keybindings/keybindings.cpp
new file mode 100644
index 0000000000..6255cc78e9
--- /dev/null
+++ b/src/modules/keybindings/keybindings.cpp
@@ -0,0 +1,616 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "keybindings.h"
+#include "m_keybindings.h"
+
+static HANDLE hKeyBindings_Register;
+static HANDLE hKeyBindings_Get;
+static WNDPROC OldEditProc;
+static TCHAR *keySeparator = _T(" + ");
+static TreeItem *currentTreeItem = NULL;
+static DWORD tempModifiers;
+static DWORD modifiers;
+static DWORD virtualKey;
+
+static int addKeyBinding(KEYBINDINGDESC *desc) {
+ int i, len;
+ TCHAR *sectionName;
+ TCHAR *actionName;
+ KeyBindingItem *item = (KeyBindingItem *)mir_alloc(sizeof(KeyBindingItem));
+ ZeroMemory(item, sizeof(KeyBindingItem));
+ if (desc->flags & KBDF_UNICODE) {
+ #ifdef _UNICODE
+ sectionName = mir_tstrdup(desc->ptszSection);
+ actionName = mir_tstrdup(desc->ptszActionName);
+ #else
+ sectionName = u2a(desc->pwszSection);
+ actionName = u2a(desc->pwszActionName);
+ #endif
+ } else {
+ #ifdef _UNICODE
+ sectionName = a2u(desc->pszSection);
+ actionName = a2u(desc->pszActionName);
+ #else
+ sectionName = mir_tstrdup(desc->ptszSection);
+ actionName = mir_tstrdup(desc->ptszActionName);
+ #endif
+ }
+ len = _tcslen(sectionName) + _tcslen(actionName) + 2;
+ item->fullActionName = (TCHAR *)mir_alloc(len * sizeof(TCHAR));
+ _tcscpy(item->fullActionName, sectionName);
+ _tcscat(item->fullActionName, _T("/"));
+ _tcscat(item->fullActionName, actionName);
+ item->actionName = actionName;
+ mir_free(sectionName);
+ item->actionGroupName = mir_strdup(desc->pszActionGroup);
+ item->action = desc->action;
+ for (i = 0; i < 5; i++) {
+ item->defaultKey[i] = desc->key[i];
+ item->key[i] = desc->key[i];
+ }
+
+ item->next = keyBindingList;
+ keyBindingList = item;
+ if (item->next != NULL)
+ item->next->prev = item;
+ {
+ DBVARIANT dbv;
+ char *paramName = mir_t2a(item->fullActionName);
+ if ( !DBGetContactSettingString(NULL, "KeyBindings", paramName, &dbv )) {
+ for (i = 0; i < 5; i++)
+ item->key[i] = 0;
+ sscanf(dbv.pszVal, "%X,%X,%X,%X,%X", &item->key[0],&item->key[1],&item->key[2],&item->key[3],&item->key[4]);
+ DBFreeVariant(&dbv);
+ }
+ mir_free(paramName);
+ }
+ return 0;
+}
+
+static KeyBindingItem* findKeyBinding(char *actionGroup, DWORD key)
+{
+ int i;
+ KeyBindingItem *ptr = NULL;
+ if (key != 0) {
+ for (ptr = keyBindingList; ptr != NULL; ptr = ptr->next)
+ if (strcmp(ptr->actionGroupName, actionGroup) == 0)
+ for (i = 0; i < 5; i++)
+ if (ptr->key[i] == key) return ptr;
+ }
+ return ptr;
+}
+
+static KeyBindingItem* findTempKeyBinding(char *actionGroup, DWORD key)
+{
+ int i;
+ KeyBindingItem *ptr = NULL;
+ if (key != 0) {
+ for (ptr = keyBindingList; ptr != NULL; ptr = ptr->next)
+ if (strcmp(ptr->actionGroupName, actionGroup) == 0)
+ for (i = 0; i < 5; i++)
+ if (ptr->tempKey[i] == key) return ptr;
+ }
+ return ptr;
+}
+
+static void removeTempKeyBinding(char *actionGroup, DWORD key)
+{
+ KeyBindingItem *ptr = NULL;
+ int i, j;
+ if (key != 0) {
+ for (ptr = keyBindingList; ptr != NULL; ptr = ptr->next)
+ if (strcmp(ptr->actionGroupName, actionGroup) == 0)
+ for (i = 0; i < 5; i++)
+ if (ptr->tempKey[i] == key) {
+ for (j = i+1; j < 5; j++)
+ ptr->tempKey[j-1] = ptr->tempKey[j];
+ ptr->tempKey[4] = 0;
+ }
+ }
+}
+
+static void loadTempKeyBinding(KeyBindingItem *item)
+{
+ int i;
+ for (i = 0; i < 5; i ++)
+ item->tempKey[i] = item->key[i];
+}
+
+static void loadTempKeyBindings()
+{
+ KeyBindingItem *ptr;
+ for (ptr = keyBindingList; ptr != NULL; ptr = ptr->next)
+ loadTempKeyBinding(ptr);
+}
+
+static void saveKeyBinding(KeyBindingItem *item)
+{
+ BOOL save = FALSE;
+ int i;
+ char buff[128];
+ for (i = 0; i < 5; i ++) {
+ if (item->key[i] != item->tempKey[i]) {
+ item->key[i] = item->tempKey[i];
+ save = TRUE;
+ }
+ }
+ if (save) {
+ char *paramName = mir_t2a(item->fullActionName);
+ mir_snprintf(buff, sizeof(buff), "%X,%X,%X,%X,%X", item->key[0],item->key[1],item->key[2],item->key[3],item->key[4]);
+ DBWriteContactSettingString(NULL, "KeyBindings", paramName, buff);
+ mir_free(paramName);
+ }
+}
+
+static void saveKeyBindings()
+{
+ KeyBindingItem *ptr;
+ for (ptr = keyBindingList; ptr != NULL; ptr = ptr->next)
+ saveKeyBinding(ptr);
+}
+
+static HTREEITEM findNamedTreeItemAt(HWND hwndTree, HTREEITEM hItem, const TCHAR *name)
+{
+ TVITEM tvi = {0};
+ TCHAR str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = TreeView_GetChild(hwndTree, hItem);
+ else
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = MAX_PATH;
+
+ while (tvi.hItem)
+ {
+ TreeView_GetItem(hwndTree, &tvi);
+ if (!lstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return NULL;
+}
+
+static void createSettingsTreeNode(HWND hwndTree, KeyBindingItem *keyBindingItem)
+{
+ TCHAR itemName[1024];
+ TCHAR* sectionName;
+ int sectionLevel = 0;
+
+ HTREEITEM hSection = NULL;
+ lstrcpy(itemName, keyBindingItem->fullActionName);
+ sectionName = itemName;
+
+ while (sectionName) {
+ HTREEITEM hItem;
+ TCHAR* pTranslatedItemName;
+ TCHAR* pItemName = sectionName;
+
+ if (sectionName = _tcschr(sectionName, '/')) {
+ *sectionName = 0;
+ }
+ pTranslatedItemName = TranslateTS( pItemName );
+ hItem = findNamedTreeItemAt(hwndTree, hSection, pTranslatedItemName);
+ if (!sectionName || !hItem) {
+ if (!hItem) {
+ TVINSERTSTRUCT tvis = {0};
+ TreeItem *treeItem = (TreeItem *)mir_alloc(sizeof(TreeItem));
+ treeItem->keyBinding = !sectionName ? keyBindingItem : NULL;
+ treeItem->paramName = mir_t2a(itemName);
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_SORT; //!sectionName ? TVI_LAST : TVI_SORT;
+ tvis.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE;
+ tvis.item.pszText = pTranslatedItemName;
+ tvis.item.lParam = (LPARAM)treeItem;
+ tvis.item.state = tvis.item.stateMask = DBGetContactSettingByte(NULL, "KeyBindingsUI", treeItem->paramName, TVIS_EXPANDED );
+ hItem = TreeView_InsertItem(hwndTree, &tvis);
+ }
+ }
+ if (sectionName) {
+ *sectionName = '/';
+ sectionName++;
+ }
+ sectionLevel++;
+ hSection = hItem;
+ }
+}
+
+static void saveCollapseState( HWND hwndTree )
+{
+ HTREEITEM hti;
+ TVITEM tvi;
+ hti = TreeView_GetRoot( hwndTree );
+ while( hti != NULL ) {
+ HTREEITEM ht;
+ tvi.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM;
+ tvi.hItem = hti;
+ tvi.stateMask = (DWORD)-1;
+ TreeView_GetItem( hwndTree, &tvi );
+ if( tvi.cChildren > 0 ) {
+ TreeItem *treeItem = (TreeItem *)tvi.lParam;
+ if ( tvi.state & TVIS_EXPANDED )
+ DBWriteContactSettingByte(NULL, "KeyBindingsUI", treeItem->paramName, TVIS_EXPANDED );
+ else
+ DBWriteContactSettingByte(NULL, "KeyBindingsUI", treeItem->paramName, 0 );
+ }
+ ht = TreeView_GetChild( hwndTree, hti );
+ if( ht == NULL ) {
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ while( ht == NULL ) {
+ hti = TreeView_GetParent( hwndTree, hti );
+ if( hti == NULL ) break;
+ ht = TreeView_GetNextSibling( hwndTree, hti );
+ } }
+ hti = ht;
+} }
+
+
+static const TCHAR* getKeyName(DWORD key) {
+ static TCHAR keyName[64];
+ int nameLen = 0;
+ ZeroMemory(keyName, sizeof(keyName));
+ if (key & KB_CTRL_FLAG) {
+ GetKeyNameText(MAKELPARAM(0, MapVirtualKey(VK_CONTROL, 0)), keyName, 64);
+ _tcscat(keyName, keySeparator);
+ nameLen = _tcslen(keyName);
+ }
+ if (key & KB_SHIFT_FLAG) {
+ GetKeyNameText(MAKELPARAM(0, MapVirtualKey(VK_SHIFT, 0)), &keyName[nameLen], 64 - nameLen);
+ _tcscat(keyName, keySeparator);
+ nameLen = _tcslen(keyName);
+ }
+ if (key & KB_ALT_FLAG) {
+ GetKeyNameText(MAKELPARAM(0, MapVirtualKey(VK_MENU, 0)), &keyName[nameLen], 64 - nameLen);
+ _tcscat(keyName, keySeparator);
+ nameLen = _tcslen(keyName);
+ }
+ if ((key & 0xFFFF) != 0) {
+ DWORD scanCode = MapVirtualKey(key & 0xFFFF, 0);
+ switch(key & 0xFFFF) {
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ case VK_NEXT:
+ case VK_PRIOR:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ scanCode |= 0x100; // Add extended bit
+ }
+ GetKeyNameText(MAKELPARAM(0, scanCode), &keyName[nameLen], 64 - nameLen);
+ nameLen = _tcslen(keyName);
+ }
+ return keyName;
+}
+
+static refreshPreview(HWND hwnd)
+{
+ TCHAR warning[1024];
+ ZeroMemory(warning, sizeof(warning));
+ SetWindowText(hwnd, getKeyName(virtualKey | modifiers));
+ if (currentTreeItem != NULL && currentTreeItem->keyBinding != NULL) {
+ KeyBindingItem *item = findTempKeyBinding(currentTreeItem->keyBinding->actionGroupName, virtualKey | modifiers);
+ if (item != NULL)
+ mir_sntprintf(warning, 1024, TranslateT("Shortcut already assigned to \"%s\" action.\nIf you click \"Add\" the shortcut will be reassigned."), item->actionName);
+ }
+ SetDlgItemText(GetParent(hwnd), IDC_MESSAGE, warning);
+ EnableWindow(GetDlgItem(GetParent(hwnd), IDC_ADD), virtualKey != 0);
+}
+
+static LRESULT CALLBACK KeyBindingsEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_CREATE:
+ virtualKey = 0;
+ break;
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ if (virtualKey != 0) {
+ virtualKey = 0;
+ }
+ switch (wParam)
+ {
+ case VK_SHIFT:
+ tempModifiers |= KB_SHIFT_FLAG;
+ break;
+ case VK_CONTROL:
+ tempModifiers |= KB_CTRL_FLAG;
+ break;
+ case VK_MENU:
+ tempModifiers |= KB_ALT_FLAG;
+ break;
+ default:
+ virtualKey = wParam;
+ break;
+ }
+ modifiers = tempModifiers;
+ refreshPreview(hwnd);
+ return 0;
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ switch (wParam)
+ {
+ case VK_SHIFT:
+ tempModifiers &= ~KB_SHIFT_FLAG;
+ break;
+ case VK_CONTROL:
+ tempModifiers &= ~KB_CTRL_FLAG;
+ break;
+ case VK_MENU:
+ tempModifiers &= ~KB_ALT_FLAG;
+ break;
+ default:
+ break;
+ }
+ if (virtualKey == 0) {
+ modifiers = tempModifiers;
+ refreshPreview(hwnd);
+ }
+ case WM_CHAR:
+ case WM_PASTE:
+ return 0;
+ case WM_SETFOCUS:
+ modifiers = 0;
+ tempModifiers = 0;
+ virtualKey = 0;
+ refreshPreview(hwnd);
+ break;
+ case WM_GETDLGCODE:
+ return DLGC_WANTARROWS|DLGC_WANTALLKEYS| DLGC_WANTTAB;
+ }
+ return CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam);
+}
+
+static void buildTree(HWND hwnd) {
+ KeyBindingItem *ptr;
+ for (ptr = keyBindingList; ptr != NULL; ptr=ptr->next) {
+ createSettingsTreeNode(hwnd, ptr);
+ }
+}
+
+static void refreshListBox(HWND hwnd) {
+ int count = 0;
+ BOOL nonDefault = FALSE;
+ BOOL canUndo = FALSE;
+ SendDlgItemMessage(hwnd, IDC_LIST, LB_RESETCONTENT, 0, 0);
+ if (currentTreeItem->keyBinding != NULL) {
+ int i;
+ for (i=0; i<5; i++) {
+ if (currentTreeItem->keyBinding->tempKey[i] != currentTreeItem->keyBinding->defaultKey[i]) nonDefault = TRUE;
+ if (currentTreeItem->keyBinding->tempKey[i] != currentTreeItem->keyBinding->key[i]) canUndo = TRUE;
+ if (currentTreeItem->keyBinding->tempKey[i] != 0) {
+ SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)getKeyName(currentTreeItem->keyBinding->tempKey[i]));
+ count++;
+ }
+ }
+ }
+ EnableWindow(GetDlgItem(hwnd, IDC_PREVIEW), currentTreeItem->keyBinding != NULL && count < 5);
+ SetDlgItemText(hwnd, IDC_PREVIEW, _T(""));
+ SetDlgItemText(hwnd, IDC_MESSAGE, _T(""));
+ EnableWindow(GetDlgItem(hwnd, IDC_ADD), FALSE);
+ EnableWindow(GetDlgItem(hwnd, IDC_DELETE), FALSE);
+ EnableWindow(GetDlgItem(hwnd, IDC_BTN_RESET), nonDefault);
+ EnableWindow(GetDlgItem(hwnd, IDC_BTN_UNDO), canUndo);
+}
+
+BOOL CALLBACK DlgProcKeyBindingsOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ OldEditProc = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PREVIEW), GWLP_WNDPROC, (LONG_PTR) KeyBindingsEditProc);
+ currentTreeItem = NULL;
+ loadTempKeyBindings();
+ buildTree(GetDlgItem(hwndDlg, IDC_CATEGORYLIST));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
+ }
+ return TRUE;
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ SendDlgItemMessage(hwndDlg, IDC_PREVIEW, msg, wParam, lParam);
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_ADD:
+ if (currentTreeItem->keyBinding != NULL) {
+ int i;
+ removeTempKeyBinding(currentTreeItem->keyBinding->actionGroupName, modifiers | virtualKey);
+ for (i=0; i<5; i++) {
+ if (currentTreeItem->keyBinding->tempKey[i] == 0) {
+ currentTreeItem->keyBinding->tempKey[i] = modifiers | virtualKey;
+ break;
+ }
+ }
+ }
+ refreshListBox(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_DELETE:
+ if (currentTreeItem->keyBinding != NULL) {
+ int index = SendDlgItemMessage(hwndDlg, IDC_LIST, LB_GETCURSEL, 0, 0);
+ if (index != LB_ERR && index <5) {
+ if (currentTreeItem->keyBinding->tempKey[index] != 0) {
+ int i;
+ for (i = index + 1; i < 5; i++) {
+ currentTreeItem->keyBinding->tempKey[i-1] = currentTreeItem->keyBinding->tempKey[i];
+ }
+ currentTreeItem->keyBinding->tempKey[4] = 0;
+ }
+ }
+ }
+ refreshListBox(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_BTN_RESET:
+ if (currentTreeItem->keyBinding != NULL) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ removeTempKeyBinding(currentTreeItem->keyBinding->actionGroupName, currentTreeItem->keyBinding->defaultKey[i]);
+ currentTreeItem->keyBinding->tempKey[i] = currentTreeItem->keyBinding->defaultKey[i];
+ }
+ }
+ refreshListBox(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_BTN_UNDO:
+ if (currentTreeItem->keyBinding != NULL) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ removeTempKeyBinding(currentTreeItem->keyBinding->actionGroupName, currentTreeItem->keyBinding->key[i]);
+ currentTreeItem->keyBinding->tempKey[i] = currentTreeItem->keyBinding->key[i];
+ }
+ }
+ refreshListBox(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_LIST:
+ if (HIWORD(wParam) == LBN_SELCHANGE) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE);
+ }
+ break;
+ }
+ break;
+ case WM_NOTIFY:
+ if (((LPNMHDR) lParam)->idFrom == IDC_CATEGORYLIST)
+ {
+ switch(((NMHDR*)lParam)->code) {
+ case TVN_SELCHANGEDA:
+ case TVN_SELCHANGEDW:
+ {
+ TVITEM tvi = {0};
+ tvi.hItem = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_CATEGORYLIST));
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_CATEGORYLIST), &tvi);
+ currentTreeItem = (TreeItem *) tvi.lParam;
+ refreshListBox(hwndDlg);
+ break;
+ }
+ case TVN_DELETEITEMA:
+ case TVN_DELETEITEMW:
+ {
+ TreeItem *treeItem = (TreeItem *)(((LPNMTREEVIEW)lParam)->itemOld.lParam);
+ if (treeItem) {
+ mir_free(treeItem->paramName);
+ mir_free(treeItem);
+ }
+ break;
+ }
+ }
+ }
+ if (((LPNMHDR) lParam)->idFrom == 0 && ((LPNMHDR) lParam)->code == PSN_APPLY ) {
+ saveKeyBindings();
+ }
+ break;
+ case WM_DESTROY:
+ saveCollapseState(GetDlgItem(hwndDlg, IDC_CATEGORYLIST));
+
+ }
+ return FALSE;
+}
+
+static INT_PTR KBRegister( WPARAM wParam, LPARAM lParam )
+{
+ return (int)addKeyBinding(( KEYBINDINGDESC* )lParam );
+}
+
+static INT_PTR KBGet( WPARAM wParam, LPARAM lParam )
+{
+ KEYBINDINGDESC* desc = ( KEYBINDINGDESC* )lParam;
+ KeyBindingItem* item = (KeyBindingItem*)findKeyBinding(desc->pszActionGroup, desc->key[0]);
+ if (item != NULL) {
+ desc->action = item->action;
+ return 0;
+ }
+ return 1;
+}
+
+static void InitKeyBinding()
+{
+ keyBindingList = NULL;
+ hKeyBindings_Register = CreateServiceFunction(MS_KEYBINDINGS_REGISTER, KBRegister);
+ hKeyBindings_Get = CreateServiceFunction(MS_KEYBINDINGS_GET, KBGet);
+}
+
+static void UninitKeyBinding()
+{
+ KeyBindingItem *ptr, *ptr2;
+ ptr = keyBindingList;
+ keyBindingList = NULL;
+ for (; ptr != NULL; ptr = ptr2) {
+ ptr2 = ptr->next;
+ mir_free(ptr->actionName);
+ mir_free(ptr->fullActionName);
+ mir_free(ptr->actionGroupName);
+ mir_free(ptr);
+ }
+ DestroyServiceFunction(hKeyBindings_Register);
+ DestroyServiceFunction(hKeyBindings_Get);
+}
+
+static int KeyBindingsOptionsInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = GetModuleHandle(NULL);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.position = -180000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_KEYBINDINGS);
+ odp.ptszTitle = TranslateT("Key Bindings");
+ odp.ptszGroup = TranslateT("Customize");
+ odp.pfnDlgProc = DlgProcKeyBindingsOpts;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static int KeyBindingsSystemModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ HookEvent(ME_OPT_INITIALISE, KeyBindingsOptionsInit);
+ return 0;
+}
+
+static int OnPreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ UninitKeyBinding();
+ return 0;
+}
+
+int LoadKeyBindingsModule( void )
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, KeyBindingsSystemModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
+ InitKeyBinding();
+ return 0;
+}
diff --git a/src/modules/keybindings/keybindings.h b/src/modules/keybindings/keybindings.h
new file mode 100644
index 0000000000..a4fbdd0c8f
--- /dev/null
+++ b/src/modules/keybindings/keybindings.h
@@ -0,0 +1,43 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+typedef struct KeyBindingItemStruct
+{
+ TCHAR* actionName;
+ TCHAR* fullActionName;
+ char* actionGroupName;
+ DWORD action;
+ DWORD defaultKey[5];
+ DWORD tempKey[5];
+ DWORD key[5];
+ struct KeyBindingItemStruct *prev;
+ struct KeyBindingItemStruct *next;
+}KeyBindingItem;
+
+static KeyBindingItem* keyBindingList = NULL;
+
+typedef struct
+{
+ char *paramName;
+ KeyBindingItem *keyBinding;
+}TreeItem;
diff --git a/src/modules/langpack/langpack.cpp b/src/modules/langpack/langpack.cpp
new file mode 100644
index 0000000000..0603a24b90
--- /dev/null
+++ b/src/modules/langpack/langpack.cpp
@@ -0,0 +1,569 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#include "../netlib/netlib.h"
+
+#define LANGPACK_BUF_SIZE 4000
+
+int LoadLangPackServices(void);
+
+struct LangPackMuuid
+{
+ MUUID muuid;
+ PLUGININFOEX* pInfo;
+};
+
+static int CompareMuuids( const LangPackMuuid* p1, const LangPackMuuid* p2 )
+{
+ return memcmp( &p1->muuid, &p2->muuid, sizeof( MUUID ));
+}
+
+static LIST<LangPackMuuid> lMuuids( 10, CompareMuuids );
+static LangPackMuuid* pCurrentMuuid = NULL;
+
+static BOOL bModuleInitialized = FALSE;
+
+struct LangPackEntry {
+ DWORD englishHash;
+ char *local;
+ wchar_t *wlocal;
+ LangPackMuuid* pMuuid;
+ LangPackEntry* pNext; // for langpack items with the same hash value
+};
+
+struct LangPackStruct {
+ TCHAR filename[MAX_PATH];
+ TCHAR filePath[MAX_PATH];
+ char language[64];
+ char lastModifiedUsing[64];
+ char authors[256];
+ char authorEmail[128];
+ LangPackEntry *entry;
+ int entryCount, entriesAlloced;
+ LCID localeID;
+ UINT defaultANSICp;
+} static langPack;
+
+static int IsEmpty(char *str)
+{
+ int i = 0;
+
+ while (str[i])
+ {
+ if (str[i]!=' '&&str[i]!='\r'&&str[i]!='\n')
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+void ConvertBackslashes(char *str, UINT fileCp)
+{
+ char *pstr;
+ for ( pstr = str; *pstr; pstr = CharNextExA( fileCp, pstr, 0 )) {
+ if( *pstr == '\\' ) {
+ switch( pstr[1] ) {
+ case 'n': *pstr = '\n'; break;
+ case 't': *pstr = '\t'; break;
+ case 'r': *pstr = '\r'; break;
+ default: *pstr = pstr[1]; break;
+ }
+ memmove(pstr+1, pstr+2, strlen(pstr+2) + 1);
+} } }
+
+#ifdef _DEBUG
+//#pragma optimize( "gt", on )
+#endif
+
+// MurmurHash2
+unsigned int __fastcall hash(const void * key, unsigned int len)
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ const unsigned int m = 0x5bd1e995;
+ const int r = 24;
+
+ // Initialize the hash to a 'random' value
+ unsigned int h = len;
+
+ // Mix 4 bytes at a time into the hash
+ const unsigned char * data = (const unsigned char *)key;
+
+ while(len >= 4)
+ {
+ unsigned int k = *(unsigned int *)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ // Handle the last few bytes of the input array
+ switch(len)
+ {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ };
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
+unsigned int __fastcall hashstrW(const char * key)
+{
+ if (key == NULL) return 0;
+ const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+ char* buf = (char*)alloca(len + 1);
+ for (unsigned i = 0; i <= len ; ++i)
+ buf[i] = key[i << 1];
+ return hash(buf, len);
+}
+
+static int SortLangPackHashesProc(LangPackEntry *arg1,LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+
+ return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1;
+}
+
+static void swapBytes( void* p, size_t iSize )
+{
+ char *head = (char *)p; // here
+ char *tail = head + iSize - 1;
+
+ for (; tail > head; --tail, ++head) {
+ char temp = *head;
+ *head = *tail;
+ *tail = temp;
+ }
+}
+
+static bool EnterMuuid( const char* p, MUUID& result )
+{
+ if ( *p++ != '{' )
+ return false;
+
+ BYTE* d = (BYTE*)&result;
+
+ for ( int nBytes = 0; *p && nBytes < 24; p++ ) {
+ if ( *p == '-' )
+ continue;
+
+ if ( *p == '}' )
+ break;
+
+ if ( !isxdigit( *p ))
+ return false;
+
+ if ( !isxdigit( p[1] ))
+ return false;
+
+ int c = 0;
+ if ( sscanf( p, "%2x", &c ) != 1 )
+ return false;
+
+ *d++ = ( BYTE )c;
+ nBytes++;
+ p++;
+ }
+
+ if ( *p != '}' )
+ return false;
+
+ swapBytes( &result.a, sizeof( result.a ));
+ swapBytes( &result.b, sizeof( result.b ));
+ swapBytes( &result.c, sizeof( result.c ));
+ return true;
+}
+
+static void LoadLangPackFile( FILE* fp, char* line, UINT fileCp )
+{
+ while ( !feof( fp )) {
+ if ( fgets( line, LANGPACK_BUF_SIZE, fp ) == NULL )
+ break;
+
+ if ( IsEmpty(line) || line[0] == ';' || line[0] == 0 )
+ continue;
+
+ rtrim( line );
+
+ if ( line[0] == '#' ) {
+ strlwr( line );
+
+ if ( !memcmp( line+1, "include", 7 )) {
+ TCHAR tszFileName[ MAX_PATH ];
+ TCHAR* fileName = mir_a2t( ltrim( line+9 ));
+ mir_sntprintf( tszFileName, SIZEOF(tszFileName), _T("%s%s"), langPack.filePath, fileName );
+ mir_free( fileName );
+
+ FILE* p = _tfopen( tszFileName, _T("r"));
+ if ( p ) {
+ line[0] = 0;
+ fgets( line, SIZEOF(line), p );
+
+ UINT fileCp = CP_ACP;
+ if (strlen(line) >= 3 && line[0]=='\xef' && line[1]=='\xbb' && line[2]=='\xbf')
+ {
+ fileCp = CP_UTF8;
+ fseek(p, 3, SEEK_SET);
+ }
+ else
+ {
+ fileCp = langPack.defaultANSICp;
+ fseek(p, 0, SEEK_SET);
+ }
+
+ LoadLangPackFile( p, line, fileCp );
+ fclose( p );
+ }
+ }
+ else if ( !memcmp( line+1, "muuid", 5 )) {
+ MUUID t;
+ if ( !EnterMuuid( line+7, t )) {
+ NetlibLogf( NULL, "Invalid MUUID: %s\n", line+7 );
+ continue;
+ }
+
+ LangPackMuuid* pNew = ( LangPackMuuid* )mir_alloc( sizeof( LangPackMuuid ));
+ memcpy( &pNew->muuid, &t, sizeof( t ));
+ pNew->pInfo = NULL;
+ lMuuids.insert( pNew );
+ pCurrentMuuid = pNew;
+ }
+
+ continue;
+ }
+
+ ConvertBackslashes( line, fileCp );
+
+ if ( line[0] == '[' && line[ lstrlenA(line)-1 ] == ']' ) {
+ if ( langPack.entryCount && langPack.entry[ langPack.entryCount-1].local == NULL )
+ langPack.entryCount--;
+
+ char* pszLine = line+1;
+ line[ lstrlenA(line)-1 ] = '\0';
+ if ( ++langPack.entryCount > langPack.entriesAlloced ) {
+ langPack.entriesAlloced += 128;
+ langPack.entry = ( LangPackEntry* )mir_realloc( langPack.entry, sizeof(LangPackEntry)*langPack.entriesAlloced );
+ }
+
+ LangPackEntry* E = &langPack.entry[ langPack.entryCount-1 ];
+ E->englishHash = hashstr(pszLine);
+ E->local = NULL;
+ E->wlocal = NULL;
+ E->pMuuid = pCurrentMuuid;
+ E->pNext = NULL;
+ continue;
+ }
+
+ if ( !langPack.entryCount )
+ continue;
+
+ LangPackEntry* E = &langPack.entry[ langPack.entryCount-1 ];
+ if ( E->local == NULL ) {
+ E->local = mir_strdup( line );
+ if ( fileCp == CP_UTF8 )
+ Utf8DecodeCP( E->local, langPack.defaultANSICp, NULL );
+
+ int iNeeded = MultiByteToWideChar(fileCp, 0, line, -1, 0, 0);
+ E->wlocal = (wchar_t *)mir_alloc((iNeeded+1) * sizeof(wchar_t));
+ MultiByteToWideChar( fileCp, 0, line, -1, E->wlocal, iNeeded );
+ }
+ else {
+ size_t iOldLenA = strlen( E->local );
+ E->local = ( char* )mir_realloc( E->local, iOldLenA + strlen(line) + 2 );
+ strcat( E->local, "\n" );
+ strcat( E->local, line );
+
+ if ( fileCp == CP_UTF8 )
+ Utf8DecodeCP( E->local + iOldLenA + 1, langPack.defaultANSICp, NULL );
+
+ int iNeeded = MultiByteToWideChar( fileCp, 0, line, -1, 0, 0 );
+ size_t iOldLen = wcslen( E->wlocal );
+ E->wlocal = ( wchar_t* )mir_realloc( E->wlocal, ( sizeof(wchar_t) * ( iOldLen + iNeeded + 2)));
+ wcscat( E->wlocal, L"\n" );
+ MultiByteToWideChar( fileCp, 0, line, -1, E->wlocal + iOldLen + 1, iNeeded );
+ }
+ }
+}
+
+static int LoadLangPack(const TCHAR *szLangPack)
+{
+ int startOfLine=0;
+ USHORT langID;
+
+ lstrcpy( langPack.filename, szLangPack );
+ lstrcpy( langPack.filePath, szLangPack );
+ TCHAR* p = _tcsrchr( langPack.filePath, '\\' );
+ if ( p )
+ p[1] = 0;
+
+ FILE *fp = _tfopen(szLangPack,_T("rt"));
+ if ( fp == NULL )
+ return 1;
+
+ char line[ LANGPACK_BUF_SIZE ] = "";
+ fgets( line, SIZEOF(line), fp );
+
+ UINT fileCp = CP_ACP;
+ size_t lineLen = strlen(line);
+ if (lineLen >= 3 && line[0]=='\xef' && line[1]=='\xbb' && line[2]=='\xbf')
+ {
+ fileCp = CP_UTF8;
+ memmove(line, line + 3, lineLen - 2);
+ }
+
+ lrtrim( line );
+ if ( lstrcmpA( line, "Miranda Language Pack Version 1" )) {
+ fclose(fp);
+ return 2;
+ }
+
+ //headers
+ while ( !feof( fp )) {
+ startOfLine = ftell( fp );
+ if ( fgets( line, SIZEOF(line), fp ) == NULL )
+ break;
+
+ lrtrim( line );
+ if( IsEmpty( line ) || line[0]==';' || line[0]==0)
+ continue;
+
+ if ( line[0] == '[' || line[0] == '#' )
+ break;
+
+ char* pszColon = strchr( line,':' );
+ if ( pszColon == NULL ) {
+ fclose( fp );
+ return 3;
+ }
+
+ *pszColon++ = 0;
+ if(!lstrcmpA(line,"Language")) {mir_snprintf(langPack.language,sizeof(langPack.language),"%s",pszColon); lrtrim(langPack.language);}
+ else if(!lstrcmpA(line,"Last-Modified-Using")) {mir_snprintf(langPack.lastModifiedUsing,sizeof(langPack.lastModifiedUsing),"%s",pszColon); lrtrim(langPack.lastModifiedUsing);}
+ else if(!lstrcmpA(line,"Authors")) {mir_snprintf(langPack.authors,sizeof(langPack.authors),"%s",pszColon); lrtrim(langPack.authors);}
+ else if(!lstrcmpA(line,"Author-email")) {mir_snprintf(langPack.authorEmail,sizeof(langPack.authorEmail),"%s",pszColon); lrtrim(langPack.authorEmail);}
+ else if(!lstrcmpA(line, "Locale")) {
+ char szBuf[20], *stopped;
+
+ lrtrim(pszColon + 1);
+ langID = (USHORT)strtol(pszColon, &stopped, 16);
+ langPack.localeID = MAKELCID(langID, 0);
+ GetLocaleInfoA(langPack.localeID, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10);
+ szBuf[5] = 0; // codepages have max. 5 digits
+ langPack.defaultANSICp = atoi(szBuf);
+ if (fileCp == CP_ACP)
+ fileCp = langPack.defaultANSICp;
+ }
+ }
+
+ //body
+ fseek( fp, startOfLine, SEEK_SET );
+ langPack.entriesAlloced = 0;
+
+ LoadLangPackFile( fp, line, fileCp );
+ fclose(fp);
+
+ qsort(langPack.entry,langPack.entryCount,sizeof(LangPackEntry),(int(*)(const void*,const void*))SortLangPackHashesProc);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int SortLangPackHashesProc2(LangPackEntry *arg1,LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+ return 0;
+}
+
+char *LangPackTranslateString(LangPackMuuid* pUuid, const char *szEnglish, const int W)
+{
+ if ( langPack.entryCount == 0 || szEnglish == NULL )
+ return (char*)szEnglish;
+
+ LangPackEntry key,*entry;
+ key.englishHash = W ? hashstrW(szEnglish) : hashstr(szEnglish);
+ entry = (LangPackEntry*)bsearch(&key, langPack.entry, langPack.entryCount, sizeof(LangPackEntry), (int(*)(const void*,const void*))SortLangPackHashesProc2 );
+ if ( entry == NULL )
+ return (char*)szEnglish;
+
+ // try to find the exact match, otherwise the first entry will be returned
+ if ( pUuid ) {
+ for ( LangPackEntry* p = entry->pNext; p != NULL; p = p->pNext ) {
+ if (p->pMuuid == pUuid) {
+ entry = p;
+ break;
+ } } }
+
+ return W ? (char *)entry->wlocal : entry->local;
+}
+
+int LangPackGetDefaultCodePage()
+{
+ return langPack.defaultANSICp;
+}
+
+int LangPackGetDefaultLocale()
+{
+ return (langPack.localeID == 0) ? LOCALE_USER_DEFAULT : langPack.localeID;
+}
+
+TCHAR* LangPackPcharToTchar( const char* pszStr )
+{
+ if ( pszStr == NULL )
+ return NULL;
+
+ #if defined( _UNICODE )
+ { int len = (int)strlen( pszStr );
+ TCHAR* result = ( TCHAR* )alloca(( len+1 )*sizeof( TCHAR ));
+ MultiByteToWideChar( LangPackGetDefaultCodePage(), 0, pszStr, -1, result, len );
+ result[len] = 0;
+ return mir_wstrdup( TranslateW( result ));
+ }
+ #else
+ return mir_strdup( Translate( pszStr ));
+ #endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+LangPackMuuid* __fastcall LangPackLookupUuid( WPARAM wParam )
+{
+ int idx = (wParam >> 16) & 0xFFFF;
+ return ( idx > 0 && idx <= lMuuids.getCount()) ? lMuuids[ idx-1 ] : NULL;
+}
+
+int LangPackMarkPluginLoaded( PLUGININFOEX* pInfo )
+{
+ LangPackMuuid tmp; tmp.muuid = pInfo->uuid;
+ int idx = lMuuids.getIndex( &tmp );
+ if ( idx == -1 )
+ return 0;
+
+ lMuuids[ idx ]->pInfo = pInfo;
+ return (idx+1) << 16;
+}
+
+void LangPackDropUnusedItems( void )
+{
+ if ( langPack.entryCount == 0 )
+ return;
+
+ LangPackEntry *s = langPack.entry+1, *d = s, *pLast = langPack.entry;
+ DWORD dwSavedHash = langPack.entry->englishHash;
+ bool bSortNeeded = false;
+
+ for ( int i=1; i < langPack.entryCount; i++, s++ ) {
+ if ( s->pMuuid != NULL && s->pMuuid->pInfo == NULL )
+ s->pMuuid = NULL;
+
+ if ( s->englishHash != dwSavedHash ) {
+ pLast = d;
+ if ( s != d )
+ *d++ = *s;
+ else
+ d++;
+ dwSavedHash = s->englishHash;
+ }
+ else {
+ bSortNeeded = true;
+ LangPackEntry* p = ( LangPackEntry* )mir_alloc( sizeof( LangPackEntry ));
+ *p = *s;
+ pLast->pNext = p; pLast = p;
+ }
+ }
+
+ if ( bSortNeeded ) {
+ langPack.entryCount = ( int )( d - langPack.entry );
+ qsort(langPack.entry,langPack.entryCount,sizeof(LangPackEntry),(int(*)(const void*,const void*))SortLangPackHashesProc);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int LoadLangPackModule(void)
+{
+ HANDLE hFind;
+ TCHAR szSearch[MAX_PATH];
+ WIN32_FIND_DATA fd;
+
+ bModuleInitialized = TRUE;
+
+ ZeroMemory(&langPack,sizeof(langPack));
+ LoadLangPackServices();
+ pathToAbsoluteT(_T("langpack_*.txt"), szSearch, NULL);
+ hFind = FindFirstFile( szSearch, &fd );
+ if( hFind != INVALID_HANDLE_VALUE ) {
+ pathToAbsoluteT(fd.cFileName, szSearch, NULL);
+ FindClose(hFind);
+ LoadLangPack(szSearch);
+ }
+ return 0;
+}
+
+void UnloadLangPackModule()
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ for ( i=0; i < lMuuids.getCount(); i++ )
+ mir_free( lMuuids[i] );
+ lMuuids.destroy();
+
+ LangPackEntry* p = langPack.entry;
+ for ( i=0; i < langPack.entryCount; i++, p++ ) {
+ if ( p->pNext != NULL ) {
+ for ( LangPackEntry* p1 = p->pNext; p1 != NULL; ) {
+ LangPackEntry* p2 = p1; p1 = p1->pNext;
+ mir_free( p2->local);
+ mir_free( p2->wlocal);
+ mir_free( p2 );
+ }
+ }
+
+ mir_free( p->local );
+ mir_free( p->wlocal );
+ }
+
+ if ( langPack.entryCount ) {
+ mir_free(langPack.entry);
+ langPack.entry=0;
+ langPack.entryCount=0;
+} }
diff --git a/src/modules/langpack/lpservices.cpp b/src/modules/langpack/lpservices.cpp
new file mode 100644
index 0000000000..26586dfda3
--- /dev/null
+++ b/src/modules/langpack/lpservices.cpp
@@ -0,0 +1,168 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#if defined( _UNICODE )
+ #define FLAGS LANG_UNICODE
+#else
+ #define FLAGS 0
+#endif
+
+LangPackMuuid* __fastcall LangPackLookupUuid( WPARAM );
+int LangPackMarkPluginLoaded( PLUGININFOEX* pInfo );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR TranslateString(WPARAM wParam,LPARAM lParam)
+{
+ return (INT_PTR)LangPackTranslateString( LangPackLookupUuid(wParam), (const char *)lParam, (wParam & LANG_UNICODE) ? 1 : 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR TranslateMenu(WPARAM wParam, LPARAM lParam)
+{
+ HMENU hMenu = ( HMENU )wParam;
+ int i;
+ MENUITEMINFO mii;
+ TCHAR str[256];
+ LangPackMuuid* uuid = LangPackLookupUuid( lParam );
+
+ mii.cbSize = MENUITEMINFO_V4_SIZE;
+ for ( i = GetMenuItemCount( hMenu )-1; i >= 0; i--) {
+ mii.fMask = MIIM_TYPE|MIIM_SUBMENU;
+ mii.dwTypeData = ( TCHAR* )str;
+ mii.cch = SIZEOF(str);
+ GetMenuItemInfo(hMenu, i, TRUE, &mii);
+
+ if ( mii.cch && mii.dwTypeData ) {
+ TCHAR* result = ( TCHAR* )LangPackTranslateString( uuid, ( const char* )mii.dwTypeData, FLAGS );
+ if ( result != mii.dwTypeData ) {
+ mii.dwTypeData = result;
+ mii.fMask = MIIM_TYPE;
+ SetMenuItemInfo( hMenu, i, TRUE, &mii );
+ } }
+
+ if ( mii.hSubMenu != NULL ) TranslateMenu(( WPARAM )mii.hSubMenu, lParam );
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void TranslateWindow( LangPackMuuid* pUuid, HWND hwnd )
+{
+ TCHAR title[2048];
+ GetWindowText(hwnd, title, SIZEOF( title ));
+ {
+ TCHAR* result = ( TCHAR* )LangPackTranslateString( pUuid, ( const char* )title, FLAGS );
+ if ( result != title )
+ SetWindowText(hwnd, result );
+} }
+
+static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd,LPARAM lParam)
+{
+ LANGPACKTRANSLATEDIALOG *lptd = (LANGPACKTRANSLATEDIALOG*)lParam;
+ TCHAR szClass[32];
+ int i,id = GetDlgCtrlID( hwnd );
+
+ if ( lptd->ignoreControls != NULL )
+ for ( i=0; lptd->ignoreControls[i]; i++ )
+ if ( lptd->ignoreControls[i] == id )
+ return TRUE;
+
+ LangPackMuuid* uuid = LangPackLookupUuid( lptd->flags );
+
+ GetClassName( hwnd, szClass, SIZEOF(szClass));
+ if(!lstrcmpi(szClass,_T("static")) || !lstrcmpi(szClass,_T("hyperlink")) || !lstrcmpi(szClass,_T("button")) || !lstrcmpi(szClass,_T("MButtonClass")) || !lstrcmpi(szClass,_T("MHeaderbarCtrl")))
+ TranslateWindow( uuid, hwnd );
+ else if ( !lstrcmpi( szClass,_T("edit"))) {
+ if( lptd->flags & LPTDF_NOIGNOREEDIT || GetWindowLongPtr(hwnd,GWL_STYLE) & ES_READONLY )
+ TranslateWindow( uuid, hwnd );
+ }
+ return TRUE;
+}
+
+static INT_PTR TranslateDialog(WPARAM wParam, LPARAM lParam)
+{
+ LANGPACKTRANSLATEDIALOG *lptd = (LANGPACKTRANSLATEDIALOG*)lParam;
+ if ( lptd == NULL || lptd->cbSize != sizeof(LANGPACKTRANSLATEDIALOG))
+ return 1;
+
+ if ( !( lptd->flags & LPTDF_NOTITLE ))
+ TranslateWindow( LangPackLookupUuid( lptd->flags ), lptd->hwndDlg );
+
+ EnumChildWindows( lptd->hwndDlg, TranslateDialogEnumProc, lParam );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR LPRegister(WPARAM wParam, LPARAM lParam)
+{
+ *( int* )wParam = LangPackMarkPluginLoaded(( PLUGININFOEX* )lParam );
+ return 0;
+}
+
+static INT_PTR GetDefaultCodePage(WPARAM,LPARAM)
+{
+ return LangPackGetDefaultCodePage();
+}
+
+static INT_PTR GetDefaultLocale(WPARAM, LPARAM)
+{
+ return LangPackGetDefaultLocale();
+}
+
+static INT_PTR PcharToTchar(WPARAM wParam, LPARAM lParam)
+{
+ char* pszStr = ( char* )lParam;
+ if ( pszStr == NULL )
+ return NULL;
+
+ LangPackMuuid* uuid = LangPackLookupUuid( wParam );
+
+ #if defined( _UNICODE )
+ { int len = (int)strlen( pszStr );
+ TCHAR* result = ( TCHAR* )alloca(( len+1 )*sizeof( TCHAR ));
+ MultiByteToWideChar( LangPackGetDefaultCodePage(), 0, pszStr, -1, result, len );
+ result[len] = 0;
+ return ( INT_PTR )mir_wstrdup(( wchar_t* )LangPackTranslateString( uuid, ( char* )result, 1 ));
+ }
+ #else
+ return ( INT_PTR )mir_strdup( LangPackTranslateString( uuid, pszStr, 0 ));
+ #endif
+}
+
+int LoadLangPackServices(void)
+{
+ CreateServiceFunction(MS_LANGPACK_TRANSLATESTRING,TranslateString);
+ CreateServiceFunction(MS_LANGPACK_TRANSLATEMENU,TranslateMenu);
+ CreateServiceFunction(MS_LANGPACK_TRANSLATEDIALOG,TranslateDialog);
+ CreateServiceFunction(MS_LANGPACK_GETCODEPAGE,GetDefaultCodePage);
+ CreateServiceFunction(MS_LANGPACK_GETLOCALE,GetDefaultLocale);
+ CreateServiceFunction(MS_LANGPACK_PCHARTOTCHAR,PcharToTchar);
+ CreateServiceFunction(MS_LANGPACK_REGISTER,LPRegister);
+ return 0;
+}
+
diff --git a/src/modules/netlib/netlib.cpp b/src/modules/netlib/netlib.cpp
new file mode 100644
index 0000000000..000639474f
--- /dev/null
+++ b/src/modules/netlib/netlib.cpp
@@ -0,0 +1,640 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+static BOOL bModuleInitialized = FALSE;
+
+HANDLE hConnectionHeaderMutex, hConnectionOpenMutex;
+DWORD g_LastConnectionTick;
+int connectionTimeout;
+HANDLE hSendEvent=NULL, hRecvEvent=NULL;
+
+typedef BOOL (WINAPI *tGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+
+static int CompareNetlibUser(const NetlibUser* p1, const NetlibUser* p2)
+{
+ return strcmp(p1->user.szSettingsModule, p2->user.szSettingsModule);
+}
+
+LIST<NetlibUser> netlibUser(5, CompareNetlibUser);
+CRITICAL_SECTION csNetlibUser;
+
+SSL_API si;
+
+void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings)
+{
+ mir_free(settings->szIncomingPorts);
+ mir_free(settings->szOutgoingPorts);
+ mir_free(settings->szProxyAuthPassword);
+ mir_free(settings->szProxyAuthUser);
+ mir_free(settings->szProxyServer);
+}
+
+void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ nlncs->dwOwningThreadId= 0;
+ nlncs->lockCount=0;
+ nlncs->hMutex=CreateMutex(NULL,FALSE,NULL);
+}
+
+void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ CloseHandle(nlncs->hMutex);
+}
+
+int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which)
+{
+ struct NetlibNestedCriticalSection *nlncs;
+ DWORD dwCurrentThreadId=GetCurrentThreadId();
+
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ if(nlc==NULL || nlc->handleType!=NLH_CONNECTION) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ nlncs = (which == NLNCS_SEND) ? &nlc->ncsSend : &nlc->ncsRecv;
+ if(nlncs->lockCount && nlncs->dwOwningThreadId==dwCurrentThreadId) {
+ nlncs->lockCount++;
+ ReleaseMutex(hConnectionHeaderMutex);
+ return 1;
+ }
+ InterlockedIncrement(&nlc->dontCloseNow);
+ ResetEvent(nlc->hOkToCloseEvent);
+ ReleaseMutex(hConnectionHeaderMutex);
+ WaitForSingleObject(nlncs->hMutex,INFINITE);
+ nlncs->dwOwningThreadId=dwCurrentThreadId;
+ nlncs->lockCount=1;
+ if(InterlockedDecrement(&nlc->dontCloseNow)==0)
+ SetEvent(nlc->hOkToCloseEvent);
+ return 1;
+}
+
+void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs)
+{
+ if(--nlncs->lockCount==0) {
+ nlncs->dwOwningThreadId=0;
+ ReleaseMutex(nlncs->hMutex);
+ }
+}
+
+static INT_PTR GetNetlibUserSettingInt(const char *szUserModule,const char *szSetting,int defValue)
+{
+ DBVARIANT dbv;
+ if(DBGetContactSetting(NULL,szUserModule,szSetting,&dbv)
+ && DBGetContactSetting(NULL,"Netlib",szSetting,&dbv))
+ return defValue;
+ if(dbv.type==DBVT_BYTE) return dbv.bVal;
+ if(dbv.type==DBVT_WORD) return dbv.wVal;
+ return dbv.dVal;
+}
+
+static char *GetNetlibUserSettingString(const char *szUserModule,const char *szSetting,int decode)
+{
+ DBVARIANT dbv;
+ if(DBGetContactSettingString(NULL,szUserModule,szSetting,&dbv)
+ && DBGetContactSettingString(NULL,"Netlib",szSetting,&dbv)) {
+ return NULL;
+ }
+ else {
+ char *szRet;
+ if(decode) CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ szRet=mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ if(szRet==NULL) SetLastError(ERROR_OUTOFMEMORY);
+ return szRet;
+ }
+}
+
+static INT_PTR NetlibRegisterUser(WPARAM,LPARAM lParam)
+{
+ NETLIBUSER *nlu=(NETLIBUSER*)lParam;
+ struct NetlibUser *thisUser;
+
+ if(nlu==NULL || nlu->cbSize!=sizeof(NETLIBUSER) || nlu->szSettingsModule==NULL
+ || (!(nlu->flags&NUF_NOOPTIONS) && nlu->szDescriptiveName==NULL)
+ || (nlu->flags&NUF_HTTPGATEWAY && (nlu->pfnHttpGatewayInit==NULL))) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ thisUser = (struct NetlibUser*)mir_calloc(sizeof(struct NetlibUser));
+ thisUser->handleType = NLH_USER;
+ thisUser->user = *nlu;
+
+ EnterCriticalSection(&csNetlibUser);
+ if (netlibUser.getIndex(thisUser) >= 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ mir_free(thisUser);
+ SetLastError(ERROR_DUP_NAME);
+ return 0;
+ }
+ LeaveCriticalSection(&csNetlibUser);
+
+ if (nlu->szDescriptiveName) {
+ thisUser->user.ptszDescriptiveName = (thisUser->user.flags&NUF_UNICODE ? mir_u2t((WCHAR*)nlu->ptszDescriptiveName) : mir_a2t(nlu->szDescriptiveName));
+ }
+ if((thisUser->user.szSettingsModule=mir_strdup(nlu->szSettingsModule))==NULL
+ || (nlu->szDescriptiveName && thisUser->user.ptszDescriptiveName ==NULL)
+ || (nlu->szHttpGatewayUserAgent && (thisUser->user.szHttpGatewayUserAgent=mir_strdup(nlu->szHttpGatewayUserAgent))==NULL))
+ {
+ mir_free(thisUser);
+ SetLastError(ERROR_OUTOFMEMORY);
+ return 0;
+ }
+ if (nlu->szHttpGatewayHello)
+ thisUser->user.szHttpGatewayHello=mir_strdup(nlu->szHttpGatewayHello);
+ else
+ thisUser->user.szHttpGatewayHello=NULL;
+
+ thisUser->settings.cbSize=sizeof(NETLIBUSERSETTINGS);
+ thisUser->settings.useProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxy",0);
+ thisUser->settings.proxyType=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyType",PROXYTYPE_SOCKS5);
+ if(thisUser->user.flags&NUF_NOHTTPSOPTION && thisUser->settings.proxyType==PROXYTYPE_HTTPS)
+ thisUser->settings.proxyType=PROXYTYPE_HTTP;
+ if(!(thisUser->user.flags&(NUF_HTTPCONNS|NUF_HTTPGATEWAY)) && thisUser->settings.proxyType==PROXYTYPE_HTTP) {
+ thisUser->settings.useProxy=0;
+ thisUser->settings.proxyType=PROXYTYPE_SOCKS5;
+ }
+ thisUser->settings.szProxyServer=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyServer",0);
+ thisUser->settings.wProxyPort=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyPort",1080);
+ thisUser->settings.useProxyAuth=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxyAuth",0);
+ thisUser->settings.szProxyAuthUser=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthUser",0);
+ thisUser->settings.szProxyAuthPassword=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthPassword",1);
+ thisUser->settings.dnsThroughProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLDnsThroughProxy",1);
+ thisUser->settings.specifyIncomingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyIncomingPorts",0);
+ thisUser->settings.szIncomingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLIncomingPorts",0);
+ thisUser->settings.specifyOutgoingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyOutgoingPorts",0);
+ thisUser->settings.szOutgoingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLOutgoingPorts",0);
+ thisUser->settings.enableUPnP=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLEnableUPnP",1); //default to on
+ thisUser->settings.validateSSL=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLValidateSSL",0);
+
+ thisUser->toLog=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLlog",1);
+
+ EnterCriticalSection(&csNetlibUser);
+ netlibUser.insert(thisUser);
+ LeaveCriticalSection(&csNetlibUser);
+ return (INT_PTR)thisUser;
+}
+
+static INT_PTR NetlibGetUserSettings(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam;
+ struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+
+ if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ *nlus=nlu->settings;
+ return 1;
+}
+
+static INT_PTR NetlibSetUserSettings(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam;
+ struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+
+ if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ NetlibSaveUserSettingsStruct(nlu->user.szSettingsModule,nlus);
+ return 1;
+}
+
+void NetlibDoClose(NetlibConnection *nlc, bool noShutdown)
+{
+ if (nlc->s == INVALID_SOCKET) return;
+
+ NetlibLogf(nlc->nlu, "(%p:%u) Connection closed internal", nlc, nlc->s);
+ if (nlc->hSsl)
+ {
+ if (!noShutdown) si.shutdown(nlc->hSsl);
+ si.sfree(nlc->hSsl);
+ nlc->hSsl = NULL;
+ }
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+}
+
+INT_PTR NetlibCloseHandle(WPARAM wParam, LPARAM)
+{
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_USER:
+ { struct NetlibUser *nlu=(struct NetlibUser*)wParam;
+ int i;
+
+ EnterCriticalSection(&csNetlibUser);
+ i = netlibUser.getIndex(nlu);
+ if (i >= 0) netlibUser.remove(i);
+ LeaveCriticalSection(&csNetlibUser);
+
+ NetlibFreeUserSettingsStruct(&nlu->settings);
+ mir_free(nlu->user.szSettingsModule);
+ mir_free(nlu->user.szDescriptiveName);
+ mir_free(nlu->user.szHttpGatewayHello);
+ mir_free(nlu->user.szHttpGatewayUserAgent);
+ mir_free(nlu->szStickyHeaders);
+ break;
+ }
+ case NLH_CONNECTION:
+ { struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ HANDLE waitHandles[4];
+ DWORD waitResult;
+
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ if (nlc->usingHttpGateway)
+ {
+ HttpGatewayRemovePacket(nlc, -1);
+ }
+ else
+ {
+ if(nlc->s != INVALID_SOCKET) {
+ NetlibDoClose(nlc);
+ }
+ if (nlc->s2 != INVALID_SOCKET) closesocket(nlc->s2);
+ nlc->s2 = INVALID_SOCKET;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ waitHandles[0]=hConnectionHeaderMutex;
+ waitHandles[1]=nlc->hOkToCloseEvent;
+ waitHandles[2]=nlc->ncsRecv.hMutex;
+ waitHandles[3]=nlc->ncsSend.hMutex;
+ waitResult=WaitForMultipleObjects( SIZEOF(waitHandles),waitHandles,TRUE,INFINITE);
+ if(waitResult<WAIT_OBJECT_0 || waitResult >= WAIT_OBJECT_0 + SIZEOF(waitHandles)) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ SetLastError(ERROR_INVALID_PARAMETER); //already been closed
+ return 0;
+ }
+ nlc->handleType=0;
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->dataBuffer);
+ mir_free((char*)nlc->nloc.szHost);
+ mir_free(nlc->szNewUrl);
+ mir_free(nlc->szProxyServer);
+ NetlibDeleteNestedCS(&nlc->ncsRecv);
+ NetlibDeleteNestedCS(&nlc->ncsSend);
+ CloseHandle(nlc->hOkToCloseEvent);
+ DeleteCriticalSection(&nlc->csHttpSequenceNums);
+ ReleaseMutex(hConnectionHeaderMutex);
+ NetlibLogf(nlc->nlu,"(%p:%u) Connection closed",nlc,nlc->s);
+ break;
+ }
+ case NLH_BOUNDPORT:
+ return NetlibFreeBoundPort((struct NetlibBoundPort*)wParam);
+ case NLH_PACKETRECVER:
+ { struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam;
+ mir_free(nlpr->packetRecver.buffer);
+ break;
+ }
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ mir_free((void*)wParam);
+ return 1;
+}
+
+static INT_PTR NetlibGetSocket(WPARAM wParam, LPARAM)
+{
+ SOCKET s;
+ if(wParam==0) {
+ s=INVALID_SOCKET;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else {
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_CONNECTION:
+ s=((struct NetlibConnection*)wParam)->s;
+ break;
+ case NLH_BOUNDPORT:
+ s=((struct NetlibBoundPort*)wParam)->s;
+ break;
+ default:
+ s=INVALID_SOCKET;
+ SetLastError(ERROR_INVALID_PARAMETER);
+ break;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ }
+ return s;
+}
+
+INT_PTR NetlibShutdown(WPARAM wParam, LPARAM)
+{
+ if (wParam)
+ {
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ switch(GetNetlibHandleType(wParam)) {
+ case NLH_CONNECTION:
+ {
+ struct NetlibConnection* nlc = (struct NetlibConnection*)wParam;
+ if (nlc->hSsl) si.shutdown(nlc->hSsl);
+ if (nlc->s != INVALID_SOCKET) shutdown(nlc->s, 2);
+ if (nlc->s2 != INVALID_SOCKET) shutdown(nlc->s2, 2);
+ nlc->termRequested = true;
+ }
+ break;
+ case NLH_BOUNDPORT:
+ {
+ struct NetlibBoundPort* nlb = (struct NetlibBoundPort*)wParam;
+ if (nlb->s != INVALID_SOCKET) shutdown(nlb->s, 2);
+ }
+ break;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ }
+ return 0;
+}
+
+static const char szHexDigits[]="0123456789ABCDEF";
+INT_PTR NetlibHttpUrlEncode(WPARAM,LPARAM lParam)
+{
+ unsigned char *szOutput,*szInput=(unsigned char*)lParam;
+ unsigned char *pszIn,*pszOut;
+ int outputLen;
+
+ if(szInput==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (INT_PTR)(char*)NULL;
+ }
+ for(outputLen=0,pszIn=szInput;*pszIn;pszIn++) {
+ if ( (48 <= *pszIn && *pszIn <= 57) ||//0-9
+ (65 <= *pszIn && *pszIn <= 90) ||//ABC...XYZ
+ (97 <= *pszIn && *pszIn <= 122) ||//abc...xyz
+ *pszIn == '-' || *pszIn == '_' || *pszIn == '.' || *pszIn == ' ') outputLen++;
+ else outputLen+=3;
+ }
+ szOutput=(unsigned char*)HeapAlloc(GetProcessHeap(),0,outputLen+1);
+ if(szOutput==NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return (INT_PTR)(unsigned char*)NULL;
+ }
+ for(pszOut=szOutput,pszIn=szInput;*pszIn;pszIn++) {
+ if ( (48 <= *pszIn && *pszIn <= 57) ||
+ (65 <= *pszIn && *pszIn <= 90) ||
+ (97 <= *pszIn && *pszIn <= 122) ||
+ *pszIn == '-' || *pszIn == '_' || *pszIn == '.') *pszOut++=*pszIn;
+ else if(*pszIn==' ') *pszOut++='+';
+ else {
+ *pszOut++='%';
+ *pszOut++=szHexDigits[*pszIn>>4];
+ *pszOut++=szHexDigits[*pszIn&0xF];
+ }
+ }
+ *pszOut='\0';
+ return (INT_PTR)szOutput;
+}
+
+static const char base64chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+INT_PTR NetlibBase64Encode(WPARAM, LPARAM lParam)
+{
+ NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
+ int iIn;
+ char *pszOut;
+ PBYTE pbIn;
+
+ if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlb64->cchEncoded<Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded)) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ nlb64->cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded);
+ for(iIn=0,pbIn=nlb64->pbDecoded,pszOut=nlb64->pszEncoded;iIn<nlb64->cbDecoded;iIn+=3,pbIn+=3,pszOut+=4) {
+ pszOut[0]=base64chars[pbIn[0]>>2];
+ if(nlb64->cbDecoded-iIn==1) {
+ pszOut[1]=base64chars[(pbIn[0]&3)<<4];
+ pszOut[2]='=';
+ pszOut[3]='=';
+ pszOut+=4;
+ break;
+ }
+ pszOut[1]=base64chars[((pbIn[0]&3)<<4)|(pbIn[1]>>4)];
+ if(nlb64->cbDecoded-iIn==2) {
+ pszOut[2]=base64chars[(pbIn[1]&0xF)<<2];
+ pszOut[3]='=';
+ pszOut+=4;
+ break;
+ }
+ pszOut[2]=base64chars[((pbIn[1]&0xF)<<2)|(pbIn[2]>>6)];
+ pszOut[3]=base64chars[pbIn[2]&0x3F];
+ }
+ pszOut[0]='\0';
+ return 1;
+}
+
+static BYTE Base64CharToInt(char c)
+{
+ if(c>='A' && c<='Z') return c-'A';
+ if(c>='a' && c<='z') return c-'a'+26;
+ if(c>='0' && c<='9') return c-'0'+52;
+ if(c=='+') return 62;
+ if(c=='/') return 63;
+ if(c=='=') return 64;
+ return 255;
+}
+
+INT_PTR NetlibBase64Decode(WPARAM, LPARAM lParam)
+{
+ NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam;
+ char *pszIn;
+ PBYTE pbOut;
+ BYTE b1,b2,b3,b4;
+ int iIn;
+
+ if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlb64->cchEncoded&3) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ if(nlb64->cbDecoded<Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded)) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ nlb64->cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded);
+ for(iIn=0,pszIn=nlb64->pszEncoded,pbOut=nlb64->pbDecoded;iIn<nlb64->cchEncoded;iIn+=4,pszIn+=4,pbOut+=3) {
+ b1=Base64CharToInt(pszIn[0]);
+ b2=Base64CharToInt(pszIn[1]);
+ b3=Base64CharToInt(pszIn[2]);
+ b4=Base64CharToInt(pszIn[3]);
+ if(b1==255 || b1==64 || b2==255 || b2==64 || b3==255 || b4==255) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ pbOut[0]=(b1<<2)|(b2>>4);
+ if(b3==64) {nlb64->cbDecoded-=2; break;}
+ pbOut[1]=(b2<<4)|(b3>>2);
+ if(b4==64) {nlb64->cbDecoded--; break;}
+ pbOut[2]=b4|(b3<<6);
+ }
+ return 1;
+}
+
+void UnloadNetlibModule(void)
+{
+ if (!bModuleInitialized) return;
+
+ if (hConnectionHeaderMutex != NULL)
+ {
+ int i;
+
+ NetlibUnloadIeProxy();
+ NetlibSecurityDestroy();
+ NetlibUPnPDestroy();
+ NetlibLogShutdown();
+
+ DestroyHookableEvent(hRecvEvent); hRecvEvent = NULL;
+ DestroyHookableEvent(hSendEvent); hSendEvent = NULL;
+
+ for (i = netlibUser.getCount(); i > 0; i--)
+ NetlibCloseHandle((WPARAM)netlibUser[i-1], 0);
+
+ netlibUser.destroy();
+
+ CloseHandle(hConnectionHeaderMutex);
+ if (hConnectionOpenMutex) CloseHandle(hConnectionOpenMutex);
+ DeleteCriticalSection(&csNetlibUser);
+ WSACleanup();
+ }
+}
+
+int LoadNetlibModule(void)
+{
+ WSADATA wsadata;
+
+ bModuleInitialized = TRUE;
+
+ WSAStartup(MAKEWORD(2,2), &wsadata);
+
+ HookEvent(ME_OPT_INITIALISE,NetlibOptInitialise);
+
+ InitializeCriticalSection(&csNetlibUser);
+ hConnectionHeaderMutex=CreateMutex(NULL,FALSE,NULL);
+ NetlibLogInit();
+
+ connectionTimeout = 0;
+
+ OSVERSIONINFOEX osvi = {0};
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (GetVersionEx((LPOSVERSIONINFO)&osvi))
+ {
+ // Connection limiting was introduced in Windows XP SP2 and later and set to 10 / sec
+ if (osvi.dwMajorVersion == 5 && ((osvi.dwMinorVersion == 1 && osvi.wServicePackMajor >= 2) || osvi.dwMinorVersion > 1))
+ connectionTimeout = 150;
+ // Connection limiting has limits based on addition Windows Vista pre SP2
+ else if (osvi.dwMajorVersion == 6 && osvi.wServicePackMajor < 2)
+ {
+ DWORD dwType = 0;
+ tGetProductInfo pGetProductInfo = (tGetProductInfo) GetProcAddress(GetModuleHandleA("kernel32"), "GetProductInfo");
+ if (pGetProductInfo != NULL) pGetProductInfo(6, 0, 0, 0, &dwType);
+ switch( dwType )
+ {
+ case 0x01: // Vista Ultimate edition have connection limit of 25 / sec - plenty for Miranda
+ case 0x1c:
+ break;
+
+ case 0x02: // Vista Home Basic edition have connection limit of 2 / sec
+ case 0x05:
+ connectionTimeout = 1000;
+ break;
+
+ default: // all other editions have connection limit of 10 / sec
+ connectionTimeout = 150;
+ break;
+ }
+ }
+ // Connection limiting is disabled by default and is controlled by registry setting in Windows Vista SP2 and later
+ else if (osvi.dwMajorVersion >= 6)
+ {
+ static const char keyn[] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
+ static const char valn[] = "EnableConnectionRateLimiting";
+
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyn, 0, KEY_QUERY_VALUE, &hSettings) == ERROR_SUCCESS)
+ {
+ DWORD tValueLen, enabled;
+ tValueLen = sizeof(enabled);
+ if (RegQueryValueExA(hSettings, valn, NULL, NULL, (BYTE*)&enabled, &tValueLen) == ERROR_SUCCESS && enabled)
+ connectionTimeout = 150; // if enabled limit is set to 10 / sec
+ RegCloseKey(hSettings);
+ }
+
+ }
+ }
+
+ hConnectionOpenMutex = connectionTimeout ? CreateMutex(NULL,FALSE,NULL) : NULL;
+ g_LastConnectionTick = GetTickCount();
+
+ CreateServiceFunction(MS_NETLIB_REGISTERUSER,NetlibRegisterUser);
+ CreateServiceFunction(MS_NETLIB_GETUSERSETTINGS,NetlibGetUserSettings);
+ CreateServiceFunction(MS_NETLIB_SETUSERSETTINGS,NetlibSetUserSettings);
+ CreateServiceFunction(MS_NETLIB_CLOSEHANDLE,NetlibCloseHandle);
+ CreateServiceFunction(MS_NETLIB_BINDPORT,NetlibBindPort);
+ CreateServiceFunction(MS_NETLIB_OPENCONNECTION,NetlibOpenConnection);
+ CreateServiceFunction(MS_NETLIB_SETHTTPPROXYINFO,NetlibHttpGatewaySetInfo);
+ CreateServiceFunction(MS_NETLIB_SETSTICKYHEADERS,NetlibHttpSetSticky);
+ CreateServiceFunction(MS_NETLIB_GETSOCKET,NetlibGetSocket);
+ CreateServiceFunction(MS_NETLIB_URLENCODE,NetlibHttpUrlEncode);
+ CreateServiceFunction(MS_NETLIB_BASE64ENCODE,NetlibBase64Encode);
+ CreateServiceFunction(MS_NETLIB_BASE64DECODE,NetlibBase64Decode);
+ CreateServiceFunction(MS_NETLIB_SENDHTTPREQUEST,NetlibHttpSendRequest);
+ CreateServiceFunction(MS_NETLIB_RECVHTTPHEADERS,NetlibHttpRecvHeaders);
+ CreateServiceFunction(MS_NETLIB_FREEHTTPREQUESTSTRUCT,NetlibHttpFreeRequestStruct);
+ CreateServiceFunction(MS_NETLIB_HTTPTRANSACTION,NetlibHttpTransaction);
+ CreateServiceFunction(MS_NETLIB_SEND,NetlibSend);
+ CreateServiceFunction(MS_NETLIB_RECV,NetlibRecv);
+ CreateServiceFunction(MS_NETLIB_SELECT,NetlibSelect);
+ CreateServiceFunction(MS_NETLIB_SELECTEX,NetlibSelectEx);
+ CreateServiceFunction(MS_NETLIB_SHUTDOWN,NetlibShutdown);
+ CreateServiceFunction(MS_NETLIB_CREATEPACKETRECVER,NetlibPacketRecverCreate);
+ CreateServiceFunction(MS_NETLIB_GETMOREPACKETS,NetlibPacketRecverGetMore);
+ CreateServiceFunction(MS_NETLIB_SETPOLLINGTIMEOUT,NetlibHttpSetPollingTimeout);
+ CreateServiceFunction(MS_NETLIB_STARTSSL,NetlibStartSsl);
+
+ hRecvEvent = CreateHookableEvent(ME_NETLIB_FASTRECV);
+ hSendEvent = CreateHookableEvent(ME_NETLIB_FASTSEND);
+
+ NetlibUPnPInit();
+ NetlibSecurityInit();
+ NetlibLoadIeProxy();
+
+ return 0;
+}
+
+void NetlibInitSsl(void)
+{
+ mir_getSI(&si);
+}
diff --git a/src/modules/netlib/netlib.h b/src/modules/netlib/netlib.h
new file mode 100644
index 0000000000..575e77f18c
--- /dev/null
+++ b/src/modules/netlib/netlib.h
@@ -0,0 +1,209 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#define GetNetlibHandleType(h) (h?*(int*)h:NLH_INVALID)
+#define NLH_INVALID 0
+#define NLH_USER 'USER'
+#define NLH_CONNECTION 'CONN'
+#define NLH_BOUNDPORT 'BIND'
+#define NLH_PACKETRECVER 'PCKT'
+
+struct NetlibUser
+{
+ int handleType;
+ NETLIBUSER user;
+ NETLIBUSERSETTINGS settings;
+ char * szStickyHeaders;
+ int toLog;
+ int inportnum;
+ int outportnum;
+};
+
+struct NetlibNestedCriticalSection
+{
+ HANDLE hMutex;
+ DWORD dwOwningThreadId;
+ int lockCount;
+};
+
+struct NetlibHTTPProxyPacketQueue
+{
+ struct NetlibHTTPProxyPacketQueue *next;
+ PBYTE dataBuffer;
+ int dataBufferLen;
+};
+
+struct NetlibConnection
+{
+ int handleType;
+ SOCKET s, s2;
+ bool usingHttpGateway;
+ bool usingDirectHttpGateway;
+ bool proxyAuthNeeded;
+ bool dnsThroughProxy;
+ bool termRequested;
+ struct NetlibUser *nlu;
+ NETLIBHTTPPROXYINFO nlhpi;
+ PBYTE dataBuffer;
+ int dataBufferLen;
+ CRITICAL_SECTION csHttpSequenceNums;
+ HANDLE hOkToCloseEvent;
+ LONG dontCloseNow;
+ struct NetlibNestedCriticalSection ncsSend,ncsRecv;
+ HSSL hSsl;
+ struct NetlibHTTPProxyPacketQueue * pHttpProxyPacketQueue;
+ char *szNewUrl;
+ char *szProxyServer;
+ WORD wProxyPort;
+ int proxyType;
+ int pollingTimeout;
+ unsigned lastPost;
+ NETLIBOPENCONNECTION nloc;
+};
+
+struct NetlibBoundPort {
+ int handleType;
+ SOCKET s;
+ WORD wPort;
+ WORD wExPort;
+ struct NetlibUser *nlu;
+ NETLIBNEWCONNECTIONPROC_V2 pfnNewConnectionV2;
+ HANDLE hThread;
+ void *pExtra;
+};
+
+struct NetlibPacketRecver {
+ int handleType;
+ struct NetlibConnection *nlc;
+ NETLIBPACKETRECVER packetRecver;
+};
+
+//netlib.c
+void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings);
+void NetlibDoClose(NetlibConnection *nlc, bool noShutdown = false);
+INT_PTR NetlibCloseHandle(WPARAM wParam,LPARAM lParam);
+void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs);
+void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs);
+#define NLNCS_SEND 0
+#define NLNCS_RECV 1
+int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which);
+void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs);
+INT_PTR NetlibBase64Encode(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibBase64Decode(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpUrlEncode(WPARAM wParam,LPARAM lParam);
+
+extern CRITICAL_SECTION csNetlibUser;
+extern LIST<NetlibUser> netlibUser;
+
+//netlibautoproxy.c
+void NetlibLoadIeProxy(void);
+void NetlibUnloadIeProxy(void);
+char* NetlibGetIeProxy(char *szUrl);
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps);
+
+//netlibbind.c
+int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp);
+INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam);
+bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn);
+
+//netlibhttp.c
+INT_PTR NetlibHttpSendRequest(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpFreeRequestStruct(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpTransaction(WPARAM wParam,LPARAM lParam);
+void NetlibHttpSetLastErrorUsingHttpResult(int result);
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection* nlc, DWORD hflags, DWORD dflags, bool isConnect = false);
+void NetlibConnFromUrl(const char* szUrl, bool secur, NETLIBOPENCONNECTION &nloc);
+
+//netlibhttpproxy.c
+int NetlibInitHttpConnection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc);
+int NetlibHttpGatewayRecv(struct NetlibConnection *nlc,char *buf,int len,int flags);
+int NetlibHttpGatewayPost(struct NetlibConnection *nlc,const char *buf,int len,int flags);
+void HttpGatewayRemovePacket(NetlibConnection *nlc, int pck);
+
+INT_PTR NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpSetPollingTimeout(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam);
+
+//netliblog.c
+void NetlibLogShowOptions(void);
+void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags);
+void NetlibLogf(NetlibUser* nlu, const char *fmt, ...);
+void NetlibLogInit(void);
+void NetlibLogShutdown(void);
+
+//netlibopenconn.c
+DWORD DnsLookup(struct NetlibUser *nlu,const char *szHost);
+int WaitUntilReadable(SOCKET s,DWORD dwTimeout, bool check = false);
+int WaitUntilWritable(SOCKET s,DWORD dwTimeout);
+bool NetlibDoConnect(NetlibConnection *nlc);
+bool NetlibReconnect(NetlibConnection *nlc);
+INT_PTR NetlibOpenConnection(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibStartSsl(WPARAM wParam, LPARAM lParam);
+
+//netlibopts.c
+int NetlibOptInitialise(WPARAM wParam,LPARAM lParam);
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings);
+
+//netlibpktrecver.c
+INT_PTR NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam);
+
+//netlibsock.c
+#define NL_SELECT_READ 0x0001
+#define NL_SELECT_WRITE 0x0002
+#define NL_SELECT_ALL (NL_SELECT_READ+NL_SELECT_WRITE)
+
+INT_PTR NetlibSend(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibSelect(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibSelectEx(WPARAM wParam,LPARAM lParam);
+INT_PTR NetlibShutdown(WPARAM wParam,LPARAM lParam);
+
+//netlibupnp.c
+bool NetlibUPnPAddPortMapping(WORD intport, char *proto,
+ WORD *extport, DWORD *extip, bool search);
+void NetlibUPnPDeletePortMapping(WORD extport, char* proto);
+void NetlibUPnPCleanup(void*);
+void NetlibUPnPInit(void);
+void NetlibUPnPDestroy(void);
+
+//netlibsecurity.c
+void NetlibSecurityInit(void);
+void NetlibSecurityDestroy(void);
+void NetlibDestroySecurityProvider(HANDLE hSecurity);
+HANDLE NetlibInitSecurityProvider(const TCHAR* szProvider, const TCHAR* szPrincipal);
+#ifdef UNICODE
+HANDLE NetlibInitSecurityProvider(const char* szProvider, const char* szPrincipal);
+#endif
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const TCHAR* login, const TCHAR* psw,
+ bool http, unsigned& complete);
+
+
+static __inline INT_PTR NLSend(struct NetlibConnection *nlc,const char *buf,int len,int flags) {
+ NETLIBBUFFER nlb={(char*)buf,len,flags};
+ return NetlibSend((WPARAM)nlc,(LPARAM)&nlb);
+}
+static __inline INT_PTR NLRecv(struct NetlibConnection *nlc,char *buf,int len,int flags) {
+ NETLIBBUFFER nlb={buf,len,flags};
+ return NetlibRecv((WPARAM)nlc,(LPARAM)&nlb);
+}
diff --git a/src/modules/netlib/netlibautoproxy.cpp b/src/modules/netlib/netlibautoproxy.cpp
new file mode 100644
index 0000000000..e169cf9c89
--- /dev/null
+++ b/src/modules/netlib/netlibautoproxy.cpp
@@ -0,0 +1,460 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2010-2011 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+#include <wininet.h>
+/*
+/////////////////////////////////////////////////////////////////////
+// ResolveHostName (a helper function)
+/////////////////////////////////////////////////////////////////////
+DWORD __stdcall ResolveHostName(LPSTR lpszHostName,
+ LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ if (*lpdwIPAddressSize < 17 || lpszIPAddress == NULL)
+ {
+ *lpdwIPAddressSize = 17;
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ IN_ADDR ip;
+ ip.s_addr = inet_addr(lpszHostName);
+ if (ip.s_addr == INADDR_NONE)
+ {
+ PHOSTENT myhost = gethostbyname(lpszHostName);
+ if (myhost != NULL)
+ ip = *(PIN_ADDR)myhost->h_addr;
+ else
+ return SOCKET_ERROR;
+ }
+ mir_snprintf(lpszIPAddress, *lpdwIPAddressSize, "%u.%u.%u.%u",
+ ip.s_net, ip.s_host, ip.s_lh, ip.s_impno);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// IsResolvable (a helper function)
+/////////////////////////////////////////////////////////////////////
+BOOL __stdcall IsResolvable(LPSTR lpszHost)
+{
+ char szDummy[255];
+ DWORD dwDummySize = sizeof (szDummy) - 1;
+
+ if (ResolveHostName(lpszHost, szDummy, &dwDummySize))
+ return FALSE;
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////
+// GetIPAddress (a helper function)
+/////////////////////////////////////////////////////////////////////
+DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ char szHostBuffer[255];
+
+ if (gethostname(szHostBuffer, sizeof (szHostBuffer) - 1) != ERROR_SUCCESS)
+ return (ERROR_INTERNET_INTERNAL_ERROR);
+ return (ResolveHostName(szHostBuffer, lpszIPAddress, lpdwIPAddressSize));
+}
+
+/////////////////////////////////////////////////////////////////////
+// IsInNet (a helper function)
+/////////////////////////////////////////////////////////////////////
+BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask)
+{
+ DWORD dwDest;
+ DWORD dwIpAddr;
+ DWORD dwMask;
+
+ dwIpAddr = inet_addr(lpszIPAddress);
+ dwDest = inet_addr(lpszDest);
+ dwMask = inet_addr(lpszMask);
+
+ if ((dwDest == INADDR_NONE) ||
+ (dwIpAddr == INADDR_NONE) || ((dwIpAddr & dwMask) != dwDest))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+static const AutoProxyHelperVtbl OurVtbl =
+{
+ IsResolvable,
+ GetIPAddress,
+ ResolveHostName,
+ IsInNet,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static AutoProxyHelperFunctions HelperFunctions = { &OurVtbl };
+*/
+
+static char *szProxyHost[3];
+static LIST<char> proxyBypass(5);
+
+static HMODULE hModJS;
+
+static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
+static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
+static pfnInternetGetProxyInfo pInternetGetProxyInfo;
+
+static bool bEnabled, bOneProxy;
+
+static void GetFile(char* szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf)
+{
+ NetlibUser nlu = {0};
+ NETLIBHTTPREQUEST nlhr = {0};
+
+ nlu.handleType = NLH_USER;
+ nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.user.szSettingsModule = "(NULL)";
+ nlu.toLog = 1;
+
+ // initialize the netlib request
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+
+ // download the page
+ NETLIBHTTPREQUEST *nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpTransaction((WPARAM)&nlu, (LPARAM)&nlhr);
+
+ if (nlhrReply)
+ {
+ if (nlhrReply->resultCode == 200)
+ {
+ buf.lpszScriptBuffer = nlhrReply->pData;
+ buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
+
+ nlhrReply->dataLength = 0;
+ nlhrReply->pData = NULL;
+ }
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+ }
+}
+
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
+{
+ bool noHttp = false;
+ bool usingSsl = false;
+ char szUrl[256] = "";
+
+ if ((nlc->nloc.flags & (NLOCF_HTTP | NLOCF_HTTPGATEWAY) && nlc->nloc.flags & NLOCF_SSL) ||
+ nlc->nloc.wPort == 443 || forceHttps)
+ {
+ mir_snprintf(szUrl, sizeof(szUrl), "https://%s", nlc->nloc.szHost);
+ usingSsl = true;
+ }
+ else if (nlc->nloc.flags & (NLOCF_HTTPGATEWAY | NLOCF_HTTP) || nlc->usingHttpGateway)
+ mir_snprintf(szUrl, sizeof(szUrl), "http://%s", nlc->nloc.szHost);
+ else
+ {
+ mir_snprintf(szUrl, sizeof(szUrl), "%s", nlc->nloc.szHost);
+ noHttp = true;
+ }
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+ nlc->wProxyPort = 0;
+ nlc->proxyType = 0;
+
+ char *mt = NetlibGetIeProxy(szUrl);
+ char *m = NEWSTR_ALLOCA(mt);
+ mir_free(mt);
+
+ if (m == NULL) return false;
+
+ // if multiple servers, use the first one
+ char *c = strchr(m, ';'); if (c) *c = 0;
+
+ // if 'direct' no proxy
+ if (_stricmp(lrtrim(m), "direct") == 0) return false;
+
+ // find proxy address
+ char *h = strchr(m, ' ');
+ if (h) { *h = 0; ++h; } else return false;
+
+ // find proxy port
+ char *p = strchr(h, ':');
+ if (p) { *p = 0; ++p; }
+
+ lrtrim(h); ltrim(p);
+ if (_stricmp(m, "proxy") == 0 && h[0])
+ {
+ nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP;
+ nlc->wProxyPort = p ? atol(p) : 8080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks") == 0 && h[0])
+ {
+ nlc->proxyType = PROXYTYPE_SOCKS4;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks5") == 0 && h[0])
+ {
+ nlc->proxyType = PROXYTYPE_SOCKS5;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+static char szAutoUrlStr[MAX_PATH] = "";
+static AUTO_PROXY_SCRIPT_BUFFER abuf = {0};
+static HANDLE hIeProxyMutex;
+static bool bAutoProxyInit;
+
+static void NetlibInitAutoProxy(void)
+{
+ if (bAutoProxyInit) return;
+
+ if (!hModJS)
+ {
+ if (!(hModJS = LoadLibraryA("jsproxy.dll")))
+ return;
+
+ pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)
+ GetProcAddress(hModJS, "InternetInitializeAutoProxyDll");
+
+ pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)
+ GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll");
+
+ pInternetGetProxyInfo = (pfnInternetGetProxyInfo)
+ GetProcAddress(hModJS, "InternetGetProxyInfo");
+ }
+
+ if (strstr(szAutoUrlStr, "file://") == NULL && strstr(szAutoUrlStr, "://") != NULL)
+ {
+ abuf.dwStructSize = sizeof(abuf);
+ GetFile(szAutoUrlStr, abuf);
+ }
+ bAutoProxyInit = true;
+}
+
+struct IeProxyParam
+{
+ char *szUrl;
+ char *szHost;
+ char *szProxy;
+};
+
+static unsigned __stdcall NetlibIeProxyThread(void * arg)
+{
+ IeProxyParam *param = (IeProxyParam*)arg;
+ param->szProxy = NULL;
+
+ if (!bAutoProxyInit)
+ {
+ WaitForSingleObject(hIeProxyMutex, INFINITE);
+ NetlibInitAutoProxy();
+ ReleaseMutex(hIeProxyMutex);
+ }
+
+ BOOL res;
+ char *loc = strstr(szAutoUrlStr, "file://");
+ if (loc || strstr(szAutoUrlStr, "://") == NULL)
+ {
+ NetlibLogf(NULL, "Autoproxy Init file: %s", loc);
+ loc = loc ? loc + 7 : szAutoUrlStr;
+ res = pInternetInitializeAutoProxyDll(0, loc, NULL, NULL /*&HelperFunctions*/, NULL);
+ }
+ else
+ {
+ NetlibLogf(NULL, "Autoproxy Init %d", abuf.dwScriptBufferSize);
+ if (abuf.dwScriptBufferSize)
+ res = pInternetInitializeAutoProxyDll(0, NULL, NULL, NULL /*&HelperFunctions*/, &abuf);
+ else
+ res = false;
+ }
+
+ if (res)
+ {
+ char proxyBuffer[1024];
+ char *proxy = proxyBuffer;
+ DWORD dwProxyLen = sizeof(proxyBuffer);
+
+ if (pInternetGetProxyInfo(param->szUrl, (DWORD)strlen(param->szUrl),
+ param->szHost, (DWORD)strlen(param->szHost), &proxy, &dwProxyLen))
+ param->szProxy = mir_strdup(lrtrim(proxy));
+
+ NetlibLogf(NULL, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
+ pInternetDeInitializeAutoProxyDll(NULL, 0);
+ }
+ else
+ NetlibLogf(NULL, "Autoproxy init failed");
+
+ return 0;
+}
+
+char* NetlibGetIeProxy(char *szUrl)
+{
+ char *res = NULL;
+ char* p = strstr(szUrl, "://");
+ if (p) p += 3; else p = szUrl;
+
+ char *szHost = NEWSTR_ALLOCA(p);
+ p = strchr(szHost, '/'); if (p) *p = 0;
+ p = strchr(szHost, ':'); if (p) *p = 0;
+ _strlwr(szHost);
+
+ if (bEnabled)
+ {
+ for (int i = 0; i < proxyBypass.getCount(); ++i)
+ {
+ if (strcmp(proxyBypass[i], "<local>") == 0)
+ {
+ if (strchr(szHost, '.') == NULL) return NULL;
+ }
+ else if (wildcmp(szHost, proxyBypass[i])) return NULL;
+ }
+
+ int ind = -1;
+ if (strstr(szUrl, "http://"))
+ ind = szProxyHost[0] ? 0 : 2;
+ else if (strstr(szUrl, "https://"))
+ ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2);
+ else
+ ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2));
+
+ if (ind < 0 || !szProxyHost[ind]) return NULL;
+
+ size_t len = strlen(szHost) + 20;
+ res = (char*)mir_alloc(len);
+ mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]);
+ return res;
+ }
+
+ if (szAutoUrlStr[0])
+ {
+ unsigned dwThreadId;
+ IeProxyParam param = { szUrl, szHost, NULL };
+ HANDLE hThread = (HANDLE)forkthreadex(NULL, 0, NetlibIeProxyThread, 0, &param, &dwThreadId);
+ WaitForSingleObject(hThread, INFINITE);
+ CloseHandle(hThread);
+ res = param.szProxy;
+ }
+ return res;
+}
+
+void NetlibLoadIeProxy(void)
+{
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
+ 0, KEY_QUERY_VALUE, &hSettings))
+ return;
+
+ DWORD tValueLen, enabled = 0;
+ char szHostStr[256] = "", szProxyBypassStr[4096] = "";
+
+ tValueLen = sizeof(enabled);
+ int tResult = RegQueryValueExA(hSettings, "ProxyEnable", NULL, NULL, (BYTE*)&enabled, &tValueLen);
+ bEnabled = enabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = SIZEOF(szHostStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyServer", NULL, NULL, (BYTE*)szHostStr, &tValueLen);
+ bEnabled = bEnabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = SIZEOF(szAutoUrlStr);
+ tResult = RegQueryValueExA(hSettings, "AutoConfigUrl", NULL, NULL, (BYTE*)szAutoUrlStr, &tValueLen);
+
+ tValueLen = SIZEOF(szProxyBypassStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyOverride", NULL, NULL, (BYTE*)szProxyBypassStr, &tValueLen);
+
+ RegCloseKey(hSettings);
+
+ if (bEnabled)
+ {
+ char* szProxy = ltrim(szHostStr);
+ if (szProxy[0] == 0) { enabled = false; return; }
+
+ for (;;)
+ {
+ char *szProxyEnd = strchr(szProxy, ';');
+ if (szProxyEnd) *szProxyEnd = 0;
+
+ int ind = -1;
+ if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; }
+ else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; }
+ else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; }
+ else if (strchr(szProxy, '=')) ind = -2;
+
+ if (ind != -2)
+ {
+ bOneProxy = ind < 0; if (ind < 0) ind = 0;
+
+ lrtrim(szProxy);
+
+ if (strchr(szProxy, ':'))
+ szProxyHost[ind] = mir_strdup(szProxy);
+ else
+ {
+ size_t len = strlen(szProxy) + 10;
+ szProxyHost[ind] = (char*)mir_alloc(len);
+ mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080);
+ }
+ if (bOneProxy) break;
+ }
+ if (szProxyEnd == NULL) break;
+ szProxy = szProxyEnd + 1;
+ }
+
+ char* szProxyBypass = szProxyBypassStr;
+ for(;;)
+ {
+ char *szProxyBypassEnd = strchr(szProxyBypass, ';');
+ if (szProxyBypassEnd) *szProxyBypassEnd = 0;
+
+ lrtrim(szProxyBypass);
+
+ proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
+ if (szProxyBypassEnd == NULL) break;
+
+ szProxyBypass = szProxyBypassEnd + 1;
+ }
+ }
+
+ if (bEnabled || szAutoUrlStr[0])
+ hIeProxyMutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+void NetlibUnloadIeProxy(void)
+{
+ int i;
+
+ for (i = 0; i < 3; ++i)
+ mir_free(szProxyHost[i]);
+
+ for (i = 0; i < proxyBypass.getCount(); ++i)
+ mir_free(proxyBypass[i]);
+
+ proxyBypass.destroy();
+ mir_free(abuf.lpszScriptBuffer);
+
+ CloseHandle(hIeProxyMutex);
+}
diff --git a/src/modules/netlib/netlibbind.cpp b/src/modules/netlib/netlibbind.cpp
new file mode 100644
index 0000000000..6c0c4c19e9
--- /dev/null
+++ b/src/modules/netlib/netlibbind.cpp
@@ -0,0 +1,287 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+bool BindSocketToPort(const char *szPorts, SOCKET s, int* portn)
+{
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ EnterCriticalSection(&csNetlibUser);
+
+ if (--*portn < 0 && s != INVALID_SOCKET)
+ {
+ BindSocketToPort(szPorts, INVALID_SOCKET, portn);
+ if (*portn == 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return false;
+ }
+ WORD num;
+ CallService(MS_UTILS_GETRANDOM, sizeof(WORD), (LPARAM)&num);
+ *portn = num % *portn;
+ }
+
+ bool before=false;
+ for (;;)
+ {
+ const char *psz;
+ char *pszEnd;
+ int portMin, portMax, port, portnum = 0;
+
+ for(psz=szPorts;*psz;)
+ {
+ while (*psz == ' ' || *psz == ',') psz++;
+ portMin = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz) break;
+ while (*pszEnd == ' ') pszEnd++;
+ if(*pszEnd == '-')
+ {
+ psz = pszEnd + 1;
+ portMax = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz) portMax = 65535;
+ if (portMin > portMax)
+ {
+ port = portMin;
+ portMin = portMax;
+ portMax = port;
+ }
+ }
+ else portMax = portMin;
+ if (portMax >= 1)
+ {
+ if (portMin <= 0) portMin = 1;
+ for (port = portMin; port <= portMax; port++)
+ {
+ if (port > 65535) break;
+
+ ++portnum;
+
+ if (s == INVALID_SOCKET) continue;
+ if (!before && portnum <= *portn) continue;
+ if (before && portnum >= *portn)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return false;
+ }
+
+ sin.sin_port = htons((WORD)port);
+ if (bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ *portn = portnum + 1;
+ return true;
+ }
+ }
+ }
+ psz = pszEnd;
+ }
+ if (*portn < 0)
+ {
+ *portn = portnum;
+ LeaveCriticalSection(&csNetlibUser);
+ return true;
+ }
+ else if (*portn >= portnum)
+ *portn = 0;
+ else
+ before = true;
+ }
+}
+
+
+int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp)
+{
+ closesocket(nlbp->s);
+ WaitForSingleObject(nlbp->hThread,INFINITE);
+ CloseHandle(nlbp->hThread);
+ NetlibLogf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
+ mir_free(nlbp);
+ return 1;
+}
+
+static unsigned __stdcall NetlibBindAcceptThread(void* param)
+{
+ SOCKET s;
+ SOCKADDR_IN sin;
+ int sinLen;
+ struct NetlibConnection *nlc;
+ struct NetlibBoundPort *nlbp = (NetlibBoundPort*)param;
+
+ NetlibLogf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
+ for(;;)
+ {
+ sinLen = sizeof(sin);
+ s = accept(nlbp->s, (struct sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) break;
+ NetlibLogf(nlbp->nlu, "New incoming connection on port %u from %s (%d)", nlbp->wPort, inet_ntoa(sin.sin_addr), s);
+ nlc = (NetlibConnection*)mir_calloc(sizeof(NetlibConnection));
+ nlc->handleType = NLH_CONNECTION;
+ nlc->nlu = nlbp->nlu;
+ nlc->s = s;
+ nlc->s2 = INVALID_SOCKET;
+ InitializeCriticalSection(&nlc->csHttpSequenceNums);
+ nlc->hOkToCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ nlc->dontCloseNow = 0;
+ NetlibInitializeNestedCS(&nlc->ncsSend);
+ NetlibInitializeNestedCS(&nlc->ncsRecv);
+ nlbp->pfnNewConnectionV2((HANDLE)nlc,ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
+ }
+ NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
+ return 0;
+}
+
+INT_PTR NetlibBindPort(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBBIND *nlb = (NETLIBBIND*)lParam;
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibBoundPort *nlbp;
+ SOCKADDR_IN sin;
+ int foundPort = 0;
+ UINT dwThreadId;
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) ||
+ nlb == NULL || nlb->pfnNewConnection == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if (nlb->cbSize != sizeof(NETLIBBIND) &&
+ nlb->cbSize != NETLIBBIND_SIZEOF_V2 &&
+ nlb->cbSize != NETLIBBIND_SIZEOF_V1)
+ {
+ return 0;
+ }
+ nlbp = (NetlibBoundPort*)mir_calloc(sizeof(NetlibBoundPort));
+ nlbp->handleType = NLH_BOUNDPORT;
+ nlbp->nlu = nlu;
+ nlbp->pfnNewConnectionV2 = nlb->pfnNewConnectionV2;
+ nlbp->s = socket(AF_INET, SOCK_STREAM, 0);
+ nlbp->pExtra = (nlb->cbSize != NETLIBBIND_SIZEOF_V1) ? nlb->pExtra : NULL;
+ if (nlbp->s == INVALID_SOCKET)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError());
+ mir_free(nlbp);
+ return 0;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = 0;
+
+ /* if the netlib user wanted a free port given in the range, then
+ they better have given wPort==0, let's hope so */
+ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0)
+ {
+ if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, &nlu->outportnum))
+ {
+ NetlibLogf(nlu, "Netlib bind: Not enough ports for incoming connections specified");
+ SetLastError(WSAEADDRINUSE);
+ }
+ else
+ foundPort = 1;
+ }
+ else
+ {
+ /* if ->wPort==0 then they'll get any free port, otherwise they'll
+ be asking for whatever was in nlb->wPort*/
+ if (nlb->wPort != 0)
+ {
+ NetlibLogf(nlu,"%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.",__FILE__,__LINE__,nlb->wPort);
+ sin.sin_port = htons(nlb->wPort);
+ }
+ if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0)
+ foundPort = 1;
+ }
+ if (!foundPort)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"bind",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+
+ if (listen(nlbp->s, 5))
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"listen",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+
+ { int len;
+ DWORD extIP;
+
+ ZeroMemory(&sin,sizeof(sin));
+ len = sizeof(sin);
+ if (getsockname(nlbp->s,(SOCKADDR *)&sin,&len))
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"getsockname",WSAGetLastError());
+ closesocket(nlbp->s);
+ mir_free(nlbp);
+ return 0;
+ }
+ nlb->wPort = ntohs(sin.sin_port);
+ nlbp->wPort = nlb->wPort;
+ nlb->dwInternalIP = ntohl(sin.sin_addr.S_un.S_addr);
+
+ if (nlb->dwInternalIP == 0)
+ {
+ char hostname[64];
+ struct hostent *he;
+
+ gethostname(hostname, SIZEOF(hostname));
+ he = gethostbyname(hostname);
+ if (he && he->h_addr_list[0])
+ nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr_list[0]);
+ }
+ if (nlu->settings.enableUPnP &&
+ NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, nlb->cbSize > NETLIBBIND_SIZEOF_V2))
+ {
+ NetlibLogf(NULL, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n",
+ nlb->wPort, nlbp->wExPort);
+ if (nlb->cbSize > NETLIBBIND_SIZEOF_V2)
+ {
+ nlb->wExPort = nlbp->wExPort;
+ nlb->dwExternalIP = extIP;
+ }
+ }
+ else
+ {
+ if (nlu->settings.enableUPnP)
+ NetlibLogf(NULL, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
+ else
+ NetlibLogf(NULL, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
+
+ nlbp->wExPort = 0;
+ if (nlb->cbSize > NETLIBBIND_SIZEOF_V2)
+ {
+ nlb->wExPort = nlb->wPort;
+ nlb->dwExternalIP = nlb->dwInternalIP;
+ }
+ }
+ }
+ nlbp->hThread = (HANDLE)forkthreadex(NULL, 0, NetlibBindAcceptThread, 0, nlbp, &dwThreadId);
+ return (INT_PTR)nlbp;
+}
diff --git a/src/modules/netlib/netlibhttp.cpp b/src/modules/netlib/netlibhttp.cpp
new file mode 100644
index 0000000000..970a28cb10
--- /dev/null
+++ b/src/modules/netlib/netlibhttp.cpp
@@ -0,0 +1,1331 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "../plugins/zlib/zlib.h"
+#include "netlib.h"
+
+#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
+#define HTTPRECVDATATIMEOUT 20000
+
+struct ResizableCharBuffer
+{
+ char *sz;
+ int iEnd, cbAlloced;
+};
+
+struct ProxyAuth
+{
+ char *szServer;
+ char *szMethod;
+// char *szUserName;
+// char *szPassword;
+
+ ProxyAuth(const char *pszServer, const char *pszMethod)
+ {
+ szServer = mir_strdup(pszServer);
+ szMethod = mir_strdup(pszMethod);
+ }
+ ~ProxyAuth()
+ {
+ mir_free(szServer);
+ mir_free(szMethod);
+ }
+ static int Compare(const ProxyAuth* p1, const ProxyAuth* p2 )
+ { return lstrcmpiA(p1->szServer, p2->szServer); }
+};
+
+struct ProxyAuthList : OBJLIST<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
+
+ void add(const char *szServer, const char *szMethod)
+ {
+ if (szServer == NULL) return;
+ int i = getIndex((ProxyAuth*)&szServer);
+ if (i >= 0)
+ {
+ ProxyAuth &rec = (*this)[i];
+ if (szMethod == NULL)
+ remove(i);
+ else if (_stricmp(rec.szMethod, szMethod))
+ {
+ mir_free(rec.szMethod);
+ rec.szMethod = mir_strdup(szMethod);
+ }
+ }
+ else
+ insert(new ProxyAuth(szServer, szMethod));
+ }
+
+ const char* find(const char *szServer)
+ {
+ ProxyAuth * rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : NULL;
+ return rec ? rec->szMethod : NULL;
+ }
+};
+
+ProxyAuthList proxyAuthList;
+
+static void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone;
+
+ if (rcb->cbAlloced == 0)
+ {
+ rcb->cbAlloced = 512;
+ rcb->sz = (char*)mir_alloc(rcb->cbAlloced);
+ }
+ va_start(va, fmt);
+ for (;;)
+ {
+ charsDone = mir_vsnprintf(rcb->sz + rcb->iEnd, rcb->cbAlloced-rcb->iEnd, fmt, va);
+ if(charsDone >= 0) break;
+ rcb->cbAlloced += 512;
+ rcb->sz = (char*)mir_realloc(rcb->sz, rcb->cbAlloced);
+ }
+ va_end(va);
+ rcb->iEnd += charsDone;
+}
+
+static int RecvWithTimeoutTime(struct NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags)
+{
+ DWORD dwTimeNow;
+
+ if (!si.pending(nlc->hSsl))
+ {
+ while ((dwTimeNow = GetTickCount()) < dwTimeoutTime)
+ {
+ unsigned dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000);
+ int res = WaitUntilReadable(nlc->s, dwDeltaTime);
+
+ switch (res)
+ {
+ case SOCKET_ERROR:
+ return SOCKET_ERROR;
+
+ case 1:
+ return NLRecv(nlc, buf, len, flags);
+ }
+
+ if (nlc->termRequested || Miranda_Terminated()) return 0;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ return NLRecv(nlc, buf, len, flags);
+}
+
+static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr)
+{
+ for (int i = 0; i < nlhrReply->headersCount; i++)
+ {
+ if (_stricmp(nlhrReply->headers[i].szName, hdr) == 0)
+ {
+ return nlhrReply->headers[i].szValue;
+ }
+ }
+ return NULL;
+}
+
+static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
+{
+ char *szBasicHdr = NULL;
+ char *szNegoHdr = NULL;
+ char *szNtlmHdr = NULL;
+
+ for (int i = 0; i < nlhrReply->headersCount; i++)
+ {
+ if (_stricmp(nlhrReply->headers[i].szName, hdr) == 0)
+ {
+ if (_strnicmp(nlhrReply->headers[i].szValue, "Negotiate", 9) == 0)
+ szNegoHdr = nlhrReply->headers[i].szValue;
+ else if (_strnicmp(nlhrReply->headers[i].szValue, "NTLM", 4) == 0)
+ szNtlmHdr = nlhrReply->headers[i].szValue;
+ else if (_strnicmp(nlhrReply->headers[i].szValue, "Basic", 5) == 0)
+ szBasicHdr = nlhrReply->headers[i].szValue;
+ }
+ }
+
+ if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr;
+ if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr;
+ if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr;
+ return NULL;
+}
+
+void NetlibConnFromUrl(const char* szUrl, bool secur, NETLIBOPENCONNECTION &nloc)
+{
+ secur = secur || _strnicmp(szUrl, "https", 5) == 0;
+ const char* phost = strstr(szUrl, "://");
+
+ char* szHost = mir_strdup(phost ? phost + 3 : szUrl);
+
+ char* ppath = strchr(szHost, '/');
+ if (ppath) *ppath = '\0';
+
+ memset(&nloc, 0, sizeof(nloc));
+ nloc.cbSize = sizeof(nloc);
+ nloc.szHost = szHost;
+
+ char* pcolon = strrchr(szHost, ':');
+ if (pcolon)
+ {
+ *pcolon = '\0';
+ nloc.wPort = (WORD)strtol(pcolon+1, NULL, 10);
+ }
+ else nloc.wPort = secur ? 443 : 80;
+ nloc.flags = (secur ? NLOCF_SSL : 0);
+}
+
+static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection* nlc,
+ const char* szUrl = NULL)
+{
+ NETLIBOPENCONNECTION nloc;
+
+ if (szUrl == NULL)
+ NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, nloc);
+ else
+ NetlibConnFromUrl(szUrl, false, nloc);
+
+ nloc.flags |= NLOCF_HTTP;
+ if (nloc.flags & NLOCF_SSL) nlhr->flags |= NLHRF_SSL; else nlhr->flags &= ~NLHRF_SSL;
+
+ if (nlc != NULL)
+ {
+ bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
+ bool sameHost = lstrcmpA(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
+
+ if (!httpProxy && !sameHost)
+ {
+ NetlibDoClose(nlc);
+
+ mir_free((char*)nlc->nloc.szHost);
+ nlc->nloc = nloc;
+ return NetlibDoConnect(nlc) ? nlc : NULL;
+ }
+ }
+ else
+ nlc = (NetlibConnection*)NetlibOpenConnection((WPARAM)nlu, (LPARAM)&nloc);
+
+ mir_free((char*)nloc.szHost);
+
+ return nlc;
+}
+
+struct HttpSecurityContext
+{
+ HANDLE m_hNtlmSecurity;
+ char *m_szHost;
+ char *m_szProvider;
+
+ HttpSecurityContext()
+ { m_hNtlmSecurity = NULL; m_szHost = NULL; m_szProvider = NULL; }
+
+ ~HttpSecurityContext() { Destroy(); }
+
+ void Destroy(void)
+ {
+ if (!m_hNtlmSecurity) return;
+
+ NetlibDestroySecurityProvider(m_hNtlmSecurity);
+ m_hNtlmSecurity = NULL;
+ mir_free(m_szHost); m_szHost = NULL;
+ mir_free(m_szProvider); m_szProvider = NULL;
+ }
+
+ bool TryBasic(void)
+ {
+ return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic");
+ }
+
+ char* Execute(NetlibConnection *nlc, char* szHost, const char* szProvider,
+ const char* szChallenge, unsigned& complete)
+ {
+ char* szAuthHdr = NULL;
+ bool justCreated = false;
+
+ if (m_hNtlmSecurity)
+ {
+ bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider);
+ newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost)));
+ if (newAuth)
+ Destroy();
+ }
+
+ if (m_hNtlmSecurity == NULL)
+ {
+ char szSpnStr[256] = "";
+ if (szHost && _stricmp(szProvider, "Basic"))
+ {
+ unsigned long ip = inet_addr(szHost);
+ PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
+ mir_snprintf(szSpnStr, SIZEOF(szSpnStr), "HTTP/%s", host && host->h_name ? host->h_name : szHost);
+ _strlwr(szSpnStr + 5);
+ NetlibLogf(nlc->nlu, "Host SPN: %s", szSpnStr);
+ }
+ m_hNtlmSecurity = NetlibInitSecurityProvider(szProvider, szSpnStr[0] ? szSpnStr : NULL);
+ if (m_hNtlmSecurity)
+ {
+ m_szProvider = mir_strdup(szProvider);
+ m_szHost = mir_strdup(szHost);
+ justCreated = true;
+ }
+ }
+
+ if (m_hNtlmSecurity)
+ {
+ TCHAR *szLogin = NULL, *szPassw = NULL;
+
+ if (nlc->nlu->settings.useProxyAuth)
+ {
+ EnterCriticalSection(&csNetlibUser);
+ szLogin = mir_a2t(nlc->nlu->settings.szProxyAuthUser);
+ szPassw = mir_a2t(nlc->nlu->settings.szProxyAuthPassword);
+ LeaveCriticalSection(&csNetlibUser);
+ }
+
+ szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity,
+ szChallenge, szLogin, szPassw, true, complete);
+
+ if (!szAuthHdr)
+ {
+ NetlibLogf(NULL, "Security login %s failed, user: " TCHAR_STR_PARAM " pssw: " TCHAR_STR_PARAM,
+ szProvider, szLogin ? szLogin : _T("(no user)"), szPassw ? _T("(exist)") : _T("(no psw)"));
+ }
+ else if (justCreated)
+ proxyAuthList.add(m_szHost, m_szProvider);
+
+ mir_free(szLogin);
+ mir_free(szPassw);
+ }
+ else
+ complete = 1;
+
+ return szAuthHdr;
+ }
+};
+
+static int HttpPeekFirstResponseLine(NetlibConnection *nlc, DWORD dwTimeoutTime,
+ DWORD recvFlags, int *resultCode,
+ char **ppszResultDescr, int *length)
+{
+ int bytesPeeked;
+ char buffer[2048];
+ char *peol;
+
+ for(;;)
+ {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, SIZEOF(buffer) - 1,
+ MSG_PEEK | recvFlags);
+
+ if (bytesPeeked == 0)
+ {
+ SetLastError(ERROR_HANDLE_EOF);
+ return 0;
+ }
+ if (bytesPeeked == SOCKET_ERROR)
+ return 0;
+
+ buffer[bytesPeeked] = '\0';
+ peol = strchr(buffer, '\n');
+ if (peol == NULL)
+ {
+ if ((int)strlen(buffer) < bytesPeeked)
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+ if (bytesPeeked == SIZEOF(buffer) - 1)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ if (Miranda_Terminated()) return 0;
+ Sleep(10);
+ }
+ else
+ break;
+ }
+
+ if (peol == buffer)
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ *peol = '\0';
+
+ if (_strnicmp(buffer, "HTTP/", 5))
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ size_t off = strcspn(buffer, " \t");
+ if (off >= (unsigned)bytesPeeked)
+ return 0;
+
+ char* pResultCode = buffer + off;
+ *(pResultCode++) = 0;
+
+ char* pResultDescr;
+ *resultCode = strtol(pResultCode, &pResultDescr, 10);
+
+ if (ppszResultDescr)
+ *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr));
+
+ if (length) *length = peol - buffer + 1;
+ return 1;
+}
+
+static int SendHttpRequestAndData(struct NetlibConnection *nlc,struct ResizableCharBuffer *httpRequest,NETLIBHTTPREQUEST *nlhr,int sendContentLengthHeader)
+{
+ bool sendData = (nlhr->requestType==REQUEST_POST || nlhr->requestType==REQUEST_PUT);
+
+ if(sendContentLengthHeader && sendData)
+ AppendToCharBuffer(httpRequest,"Content-Length: %d\r\n\r\n", nlhr->dataLength);
+ else
+ AppendToCharBuffer(httpRequest,"\r\n");
+
+ DWORD hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int bytesSent = NLSend(nlc, httpRequest->sz, httpRequest->iEnd, hflags);
+ if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength)
+ {
+ DWORD sflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int sendResult = NLSend(nlc,nlhr->pData,nlhr->dataLength, sflags);
+
+ bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
+ }
+ mir_free(httpRequest->sz);
+ memset(httpRequest, 0, sizeof(*httpRequest));
+
+ return bytesSent;
+}
+
+INT_PTR NetlibHttpSendRequest(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam;
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+ HttpSecurityContext httpSecurity;
+
+ struct ResizableCharBuffer httpRequest={0};
+ const char *pszRequest, *pszUrl, *pszFullUrl;
+ char *szHost = NULL, *szNewUrl = NULL;
+ char *pszProxyAuthHdr = NULL, *pszAuthHdr = NULL;
+ int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
+ int bytesSent;
+ bool lastFirstLineFail = false;
+
+ if (nlhr == NULL || nlhr->cbSize < NETLIBHTTPREQUEST_V1_SIZE || nlhr->szUrl == NULL || nlhr->szUrl[0] == '\0')
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int hdrTimeout = nlhr->cbSize > NETLIBHTTPREQUEST_V1_SIZE && nlhr->timeout ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
+
+ switch(nlhr->requestType)
+ {
+ case REQUEST_GET: pszRequest = "GET"; break;
+ case REQUEST_POST: pszRequest = "POST"; break;
+ case REQUEST_CONNECT: pszRequest = "CONNECT"; break;
+ case REQUEST_HEAD: pszRequest = "HEAD"; break;
+ case REQUEST_PUT: pszRequest = "PUT"; break;
+ case REQUEST_DELETE: pszRequest = "DELETE"; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if (!nlc->usingHttpGateway)
+ {
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+ }
+
+ pszFullUrl = nlhr->szUrl;
+ pszUrl = NULL;
+
+ unsigned complete = false;
+ int count = 11;
+ while (--count)
+ {
+ if (!NetlibReconnect(nlc))
+ {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!pszUrl)
+ {
+ pszUrl = pszFullUrl;
+ if (nlhr->flags & (NLHRF_SMARTREMOVEHOST | NLHRF_REMOVEHOST | NLHRF_GENERATEHOST))
+ {
+ bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
+
+ mir_free(szHost);
+ szHost = NULL;
+
+ const char *ppath, *phost;
+ phost = strstr(pszUrl, "://");
+ if (phost == NULL) phost = pszUrl;
+ else phost += 3;
+ ppath = strchr(phost, '/');
+ if (ppath == phost) phost = NULL;
+
+ if (nlhr->flags & NLHRF_GENERATEHOST)
+ {
+ szHost = mir_strdup(phost);
+ if (ppath && phost) szHost[ppath - phost] = 0;
+ }
+
+ if (nlhr->flags & NLHRF_REMOVEHOST || (nlhr->flags & NLHRF_SMARTREMOVEHOST && !usingProxy))
+ {
+ pszUrl = ppath ? ppath : "/";
+ }
+
+ if (usingProxy && phost && !nlc->dnsThroughProxy)
+ {
+ char* tszHost = mir_strdup(phost);
+ if (ppath && phost) tszHost[ppath - phost] = 0;
+ char* cln = strchr(tszHost, ':'); if (cln) *cln = 0;
+
+ if (inet_addr(tszHost) == INADDR_NONE)
+ {
+ DWORD ip = DnsLookup(nlc->nlu, tszHost);
+ if (ip && szHost)
+ {
+ mir_free(szHost);
+ szHost = (char*)mir_alloc(30);
+ if (cln) *cln = ':';
+ mir_snprintf(szHost, 30, "%s%s", inet_ntoa(*(PIN_ADDR)&ip), cln ? cln : "");
+ }
+/*
+ if (ip && pszUrl[0] != '/')
+ {
+ mir_free(szNewUrl);
+ szNewUrl = (char*)mir_alloc(strlen(pszUrl) + 60);
+ szNewUrl[0] = 0;
+
+ phost = strstr(pszUrl, "://");
+ if (phost)
+ {
+ phost += 3;
+ size_t len = phost - pszUrl;
+ memcpy(szNewUrl, pszUrl, len);
+ szNewUrl[len] = 0;
+ }
+ strcat(szNewUrl, inet_ntoa(*(PIN_ADDR)&ip));
+ ppath = strchr(phost, '/');
+ if (ppath) strcat(szNewUrl, ppath);
+ pszUrl = szNewUrl;
+ }
+*/
+ }
+ mir_free(tszHost);
+ }
+ }
+ }
+
+ if (nlc->proxyAuthNeeded && proxyAuthList.getCount())
+ {
+ if (httpSecurity.m_szProvider == NULL && nlc->szProxyServer)
+ {
+ const char* szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer);
+
+ if (szAuthMethodNlu)
+ {
+ mir_free(pszProxyAuthHdr);
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete);
+ }
+ }
+ }
+ nlc->proxyAuthNeeded = false;
+
+ AppendToCharBuffer(&httpRequest, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0);
+
+ //HTTP headers
+ doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0;
+ for (i=0; i < nlhr->headersCount; i++)
+ {
+ if (!lstrcmpiA(nlhr->headers[i].szName, "Host")) doneHostHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Content-Length")) doneContentLengthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Authorization")) doneAuthHeader = 1;
+ else if (!lstrcmpiA(nlhr->headers[i].szName, "Connection")) continue;
+ if (nlhr->headers[i].szValue == NULL) continue;
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", nlhr->headers[i].szName, nlhr->headers[i].szValue);
+ }
+ if (szHost && !doneHostHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Host", szHost);
+ if (pszProxyAuthHdr && !doneProxyAuthHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
+ if (pszAuthHdr && !doneAuthHeader)
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Authorization", pszAuthHdr);
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Connection", "Keep-Alive");
+ AppendToCharBuffer(&httpRequest, "%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
+
+ // Add Sticky Headers
+ if (nlc->nlu->szStickyHeaders != NULL)
+ AppendToCharBuffer(&httpRequest, "%s\r\n", nlc->nlu->szStickyHeaders);
+
+ //send it
+ bytesSent = SendHttpRequestAndData(nlc, &httpRequest, nlhr, !doneContentLengthHeader);
+ if (bytesSent == SOCKET_ERROR) break;
+
+ //ntlm reply
+ if (!doneContentLengthHeader || nlhr->requestType == REQUEST_HEAD)
+ {
+ int resultCode = 0;
+
+ DWORD fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0);
+ DWORD dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout;
+ if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &resultCode, NULL, NULL))
+ {
+ NetlibLogf(nlc->nlu, "%s %d: %s Failed (%u %u)",__FILE__,__LINE__,"HttpPeekFirstResponseLine",GetLastError(), count);
+ DWORD err = GetLastError();
+ if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW ||
+ lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT)
+ {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ else
+ {
+ lastFirstLineFail = true;
+ continue;
+ }
+ }
+ lastFirstLineFail = false;
+
+ DWORD hflags = (nlhr->flags & (NLHRF_NODUMP|NLHRF_NODUMPHEADERS|NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ DWORD dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP;
+
+ if (resultCode == 100)
+ {
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ }
+ else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) // redirect
+ && (nlhr->flags & NLHRF_REDIRECT)))
+ {
+ pszUrl = NULL;
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply)
+ {
+ char* tmpUrl = NetlibHttpFindHeader(nlhrReply, "Location");
+ if (tmpUrl)
+ {
+ size_t rlen = 0;
+ if (tmpUrl[0] == '/')
+ {
+ const char *ppath, *phost;
+ phost = strstr(pszFullUrl, "://");
+ phost = phost ? phost + 3 : pszFullUrl;
+ ppath = strchr(phost, '/');
+ rlen = ppath ? ppath - pszFullUrl : strlen(pszFullUrl);
+ }
+
+ nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + strlen(tmpUrl) * 3 + 1);
+
+ strncpy(nlc->szNewUrl, pszFullUrl, rlen);
+ strcpy(nlc->szNewUrl + rlen, tmpUrl);
+ pszFullUrl = nlc->szNewUrl;
+ pszUrl = NULL;
+
+ if (NetlibHttpProcessUrl(nlhr, nlc->nlu, nlc, pszFullUrl) == NULL)
+ {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 401 && !doneAuthHeader) //auth required
+ {
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszAuthHdr); pszAuthHdr = NULL;
+ if (nlhrReply)
+ {
+ char *szAuthStr = NULL;
+ if (!complete)
+ {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate",
+ httpSecurity.m_szProvider);
+ if (szAuthStr)
+ {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge)) complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ {
+ szAuthStr = httpSecurity.TryBasic() ?
+ NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : NULL;
+ }
+
+ if (szAuthStr)
+ {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszAuthHdr == NULL)
+ {
+ proxyAuthList.add(szHost, NULL);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 407 && !doneProxyAuthHeader) //proxy auth required
+ {
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = NULL;
+ if (nlhrReply)
+ {
+ char *szAuthStr = NULL;
+ if (!complete)
+ {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate",
+ httpSecurity.m_szProvider);
+ if (szAuthStr)
+ {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge + 1)) complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ {
+ szAuthStr = httpSecurity.TryBasic() ?
+ NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : NULL;
+ }
+
+ if (szAuthStr)
+ {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszProxyAuthHdr == NULL)
+ {
+ proxyAuthList.add(nlc->szProxyServer, NULL);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else
+ break;
+
+ if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
+ {
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = NULL;
+ }
+ if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
+ {
+ mir_free(pszAuthHdr); pszAuthHdr = NULL;
+ }
+
+ if (nlhrReply)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ nlhrReply = NULL;
+ }
+ }
+ else
+ break;
+ }
+ if (count == 0) bytesSent = SOCKET_ERROR;
+ if (nlhrReply) NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ //clean up
+ mir_free(pszProxyAuthHdr);
+ mir_free(pszAuthHdr);
+ mir_free(szHost);
+ mir_free(szNewUrl);
+
+ if (!nlc->usingHttpGateway)
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ return bytesSent;
+}
+
+INT_PTR NetlibHttpFreeRequestStruct(WPARAM, LPARAM lParam)
+{
+ NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam;
+
+ if (nlhr == NULL || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if(nlhr->headers)
+ {
+ int i;
+ for(i=0; i<nlhr->headersCount; i++)
+ {
+ mir_free(nlhr->headers[i].szName);
+ mir_free(nlhr->headers[i].szValue);
+ }
+ mir_free(nlhr->headers);
+ }
+ mir_free(nlhr->pData);
+ mir_free(nlhr->szResultDescr);
+ mir_free(nlhr->szUrl);
+ mir_free(nlhr);
+ return 1;
+}
+
+INT_PTR NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc = (struct NetlibConnection*)wParam;
+ NETLIBHTTPREQUEST *nlhr;
+ char *peol, *pbuffer;
+ char *buffer = NULL;
+ DWORD dwRequestTimeoutTime;
+ int bytesPeeked, firstLineLength = 0;
+ int headersCount = 0, bufferSize = 8192;
+ bool headersCompleted = false;
+
+ if(!NetlibEnterNestedCS(nlc,NLNCS_RECV))
+ return 0;
+
+ dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
+ nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions
+ nlhr->requestType = REQUEST_RESPONSE;
+
+ if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, lParam | MSG_PEEK,
+ &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength))
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ return 0;
+ }
+
+ buffer = (char*)mir_alloc(bufferSize + 1);
+ bytesPeeked = NLRecv(nlc, buffer, min(firstLineLength, bufferSize), lParam | MSG_DUMPASTEXT);
+ if (bytesPeeked != firstLineLength)
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ if (bytesPeeked != SOCKET_ERROR) SetLastError(ERROR_HANDLE_EOF);
+ mir_free(buffer);
+ return 0;
+ }
+
+ // Make sure all headers arrived
+ bytesPeeked = 0;
+ while (!headersCompleted)
+ {
+ if (bytesPeeked >= bufferSize)
+ {
+ bufferSize += 8192;
+ mir_free(buffer);
+ if (bufferSize > 32 * 1024)
+ {
+ bytesPeeked = 0;
+ break;
+ }
+ buffer = (char*)mir_alloc(bufferSize + 1);
+ }
+
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, bufferSize,
+ MSG_PEEK | MSG_NODUMP | lParam);
+ if (bytesPeeked == 0) break;
+
+ if (bytesPeeked == SOCKET_ERROR)
+ {
+ bytesPeeked = 0;
+ break;
+ }
+ buffer[bytesPeeked] = 0;
+
+ for (pbuffer = buffer, headersCount = 0; ; pbuffer = peol + 1, ++headersCount)
+ {
+ peol = strchr(pbuffer, '\n');
+ if (peol == NULL) break;
+ if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r'))
+ {
+ bytesPeeked = peol - buffer + 1;
+ headersCompleted = true;
+ break;
+ }
+ }
+ }
+
+ // Recieve headers
+ if (bytesPeeked > 0)
+ bytesPeeked = NLRecv(nlc, buffer, bytesPeeked, lParam | MSG_DUMPASTEXT);
+ if (bytesPeeked <= 0)
+ {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr);
+ mir_free(buffer);
+ return 0;
+ }
+ buffer[bytesPeeked] = 0;
+
+ nlhr->headersCount = headersCount;
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
+
+ for (pbuffer = buffer, headersCount = 0; ; pbuffer = peol + 1, ++headersCount)
+ {
+ peol = strchr(pbuffer, '\n');
+ if (peol == NULL || peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) break;
+ *peol = 0;
+
+ char *pColon = strchr(pbuffer, ':');
+ if (pColon == NULL)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhr); nlhr = NULL;
+ SetLastError(ERROR_INVALID_DATA);
+ break;
+ }
+
+ *(pColon++) = 0;
+ nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
+ nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon));
+ }
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ mir_free(buffer);
+ return (INT_PTR)nlhr;
+}
+
+INT_PTR NetlibHttpTransaction(WPARAM wParam, LPARAM lParam)
+{
+ NetlibUser *nlu = (NetlibUser*)wParam;
+ NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)lParam, *nlhrReply;
+ DWORD dflags, hflags;
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
+ nlhr == NULL || nlhr->cbSize < NETLIBHTTPREQUEST_V1_SIZE ||
+ nlhr->szUrl == NULL || nlhr->szUrl[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ NetlibConnection* nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
+ if (nlc == NULL) return 0;
+
+ {
+ NETLIBHTTPREQUEST nlhrSend;
+ char szUserAgent[64];
+
+ nlhrSend = *nlhr;
+ nlhrSend.flags &= ~NLHRF_REMOVEHOST;
+ nlhrSend.flags |= NLHRF_GENERATEHOST | NLHRF_SMARTREMOVEHOST | NLHRF_SMARTAUTHHEADER;
+
+ bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != NULL;
+ bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != NULL;
+
+ if (!doneUserAgentHeader || !doneAcceptEncoding)
+ {
+ nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
+ memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
+ }
+ if (!doneUserAgentHeader)
+ {
+ char *pspace,szMirandaVer[64];
+
+ nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
+ ++nlhrSend.headersCount;
+ CallService(MS_SYSTEM_GETVERSIONTEXT,SIZEOF(szMirandaVer),(LPARAM)szMirandaVer);
+ pspace=strchr(szMirandaVer,' ');
+ if(pspace)
+ {
+ *pspace++ = '\0';
+ mir_snprintf(szUserAgent, SIZEOF(szUserAgent), "Miranda/%s (%s)", szMirandaVer, pspace);
+ }
+ else
+ mir_snprintf(szUserAgent, SIZEOF(szUserAgent), "Miranda/%s", szMirandaVer);
+ }
+ if (!doneAcceptEncoding)
+ {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
+ ++nlhrSend.headersCount;
+ }
+ if (NetlibHttpSendRequest((WPARAM)nlc, (LPARAM)&nlhrSend) == SOCKET_ERROR)
+ {
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ NetlibCloseHandle((WPARAM)nlc, 0);
+ return 0;
+ }
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ }
+
+ dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT:0) |
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ hflags =
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, 0);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply)
+ {
+ nlhrReply->szUrl = nlc->szNewUrl;
+ nlc->szNewUrl = NULL;
+ }
+
+ if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == NULL)
+ {
+ NetlibCloseHandle((WPARAM)nlc, 0);
+ if (nlhrReply) nlhrReply->nlc = NULL;
+ }
+ else
+ nlhrReply->nlc = nlc;
+
+ return (INT_PTR)nlhrReply;
+}
+
+void NetlibHttpSetLastErrorUsingHttpResult(int result)
+{
+ if (result >= 200 && result < 300)
+ {
+ SetLastError(ERROR_SUCCESS);
+ return;
+ }
+ switch(result)
+ {
+ case 400: SetLastError(ERROR_BAD_FORMAT); break;
+ case 401:
+ case 402:
+ case 403:
+ case 407: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 404: SetLastError(ERROR_FILE_NOT_FOUND); break;
+ case 405:
+ case 406: SetLastError(ERROR_INVALID_FUNCTION); break;
+ case 408: SetLastError(ERROR_TIMEOUT); break;
+ default: SetLastError(ERROR_GEN_FAILURE); break;
+ }
+}
+
+char* gzip_decode(char *gzip_data, int *len_ptr, int window)
+{
+ if (*len_ptr == 0) return NULL;
+
+ int gzip_len = *len_ptr * 5;
+ char* output_data = NULL;
+
+ int gzip_err;
+ z_stream zstr;
+
+ do
+ {
+ output_data = (char*)mir_realloc(output_data, gzip_len+1);
+
+ zstr.next_in = (Bytef*)gzip_data;
+ zstr.avail_in = *len_ptr;
+ zstr.zalloc = Z_NULL;
+ zstr.zfree = Z_NULL;
+ zstr.opaque = Z_NULL;
+ inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream));
+
+ zstr.next_out = (Bytef*)output_data;
+ zstr.avail_out = gzip_len;
+
+ gzip_err = inflate(&zstr, Z_FINISH);
+
+ inflateEnd(&zstr);
+ gzip_len *= 2;
+ }
+ while (gzip_err == Z_BUF_ERROR);
+
+ gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;
+
+ if (gzip_len <= 0)
+ {
+ mir_free(output_data);
+ output_data = NULL;
+ }
+ else
+ output_data[gzip_len] = 0;
+
+ *len_ptr = gzip_len;
+ return output_data;
+}
+
+static int NetlibHttpRecvChunkHeader(NetlibConnection* nlc, bool first, DWORD flags)
+{
+ char data[64], *peol1;
+
+ for (;;)
+ {
+ int recvResult = NLRecv(nlc, data, 31, MSG_RAW | MSG_PEEK);
+ if (recvResult <= 0) return SOCKET_ERROR;
+
+ data[recvResult] = 0;
+
+ peol1 = strchr(data, '\n');
+ if (peol1 != NULL)
+ {
+ char *peol2 = first ? peol1 : strchr(peol1 + 1, '\n');
+ if (peol2 != NULL)
+ {
+ int sz = peol2 - data + 1;
+ int r = strtol(first ? data : peol1 + 1, NULL, 16);
+ if (r == 0)
+ {
+ char *peol3 = strchr(peol2 + 1, '\n');
+ if (peol3 == NULL) continue;
+ sz = peol3 - data + 1;
+ }
+ NLRecv(nlc, data, sz, MSG_RAW | flags);
+ return r;
+ }
+ else
+ if (recvResult >= 31) return SOCKET_ERROR;
+ }
+ }
+}
+
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection* nlc, DWORD hflags, DWORD dflags, bool isConnect)
+{
+ int dataLen = -1, i, chunkhdr = 0;
+ bool chunked = false;
+ int cenc = 0, cenctype = 0, close = 0;
+
+next:
+ NETLIBHTTPREQUEST *nlhrReply = (NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc, hflags);
+ if (nlhrReply == NULL)
+ return NULL;
+
+ if (nlhrReply->resultCode == 100)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ goto next;
+ }
+
+ for (i=0; i<nlhrReply->headersCount; i++)
+ {
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Content-Length"))
+ dataLen = atoi(nlhrReply->headers[i].szValue);
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Content-Encoding"))
+ {
+ cenc = i;
+ if (strstr(nlhrReply->headers[i].szValue, "gzip"))
+ cenctype = 1;
+ else if (strstr(nlhrReply->headers[i].szValue, "deflate"))
+ cenctype = 2;
+ }
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Connection"))
+ close = !lstrcmpiA(nlhrReply->headers[i].szValue, "close");
+
+ if (!lstrcmpiA(nlhrReply->headers[i].szName, "Transfer-Encoding") &&
+ !lstrcmpiA(nlhrReply->headers[i].szValue, "chunked"))
+ {
+ chunked = true;
+ chunkhdr = i;
+ dataLen = -1;
+ }
+ }
+
+ if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0)))
+ {
+ int recvResult, chunksz = -1;
+ int dataBufferAlloced;
+
+ if (chunked)
+ {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags);
+ if (chunksz == SOCKET_ERROR)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ dataLen = chunksz;
+ }
+ dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+
+ while (chunksz != 0)
+ {
+ for(;;)
+ {
+ recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT,
+ nlhrReply->pData + nlhrReply->dataLength,
+ dataBufferAlloced - nlhrReply->dataLength - 1,
+ dflags | (cenctype ? MSG_NODUMP : 0));
+
+ if (recvResult == 0) break;
+ if (recvResult == SOCKET_ERROR)
+ {
+ NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply);
+ return NULL;
+ }
+ nlhrReply->dataLength += recvResult;
+
+ if (dataLen >= 0)
+ {
+ if (nlhrReply->dataLength >= dataLen) break;
+ }
+ else
+ {
+ if ((dataBufferAlloced - nlhrReply->dataLength) < 256)
+ {
+ dataBufferAlloced += 2048;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ if(nlhrReply->pData == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ }
+ }
+ Sleep(10);
+ }
+
+ if (chunked)
+ {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags);
+ if (chunksz == SOCKET_ERROR)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return NULL;
+ }
+ dataLen += chunksz;
+ dataBufferAlloced += chunksz;
+
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ }
+ else
+ break;
+ }
+
+ nlhrReply->pData[nlhrReply->dataLength] = '\0';
+ }
+
+ if (chunked)
+ {
+ nlhrReply->headers[chunkhdr].szName = ( char* )mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
+ lstrcpyA(nlhrReply->headers[chunkhdr].szName, "Content-Length");
+
+ nlhrReply->headers[chunkhdr].szValue = ( char* )mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16);
+ mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength);
+ }
+
+ if (cenctype)
+ {
+ int bufsz = nlhrReply->dataLength;
+ char* szData = NULL;
+
+ switch (cenctype)
+ {
+ case 1:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS);
+ break;
+
+ case 2:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS);
+ if (bufsz < 0)
+ {
+ bufsz = nlhrReply->dataLength;
+ szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS);
+ }
+ break;
+ }
+
+ if (bufsz > 0)
+ {
+ NetlibDumpData(nlc, (PBYTE)szData, bufsz, 0, dflags);
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = szData;
+ nlhrReply->dataLength = bufsz;
+
+ mir_free(nlhrReply->headers[cenc].szName);
+ mir_free(nlhrReply->headers[cenc].szValue);
+ memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0]));
+ }
+ else if (bufsz == 0)
+ {
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = NULL;
+ nlhrReply->dataLength = 0;
+ }
+ }
+
+ if (close &&
+ (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) &&
+ (!isConnect || nlhrReply->resultCode != 200))
+ NetlibDoClose(nlc);
+
+ return nlhrReply;
+}
diff --git a/src/modules/netlib/netlibhttpproxy.cpp b/src/modules/netlib/netlibhttpproxy.cpp
new file mode 100644
index 0000000000..153d53660f
--- /dev/null
+++ b/src/modules/netlib/netlibhttpproxy.cpp
@@ -0,0 +1,522 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+typedef enum
+{
+ reqHelloGet,
+ reqOldGet,
+ reqOldPost,
+ reqNewPost,
+}
+RequestType;
+
+
+static int HttpGatewayReadSetResult(NetlibConnection *nlc, char *buf, int num, int peek)
+{
+ if (nlc->dataBufferLen == 0) return 0;
+
+ int bytes = min(num, nlc->dataBufferLen);
+ int rbytes = nlc->dataBufferLen - bytes;
+
+ memcpy(buf, nlc->dataBuffer, bytes);
+ if (!peek)
+ {
+ memmove(nlc->dataBuffer, nlc->dataBuffer + bytes, rbytes);
+ nlc->dataBufferLen = rbytes;
+ }
+
+ return bytes;
+}
+
+void HttpGatewayRemovePacket(NetlibConnection *nlc, int pck)
+{
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+ while (pck-- && nlc->pHttpProxyPacketQueue != NULL)
+ {
+ NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue;
+ nlc->pHttpProxyPacketQueue = nlc->pHttpProxyPacketQueue->next;
+
+ mir_free(p->dataBuffer);
+ mir_free(p);
+ }
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+}
+
+
+static bool NetlibHttpGatewaySend(struct NetlibConnection *nlc, RequestType reqType, const char *buf, int len)
+{
+ NETLIBHTTPREQUEST nlhrSend = {0};
+ char szUrl[512];
+
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.nlc = nlc;
+
+ nlhrSend.pData = (char*)buf;
+ nlhrSend.dataLength = len;
+
+ nlhrSend.flags = NLHRF_GENERATEHOST | NLHRF_DUMPPROXY | NLHRF_SMARTAUTHHEADER | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ if (nlc->nlhpi.flags & NLHPIF_HTTP11) nlhrSend.flags |= NLHRF_HTTP11;
+
+ switch (reqType)
+ {
+ case reqHelloGet:
+ nlhrSend.requestType = REQUEST_GET;
+ nlhrSend.szUrl=nlc->nlu->user.szHttpGatewayHello;
+ break;
+
+ case reqOldGet:
+ nlhrSend.requestType = REQUEST_GET;
+ nlhrSend.timeout = -1;
+ if ((nlc->nlhpi.flags & NLHPIF_USEGETSEQUENCE) && (nlc->nlhpi.szHttpGetUrl != NULL))
+ {
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s%u", nlc->nlhpi.szHttpGetUrl, nlc->nlhpi.firstGetSequence++);
+ if (nlc->nlhpi.flags & NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstPostSequence++;
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+ nlhrSend.szUrl = szUrl;
+ }
+ else
+ nlhrSend.szUrl = nlc->nlhpi.szHttpGetUrl;
+ break;
+
+ case reqOldPost:
+ nlhrSend.requestType = REQUEST_POST;
+ if ((nlc->nlhpi.flags & NLHPIF_USEPOSTSEQUENCE) && (nlc->nlhpi.szHttpPostUrl != NULL))
+ {
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s%u", nlc->nlhpi.szHttpPostUrl, nlc->nlhpi.firstPostSequence);
+ nlhrSend.szUrl = szUrl;
+ }
+ else
+ nlhrSend.szUrl = nlc->nlhpi.szHttpPostUrl;
+ break;
+
+ case reqNewPost:
+ nlhrSend.requestType = REQUEST_POST;
+ nlhrSend.szUrl = nlc->nlhpi.szHttpPostUrl;
+ break;
+ }
+
+ if (nlc->usingDirectHttpGateway)
+ {
+ NETLIBOPENCONNECTION nloc;
+ NetlibConnFromUrl(nlhrSend.szUrl, false, nloc);
+
+ bool sameHost = lstrcmpA(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
+
+ if (!sameHost)
+ {
+ NetlibDoClose(nlc);
+
+ mir_free((char*)nlc->nloc.szHost);
+ nlc->nloc = nloc;
+ if (!NetlibDoConnect(nlc))
+ return false;
+ }
+ else
+ mir_free((char*)nloc.szHost);
+ }
+
+ nlhrSend.headersCount = 3;
+ nlhrSend.headers = (NETLIBHTTPHEADER*)alloca(sizeof(NETLIBHTTPHEADER) * nlhrSend.headersCount);
+ nlhrSend.headers[0].szName = "User-Agent";
+ nlhrSend.headers[0].szValue = nlc->nlu->user.szHttpGatewayUserAgent;
+ nlhrSend.headers[1].szName = "Cache-Control";
+ nlhrSend.headers[1].szValue = "no-cache, no-store ";
+ nlhrSend.headers[2].szName = "Pragma";
+ nlhrSend.headers[2].szValue = "no-cache";
+// nlhrSend.headers[3].szName = "Accept-Encoding";
+// nlhrSend.headers[3].szValue = "deflate, gzip";
+
+ return NetlibHttpSendRequest((WPARAM)nlc,(LPARAM)&nlhrSend) != SOCKET_ERROR;
+}
+
+static bool NetlibHttpGatewayStdPost(NetlibConnection *nlc, int& numPackets)
+{
+ int np = 0, len = 0;
+
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue;
+ while (p != NULL && np < nlc->nlhpi.combinePackets) { ++np; len += p->dataBufferLen; p = p->next;}
+
+ char *buf = (char*)alloca(len);
+
+ numPackets = np;
+ int dlen = 0;
+
+ p = nlc->pHttpProxyPacketQueue;
+ while (np--)
+ {
+ memcpy(buf + dlen, p->dataBuffer, p->dataBufferLen);
+ dlen += p->dataBufferLen;
+ p = p->next;
+ }
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ return NetlibHttpGatewaySend(nlc, reqNewPost, buf, len);
+}
+
+static bool NetlibHttpGatewayOscarPost(NetlibConnection *nlc, const char *buf, int len, int flags)
+{
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+ NetlibConnection nlcSend = {0};
+
+ nlcSend.handleType = NLH_CONNECTION;
+ nlcSend.nlu = nlc->nlu;
+ nlcSend.nlhpi = nlc->nlhpi;
+ nlcSend.s = nlc->s2;
+ nlcSend.usingHttpGateway = nlc->usingHttpGateway;
+ nlcSend.szProxyServer = nlc->szProxyServer;
+ nlcSend.wProxyPort = nlc->wProxyPort;
+ nlcSend.proxyType = nlc->proxyType;
+
+ if (!NetlibReconnect(&nlcSend)) return false;
+ nlc->s2 = nlcSend.s;
+
+ nlcSend.hOkToCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ NetlibInitializeNestedCS(&nlcSend.ncsRecv);
+ NetlibInitializeNestedCS(&nlcSend.ncsSend);
+
+ bool res = NetlibHttpGatewaySend(&nlcSend, reqOldPost, buf, len);
+ if (res)
+ {
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(&nlcSend, flags | MSG_RAW | MSG_DUMPPROXY, MSG_RAW | MSG_DUMPPROXY);
+ if (nlhrReply != NULL)
+ {
+ if (nlhrReply->resultCode != 200)
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ res = false;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ }
+ else
+ res = false;
+ }
+
+ NetlibDeleteNestedCS(&nlcSend.ncsSend);
+ NetlibDeleteNestedCS(&nlcSend.ncsRecv);
+ CloseHandle(nlcSend.hOkToCloseEvent);
+
+ nlc->s2 = nlcSend.s;
+ mir_free((char*)nlcSend.nloc.szHost);
+
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+
+ nlc->nlhpi.firstPostSequence++;
+ if (nlc->nlhpi.flags & NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstGetSequence++;
+
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ return res;
+}
+
+ int NetlibHttpGatewayPost(struct NetlibConnection *nlc, const char *buf, int len, int flags)
+{
+ struct NetlibHTTPProxyPacketQueue *p;
+
+ if (nlc->nlhpi.szHttpGetUrl != NULL)
+ {
+ return NetlibHttpGatewayOscarPost(nlc, buf, len, flags) ? len : SOCKET_ERROR;
+ }
+
+ /*
+ * Gena01 - many changes here, do compare against the other version.
+ *
+ * Change #1: simplify to use similar code to GET
+ * Change #2: we need to allow to parse POST reply if szHttpGetUrl is NULL
+ * Change #3: Keep connection open if we need to.
+ *
+ * Impact: NONE! Since currently miranda doesn't allow szHttpGetUrl to be NULL, it will not connect
+ * with the new plugins that use this code.
+ */
+
+ p = ( NetlibHTTPProxyPacketQueue* )mir_alloc(sizeof(struct NetlibHTTPProxyPacketQueue));
+ p->dataBuffer = ( PBYTE )mir_alloc(len);
+ memcpy(p->dataBuffer, buf, len);
+ p->dataBufferLen = len;
+ p->next = NULL;
+
+ /*
+ * Now check to see where to insert this in our queue
+ */
+ EnterCriticalSection(&nlc->csHttpSequenceNums);
+ if (nlc->pHttpProxyPacketQueue == NULL)
+ {
+ nlc->pHttpProxyPacketQueue = p;
+ }
+ else
+ {
+ struct NetlibHTTPProxyPacketQueue *t = nlc->pHttpProxyPacketQueue;
+
+ while (t->next != NULL) t = t->next;
+ t->next = p;
+ }
+ LeaveCriticalSection(&nlc->csHttpSequenceNums);
+
+ /*
+ * Gena01 - fake a Send!! tell 'em all is ok. We catch errors in Recv.
+ */
+ return len;
+}
+
+#define NETLIBHTTP_RETRYCOUNT 3
+#define NETLIBHTTP_RETRYTIMEOUT 2000
+
+int NetlibHttpGatewayRecv(struct NetlibConnection *nlc, char *buf, int len, int flags)
+{
+ bool peek = (flags & MSG_PEEK) != 0;
+
+ if (nlc->dataBufferLen != 0 && (!peek || nlc->dataBufferLen >= len))
+ {
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+
+ for (int retryCount = 0; retryCount < NETLIBHTTP_RETRYCOUNT; )
+ {
+ if (nlc->nlhpi.szHttpGetUrl == NULL && retryCount == 0)
+ {
+ if (nlc->pollingTimeout == 0) nlc->pollingTimeout = 30;
+
+ /* We Need to sleep/wait for the data to send before we do receive */
+ for (int pollCount = nlc->pollingTimeout; pollCount--; )
+ {
+ if (nlc->pHttpProxyPacketQueue != NULL && GetTickCount() - nlc->lastPost > 1000)
+ break;
+
+ if (nlc->termRequested || (SleepEx(1000, TRUE) && Miranda_Terminated()))
+ return SOCKET_ERROR;
+ }
+
+ nlc->lastPost = GetTickCount();
+ if (nlc->pHttpProxyPacketQueue == NULL && nlc->nlu->user.pfnHttpGatewayWrapSend != NULL)
+ {
+ if (nlc->nlu->user.pfnHttpGatewayWrapSend(nlc, (PBYTE)"", 0, MSG_NOHTTPGATEWAYWRAP, NetlibSend) == SOCKET_ERROR)
+ return SOCKET_ERROR;
+ }
+ }
+
+ int numPackets = 0;
+ if (nlc->nlhpi.szHttpGetUrl)
+ {
+ if (!NetlibHttpGatewaySend(nlc, reqOldGet, NULL, 0))
+ {
+ if (GetLastError() == ERROR_ACCESS_DENIED || nlc->termRequested)
+ break;
+
+ ++retryCount;
+ continue;
+ }
+ }
+ else
+ {
+ if (!NetlibHttpGatewayStdPost(nlc, numPackets))
+ {
+ if (GetLastError() == ERROR_ACCESS_DENIED || nlc->termRequested)
+ break;
+
+ ++retryCount;
+ continue;
+ }
+ }
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, flags | MSG_RAW | MSG_DUMPPROXY, MSG_RAW | MSG_DUMPPROXY);
+ if (nlhrReply == NULL) return SOCKET_ERROR;
+
+ if (nlc->nlu->user.pfnHttpGatewayUnwrapRecv && !(flags & MSG_NOHTTPGATEWAYWRAP))
+ {
+ nlhrReply->pData = (char*)nlc->nlu->user.pfnHttpGatewayUnwrapRecv(nlhrReply,
+ (PBYTE)nlhrReply->pData, nlhrReply->dataLength, &nlhrReply->dataLength, mir_realloc);
+/*
+ if (newBuffer == NULL)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return SOCKET_ERROR;
+ }
+ else
+ nlhrReply->pData = (char*)newBuffer;
+*/
+ }
+
+ if (nlhrReply->resultCode >= 300)
+ {
+ int resultCode = nlhrReply->resultCode;
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ if (nlc->nlhpi.szHttpGetUrl && resultCode != 404)
+ {
+ NetlibLogf(nlc->nlu, "Error received from proxy, retrying");
+ continue;
+ }
+ else
+ {
+ NetlibLogf(nlc->nlu, "Error received from proxy, retry attempts exceeded (%u)", retryCount);
+ SetLastError(ERROR_GEN_FAILURE);
+ return SOCKET_ERROR;
+ }
+ }
+ else
+ {
+ retryCount = 0;
+ HttpGatewayRemovePacket(nlc, numPackets);
+ }
+
+ if (nlhrReply->dataLength)
+ {
+ if (peek)
+ {
+ int rbytes = nlc->dataBufferLen + nlhrReply->dataLength;
+
+ nlc->dataBuffer = (PBYTE)mir_realloc(nlc->dataBuffer, rbytes);
+ memcpy(nlc->dataBuffer + nlc->dataBufferLen, nlhrReply->pData, nlhrReply->dataLength);
+ nlc->dataBufferLen = rbytes;
+
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+ else
+ {
+ int bytes = min(len, nlhrReply->dataLength);
+ int rbytes = nlhrReply->dataLength - bytes;
+
+ memcpy(buf, nlhrReply->pData, bytes);
+
+ nlc->dataBuffer = (PBYTE)mir_realloc(nlc->dataBuffer, rbytes);
+ if (rbytes) memcpy(nlc->dataBuffer, nlhrReply->pData + bytes, rbytes);
+ nlc->dataBufferLen = rbytes;
+
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return bytes;
+ }
+ }
+ else
+ {
+ if ((peek && nlc->dataBufferLen != 0) || nlhrReply->pData)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return HttpGatewayReadSetResult(nlc, buf, len, peek);
+ }
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ }
+
+ SetLastError(ERROR_GEN_FAILURE);
+ return SOCKET_ERROR;
+}
+
+int NetlibInitHttpConnection(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{
+ NETLIBHTTPREQUEST *nlhrReply = NULL;
+
+ nlc->nlhpi.firstGetSequence = 1;
+ nlc->nlhpi.firstPostSequence = 1;
+
+ if (nlu->user.szHttpGatewayHello != NULL)
+ {
+ nlc->usingHttpGateway = true;
+ if (NetlibHttpGatewaySend(nlc, reqHelloGet, NULL, 0))
+ nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW);
+ nlc->usingHttpGateway = false;
+ if (nlhrReply == NULL) return 0;
+
+ if (nlhrReply->resultCode != 200)
+ {
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ }
+ if (!nlu->user.pfnHttpGatewayInit(nlc, nloc, nlhrReply))
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+
+ /*
+ * Gena01 - Ok, we should be able to use just POST. Needed for Yahoo, NO GET requests
+ */
+ if(nlc->nlhpi.szHttpPostUrl == NULL)
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ nlc->usingHttpGateway = true;
+
+ //now properly connected
+ if (nlu->user.pfnHttpGatewayBegin && !nlu->user.pfnHttpGatewayBegin(nlc, nloc))
+ return 0;
+
+ return 1;
+}
+
+INT_PTR NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBHTTPPROXYINFO *nlhpi=(NETLIBHTTPPROXYINFO*)lParam;
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == NULL ||
+ nlhpi->cbSize < (sizeof(NETLIBHTTPPROXYINFO) - sizeof(int)) ||
+ nlhpi->szHttpPostUrl == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+
+ nlc->nlhpi.combinePackets = 1;
+ memcpy(&nlc->nlhpi, nlhpi, min(nlhpi->cbSize, sizeof(*nlhpi)));
+ if (nlc->nlhpi.combinePackets == 0) nlc->nlhpi.combinePackets = 1;
+
+ nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl);
+ nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl);
+
+ return 1;
+}
+
+INT_PTR NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibUser * nu = (struct NetlibUser*)wParam;
+ if (GetNetlibHandleType(nu)!=NLH_USER) return ERROR_INVALID_PARAMETER;
+ mir_free(nu->szStickyHeaders);
+ nu->szStickyHeaders = mir_strdup((char*)lParam); // pointer is ours
+ return 0;
+}
+
+INT_PTR NetlibHttpSetPollingTimeout(WPARAM wParam, LPARAM lParam)
+{
+ int oldTimeout;
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ if (GetNetlibHandleType(nlc)!=NLH_CONNECTION) return -1;
+ oldTimeout = nlc->pollingTimeout;
+ nlc->pollingTimeout = lParam;
+ return oldTimeout;
+}
diff --git a/src/modules/netlib/netliblog.cpp b/src/modules/netlib/netliblog.cpp
new file mode 100644
index 0000000000..fb3fe1d5f2
--- /dev/null
+++ b/src/modules/netlib/netliblog.cpp
@@ -0,0 +1,637 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "netlib.h"
+#include "../srfile/file.h"
+
+#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
+
+extern HANDLE hConnectionHeaderMutex;
+
+#define TIMEFORMAT_NONE 0
+#define TIMEFORMAT_HHMMSS 1
+#define TIMEFORMAT_MILLISECONDS 2
+#define TIMEFORMAT_MICROSECONDS 3
+struct {
+ HWND hwndOpts;
+ int toOutputDebugString;
+ int toFile;
+ int toLog;
+ TCHAR* szFile;
+ TCHAR* szUserFile;
+ int timeFormat;
+ int showUser;
+ int dumpSent,dumpRecv,dumpProxy,dumpSsl;
+ int textDumps,autoDetectText;
+ CRITICAL_SECTION cs;
+ int save;
+} logOptions = {0};
+
+typedef struct {
+ const char* pszHead;
+ const char* pszMsg;
+} LOGMSG;
+
+static __int64 mirandaStartTime,perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = NULL;
+
+static const TCHAR* szTimeFormats[] =
+{
+ _T( "No times" ),
+ _T( "Standard hh:mm:ss times" ),
+ _T( "Times in milliseconds" ),
+ _T( "Times in microseconds" )
+};
+
+static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ logOptions.hwndOpts=hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg,IDC_DUMPRECV,logOptions.dumpRecv?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPSENT,logOptions.dumpSent?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPPROXY,logOptions.dumpProxy?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_DUMPSSL,logOptions.dumpSsl?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TEXTDUMPS,logOptions.textDumps?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_AUTODETECTTEXT,logOptions.autoDetectText?BST_CHECKED:BST_UNCHECKED);
+ { int i;
+ for( i=0; i < SIZEOF(szTimeFormats); i++ )
+ SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_ADDSTRING,0,(LPARAM)TranslateTS( szTimeFormats[i] ));
+ }
+ SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_SETCURSEL,logOptions.timeFormat,0);
+ CheckDlgButton(hwndDlg,IDC_SHOWNAMES,logOptions.showUser?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TOOUTPUTDEBUGSTRING,logOptions.toOutputDebugString?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg,IDC_TOFILE,logOptions.toFile?BST_CHECKED:BST_UNCHECKED);
+ SetDlgItemText(hwndDlg,IDC_FILENAME,logOptions.szUserFile);
+ SetDlgItemText(hwndDlg,IDC_PATH,logOptions.szFile);
+ CheckDlgButton(hwndDlg,IDC_SHOWTHISDLGATSTART,DBGetContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",0)?BST_CHECKED:BST_UNCHECKED);
+ { DBVARIANT dbv;
+ if(!DBGetContactSettingString(NULL, "Netlib", "RunAtStart",&dbv)) {
+ SetDlgItemTextA(hwndDlg,IDC_RUNATSTART,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ logOptions.save = 0;
+ {
+ TVINSERTSTRUCT tvis = {0};
+ int i;
+ HWND hwndFilter = GetDlgItem(hwndDlg,IDC_FILTER);
+
+ SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ tvis.hParent=NULL;
+ tvis.hInsertAfter=TVI_SORT;
+ tvis.item.mask=TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
+ tvis.item.stateMask=TVIS_STATEIMAGEMASK;
+
+ for (i = 0; i < netlibUser.getCount(); ++i)
+ {
+ tvis.item.pszText=netlibUser[i]->user.ptszDescriptiveName;
+ tvis.item.lParam=i;
+ tvis.item.state=INDEXTOSTATEIMAGEMASK( (netlibUser[i]->toLog) ? 2 : 1 );
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ tvis.item.lParam=-1;
+ tvis.item.pszText=TranslateT("(Miranda Core Logging)");
+ tvis.item.state=INDEXTOSTATEIMAGEMASK( (logOptions.toLog) ? 2 : 1 );
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+/*
+ case IDC_DUMPRECV:
+ case IDC_DUMPSENT:
+ case IDC_DUMPPROXY:
+ case IDC_TEXTDUMPS:
+ case IDC_AUTODETECTTEXT:
+ case IDC_TIMEFORMAT:
+ case IDC_SHOWNAMES:
+ case IDC_TOOUTPUTDEBUGSTRING:
+ case IDC_TOFILE:
+ case IDC_SHOWTHISDLGATSTART:
+ case IDC_RUNATSTART:
+ break;
+*/
+ case IDC_FILENAME:
+ if(HIWORD(wParam)!=EN_CHANGE) break;
+ if((HWND)lParam==GetFocus())
+ CheckDlgButton(hwndDlg,IDC_TOFILE,BST_CHECKED);
+
+ {
+ TCHAR path[MAX_PATH];
+ GetWindowText((HWND)lParam, path, MAX_PATH);
+
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(path);
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ SetDlgItemText(hwndDlg, IDC_PATH, path);
+ mir_free(pszNewPath);
+ }
+ break;
+ case IDC_FILENAMEBROWSE:
+ case IDC_RUNATSTARTBROWSE:
+ { TCHAR str[MAX_PATH+2];
+ OPENFILENAME ofn={0};
+ TCHAR filter[512],*pfilter;
+
+ GetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str,SIZEOF(str));
+ ofn.lStructSize=OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner=hwndDlg;
+ ofn.Flags=OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if (LOWORD(wParam)==IDC_FILENAMEBROWSE) {
+ ofn.lpstrTitle=TranslateT("Select where log file will be created");
+ } else {
+ ofn.Flags|=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
+ ofn.lpstrTitle=TranslateT("Select program to be run");
+ }
+ _tcscpy(filter,TranslateT("All Files"));
+ _tcscat(filter,_T(" (*)"));
+ pfilter=filter+lstrlen(filter)+1;
+ _tcscpy(pfilter,_T("*"));
+ pfilter=pfilter+lstrlen(pfilter)+1;
+ *pfilter='\0';
+ ofn.lpstrFilter=filter;
+ ofn.lpstrFile=str;
+ ofn.nMaxFile=SIZEOF(str)-2;
+ ofn.nMaxFileTitle=MAX_PATH;
+ if (LOWORD(wParam)==IDC_FILENAMEBROWSE) {
+ if(!GetSaveFileName(&ofn)) return 1;
+ } else {
+ if(!GetOpenFileName(&ofn)) return 1;
+ }
+ if(LOWORD(wParam)==IDC_RUNATSTARTBROWSE && _tcschr(str,' ')!=NULL) {
+ MoveMemory(str+1,str,SIZEOF(str)-2);
+ str[0]='"';
+ lstrcat(str,_T("\""));
+ }
+ SetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str);
+ break;
+ }
+ case IDC_RUNNOW:
+ { TCHAR str[MAX_PATH+1];
+ STARTUPINFO si={0};
+ PROCESS_INFORMATION pi;
+ GetDlgItemText(hwndDlg,IDC_RUNATSTART,str,MAX_PATH);
+ si.cb=sizeof(si);
+ if(str[0]) CreateProcess(NULL,str,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
+ }
+ break;
+ case IDC_SAVE:
+ logOptions.save = 1;
+ //
+ case IDOK:
+ {
+ TCHAR str[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, MAX_PATH);
+ DBWriteContactSettingTString(NULL, "Netlib", "RunAtStart",str);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowLogOptsAtStart",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_SHOWTHISDLGATSTART));
+
+ EnterCriticalSection(&logOptions.cs);
+
+ mir_free(logOptions.szUserFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_FILENAME), str, MAX_PATH );
+ logOptions.szUserFile = mir_tstrdup(str);
+
+ mir_free(logOptions.szFile);
+ GetWindowText(GetDlgItem(hwndDlg,IDC_PATH), str, MAX_PATH );
+ logOptions.szFile = mir_tstrdup(str);
+
+ logOptions.dumpRecv=IsDlgButtonChecked(hwndDlg,IDC_DUMPRECV);
+ logOptions.dumpSent=IsDlgButtonChecked(hwndDlg,IDC_DUMPSENT);
+ logOptions.dumpProxy=IsDlgButtonChecked(hwndDlg,IDC_DUMPPROXY);
+ logOptions.dumpSsl=IsDlgButtonChecked(hwndDlg,IDC_DUMPSSL);
+ logOptions.textDumps=IsDlgButtonChecked(hwndDlg,IDC_TEXTDUMPS);
+ logOptions.autoDetectText=IsDlgButtonChecked(hwndDlg,IDC_AUTODETECTTEXT);
+ logOptions.timeFormat=SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_GETCURSEL,0,0);
+ logOptions.showUser=IsDlgButtonChecked(hwndDlg,IDC_SHOWNAMES);
+ logOptions.toOutputDebugString=IsDlgButtonChecked(hwndDlg,IDC_TOOUTPUTDEBUGSTRING);
+ logOptions.toFile=IsDlgButtonChecked(hwndDlg,IDC_TOFILE);
+
+ LeaveCriticalSection(&logOptions.cs);
+ }
+ {
+ HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER);
+ TVITEM tvi={0};
+ BOOL checked;
+
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT;
+ tvi.hItem=TreeView_GetRoot(hwndFilter);
+
+ while(tvi.hItem)
+ {
+ TreeView_GetItem(hwndFilter,&tvi);
+ checked = ((tvi.state&TVIS_STATEIMAGEMASK)>>12==2);
+
+ if (tvi.lParam == -1) {
+ logOptions.toLog = checked;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL, "Netlib", "NLlog",checked);
+ }
+ else
+ if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ if ( logOptions.save )
+ DBWriteContactSettingDword(NULL,netlibUser[tvi.lParam]->user.szSettingsModule,"NLlog",checked);
+ }
+
+ tvi.hItem=TreeView_GetNextSibling(hwndFilter,tvi.hItem);
+ }
+ }
+
+ if ( logOptions.save ) {
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpRecv",(BYTE)logOptions.dumpRecv);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSent",(BYTE)logOptions.dumpSent);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpProxy",(BYTE)logOptions.dumpProxy);
+ DBWriteContactSettingByte(NULL, "Netlib", "DumpSsl",(BYTE)logOptions.dumpSsl);
+ DBWriteContactSettingByte(NULL, "Netlib", "TextDumps",(BYTE)logOptions.textDumps);
+ DBWriteContactSettingByte(NULL, "Netlib", "AutoDetectText",(BYTE)logOptions.autoDetectText);
+ DBWriteContactSettingByte(NULL, "Netlib", "TimeFormat",(BYTE)logOptions.timeFormat);
+ DBWriteContactSettingByte(NULL, "Netlib", "ShowUser",(BYTE)logOptions.showUser);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToOutputDebugString",(BYTE)logOptions.toOutputDebugString);
+ DBWriteContactSettingByte(NULL, "Netlib", "ToFile",(BYTE)logOptions.toFile);
+ DBWriteContactSettingTString(NULL, "Netlib", "File", logOptions.szFile ? logOptions.szUserFile: _T(""));
+ logOptions.save = 0;
+ }
+ else
+ DestroyWindow(hwndDlg);
+
+ break;
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts=NULL;
+ break;
+ }
+ return FALSE;
+}
+
+void NetlibLogShowOptions(void)
+{
+ if(logOptions.hwndOpts==NULL)
+ logOptions.hwndOpts=CreateDialog(hMirandaInst,MAKEINTRESOURCE(IDD_NETLIBLOGOPTS),NULL,LogOptionsDlgProc);
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+static INT_PTR NetlibLog(WPARAM wParam, LPARAM lParam)
+{
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibUser nludummy;
+ const char *pszMsg = (const char*)lParam;
+ char szTime[32], szHead[128];
+ LARGE_INTEGER liTimeNow;
+ DWORD dwOriginalLastError;
+
+ if (!bIsActive)
+ return 0;
+
+ if ((nlu != NULL && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ if (nlu == NULL) /* if the Netlib user handle is NULL, just pretend its not */
+ {
+ if (!logOptions.toLog)
+ return 1;
+ nlu = &nludummy;
+ nlu->user.szSettingsModule = "(NULL)";
+ }
+ else if (!nlu->toLog)
+ return 1;
+
+ dwOriginalLastError = GetLastError();
+ switch (logOptions.timeFormat)
+ {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
+ NULL, NULL, szTime, SIZEOF(szTime));
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%03I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, SIZEOF(szTime), "%I64u.%06I64u", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = '\0';
+ break;
+ }
+ if(logOptions.timeFormat || logOptions.showUser)
+ mir_snprintf(szHead, SIZEOF(szHead) - 1, "[%s%s%s] ", szTime,
+ (logOptions.showUser && logOptions.timeFormat) ? " " : "",
+ logOptions.showUser ? nlu->user.szSettingsModule : "");
+ else
+ szHead[0]=0;
+
+ if(logOptions.toOutputDebugString)
+ {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && logOptions.szFile[0])
+ {
+ EnterCriticalSection(&logOptions.cs);
+
+ FILE *fp;
+ fp = _tfopen(logOptions.szFile, _T("ab"));
+ if (!fp)
+ {
+ CreatePathToFileT(logOptions.szFile);
+ fp = _tfopen(logOptions.szFile, _T("at"));
+ }
+ if (fp)
+ {
+ size_t len = strlen(pszMsg);
+ fprintf(fp,"%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ fclose(fp);
+ }
+ LeaveCriticalSection(&logOptions.cs);
+ }
+
+ if (((THook*)hLogEvent)->subscriberCount)
+ {
+ LOGMSG logMsg = { szHead, pszMsg };
+ CallHookSubscribers(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+ }
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+static INT_PTR NetlibLogW(WPARAM wParam, LPARAM lParam)
+{
+ const wchar_t *pszMsg = (const wchar_t*)lParam;
+ char* szMsg = Utf8EncodeUcs2(pszMsg);
+ INT_PTR res = NetlibLog(wParam, (LPARAM)szMsg);
+ mir_free(szMsg);
+ return res;
+}
+
+void NetlibLogf(NetlibUser* nlu, const char *fmt, ...)
+{
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ va_list va;
+ char szText[1024];
+
+ va_start(va,fmt);
+ mir_vsnprintf(szText, sizeof(szText), fmt, va);
+ va_end(va);
+
+ NetlibLog((WPARAM)nlu, (LPARAM)szText);
+}
+
+
+void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags)
+{
+ int isText=1;
+ char szTitleLine[128];
+ char *szBuf;
+ int titleLineLen;
+ struct NetlibUser *nlu;
+ bool useStack = false;
+
+ // This section checks a number of conditions and aborts
+ // the dump if the data should not be written to the log
+
+ // Check packet flags
+ if (flags & (MSG_PEEK | MSG_NODUMP))
+ return;
+
+ // Check user's log settings
+ if (!(logOptions.toOutputDebugString ||
+ ((THook*)hLogEvent)->subscriberCount ||
+ (logOptions.toFile && logOptions.szFile[0])))
+ return;
+ if ((sent && !logOptions.dumpSent) ||
+ (!sent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ nlu = nlc ? nlc->nlu : NULL;
+ titleLineLen = mir_snprintf(szTitleLine, SIZEOF(szTitleLine), "(%p:%u) Data %s%s\r\n",
+ nlc, nlc ? nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ // check filter settings
+ if (nlu == NULL)
+ {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ if (!logOptions.textDumps)
+ isText = 0;
+ else if (!(flags&MSG_DUMPASTEXT))
+ {
+ if (logOptions.autoDetectText)
+ {
+ int i;
+ for(i = 0; i<len; i++)
+ {
+ if ((buf[i]<' ' && buf[i]!='\t' && buf[i]!='\r' && buf[i]!='\n') || buf[i]>=0x80)
+ {
+ isText = 0;
+ break;
+ }
+ }
+ }
+ else
+ isText = 0;
+ }
+
+ // Text data
+ if ( isText ) {
+ int sz = titleLineLen + len + 1;
+ useStack = sz <= 8192;
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory( szBuf, szTitleLine, titleLineLen );
+ CopyMemory( szBuf + titleLineLen, (const char*)buf, len );
+ szBuf[titleLineLen + len] = '\0';
+ }
+ // Binary data
+ else {
+ int line, col, colsInLine;
+ char *pszBuf;
+ int sz = titleLineLen + ((len+16)>>4) * 78 + 1;
+ useStack = sz <= 8192;
+
+ szBuf = (char*)(useStack ? alloca(sz) : mir_alloc(sz));
+ CopyMemory(szBuf, szTitleLine, titleLineLen);
+ pszBuf = szBuf + titleLineLen;
+ for ( line = 0; ; line += 16 ) {
+ colsInLine = min(16, len - line);
+
+ if (colsInLine == 16) {
+ PBYTE p = buf + line;
+ pszBuf += wsprintfA(
+ pszBuf, "%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
+ line, p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15] );
+ }
+ else {
+ pszBuf += wsprintfA(pszBuf, "%08X: ", line);
+ // Dump data as hex
+ for (col = 0; col < colsInLine; col++)
+ pszBuf += wsprintfA(pszBuf, "%02X%c", buf[line + col], ((col&3)==3 && col != 15)?'-':' ');
+ // Fill out last line with blanks
+ for ( ; col<16; col++)
+ {
+ lstrcpyA(pszBuf, " ");
+ pszBuf += 3;
+ }
+ *pszBuf++ = ' ';
+ }
+
+ for (col = 0; col < colsInLine; col++)
+ *pszBuf++ = buf[line+col]<' '?'.':(char)buf[line+col];
+
+ if (len-line<=16)
+ break;
+
+ *pszBuf++ = '\r'; // End each line with a break
+ *pszBuf++ = '\n'; // End each line with a break
+ }
+ *pszBuf = '\0';
+ }
+
+ NetlibLog((WPARAM)nlu,(LPARAM)szBuf);
+ if (!useStack) mir_free(szBuf);
+}
+
+void NetlibLogInit(void)
+{
+ DBVARIANT dbv;
+ LARGE_INTEGER li;
+
+ QueryPerformanceFrequency( &li );
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter( &li );
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction( MS_NETLIB_LOGWIN, ShowOptions );
+ CreateServiceFunction( MS_NETLIB_LOG, NetlibLog );
+ CreateServiceFunction( MS_NETLIB_LOGW, NetlibLogW );
+ hLogEvent = CreateHookableEvent( ME_NETLIB_FASTDUMP );
+
+ InitializeCriticalSection(&logOptions.cs);
+ logOptions.dumpRecv = DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", 1 );
+ logOptions.dumpSent = DBGetContactSettingByte( NULL, "Netlib", "DumpSent", 1 );
+ logOptions.dumpProxy = DBGetContactSettingByte( NULL, "Netlib", "DumpProxy", 1 );
+ logOptions.dumpSsl = DBGetContactSettingByte( NULL, "Netlib", "DumpSsl", 0 );
+ logOptions.textDumps = DBGetContactSettingByte( NULL, "Netlib", "TextDumps", 1 );
+ logOptions.autoDetectText = DBGetContactSettingByte( NULL, "Netlib", "AutoDetectText", 1 );
+ logOptions.timeFormat = DBGetContactSettingByte( NULL, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS );
+ logOptions.showUser = DBGetContactSettingByte( NULL, "Netlib", "ShowUser", 1 );
+ logOptions.toOutputDebugString = DBGetContactSettingByte( NULL, "Netlib", "ToOutputDebugString", 0 );
+ logOptions.toFile = DBGetContactSettingByte( NULL, "Netlib", "ToFile", 0 );
+ logOptions.toLog = DBGetContactSettingDword( NULL, "Netlib", "NLlog", 1 );
+
+ if (!DBGetContactSettingTString(NULL, "Netlib", "File", &dbv))
+ {
+ logOptions.szUserFile = mir_tstrdup(dbv.ptszVal);
+ TCHAR *pszNewPath = Utils_ReplaceVarsT(dbv.ptszVal);
+
+ TCHAR path[MAX_PATH];
+ pathToAbsoluteT(pszNewPath, path, NULL);
+ logOptions.szFile = mir_tstrdup(path);
+
+ mir_free(pszNewPath);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ logOptions.szUserFile = mir_tstrdup(_T("%miranda_logpath%\\netlog.txt"));
+ logOptions.szFile = Utils_ReplaceVarsT(logOptions.szUserFile);
+ }
+
+ if ( logOptions.toFile && logOptions.szFile[0] ) {
+ FILE *fp;
+ fp = _tfopen( logOptions.szFile, _T("wt"));
+ if ( fp )
+ fclose(fp);
+ }
+
+ if ( DBGetContactSettingByte( NULL, "Netlib", "ShowLogOptsAtStart", 0 ))
+ NetlibLogShowOptions();
+
+ if ( !DBGetContactSettingTString( NULL, "Netlib", "RunAtStart", &dbv )) {
+ STARTUPINFO si = { 0 };
+ PROCESS_INFORMATION pi;
+ si.cb = sizeof( si );
+ if ( dbv.ptszVal[0] )
+ CreateProcess( NULL, dbv.ptszVal, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
+ DBFreeVariant( &dbv );
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent( hLogEvent ); hLogEvent = NULL;
+ if ( IsWindow( logOptions.hwndOpts ))
+ DestroyWindow( logOptions.hwndOpts );
+ DeleteCriticalSection( &logOptions.cs );
+ mir_free( logOptions.szFile );
+ mir_free( logOptions.szUserFile );
+}
diff --git a/src/modules/netlib/netlibopenconn.cpp b/src/modules/netlib/netlibopenconn.cpp
new file mode 100644
index 0000000000..b9d4753b9a
--- /dev/null
+++ b/src/modules/netlib/netlibopenconn.cpp
@@ -0,0 +1,944 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+extern CRITICAL_SECTION csNetlibUser;
+extern HANDLE hConnectionOpenMutex;
+extern DWORD g_LastConnectionTick;
+extern int connectionTimeout;
+static int iUPnPCleanup = 0;
+
+#define RECV_DEFAULT_TIMEOUT 60000
+
+//returns in network byte order
+DWORD DnsLookup(struct NetlibUser *nlu,const char *szHost)
+{
+ HOSTENT* host;
+ DWORD ip = inet_addr(szHost);
+ if (ip != INADDR_NONE)
+ return ip;
+
+ __try
+ {
+ host = gethostbyname(szHost);
+ if ( host )
+ return *(u_long*)host->h_addr_list[0];
+
+ NetlibLogf(nlu,"%s %d: %s() for host %s failed (%u)",__FILE__,__LINE__,"gethostbyname", szHost, WSAGetLastError());
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {}
+
+ return 0;
+}
+
+int WaitUntilReadable(SOCKET s, DWORD dwTimeout, bool check)
+{
+ fd_set readfd;
+ TIMEVAL tv;
+
+ if (s == INVALID_SOCKET) return SOCKET_ERROR;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ int result = select(0, &readfd, 0, 0, &tv);
+ if (result == 0 && !check) SetLastError(ERROR_TIMEOUT);
+ return result;
+}
+
+int WaitUntilWritable(SOCKET s,DWORD dwTimeout)
+{
+ fd_set writefd;
+ TIMEVAL tv;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&writefd);
+ FD_SET(s, &writefd);
+
+ switch(select(0, 0, &writefd, 0, &tv))
+ {
+ case 0:
+ SetLastError(ERROR_TIMEOUT);
+ case SOCKET_ERROR:
+ return 0;
+ }
+ return 1;
+}
+
+bool RecvUntilTimeout(struct NetlibConnection *nlc, char *buf, int len, int flags, DWORD dwTimeout)
+{
+ int nReceived = 0;
+ DWORD dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout;
+
+ while ((dwTimeNow = GetTickCount()) < dwCompleteTime)
+ {
+ if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false;
+ nReceived = NLRecv(nlc, buf, len, flags);
+ if (nReceived <= 0) return false;
+
+ buf += nReceived;
+ len -= nReceived;
+ if (len <= 0) return true;
+ }
+ SetLastError( ERROR_TIMEOUT );
+ return false;
+}
+
+static int NetlibInitSocks4Connection(NetlibConnection *nlc, NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{
+ // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol
+ if (!nloc->szHost || !nloc->szHost[0]) return 0;
+
+ size_t nHostLen = strlen(nloc->szHost) + 1;
+ size_t nUserLen = nlu->settings.szProxyAuthUser ? strlen(nlu->settings.szProxyAuthUser) + 1 : 1;
+ size_t len = 8 + nUserLen;
+
+ char* pInit = (char*)alloca(len + nHostLen);
+ pInit[0] = 4; // SOCKS4
+ pInit[1] = 1; //connect
+ *(PWORD)&pInit[2] = htons(nloc->wPort);
+
+ if (nUserLen <= 1) pInit[8] = 0;
+ else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen);
+
+ //if cannot resolve host, try resolving through proxy (requires SOCKS4a)
+ DWORD ip = DnsLookup(nlu, nloc->szHost);
+ *(PDWORD)&pInit[4] = ip ? ip : 0x01000000;
+ if (!ip)
+ {
+ memcpy(&pInit[len], nloc->szHost, nHostLen);
+ len += nHostLen;
+ }
+
+ if (NLSend(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR)
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ return 0;
+ }
+
+ char reply[8];
+ if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT))
+ {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+
+ switch ((BYTE)reply[1])
+ {
+ case 90: return 1;
+ case 91: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break;
+ case 93: SetLastError(ERROR_INVALID_ACCESS); break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ NetlibLogf(nlu,"%s %d: Proxy connection failed (%x %u)",__FILE__,__LINE__, (BYTE)reply[1], GetLastError());
+ return 0;
+}
+
+static int NetlibInitSocks5Connection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc)
+{ //rfc1928
+ BYTE buf[258];
+
+ buf[0]=5; //yep, socks5
+ buf[1]=1; //one auth method
+ buf[2]=nlu->settings.useProxyAuth?2:0;
+ if(NLSend(nlc,(char*)buf,3,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ return 0;
+ }
+
+ //confirmation of auth method
+ if (!RecvUntilTimeout(nlc,(char*)buf,2,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ if((buf[1]!=0 && buf[1]!=2)) {
+ SetLastError(ERROR_INVALID_ID_AUTHORITY);
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError());
+ return 0;
+ }
+
+ if(buf[1]==2) { //rfc1929
+ int nUserLen,nPassLen;
+ PBYTE pAuthBuf;
+
+ nUserLen=lstrlenA(nlu->settings.szProxyAuthUser);
+ nPassLen=lstrlenA(nlu->settings.szProxyAuthPassword);
+ pAuthBuf=(PBYTE)mir_alloc(3+nUserLen+nPassLen);
+ pAuthBuf[0]=1; //auth version
+ pAuthBuf[1]=nUserLen;
+ memcpy(pAuthBuf+2,nlu->settings.szProxyAuthUser,nUserLen);
+ pAuthBuf[2+nUserLen]=nPassLen;
+ memcpy(pAuthBuf+3+nUserLen,nlu->settings.szProxyAuthPassword,nPassLen);
+ if(NLSend(nlc,(char*)pAuthBuf,3+nUserLen+nPassLen,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ mir_free(pAuthBuf);
+ return 0;
+ }
+ mir_free(pAuthBuf);
+
+ if (!RecvUntilTimeout(nlc,(char*)buf,2,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ if(buf[1]) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ }
+
+ {
+ PBYTE pInit;
+ int nHostLen;
+ DWORD hostIP;
+
+ if(nlc->dnsThroughProxy) {
+ if((hostIP=inet_addr(nloc->szHost))==INADDR_NONE)
+ nHostLen=lstrlenA(nloc->szHost)+1;
+ else nHostLen=4;
+ }
+ else {
+ if((hostIP=DnsLookup(nlu,nloc->szHost))==0)
+ return 0;
+ nHostLen=4;
+ }
+ pInit=(PBYTE)mir_alloc(6+nHostLen);
+ pInit[0]=5; //SOCKS5
+ pInit[1]= nloc->flags & NLOCF_UDP ? 3 : 1; //connect or UDP
+ pInit[2]=0; //reserved
+ if(hostIP==INADDR_NONE) { //DNS lookup through proxy
+ pInit[3]=3;
+ pInit[4]=nHostLen-1;
+ memcpy(pInit+5,nloc->szHost,nHostLen-1);
+ }
+ else {
+ pInit[3]=1;
+ *(PDWORD)(pInit+4)=hostIP;
+ }
+ *(PWORD)(pInit+4+nHostLen)=htons(nloc->wPort);
+ if(NLSend(nlc,(char*)pInit,6+nHostLen,MSG_DUMPPROXY)==SOCKET_ERROR) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError());
+ mir_free(pInit);
+ return 0;
+ }
+ mir_free(pInit);
+ }
+
+ if (!RecvUntilTimeout(nlc,(char*)buf,5,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+
+ if ( buf[0]!=5 || buf[1] ) {
+ const char* err = "Unknown response";
+ if ( buf[0] != 5 )
+ SetLastError(ERROR_BAD_FORMAT);
+ else
+ {
+ switch(buf[1])
+ {
+ case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break;
+ case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break;
+ case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break;
+ case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break;
+ case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break;
+ case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break;
+ case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break;
+ case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ }
+ NetlibLogf(nlu,"%s %d: Proxy conection failed. %s.",__FILE__,__LINE__, err);
+ return 0;
+ }
+ {
+ int nRecvSize = 0;
+ switch( buf[3] ) {
+ case 1:// ipv4 addr
+ nRecvSize = 5;
+ break;
+ case 3:// dns name addr
+ nRecvSize = buf[4] + 2;
+ break;
+ case 4:// ipv6 addr
+ nRecvSize = 17;
+ break;
+ default:
+ NetlibLogf(nlu,"%s %d: %s() unknown address type (%u)",__FILE__,__LINE__,"NetlibInitSocks5Connection",(int)buf[3]);
+ return 0;
+ }
+ if (!RecvUntilTimeout(nlc,(char*)buf,nRecvSize,MSG_DUMPPROXY,RECV_DEFAULT_TIMEOUT)) {
+ NetlibLogf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"RecvUntilTimeout",GetLastError());
+ return 0;
+ }
+ }
+
+ //connected
+ return 1;
+}
+
+static bool NetlibInitHttpsConnection(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{ //rfc2817
+ NETLIBHTTPREQUEST nlhrSend = {0}, *nlhrReply;
+ char szUrl[512];
+
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.requestType = REQUEST_CONNECT;
+ nlhrSend.flags = NLHRF_GENERATEHOST | NLHRF_DUMPPROXY | NLHRF_SMARTAUTHHEADER | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ if (nlc->dnsThroughProxy)
+ {
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s:%u", nloc->szHost, nloc->wPort);
+ }
+ else
+ {
+ DWORD ip = DnsLookup(nlu, nloc->szHost);
+ if (ip == 0) return false;
+ mir_snprintf(szUrl, SIZEOF(szUrl), "%s:%u", inet_ntoa(*(PIN_ADDR)&ip), nloc->wPort);
+ }
+ nlhrSend.szUrl = szUrl;
+
+ nlc->usingHttpGateway = true;
+
+ if (NetlibHttpSendRequest((WPARAM)nlc, (LPARAM)&nlhrSend) == SOCKET_ERROR)
+ {
+ nlc->usingHttpGateway = false;
+ return 0;
+ }
+ nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true);
+ nlc->usingHttpGateway = false;
+ if (nlhrReply == NULL) return false;
+ if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300)
+ {
+ if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy)
+ {
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ nlc->dnsThroughProxy = 0;
+ return NetlibInitHttpsConnection(nlc, nlu, nloc);
+ }
+
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ NetlibLogf(nlu,"%s %d: %s request failed (%u %s)",__FILE__,__LINE__,nlu->settings.proxyType==PROXYTYPE_HTTP?"HTTP":"HTTPS",nlhrReply->resultCode,nlhrReply->szResultDescr);
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ return 0;
+ }
+ NetlibHttpFreeRequestStruct(0, (LPARAM)nlhrReply);
+ //connected
+ return true;
+}
+
+static void FreePartiallyInitedConnection(struct NetlibConnection *nlc)
+{
+ DWORD dwOriginalLastError=GetLastError();
+
+ if (nlc->s!=INVALID_SOCKET) closesocket(nlc->s);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free((char*)nlc->nloc.szHost);
+ mir_free(nlc->szProxyServer);
+ NetlibDeleteNestedCS(&nlc->ncsSend);
+ NetlibDeleteNestedCS(&nlc->ncsRecv);
+ CloseHandle(nlc->hOkToCloseEvent);
+ DeleteCriticalSection(&nlc->csHttpSequenceNums);
+ mir_free(nlc);
+ SetLastError(dwOriginalLastError);
+}
+
+static bool my_connectIPv4(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc)
+{
+ int rc = 0, retrycnt = 0;
+ u_long notblocking = 1;
+ DWORD lasterr = 0;
+ static const TIMEVAL tv = { 1, 0 };
+
+ unsigned int dwTimeout = (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2) ? nloc->timeout : 0;
+ // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway
+ if (dwTimeout == 0) dwTimeout = 30;
+
+ // this is for XP SP2 where there is a default connection attempt limit of 10/second
+ if (connectionTimeout)
+ {
+ WaitForSingleObject(hConnectionOpenMutex, 10000);
+ int waitdiff = GetTickCount() - g_LastConnectionTick;
+ if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE);
+ g_LastConnectionTick = GetTickCount();
+ ReleaseMutex(hConnectionOpenMutex);
+
+ // might of died in between the wait
+ if (Miranda_Terminated()) return false;
+ }
+
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+
+ if (nlc->proxyType)
+ {
+ if (!nlc->szProxyServer) return false;
+
+ if (nloc)
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort);
+ else
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort);
+
+ sin.sin_port = htons(nlc->wProxyPort);
+ sin.sin_addr.s_addr = DnsLookup(nlc->nlu, nlc->szProxyServer);
+ }
+ else
+ {
+ if (!nloc || !nloc->szHost) return false;
+ NetlibLogf(nlc->nlu,"(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort);
+
+ sin.sin_port = htons(nloc->wPort);
+ sin.sin_addr.s_addr = DnsLookup(nlc->nlu, nloc->szHost);
+ }
+
+retry:
+ nlc->s = socket(AF_INET,nloc->flags & NLOCF_UDP ? SOCK_DGRAM : SOCK_STREAM, 0);
+ if (nlc->s == INVALID_SOCKET) return false;
+
+ // return the socket to non blocking
+ if (ioctlsocket(nlc->s, FIONBIO, &notblocking) != 0) return false;
+
+ if (nlc->nlu->settings.specifyOutgoingPorts && nlc->nlu->settings.szOutgoingPorts && nlc->nlu->settings.szOutgoingPorts[0])
+ {
+ if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, nlc->s, &nlc->nlu->inportnum))
+ NetlibLogf(nlc->nlu,"Netlib connect: Not enough ports for outgoing connections specified");
+ }
+
+ // try a connect
+ if (connect(nlc->s, (LPSOCKADDR)&sin, sizeof(sin)) == 0)
+ {
+ goto unblock;
+ }
+
+ // didn't work, was it cos of nonblocking?
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ rc = SOCKET_ERROR;
+ goto unblock;
+ }
+
+ for (;;)
+ {
+ fd_set r, w, e;
+ FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
+ FD_SET(nlc->s, &r);
+ FD_SET(nlc->s, &w);
+ FD_SET(nlc->s, &e);
+ if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR)
+ break;
+
+ if (rc > 0)
+ {
+ if (FD_ISSET(nlc->s, &w))
+ {
+ // connection was successful
+ rc = 0;
+ }
+ if (FD_ISSET(nlc->s, &r))
+ {
+ // connection was closed
+ rc = SOCKET_ERROR;
+ lasterr = WSAECONNRESET;
+ }
+ if (FD_ISSET(nlc->s, &e))
+ {
+ // connection failed.
+ int len = sizeof(lasterr);
+ rc = SOCKET_ERROR;
+ getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len);
+ if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2)
+ {
+ closesocket(nlc->s);
+ goto retry;
+ }
+ }
+ break;
+ }
+ else if (Miranda_Terminated())
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ else if (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2 &&
+ nloc->waitcallback != NULL && nloc->waitcallback(&dwTimeout) == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ if (--dwTimeout == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+unblock:
+ notblocking = 0;
+ ioctlsocket(nlc->s, FIONBIO, &notblocking);
+ if (lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static bool my_connectIPv6(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc)
+{
+ int rc = SOCKET_ERROR, retrycnt = 0;
+ u_long notblocking = 1;
+ DWORD lasterr = 0;
+ static const TIMEVAL tv = { 1, 0 };
+
+ unsigned int dwTimeout = (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2) ? nloc->timeout : 0;
+ // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway
+ if (dwTimeout == 0) dwTimeout = 30;
+
+ // this is for XP SP2 where there is a default connection attempt limit of 10/second
+ if (connectionTimeout)
+ {
+ WaitForSingleObject(hConnectionOpenMutex, 10000);
+ int waitdiff = GetTickCount() - g_LastConnectionTick;
+ if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE);
+ g_LastConnectionTick = GetTickCount();
+ ReleaseMutex(hConnectionOpenMutex);
+
+ // might of died in between the wait
+ if (Miranda_Terminated()) return false;
+ }
+
+ char szPort[6];
+ addrinfo *air = NULL, *ai, hints = {0};
+
+ hints.ai_family = AF_UNSPEC;
+
+ if (nloc->flags & NLOCF_UDP)
+ {
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ }
+ else
+ {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ if (nlc->proxyType)
+ {
+ if (!nlc->szProxyServer) return false;
+
+ if (nloc)
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, nloc->szHost, nloc->wPort);
+ else
+ NetlibLogf(nlc->nlu,"(%p) Connecting to proxy %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort);
+
+ _itoa(nlc->wProxyPort, szPort, 10);
+ if (MyGetaddrinfo(nlc->szProxyServer, szPort, &hints, &air))
+ {
+ NetlibLogf(nlc->nlu,"%s %d: %s() for host %s failed (%u)",__FILE__,__LINE__,"getaddrinfo", nlc->szProxyServer, WSAGetLastError());
+ return false;
+ }
+ }
+ else
+ {
+ if (!nloc || !nloc->szHost) return false;
+ NetlibLogf(nlc->nlu,"(%p) Connecting to server %s:%d....", nlc, nloc->szHost, nloc->wPort);
+
+ _itoa(nlc->nloc.wPort, szPort, 10);
+
+ if (MyGetaddrinfo(nlc->nloc.szHost, szPort, &hints, &air))
+ {
+ NetlibLogf(nlc->nlu,"%s %d: %s() for host %s failed (%u)",__FILE__,__LINE__,"getaddrinfo", nlc->nloc.szHost, WSAGetLastError());
+ return false;
+ }
+ }
+
+ for (ai = air; ai && !Miranda_Terminated(); ai = ai->ai_next)
+ {
+retry:
+ nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (nlc->s == INVALID_SOCKET) return false;
+
+ // return the socket to non blocking
+ if (ioctlsocket(nlc->s, FIONBIO, &notblocking) != 0) return false;
+
+ if (nlc->nlu->settings.specifyOutgoingPorts && nlc->nlu->settings.szOutgoingPorts && nlc->nlu->settings.szOutgoingPorts[0])
+ {
+ if (!BindSocketToPort(nlc->nlu->settings.szOutgoingPorts, nlc->s, &nlc->nlu->inportnum))
+ NetlibLogf(nlc->nlu,"Netlib connect: Not enough ports for outgoing connections specified");
+ }
+
+ // try a connect
+ if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0)
+ {
+ rc = 0;
+ break;
+ }
+
+ // didn't work, was it cos of nonblocking?
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ rc = SOCKET_ERROR;
+ break;
+ }
+
+ for (;;) // timeout loop
+ {
+ fd_set r, w, e;
+ FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
+ FD_SET(nlc->s, &r);
+ FD_SET(nlc->s, &w);
+ FD_SET(nlc->s, &e);
+ if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR)
+ break;
+
+ if (rc > 0)
+ {
+ if (FD_ISSET(nlc->s, &w))
+ {
+ // connection was successful
+ rc = 0;
+ lasterr = 0;
+ }
+ if (FD_ISSET(nlc->s, &r))
+ {
+ // connection was closed
+ rc = SOCKET_ERROR;
+ lasterr = WSAECONNRESET;
+ }
+ if (FD_ISSET(nlc->s, &e))
+ {
+ // connection failed.
+ int len = sizeof(lasterr);
+ rc = SOCKET_ERROR;
+ getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len);
+ if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2)
+ {
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ goto retry;
+ }
+ }
+ break;
+ }
+ else if (Miranda_Terminated())
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ else if (nloc->cbSize == sizeof(NETLIBOPENCONNECTION) && nloc->flags & NLOCF_V2 &&
+ nloc->waitcallback != NULL && nloc->waitcallback(&dwTimeout) == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ if (--dwTimeout == 0)
+ {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ if (rc == 0) break;
+
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ }
+
+ MyFreeaddrinfo(air);
+
+ notblocking = 0;
+ if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, &notblocking);
+ if (rc && lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static bool my_connect(NetlibConnection *nlc, NETLIBOPENCONNECTION * nloc)
+{
+ return MyGetaddrinfo && MyFreeaddrinfo ? my_connectIPv6(nlc, nloc) : my_connectIPv4(nlc, nloc);
+}
+
+static int NetlibHttpFallbackToDirect(struct NetlibConnection *nlc, struct NetlibUser *nlu, NETLIBOPENCONNECTION *nloc)
+{
+ NetlibDoClose(nlc, true);
+
+ NetlibLogf(nlu,"Fallback to direct connection");
+
+ nlc->proxyAuthNeeded = false;
+ nlc->proxyType = 0;
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+ if (!my_connect(nlc, nloc))
+ {
+ NetlibLogf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+bool NetlibDoConnect(NetlibConnection *nlc)
+{
+ NETLIBOPENCONNECTION *nloc = &nlc->nloc;
+ NetlibUser *nlu = nlc->nlu;
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = NULL;
+
+ bool usingProxy = false, forceHttps = false;
+ if (nlu->settings.useProxy)
+ {
+ if (nlu->settings.proxyType == PROXYTYPE_IE)
+ {
+ usingProxy = NetlibGetIeProxyConn(nlc, false);
+ }
+ else
+ {
+ if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0])
+ {
+ nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer);
+ nlc->wProxyPort = nlu->settings.wProxyPort;
+ nlc->proxyType = nlu->settings.proxyType;
+ usingProxy = true;
+ }
+ }
+ }
+
+retry:
+ if (usingProxy)
+ {
+ if (!my_connect(nlc, nloc))
+ {
+ usingProxy = false;
+ nlc->proxyType = 0;
+ }
+ }
+ if (!usingProxy)
+ {
+ my_connect(nlc, nloc);
+ }
+
+ if (nlc->s == INVALID_SOCKET)
+ {
+ if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP))
+ {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ {
+ NetlibLogf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ }
+ else
+ {
+ if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps)
+ {
+ forceHttps = true;
+ usingProxy = NetlibGetIeProxyConn(nlc, true);
+ if (usingProxy) goto retry;
+ }
+ NetlibLogf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ }
+
+ if (usingProxy && !((nloc->flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP &&
+ (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS)))
+ {
+ if (!WaitUntilWritable(nlc->s, 30000)) return false;
+
+ switch (nlc->proxyType)
+ {
+ case PROXYTYPE_SOCKS4:
+ if (!NetlibInitSocks4Connection(nlc, nlu, nloc)) return false;
+ break;
+
+ case PROXYTYPE_SOCKS5:
+ if (!NetlibInitSocks5Connection(nlc, nlu, nloc)) return false;
+ break;
+
+ case PROXYTYPE_HTTPS:
+ nlc->proxyAuthNeeded = true;
+ if (!NetlibInitHttpsConnection(nlc, nlu, nloc))
+ {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ return false;
+ }
+ break;
+
+ case PROXYTYPE_HTTP:
+ nlc->proxyAuthNeeded = true;
+ if (!(nlu->user.flags & NUF_HTTPGATEWAY || nloc->flags & NLOCF_HTTPGATEWAY) || nloc->flags & NLOCF_SSL)
+ {
+ //NLOCF_HTTP not specified and no HTTP gateway available: try HTTPS
+ if (!NetlibInitHttpsConnection(nlc, nlu, nloc))
+ {
+ //can't do HTTPS: try direct
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc, nlu, nloc))
+ return false;
+ }
+ }
+ else
+ {
+ if (!NetlibInitHttpConnection(nlc, nlu, nloc)) return false;
+ }
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ FreePartiallyInitedConnection(nlc);
+ return false;
+ }
+ }
+ else if (nloc->flags & NLOCF_HTTPGATEWAY)
+ {
+ if (!NetlibInitHttpConnection(nlc, nlu, nloc)) return false;
+ nlc->usingDirectHttpGateway = true;
+ }
+
+ NetlibLogf(nlu,"(%d) Connected to %s:%d", nlc->s, nloc->szHost, nloc->wPort);
+
+ if (NLOCF_SSL & nloc->flags)
+ {
+ return NetlibStartSsl((WPARAM)nlc, 0) != 0;
+ }
+
+ return true;
+}
+
+bool NetlibReconnect(NetlibConnection *nlc)
+{
+ char buf[4];
+ bool opened = nlc->s != INVALID_SOCKET;
+
+ if (opened)
+ {
+ switch (WaitUntilReadable(nlc->s, 0, true))
+ {
+ case SOCKET_ERROR:
+ opened = false;
+ break;
+
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ NetlibDoClose(nlc, true);
+ }
+
+ if (!opened)
+ {
+ if (Miranda_Terminated()) return false;
+ if (nlc->usingHttpGateway)
+ {
+ nlc->proxyAuthNeeded = true;
+ return my_connect(nlc, &nlc->nloc);
+ }
+ else
+ return NetlibDoConnect(nlc);
+ }
+ return true;
+}
+
+INT_PTR NetlibOpenConnection(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBOPENCONNECTION *nloc = (NETLIBOPENCONNECTION*)lParam;
+ struct NetlibUser *nlu = (struct NetlibUser*)wParam;
+ struct NetlibConnection *nlc;
+
+ NetlibLogf(nlu,"Connection request to %s:%d (Flags %x)....", nloc->szHost, nloc->wPort, nloc->flags);
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || nloc == NULL ||
+ (nloc->cbSize != NETLIBOPENCONNECTION_V1_SIZE && nloc->cbSize != sizeof(NETLIBOPENCONNECTION)) ||
+ nloc->szHost == NULL || nloc->wPort == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ nlc = (struct NetlibConnection*)mir_calloc(sizeof(struct NetlibConnection));
+ nlc->handleType = NLH_CONNECTION;
+ nlc->nlu = nlu;
+ nlc->nloc = *nloc;
+ nlc->nloc.szHost = mir_strdup(nloc->szHost);
+ nlc->s = INVALID_SOCKET;
+ nlc->s2 = INVALID_SOCKET;
+ nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0;
+
+ InitializeCriticalSection(&nlc->csHttpSequenceNums);
+ nlc->hOkToCloseEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
+ nlc->dontCloseNow = 0;
+ NetlibInitializeNestedCS(&nlc->ncsSend);
+ NetlibInitializeNestedCS(&nlc->ncsRecv);
+
+ if (!NetlibDoConnect(nlc))
+ {
+ FreePartiallyInitedConnection(nlc);
+ return 0;
+ }
+
+ if (iUPnPCleanup == 0)
+ {
+ EnterCriticalSection(&csNetlibUser);
+ if (iUPnPCleanup == 0)
+ {
+ iUPnPCleanup = 1;
+ forkthread(NetlibUPnPCleanup, 0, NULL);
+ }
+ LeaveCriticalSection(&csNetlibUser);
+ }
+
+ return (INT_PTR)nlc;
+}
+
+INT_PTR NetlibStartSsl(WPARAM wParam, LPARAM lParam)
+{
+ NetlibConnection *nlc = (NetlibConnection*)wParam;
+ if (nlc == NULL) return 0;
+
+ NETLIBSSL *sp = (NETLIBSSL*)lParam;
+ const char *szHost = sp ? sp->host : nlc->nloc.szHost;
+
+ NetlibLogf(nlc->nlu, "(%d %s) Starting SSL negotiation", nlc->s, szHost);
+ nlc->hSsl = si.connect(nlc->s, szHost, nlc->nlu->settings.validateSSL);
+
+ if (nlc->hSsl == NULL)
+ NetlibLogf(nlc->nlu,"(%d %s) Failure to negotiate SSL connection", nlc->s, szHost);
+ else
+ NetlibLogf(nlc->nlu, "(%d %s) SSL negotiation successful", nlc->s, szHost);
+
+ return nlc->hSsl != NULL;
+}
diff --git a/src/modules/netlib/netlibopts.cpp b/src/modules/netlib/netlibopts.cpp
new file mode 100644
index 0000000000..0c562a5cff
--- /dev/null
+++ b/src/modules/netlib/netlibopts.cpp
@@ -0,0 +1,556 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+struct NetlibTempSettings
+{
+ DWORD flags;
+ char *szSettingsModule;
+ NETLIBUSERSETTINGS settings;
+};
+
+static LIST <NetlibTempSettings> tempSettings(5);
+
+static const UINT outgoingConnectionsControls[] =
+{
+ IDC_STATIC12,
+ IDC_USEPROXY,
+ IDC_STATIC21,IDC_PROXYTYPE,
+ IDC_STATIC22,IDC_PROXYHOST,IDC_STATIC23,IDC_PROXYPORT,IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31,IDC_PROXYUSER,IDC_STATIC32,IDC_PROXYPASS,
+ IDC_PROXYDNS,
+ IDC_SPECIFYPORTSO,
+ IDC_PORTSRANGEO,
+ IDC_STATIC54,
+ IDC_VALIDATESSL};
+static const UINT useProxyControls[]={
+ IDC_STATIC21,IDC_PROXYTYPE,
+ IDC_STATIC22,IDC_PROXYHOST,IDC_STATIC23,IDC_PROXYPORT,IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31,IDC_PROXYUSER,IDC_STATIC32,IDC_PROXYPASS,
+ IDC_PROXYDNS};
+static const UINT specifyOPortsControls[]={
+ IDC_PORTSRANGEO,
+ IDC_STATIC54
+};
+static const UINT incomingConnectionsControls[]={
+ IDC_STATIC43,
+ IDC_SPECIFYPORTS,
+ IDC_PORTSRANGE,
+ IDC_STATIC52,
+ IDC_ENABLEUPNP};
+static const UINT specifyPortsControls[]={
+ IDC_PORTSRANGE,
+ IDC_STATIC52};
+
+static const TCHAR* szProxyTypes[]={_T("<mixed>"),_T("SOCKS4"),_T("SOCKS5"),_T("HTTP"),_T("HTTPS"),_T("Internet Explorer")};
+static const WORD oftenProxyPorts[]={1080,1080,1080,8080,8080,8080};
+
+#define M_REFRESHALL (WM_USER+100)
+#define M_REFRESHENABLING (WM_USER+101)
+
+static void ShowMultipleControls(HWND hwndDlg,const UINT *controls,int cControls,int state)
+{
+ int i;
+ for(i=0;i<cControls;i++) ShowWindow(GetDlgItem(hwndDlg,controls[i]),state);
+}
+
+static void EnableMultipleControls(HWND hwndDlg,const UINT *controls,int cControls,int state)
+{
+ int i;
+ for(i=0;i<cControls;i++) EnableWindow(GetDlgItem(hwndDlg,controls[i]),state);
+}
+
+static void AddProxyTypeItem(HWND hwndDlg,int type,int selectType)
+{
+ int i;
+ i = SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_ADDSTRING,0,(LPARAM)(type==0?TranslateTS(szProxyTypes[type]):szProxyTypes[type]));
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type);
+ if (type == selectType) SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0);
+}
+
+static void CopySettingsStruct(NETLIBUSERSETTINGS *dest,NETLIBUSERSETTINGS *source)
+{
+ *dest=*source;
+ if(dest->szIncomingPorts) dest->szIncomingPorts=mir_strdup(dest->szIncomingPorts);
+ if(dest->szOutgoingPorts) dest->szOutgoingPorts=mir_strdup(dest->szOutgoingPorts);
+ if(dest->szProxyAuthPassword) dest->szProxyAuthPassword=mir_strdup(dest->szProxyAuthPassword);
+ if(dest->szProxyAuthUser) dest->szProxyAuthUser=mir_strdup(dest->szProxyAuthUser);
+ if(dest->szProxyServer) dest->szProxyServer=mir_strdup(dest->szProxyServer);
+}
+
+static void CombineSettingsStrings(char **dest,char **source)
+{
+ if(*dest!=NULL && (*source==NULL || lstrcmpiA(*dest,*source))) {mir_free(*dest); *dest=NULL;}
+}
+
+static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest,DWORD *destFlags,NETLIBUSERSETTINGS *source,DWORD sourceFlags)
+{
+ if(sourceFlags&NUF_OUTGOING) {
+ if(*destFlags&NUF_OUTGOING) {
+ if(dest->validateSSL!=source->validateSSL) dest->validateSSL=2;
+ if(dest->useProxy!=source->useProxy) dest->useProxy=2;
+ if(dest->proxyType!=source->proxyType) dest->proxyType=0;
+ CombineSettingsStrings(&dest->szProxyServer,&source->szProxyServer);
+ if(dest->wProxyPort!=source->wProxyPort) dest->wProxyPort=0;
+ if(dest->useProxyAuth!=source->useProxyAuth) dest->useProxyAuth=2;
+ CombineSettingsStrings(&dest->szProxyAuthUser,&source->szProxyAuthUser);
+ CombineSettingsStrings(&dest->szProxyAuthPassword,&source->szProxyAuthPassword);
+ if(dest->dnsThroughProxy!=source->dnsThroughProxy) dest->dnsThroughProxy=2;
+ if(dest->specifyOutgoingPorts!=source->specifyOutgoingPorts) dest->specifyOutgoingPorts=2;
+ CombineSettingsStrings(&dest->szOutgoingPorts,&source->szOutgoingPorts);
+ }
+ else {
+ dest->validateSSL=source->validateSSL;
+ dest->useProxy=source->useProxy;
+ dest->proxyType=source->proxyType;
+ dest->szProxyServer=source->szProxyServer;
+ if(dest->szProxyServer) dest->szProxyServer=mir_strdup(dest->szProxyServer);
+ dest->wProxyPort=source->wProxyPort;
+ dest->useProxyAuth=source->useProxyAuth;
+ dest->szProxyAuthUser=source->szProxyAuthUser;
+ if(dest->szProxyAuthUser) dest->szProxyAuthUser=mir_strdup(dest->szProxyAuthUser);
+ dest->szProxyAuthPassword=source->szProxyAuthPassword;
+ if(dest->szProxyAuthPassword) dest->szProxyAuthPassword=mir_strdup(dest->szProxyAuthPassword);
+ dest->dnsThroughProxy=source->dnsThroughProxy;
+ dest->specifyOutgoingPorts=source->specifyOutgoingPorts;
+ dest->szOutgoingPorts=source->szOutgoingPorts;
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts=mir_strdup(dest->szOutgoingPorts);
+ }
+ }
+ if(sourceFlags&NUF_INCOMING) {
+ if(*destFlags&NUF_INCOMING) {
+ if(dest->enableUPnP!=source->enableUPnP) dest->enableUPnP=2;
+ if(dest->specifyIncomingPorts!=source->specifyIncomingPorts) dest->specifyIncomingPorts=2;
+ CombineSettingsStrings(&dest->szIncomingPorts,&source->szIncomingPorts);
+ }
+ else {
+ dest->enableUPnP=source->enableUPnP;
+ dest->specifyIncomingPorts=source->specifyIncomingPorts;
+ dest->szIncomingPorts=source->szIncomingPorts;
+ if(dest->szIncomingPorts) dest->szIncomingPorts=mir_strdup(dest->szIncomingPorts);
+ }
+ }
+ if((*destFlags&NUF_NOHTTPSOPTION)!=(sourceFlags&NUF_NOHTTPSOPTION))
+ *destFlags=(*destFlags|sourceFlags)&~NUF_NOHTTPSOPTION;
+ else *destFlags|=sourceFlags;
+}
+
+static void ChangeSettingIntByCheckbox(HWND hwndDlg,UINT ctrlId,int iUser,int memberOffset)
+{
+ int newValue,i;
+
+ newValue=IsDlgButtonChecked(hwndDlg,ctrlId)!=BST_CHECKED;
+ CheckDlgButton(hwndDlg,ctrlId,newValue?BST_CHECKED:BST_UNCHECKED);
+ if (iUser == -1)
+ {
+ for (i=0; i<tempSettings.getCount(); i++)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ *(int*)(((PBYTE)&tempSettings[i]->settings) + memberOffset) = newValue;
+ }
+ else *(int*)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset)=newValue;
+ SendMessage(hwndDlg,M_REFRESHENABLING,0,0);
+}
+
+static void ChangeSettingStringByEdit(HWND hwndDlg,UINT ctrlId,int iUser,int memberOffset)
+{
+ int i,newValueLen;
+ char *szNewValue,**ppszNew;
+
+ newValueLen=GetWindowTextLength(GetDlgItem(hwndDlg,ctrlId));
+ szNewValue=(char*)mir_alloc(newValueLen+1);
+ GetDlgItemTextA(hwndDlg,ctrlId,szNewValue,newValueLen+1);
+ if (iUser == -1)
+ {
+ for (i=0; i<tempSettings.getCount(); ++i)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ {
+ ppszNew=(char**)(((PBYTE)&tempSettings[i]->settings)+memberOffset);
+ if(*ppszNew) mir_free(*ppszNew);
+ *ppszNew=mir_strdup(szNewValue);
+ }
+ mir_free(szNewValue);
+ }
+ else {
+ ppszNew=(char**)(((PBYTE)&tempSettings[iUser]->settings)+memberOffset);
+ if(*ppszNew) mir_free(*ppszNew);
+ *ppszNew=szNewValue;
+ }
+}
+
+static void WriteSettingsStructToDb(const char *szSettingsModule,NETLIBUSERSETTINGS *settings,DWORD flags)
+{
+ if(flags&NUF_OUTGOING) {
+ char szEncodedPassword[512];
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLValidateSSL",(BYTE)settings->validateSSL);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxy",(BYTE)settings->useProxy);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLProxyType",(BYTE)settings->proxyType);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyServer",settings->szProxyServer?settings->szProxyServer:"");
+ DBWriteContactSettingWord(NULL,szSettingsModule,"NLProxyPort",(WORD)settings->wProxyPort);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxyAuth",(BYTE)settings->useProxyAuth);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthUser",settings->szProxyAuthUser?settings->szProxyAuthUser:"");
+ lstrcpynA(szEncodedPassword,settings->szProxyAuthPassword?settings->szProxyAuthPassword:"",SIZEOF(szEncodedPassword));
+ CallService(MS_DB_CRYPT_ENCODESTRING,SIZEOF(szEncodedPassword),(LPARAM)szEncodedPassword);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthPassword",szEncodedPassword);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLDnsThroughProxy",(BYTE)settings->dnsThroughProxy);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyOutgoingPorts",(BYTE)settings->specifyOutgoingPorts);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLOutgoingPorts",settings->szOutgoingPorts?settings->szOutgoingPorts:"");
+ }
+ if(flags&NUF_INCOMING) {
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLEnableUPnP",(BYTE)settings->enableUPnP);
+ DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyIncomingPorts",(BYTE)settings->specifyIncomingPorts);
+ DBWriteContactSettingString(NULL,szSettingsModule,"NLIncomingPorts",settings->szIncomingPorts?settings->szIncomingPorts:"");
+ }
+}
+
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings)
+{
+ int i;
+ NETLIBUSERSETTINGS combinedSettings={0};
+ DWORD flags;
+
+ EnterCriticalSection(&csNetlibUser);
+
+ NetlibUser *thisUser, tUser;
+ tUser.user.szSettingsModule = (char*)szSettingsModule;
+ thisUser = netlibUser.find(&tUser);
+
+ if (thisUser == NULL)
+ {
+ LeaveCriticalSection(&csNetlibUser);
+ return;
+ }
+
+ NetlibFreeUserSettingsStruct(&thisUser->settings);
+ CopySettingsStruct(&thisUser->settings, settings);
+ WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
+ combinedSettings.cbSize = sizeof(combinedSettings);
+ for (i=0, flags=0; i < netlibUser.getCount(); ++i)
+ {
+ if (thisUser->user.flags & NUF_NOOPTIONS) continue;
+ CombineSettingsStructs(&combinedSettings, &flags, &thisUser->settings, thisUser->user.flags);
+ }
+ if(combinedSettings.validateSSL==2) combinedSettings.validateSSL=0;
+ if(combinedSettings.useProxy==2) combinedSettings.useProxy=0;
+ if(combinedSettings.proxyType==0) combinedSettings.proxyType=PROXYTYPE_SOCKS5;
+ if(combinedSettings.useProxyAuth==2) combinedSettings.useProxyAuth=0;
+ if(combinedSettings.dnsThroughProxy==2) combinedSettings.dnsThroughProxy=1;
+ if(combinedSettings.enableUPnP==2) combinedSettings.enableUPnP=1;
+ if(combinedSettings.specifyIncomingPorts==2) combinedSettings.specifyIncomingPorts=0;
+ WriteSettingsStructToDb("Netlib",&combinedSettings,flags);
+ NetlibFreeUserSettingsStruct(&combinedSettings);
+ LeaveCriticalSection(&csNetlibUser);
+}
+
+static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ { int iUser,iItem;
+
+ TranslateDialogDefault(hwndDlg);
+ iItem=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_ADDSTRING,0,(LPARAM)TranslateT("<All connections>"));
+ SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETITEMDATA,iItem,(LPARAM)-1);
+ SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETCURSEL,iItem,0);
+
+ EnterCriticalSection(&csNetlibUser);
+ for (iUser = 0; iUser < netlibUser.getCount(); ++iUser)
+ {
+ NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
+ thisSettings->flags = netlibUser[iUser]->user.flags;
+ thisSettings->szSettingsModule = mir_strdup(netlibUser[iUser]->user.szSettingsModule);
+ CopySettingsStruct(&thisSettings->settings, &netlibUser[iUser]->settings);
+ tempSettings.insert(thisSettings);
+
+ if (netlibUser[iUser]->user.flags & NUF_NOOPTIONS) continue;
+ iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0,
+ (LPARAM)netlibUser[iUser]->user.ptszDescriptiveName);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS,CB_SETITEMDATA, iItem, iUser);
+ }
+ LeaveCriticalSection(&csNetlibUser);
+
+ SendMessage(hwndDlg,M_REFRESHALL,0,0);
+ return TRUE;
+ }
+ case M_REFRESHALL:
+ { int iUser=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETCURSEL,0,0),0);
+ NETLIBUSERSETTINGS settings = {0};
+ DWORD flags;
+
+ if (iUser == -1)
+ {
+ int i;
+ settings.cbSize=sizeof(settings);
+ for (i = 0, flags = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (tempSettings[i]->flags & NUF_NOOPTIONS) continue;
+ CombineSettingsStructs(&settings, &flags, &tempSettings[i]->settings, tempSettings[i]->flags);
+ }
+ }
+ else
+ {
+ NetlibFreeUserSettingsStruct(&settings);
+ CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
+ flags = tempSettings[iUser]->flags;
+ }
+ ShowMultipleControls(hwndDlg,outgoingConnectionsControls,SIZEOF(outgoingConnectionsControls),flags&NUF_OUTGOING?SW_SHOW:SW_HIDE);
+ CheckDlgButton(hwndDlg,IDC_USEPROXY,settings.useProxy);
+ SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_RESETCONTENT,0,0);
+ if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg,0,settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType);
+ if (flags & (NUF_HTTPCONNS | NUF_HTTPGATEWAY)) AddProxyTypeItem(hwndDlg,PROXYTYPE_HTTP,settings.proxyType);
+ if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg,PROXYTYPE_HTTPS,settings.proxyType);
+ if (flags & (NUF_HTTPCONNS | NUF_HTTPGATEWAY) || !(flags & NUF_NOHTTPSOPTION))
+ AddProxyTypeItem(hwndDlg,PROXYTYPE_IE,settings.proxyType);
+ SetDlgItemTextA(hwndDlg,IDC_PROXYHOST,settings.szProxyServer?settings.szProxyServer:"");
+ if (settings.wProxyPort) SetDlgItemInt(hwndDlg,IDC_PROXYPORT,settings.wProxyPort,FALSE);
+ else SetDlgItemTextA(hwndDlg,IDC_PROXYPORT,"");
+ CheckDlgButton(hwndDlg,IDC_PROXYAUTH,settings.useProxyAuth);
+ SetDlgItemTextA(hwndDlg,IDC_PROXYUSER,settings.szProxyAuthUser?settings.szProxyAuthUser:"");
+ SetDlgItemTextA(hwndDlg,IDC_PROXYPASS,settings.szProxyAuthPassword?settings.szProxyAuthPassword:"");
+ CheckDlgButton(hwndDlg,IDC_PROXYDNS,settings.dnsThroughProxy);
+ CheckDlgButton(hwndDlg,IDC_VALIDATESSL,settings.validateSSL);
+
+ ShowMultipleControls(hwndDlg,incomingConnectionsControls,SIZEOF(incomingConnectionsControls),flags&NUF_INCOMING?SW_SHOW:SW_HIDE);
+ CheckDlgButton(hwndDlg,IDC_SPECIFYPORTS,settings.specifyIncomingPorts);
+ SetDlgItemTextA(hwndDlg,IDC_PORTSRANGE,settings.szIncomingPorts?settings.szIncomingPorts:"");
+
+ CheckDlgButton(hwndDlg,IDC_SPECIFYPORTSO,settings.specifyOutgoingPorts);
+ SetDlgItemTextA(hwndDlg,IDC_PORTSRANGEO,settings.szOutgoingPorts?settings.szOutgoingPorts:"");
+
+ CheckDlgButton(hwndDlg,IDC_ENABLEUPNP,settings.enableUPnP);
+
+ NetlibFreeUserSettingsStruct(&settings);
+ SendMessage(hwndDlg,M_REFRESHENABLING,0,0);
+ break;
+ }
+ case M_REFRESHENABLING:
+ { int selectedProxyType;
+ TCHAR str[80];
+
+ selectedProxyType=SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETCURSEL,0,0),0);
+ mir_sntprintf(str, SIZEOF(str), TranslateT("(often %d)"),oftenProxyPorts[selectedProxyType]);
+ SetDlgItemText(hwndDlg,IDC_STOFTENPORT,str);
+ if (IsDlgButtonChecked(hwndDlg,IDC_USEPROXY) != BST_UNCHECKED)
+ {
+ int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1;
+ EnableMultipleControls(hwndDlg, useProxyControls, SIZEOF(useProxyControls), TRUE);
+ if (selectedProxyType == 0)
+ {
+ int i;
+ for (i = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (!tempSettings[i]->settings.useProxy ||
+ tempSettings[i]->flags & NUF_NOOPTIONS || !(tempSettings[i]->flags & NUF_OUTGOING))
+ continue;
+
+ if (tempSettings[i]->settings.proxyType==PROXYTYPE_SOCKS4) enableUser=1;
+ else
+ {
+ enableAuth=1;
+ if (tempSettings[i]->settings.useProxyAuth)
+ {
+ enableUser=enablePass=1;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser=1;
+ else
+ {
+ if (selectedProxyType == PROXYTYPE_IE) enableServer=0;
+ enableAuth=1;
+ if (IsDlgButtonChecked(hwndDlg,IDC_PROXYAUTH) != BST_UNCHECKED)
+ enableUser = enablePass = 1;
+ }
+ }
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PROXYAUTH), enableAuth);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_STATIC31), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PROXYUSER), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_STATIC32), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PROXYPASS), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PROXYHOST), enableServer);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PROXYPORT), enableServer);
+ }
+ else EnableMultipleControls(hwndDlg,useProxyControls,SIZEOF(useProxyControls),FALSE);
+ EnableMultipleControls(hwndDlg,specifyPortsControls,SIZEOF(specifyPortsControls),IsDlgButtonChecked(hwndDlg,IDC_SPECIFYPORTS)!=BST_UNCHECKED);
+ EnableMultipleControls(hwndDlg,specifyOPortsControls,SIZEOF(specifyOPortsControls),IsDlgButtonChecked(hwndDlg,IDC_SPECIFYPORTSO)!=BST_UNCHECKED);
+ break;
+ }
+ case WM_COMMAND:
+ { int iUser=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETCURSEL,0,0),0);
+ switch(LOWORD(wParam))
+ {
+ case IDC_NETLIBUSERS:
+ if(HIWORD(wParam)==CBN_SELCHANGE) SendMessage(hwndDlg,M_REFRESHALL,0,0);
+ return 0;
+
+ case IDC_LOGOPTIONS:
+ NetlibLogShowOptions();
+ return 0;
+
+ case IDC_PROXYTYPE:
+ if (HIWORD(wParam) != CBN_SELCHANGE) return 0;
+ { int newValue,i;
+ newValue = SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_PROXYTYPE,CB_GETCURSEL,0,0),0);
+ if (iUser == -1)
+ {
+ if (newValue == 0) return 0;
+ for (i = 0; i < tempSettings.getCount(); ++i)
+ {
+ if (tempSettings[i]->flags & NUF_NOOPTIONS) continue;
+ if (newValue == PROXYTYPE_HTTP && !(tempSettings[i]->flags & (NUF_HTTPCONNS|NUF_HTTPGATEWAY)))
+ tempSettings[i]->settings.proxyType = PROXYTYPE_HTTPS;
+ else if (newValue == PROXYTYPE_HTTPS && tempSettings[i]->flags & NUF_NOHTTPSOPTION)
+ tempSettings[i]->settings.proxyType = PROXYTYPE_HTTP;
+ else tempSettings[i]->settings.proxyType = newValue;
+ }
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ }
+ else
+ {
+ tempSettings[iUser]->settings.proxyType = newValue;
+ SendMessage(hwndDlg,M_REFRESHENABLING,0,0);
+ }
+ }
+ break;
+ case IDC_USEPROXY:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,useProxy));
+ break;
+ case IDC_PROXYAUTH:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,useProxyAuth));
+ break;
+ case IDC_PROXYDNS:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,dnsThroughProxy));
+ break;
+ case IDC_SPECIFYPORTS:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,specifyIncomingPorts));
+ break;
+ case IDC_SPECIFYPORTSO:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,specifyOutgoingPorts));
+ break;
+ case IDC_ENABLEUPNP:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,enableUPnP));
+ break;
+ case IDC_VALIDATESSL:
+ ChangeSettingIntByCheckbox(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,validateSSL));
+ break;
+ case IDC_PROXYHOST:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,szProxyServer));
+ break;
+ case IDC_PROXYPORT:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ { int newValue,i;
+ newValue=GetDlgItemInt(hwndDlg,LOWORD(wParam),NULL,FALSE);
+ if (iUser == -1)
+ {
+ for (i = 0; i < tempSettings.getCount(); ++i)
+ if (!(tempSettings[i]->flags & NUF_NOOPTIONS))
+ tempSettings[i]->settings.wProxyPort = newValue;
+ }
+ else tempSettings[iUser]->settings.wProxyPort = newValue;
+ }
+ break;
+ case IDC_PROXYUSER:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,szProxyAuthUser));
+ break;
+ case IDC_PROXYPASS:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,szProxyAuthPassword));
+ break;
+ case IDC_PORTSRANGE:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,szIncomingPorts));
+ break;
+ case IDC_PORTSRANGEO:
+ if(HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg,LOWORD(wParam),iUser,offsetof(NETLIBUSERSETTINGS,szOutgoingPorts));
+ break;
+ }
+ ShowWindow(GetDlgItem(hwndDlg,IDC_RECONNECTREQD),SW_SHOW);
+ SendMessage(GetParent(hwndDlg),PSM_CHANGED,0,0);
+ break;
+ }
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { int iUser;
+ for (iUser = 0; iUser < tempSettings.getCount(); iUser++)
+ NetlibSaveUserSettingsStruct(tempSettings[iUser]->szSettingsModule,
+ &tempSettings[iUser]->settings);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ { int iUser;
+ for (iUser = 0; iUser < tempSettings.getCount(); ++iUser)
+ {
+ mir_free(tempSettings[iUser]->szSettingsModule);
+ NetlibFreeUserSettingsStruct(&tempSettings[iUser]->settings);
+ mir_free(tempSettings[iUser]);
+ }
+ tempSettings.destroy();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static UINT expertOnlyControls[]={IDC_LOGOPTIONS};
+int NetlibOptInitialise(WPARAM wParam,LPARAM)
+{
+ int optionsCount = 0;
+ EnterCriticalSection(&csNetlibUser);
+ for (int i = 0; i < netlibUser.getCount(); ++i)
+ if (!(netlibUser[i]->user.flags & NUF_NOOPTIONS)) ++optionsCount;
+ LeaveCriticalSection(&csNetlibUser);
+ if (optionsCount == 0) return 0;
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 900000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
+ odp.pszTitle = LPGEN("Network");
+ odp.pfnDlgProc = DlgProcNetlibOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.expertOnlyControls = expertOnlyControls;
+ odp.nExpertOnlyControls = SIZEOF( expertOnlyControls );
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
diff --git a/src/modules/netlib/netlibpktrecver.cpp b/src/modules/netlib/netlibpktrecver.cpp
new file mode 100644
index 0000000000..9722324a4f
--- /dev/null
+++ b/src/modules/netlib/netlibpktrecver.cpp
@@ -0,0 +1,85 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+INT_PTR NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ struct NetlibPacketRecver *nlpr;
+
+ if(GetNetlibHandleType(nlc)!=NLH_CONNECTION || lParam==0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (INT_PTR)(struct NetlibPacketRecver*)NULL;
+ }
+ nlpr=(struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
+ if(nlpr==NULL) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return (INT_PTR)(struct NetlibPacketRecver*)NULL;
+ }
+ nlpr->handleType=NLH_PACKETRECVER;
+ nlpr->nlc=nlc;
+ nlpr->packetRecver.cbSize=sizeof(nlpr->packetRecver);
+ nlpr->packetRecver.bufferSize=lParam;
+ nlpr->packetRecver.buffer=(PBYTE)mir_alloc(nlpr->packetRecver.bufferSize);
+ nlpr->packetRecver.bytesUsed=0;
+ nlpr->packetRecver.bytesAvailable=0;
+ return (INT_PTR)nlpr;
+}
+
+INT_PTR NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam;
+ NETLIBPACKETRECVER *nlprParam=(NETLIBPACKETRECVER*)lParam;
+ INT_PTR recvResult;
+
+ if(GetNetlibHandleType(nlpr)!=NLH_PACKETRECVER || nlprParam==NULL || nlprParam->cbSize!=sizeof(NETLIBPACKETRECVER) || nlprParam->bytesUsed>nlpr->packetRecver.bytesAvailable) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+ if (Miranda_Terminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ nlpr->packetRecver.dwTimeout=nlprParam->dwTimeout;
+ if(nlprParam->bytesUsed==0) {
+ if(nlpr->packetRecver.bytesAvailable==nlpr->packetRecver.bufferSize) {
+ nlpr->packetRecver.bytesAvailable=0;
+ NetlibLogf(nlpr->nlc->nlu,"Packet recver: packet overflowed buffer, ditching");
+ }
+ }
+ else {
+ MoveMemory(nlpr->packetRecver.buffer,nlpr->packetRecver.buffer+nlprParam->bytesUsed,nlpr->packetRecver.bytesAvailable-nlprParam->bytesUsed);
+ nlpr->packetRecver.bytesAvailable-=nlprParam->bytesUsed;
+ }
+ if(nlprParam->dwTimeout!=INFINITE) {
+ if(!si.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s,nlprParam->dwTimeout) <= 0) {
+ *nlprParam=nlpr->packetRecver;
+ return SOCKET_ERROR;
+ }
+ }
+ recvResult=NLRecv(nlpr->nlc, (char*)nlpr->packetRecver.buffer+nlpr->packetRecver.bytesAvailable,nlpr->packetRecver.bufferSize-nlpr->packetRecver.bytesAvailable,0);
+ if(recvResult>0) nlpr->packetRecver.bytesAvailable+=recvResult;
+ *nlprParam=nlpr->packetRecver;
+ return recvResult;
+}
diff --git a/src/modules/netlib/netlibsecurity.cpp b/src/modules/netlib/netlibsecurity.cpp
new file mode 100644
index 0000000000..c20dcf10a1
--- /dev/null
+++ b/src/modules/netlib/netlibsecurity.cpp
@@ -0,0 +1,570 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <rpcdce.h>
+
+static HMODULE g_hSecurity = NULL;
+static PSecurityFunctionTable g_pSSPI = NULL;
+
+typedef struct
+{
+ CtxtHandle hClientContext;
+ CredHandle hClientCredential;
+ TCHAR* szProvider;
+ TCHAR* szPrincipal;
+ unsigned cbMaxToken;
+ bool hasDomain;
+}
+ NtlmHandleType;
+
+typedef struct
+{
+ WORD len;
+ WORD allocedSpace;
+ DWORD offset;
+}
+ NTLM_String;
+
+typedef struct
+{
+ char sign[8];
+ DWORD type; // == 2
+ NTLM_String targetName;
+ DWORD flags;
+ BYTE challenge[8];
+ BYTE context[8];
+ NTLM_String targetInfo;
+}
+ NtlmType2packet;
+
+static unsigned secCnt = 0, ntlmCnt = 0;
+static HANDLE hSecMutex;
+
+static void ReportSecError(SECURITY_STATUS scRet, int line)
+{
+ char szMsgBuf[256];
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, scRet, LANG_USER_DEFAULT, szMsgBuf, SIZEOF(szMsgBuf), NULL);
+
+ char *p = strchr(szMsgBuf, 13); if (p) *p = 0;
+
+ NetlibLogf(NULL, "Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
+}
+
+static void LoadSecurityLibrary(void)
+{
+ INIT_SECURITY_INTERFACE pInitSecurityInterface;
+
+ g_hSecurity = LoadLibraryA("secur32.dll");
+ if (g_hSecurity == NULL)
+ g_hSecurity = LoadLibraryA("security.dll");
+
+ if (g_hSecurity == NULL)
+ return;
+
+ pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(g_hSecurity, SECURITY_ENTRYPOINT_ANSI);
+ if (pInitSecurityInterface != NULL)
+ {
+ g_pSSPI = pInitSecurityInterface();
+ }
+
+ if (g_pSSPI == NULL)
+ {
+ FreeLibrary(g_hSecurity);
+ g_hSecurity = NULL;
+ }
+}
+
+static void FreeSecurityLibrary(void)
+{
+ FreeLibrary(g_hSecurity);
+ g_hSecurity = NULL;
+ g_pSSPI = NULL;
+}
+
+HANDLE NetlibInitSecurityProvider(const TCHAR* szProvider, const TCHAR* szPrincipal)
+{
+ HANDLE hSecurity = NULL;
+
+ if (_tcsicmp(szProvider, _T("Basic")) == 0)
+ {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->szProvider = mir_tstrdup(szProvider);
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+
+ return hNtlm;
+ }
+
+ WaitForSingleObject(hSecMutex, INFINITE);
+
+ if (secCnt == 0 )
+ {
+ LoadSecurityLibrary();
+ secCnt += g_hSecurity != NULL;
+ }
+ else secCnt++;
+
+ if (g_pSSPI != NULL)
+ {
+ PSecPkgInfo ntlmSecurityPackageInfo;
+ bool isGSSAPI = _tcsicmp(szProvider, _T("GSSAPI")) == 0;
+ const TCHAR *szProviderC = isGSSAPI ? _T("Kerberos") : szProvider;
+ SECURITY_STATUS sc = g_pSSPI->QuerySecurityPackageInfo((LPTSTR)szProviderC, &ntlmSecurityPackageInfo);
+ if (sc == SEC_E_OK)
+ {
+ NtlmHandleType* hNtlm;
+
+ hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
+ g_pSSPI->FreeContextBuffer(ntlmSecurityPackageInfo);
+
+ hNtlm->szProvider = mir_tstrdup(szProvider);
+ hNtlm->szPrincipal = mir_tstrdup(szPrincipal ? szPrincipal : _T(""));
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+ }
+ }
+
+ ReleaseMutex(hSecMutex);
+ return hSecurity;
+}
+
+#ifdef UNICODE
+HANDLE NetlibInitSecurityProvider(const char* szProvider, const char* szPrincipal)
+{
+ return NetlibInitSecurityProvider(StrConvT(szProvider), StrConvT(szPrincipal));
+}
+#endif
+
+void NetlibDestroySecurityProvider(HANDLE hSecurity)
+{
+ if (hSecurity == NULL) return;
+
+ WaitForSingleObject(hSecMutex, INFINITE);
+
+ if (ntlmCnt != 0)
+ {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential);
+ mir_free(hNtlm->szProvider);
+ mir_free(hNtlm->szPrincipal);
+
+ --ntlmCnt;
+
+ mir_free(hNtlm);
+ }
+
+ if (secCnt && --secCnt == 0)
+ FreeSecurityLibrary();
+
+ ReleaseMutex(hSecMutex);
+}
+
+char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
+{
+ if (!szChallenge || !szChallenge[0]) return NULL;
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ unsigned char inDataBuffer[1024];
+
+ SecBuffer inBuffers[2] =
+ {
+ { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer },
+ { chlsz, SECBUFFER_STREAM, szChallenge },
+ };
+
+ SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers };
+
+ unsigned long qop = 0;
+ SECURITY_STATUS sc = g_pSSPI->DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ unsigned char LayerMask = inDataBuffer[0];
+ unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
+
+ SecPkgContext_Sizes sizes;
+ sc = g_pSSPI->QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer);
+ unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize);
+
+ unsigned char outDataBuffer[4] = { 1, 0, 16, 0 };
+
+ SecBuffer outBuffers[3] =
+ {
+ { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer },
+ { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer },
+ { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer }
+ };
+ SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers };
+
+ sc = g_pSSPI->EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ unsigned i, ressz = 0;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++)
+ ressz += outBuffersDesc.pBuffers[i].cbBuffer;
+
+
+ unsigned char *response = (unsigned char*)alloca(ressz), *p = response;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++)
+ {
+ memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer);
+ p += outBuffersDesc.pBuffers[i].cbBuffer;
+ }
+
+ NETLIBBASE64 nlb64;
+ nlb64.cbDecoded = ressz;
+ nlb64.pbDecoded = response;
+ nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded);
+ if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL;
+
+ return mir_strdup(nlb64.pszEncoded);
+}
+
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const TCHAR* login, const TCHAR* psw,
+ bool http, unsigned& complete)
+{
+ SECURITY_STATUS sc;
+ SecBufferDesc outputBufferDescriptor,inputBufferDescriptor;
+ SecBuffer outputSecurityToken,inputSecurityToken;
+ TimeStamp tokenExpiration;
+ ULONG contextAttributes;
+ NETLIBBASE64 nlb64 = { 0 };
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+
+ if (hSecurity == NULL || ntlmCnt == 0) return NULL;
+
+ if (_tcsicmp(hNtlm->szProvider, _T("Basic")))
+ {
+ bool isGSSAPI = _tcsicmp(hNtlm->szProvider, _T("GSSAPI")) == 0;
+ TCHAR *szProvider = isGSSAPI ? _T("Kerberos") : hNtlm->szProvider;
+ bool hasChallenge = szChallenge != NULL && szChallenge[0] != '\0';
+ if (hasChallenge)
+ {
+ nlb64.cchEncoded = lstrlenA(szChallenge);
+ nlb64.pszEncoded = (char*)szChallenge;
+ nlb64.cbDecoded = Netlib_GetBase64DecodedBufferSize(nlb64.cchEncoded);
+ nlb64.pbDecoded = (PBYTE)alloca(nlb64.cbDecoded);
+ if (!NetlibBase64Decode(0, (LPARAM)&nlb64)) return NULL;
+
+ if (isGSSAPI && complete)
+ return CompleteGssapi(hSecurity, nlb64.pbDecoded, nlb64.cbDecoded);
+
+ inputBufferDescriptor.cBuffers = 1;
+ inputBufferDescriptor.pBuffers = &inputSecurityToken;
+ inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ inputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ inputSecurityToken.cbBuffer = nlb64.cbDecoded;
+ inputSecurityToken.pvBuffer = nlb64.pbDecoded;
+
+ // try to decode the domain name from the NTLM challenge
+ if (login != NULL && login[0] != '\0' && !hNtlm->hasDomain)
+ {
+ NtlmType2packet* pkt = ( NtlmType2packet* )nlb64.pbDecoded;
+ if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2)
+ {
+#ifdef UNICODE
+ wchar_t* domainName = (wchar_t*)&nlb64.pbDecoded[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate ANSI? if yes, convert the ANSI name to unicode
+ if ((pkt->flags & 1) == 0)
+ {
+ int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, NULL, 0);
+ wchar_t* buf = (wchar_t*)alloca(bufsz * sizeof(wchar_t));
+ domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
+ domainName = buf;
+ }
+ else
+ domainLen /= sizeof(wchar_t);
+#else
+ char* domainName = (char*)&nlb64.pbDecoded[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate Unicode? if yes, convert the unicode name to ANSI
+ if (pkt->flags & 1)
+ {
+ int bufsz = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, NULL, 0, NULL, NULL);
+ char* buf = (char*)alloca(bufsz);
+ domainLen = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, buf, bufsz, NULL, NULL) - 1;
+ domainName = buf;
+ }
+#endif
+
+ if (domainLen)
+ {
+ size_t newLoginLen = _tcslen(login) + domainLen + 1;
+ TCHAR *newLogin = (TCHAR*)alloca(newLoginLen * sizeof(TCHAR));
+
+ _tcsncpy(newLogin, domainName, domainLen);
+ newLogin[domainLen] = '\\';
+ _tcscpy(newLogin + domainLen + 1, login);
+
+ char* szChl = NtlmCreateResponseFromChallenge(hSecurity, NULL, newLogin, psw, http, complete);
+ mir_free(szChl);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+
+ if (login != NULL && login[0] != '\0')
+ {
+ memset(&auth, 0, sizeof(auth));
+#ifdef _UNICODE
+ NetlibLogf(NULL, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
+#else
+ NetlibLogf(NULL, "Security login requested, user: %s pssw: %s", login, psw ? "(exist)" : "(no psw)");
+#endif
+
+ const TCHAR* loginName = login;
+ const TCHAR* domainName = _tcschr(login, '\\');
+ int domainLen = 0;
+ int loginLen = lstrlen(loginName);
+ if (domainName != NULL)
+ {
+ loginName = domainName + 1;
+ loginLen = lstrlen(loginName);
+ domainLen = domainName - login;
+ domainName = login;
+ }
+ else if ((domainName = _tcschr(login, '@')) != NULL)
+ {
+ loginName = login;
+ loginLen = domainName - login;
+ domainLen = lstrlen(++domainName);
+ }
+
+#ifdef UNICODE
+ auth.User = (PWORD)loginName;
+ auth.UserLength = loginLen;
+ auth.Password = (PWORD)psw;
+ auth.PasswordLength = lstrlen(psw);
+ auth.Domain = (PWORD)domainName;
+ auth.DomainLength = domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+ auth.User = (PBYTE)loginName;
+ auth.UserLength = loginLen;
+ auth.Password = (PBYTE)psw;
+ auth.PasswordLength = lstrlen(psw);
+ auth.Domain = (PBYTE)domainName;
+ auth.DomainLength = domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+
+ hNtlm->hasDomain = domainLen != 0;
+ }
+
+ sc = g_pSSPI->AcquireCredentialsHandle(NULL, szProvider,
+ SECPKG_CRED_OUTBOUND, NULL, hNtlm->hasDomain ? &auth : NULL, NULL, NULL,
+ &hNtlm->hClientCredential, &tokenExpiration);
+ if (sc != SEC_E_OK)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+ }
+
+ outputBufferDescriptor.cBuffers = 1;
+ outputBufferDescriptor.pBuffers = &outputSecurityToken;
+ outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ outputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
+ outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
+
+ sc = g_pSSPI->InitializeSecurityContext(&hNtlm->hClientCredential,
+ hasChallenge ? &hNtlm->hClientContext : NULL,
+ hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
+ hasChallenge ? &inputBufferDescriptor : NULL, 0, &hNtlm->hClientContext,
+ &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
+
+ complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED);
+
+ if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE)
+ {
+ sc = g_pSSPI->CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
+ }
+
+ if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED)
+ {
+ ReportSecError(sc, __LINE__);
+ return NULL;
+ }
+
+ nlb64.cbDecoded = outputSecurityToken.cbBuffer;
+ nlb64.pbDecoded = (PBYTE)outputSecurityToken.pvBuffer;
+ }
+ else
+ {
+ if (!login || !psw) return NULL;
+
+ char *szLogin = mir_t2a(login);
+ char *szPassw = mir_t2a(psw);
+
+ size_t authLen = strlen(szLogin) + strlen(szPassw) + 5;
+ char *szAuth = (char*)alloca(authLen);
+
+ nlb64.cbDecoded = mir_snprintf(szAuth, authLen,"%s:%s", szLogin, szPassw);
+ nlb64.pbDecoded=(PBYTE)szAuth;
+ complete = true;
+
+ mir_free(szPassw);
+ mir_free(szLogin);
+ }
+
+ nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded);
+ if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL;
+
+ char* result;
+ if (http)
+ {
+ char* szProvider = mir_t2a(hNtlm->szProvider);
+ nlb64.cchEncoded += (int)strlen(szProvider) + 10;
+ result = (char*)mir_alloc(nlb64.cchEncoded);
+ mir_snprintf(result, nlb64.cchEncoded, "%s %s", szProvider, nlb64.pszEncoded);
+ mir_free(szProvider);
+ }
+ else
+ result = mir_strdup(nlb64.pszEncoded);
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR InitSecurityProviderService(WPARAM, LPARAM lParam)
+{
+ HANDLE hSecurity = NetlibInitSecurityProvider((char*)lParam, NULL);
+ return (INT_PTR)hSecurity;
+}
+
+static INT_PTR InitSecurityProviderService2(WPARAM, LPARAM lParam)
+{
+ NETLIBNTLMINIT2 *req = ( NETLIBNTLMINIT2* )lParam;
+ if (req->cbSize < sizeof(*req)) return 0;
+
+ HANDLE hSecurity;
+
+#ifdef UNICODE
+ if (req->flags & NNR_UNICODE)
+ hSecurity = NetlibInitSecurityProvider(req->szProviderName, req->szPrincipal);
+ else
+#endif
+ hSecurity = NetlibInitSecurityProvider((char*)req->szProviderName, (char*)req->szPrincipal);
+
+ return (INT_PTR)hSecurity;
+}
+
+static INT_PTR DestroySecurityProviderService( WPARAM, LPARAM lParam )
+{
+ NetlibDestroySecurityProvider(( HANDLE )lParam );
+ return 0;
+}
+
+static INT_PTR NtlmCreateResponseService( WPARAM wParam, LPARAM lParam )
+{
+ NETLIBNTLMREQUEST* req = ( NETLIBNTLMREQUEST* )lParam;
+ unsigned complete;
+
+ char* response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ StrConvT(req->userName), StrConvT(req->password), false, complete );
+
+ return (INT_PTR)response;
+}
+
+static INT_PTR NtlmCreateResponseService2( WPARAM wParam, LPARAM lParam )
+{
+ NETLIBNTLMREQUEST2* req = ( NETLIBNTLMREQUEST2* )lParam;
+ if (req->cbSize < sizeof(*req)) return 0;
+
+ char* response;
+
+#ifdef UNICODE
+ if (req->flags & NNR_UNICODE)
+ {
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ req->szUserName, req->szPassword, false, req->complete );
+ }
+ else
+ {
+ TCHAR *szLogin = mir_a2t((char*)req->szUserName);
+ TCHAR *szPassw = mir_a2t((char*)req->szPassword);
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ szLogin, szPassw, false, req->complete );
+ mir_free(szLogin);
+ mir_free(szPassw);
+ }
+#else
+ response = NtlmCreateResponseFromChallenge(( HANDLE )wParam, req->szChallenge,
+ req->szUserName, req->szPassword, false, req->complete );
+#endif
+
+ return (INT_PTR)response;
+}
+
+void NetlibSecurityInit(void)
+{
+ hSecMutex = CreateMutex(NULL, FALSE, NULL);
+
+ CreateServiceFunction( MS_NETLIB_INITSECURITYPROVIDER, InitSecurityProviderService );
+ CreateServiceFunction( MS_NETLIB_INITSECURITYPROVIDER2, InitSecurityProviderService2 );
+ CreateServiceFunction( MS_NETLIB_DESTROYSECURITYPROVIDER, DestroySecurityProviderService );
+ CreateServiceFunction( MS_NETLIB_NTLMCREATERESPONSE, NtlmCreateResponseService );
+ CreateServiceFunction( MS_NETLIB_NTLMCREATERESPONSE2, NtlmCreateResponseService2 );
+}
+
+void NetlibSecurityDestroy(void)
+{
+ CloseHandle(hSecMutex);
+} \ No newline at end of file
diff --git a/src/modules/netlib/netlibsock.cpp b/src/modules/netlib/netlibsock.cpp
new file mode 100644
index 0000000000..875c47b0a1
--- /dev/null
+++ b/src/modules/netlib/netlibsock.cpp
@@ -0,0 +1,203 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+extern HANDLE hConnectionHeaderMutex,hSendEvent,hRecvEvent;
+
+INT_PTR NetlibSend(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
+ NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam;
+ INT_PTR result;
+
+ if ( nlb == NULL ) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if ( !NetlibEnterNestedCS( nlc, NLNCS_SEND ))
+ return SOCKET_ERROR;
+
+ if ( nlc->usingHttpGateway && !( nlb->flags & MSG_RAW )) {
+ if ( !( nlb->flags & MSG_NOHTTPGATEWAYWRAP ) && nlc->nlu->user.pfnHttpGatewayWrapSend ) {
+ NetlibDumpData( nlc, ( PBYTE )nlb->buf, nlb->len, 1, nlb->flags );
+ result = nlc->nlu->user.pfnHttpGatewayWrapSend(( HANDLE )nlc, ( PBYTE )nlb->buf, nlb->len, nlb->flags | MSG_NOHTTPGATEWAYWRAP, NetlibSend );
+ }
+ else result = NetlibHttpGatewayPost( nlc, nlb->buf, nlb->len, nlb->flags );
+ }
+ else {
+ NetlibDumpData( nlc, ( PBYTE )nlb->buf, nlb->len, 1, nlb->flags );
+ if (nlc->hSsl)
+ result = si.write( nlc->hSsl, nlb->buf, nlb->len );
+ else
+ result = send( nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF );
+ }
+ NetlibLeaveNestedCS( &nlc->ncsSend );
+
+ if ((( THook* )hSendEvent)->subscriberCount ) {
+ NETLIBNOTIFY nln = { nlb, result };
+ CallHookSubscribers( hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user );
+ }
+ return result;
+}
+
+INT_PTR NetlibRecv(WPARAM wParam,LPARAM lParam)
+{
+ struct NetlibConnection *nlc = (struct NetlibConnection*)wParam;
+ NETLIBBUFFER* nlb = ( NETLIBBUFFER* )lParam;
+ int recvResult;
+
+ if ( nlb == NULL ) {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return SOCKET_ERROR;
+ }
+
+ if ( !NetlibEnterNestedCS( nlc, NLNCS_RECV ))
+ return SOCKET_ERROR;
+
+ if ( nlc->usingHttpGateway && !( nlb->flags & MSG_RAW ))
+ recvResult = NetlibHttpGatewayRecv( nlc, nlb->buf, nlb->len, nlb->flags );
+ else
+ {
+ if (nlc->hSsl)
+ recvResult = si.read( nlc->hSsl, nlb->buf, nlb->len, (nlb->flags & MSG_PEEK) != 0 );
+ else
+ recvResult = recv( nlc->s, nlb->buf, nlb->len, nlb->flags & 0xFFFF );
+ }
+ NetlibLeaveNestedCS( &nlc->ncsRecv );
+ if (recvResult <= 0)
+ return recvResult;
+
+ NetlibDumpData(nlc, (PBYTE)nlb->buf, recvResult, 0, nlb->flags);
+
+ if ((nlb->flags & MSG_PEEK) == 0 && ((THook*)hRecvEvent)->subscriberCount)
+ {
+ NETLIBNOTIFY nln = { nlb, recvResult };
+ CallHookSubscribers(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+ }
+ return recvResult;
+}
+
+static int ConnectionListToSocketList(HANDLE *hConns, fd_set *fd, int& pending)
+{
+ struct NetlibConnection *nlcCheck;
+ int i;
+
+ FD_ZERO(fd);
+ for(i=0;hConns[i] && hConns[i]!=INVALID_HANDLE_VALUE && i<FD_SETSIZE;i++) {
+ nlcCheck=(struct NetlibConnection*)hConns[i];
+ if (nlcCheck->handleType!=NLH_CONNECTION && nlcCheck->handleType!=NLH_BOUNDPORT) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ FD_SET(nlcCheck->s,fd);
+ if ( si.pending( nlcCheck->hSsl ))
+ pending++;
+ }
+ return 1;
+}
+
+INT_PTR NetlibSelect(WPARAM,LPARAM lParam)
+{
+ NETLIBSELECT *nls=(NETLIBSELECT*)lParam;
+ if (nls==NULL || nls->cbSize!=sizeof(NETLIBSELECT)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ TIMEVAL tv;
+ tv.tv_sec=nls->dwTimeout/1000;
+ tv.tv_usec=(nls->dwTimeout%1000)*1000;
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ if (!ConnectionListToSocketList(nls->hReadConns,&readfd,pending)
+ || !ConnectionListToSocketList(nls->hWriteConns,&writefd,pending)
+ || !ConnectionListToSocketList(nls->hExceptConns,&exceptfd,pending)) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ return SOCKET_ERROR;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ if (pending)
+ return 1;
+
+ return select(0,&readfd,&writefd,&exceptfd,nls->dwTimeout==INFINITE?NULL:&tv);
+}
+
+INT_PTR NetlibSelectEx(WPARAM, LPARAM lParam)
+{
+ NETLIBSELECTEX *nls=(NETLIBSELECTEX*)lParam;
+ if (nls==NULL || nls->cbSize!=sizeof(NETLIBSELECTEX)) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ TIMEVAL tv;
+ tv.tv_sec=nls->dwTimeout/1000;
+ tv.tv_usec=(nls->dwTimeout%1000)*1000;
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+
+ int pending = 0;
+ fd_set readfd,writefd,exceptfd;
+ if (!ConnectionListToSocketList(nls->hReadConns,&readfd,pending)
+ || !ConnectionListToSocketList(nls->hWriteConns,&writefd,pending)
+ || !ConnectionListToSocketList(nls->hExceptConns,&exceptfd,pending)) {
+ ReleaseMutex(hConnectionHeaderMutex);
+ return SOCKET_ERROR;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ int rc = (pending) ? pending : select(0,&readfd,&writefd,&exceptfd,nls->dwTimeout==INFINITE?NULL:&tv);
+
+ WaitForSingleObject(hConnectionHeaderMutex,INFINITE);
+ /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET()
+ to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not)
+ This happens for read/write/except */
+ struct NetlibConnection *conn=NULL;
+ int j;
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hReadConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+
+ if (si.pending(conn->hSsl))
+ nls->hReadStatus[j] = TRUE;
+ if (conn->usingHttpGateway && conn->nlhpi.szHttpGetUrl == NULL && conn->dataBuffer == NULL)
+ nls->hReadStatus[j] = (conn->pHttpProxyPacketQueue != NULL);
+ else
+ nls->hReadStatus[j] = FD_ISSET(conn->s,&readfd);
+ }
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hWriteConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+ nls->hWriteStatus[j] = FD_ISSET(conn->s,&writefd);
+ }
+ for (j=0; j<FD_SETSIZE; j++) {
+ conn=(struct NetlibConnection*)nls->hExceptConns[j];
+ if (conn==NULL || conn==INVALID_HANDLE_VALUE) break;
+ nls->hExceptStatus[j] = FD_ISSET(conn->s,&exceptfd);
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ return rc;
+}
diff --git a/src/modules/netlib/netlibssl.cpp b/src/modules/netlib/netlibssl.cpp
new file mode 100644
index 0000000000..2db769a3da
--- /dev/null
+++ b/src/modules/netlib/netlibssl.cpp
@@ -0,0 +1,981 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include <m_popup.h>
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <schannel.h>
+
+//#include <SCHNLSP.H>
+
+typedef BOOL (* SSL_EMPTY_CACHE_FN_M)(VOID);
+
+static HMODULE g_hSchannel;
+static PSecurityFunctionTableA g_pSSPI;
+static HANDLE g_hSslMutex;
+static SSL_EMPTY_CACHE_FN_M MySslEmptyCache;
+static CredHandle hCreds;
+static bool bSslInitDone;
+
+typedef BOOL (WINAPI *pfnCertGetCertificateChain)(HCERTCHAINENGINE, PCCERT_CONTEXT, LPFILETIME, HCERTSTORE, PCERT_CHAIN_PARA, DWORD, LPVOID, PCCERT_CHAIN_CONTEXT*);
+static pfnCertGetCertificateChain fnCertGetCertificateChain;
+
+typedef VOID (WINAPI *pfnCertFreeCertificateChain)(PCCERT_CHAIN_CONTEXT);
+static pfnCertFreeCertificateChain fnCertFreeCertificateChain;
+
+typedef BOOL (WINAPI *pfnCertFreeCertificateContext)(PCCERT_CONTEXT);
+static pfnCertFreeCertificateContext fnCertFreeCertificateContext;
+
+typedef BOOL (WINAPI *pfnCertVerifyCertificateChainPolicy)(LPCSTR, PCCERT_CHAIN_CONTEXT, PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS);
+static pfnCertVerifyCertificateChainPolicy fnCertVerifyCertificateChainPolicy;
+
+typedef enum
+{
+ sockOpen,
+ sockClosed,
+ sockError
+} SocketState;
+
+
+struct SslHandle
+{
+ SOCKET s;
+
+ CtxtHandle hContext;
+
+ BYTE *pbRecDataBuf;
+ int cbRecDataBuf;
+ int sbRecDataBuf;
+
+ BYTE *pbIoBuffer;
+ int cbIoBuffer;
+ int sbIoBuffer;
+
+ SocketState state;
+};
+
+static void ReportSslError(SECURITY_STATUS scRet, int line, bool showPopup = false)
+{
+ TCHAR szMsgBuf[256];
+ switch (scRet)
+ {
+ case 0:
+ case ERROR_NOT_READY:
+ return;
+
+ case SEC_E_INVALID_TOKEN:
+ _tcscpy(szMsgBuf, TranslateT("Client cannot decode host message. Possible causes: Host does not support SSL or requires not existing security package"));
+ break;
+
+ case CERT_E_CN_NO_MATCH:
+ case SEC_E_WRONG_PRINCIPAL:
+ _tcscpy(szMsgBuf, TranslateT("Host we are connecting to is not the one certificate was issued for"));
+ break;
+
+ default:
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, scRet, LANG_USER_DEFAULT, szMsgBuf, SIZEOF(szMsgBuf), NULL);
+ }
+
+ TCHAR szMsgBuf2[512];
+ mir_sntprintf(szMsgBuf2, SIZEOF(szMsgBuf2), _T("SSL connection failure (%x %u): %s"), scRet, line, szMsgBuf);
+
+ char* szMsg = Utf8EncodeT(szMsgBuf2);
+ NetlibLogf(NULL, szMsg);
+ mir_free(szMsg);
+
+ SetLastError(scRet);
+ PUShowMessageT(szMsgBuf2, SM_WARNING);
+}
+
+static bool AcquireCredentials(void)
+{
+ SCHANNEL_CRED SchannelCred;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS /*| 0xA00 TLS1.1 & 1.2*/;
+
+ SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ // Create an SSPI credential.
+ scRet = g_pSSPI->AcquireCredentialsHandleA(
+ NULL, // Name of principal
+ UNISP_NAME_A, // Name of package
+ SECPKG_CRED_OUTBOUND, // Flags indicating use
+ NULL, // Pointer to logon ID
+ &SchannelCred, // Package specific data
+ NULL, // Pointer to GetKey() func
+ NULL, // Value to pass to GetKey()
+ &hCreds, // (out) Cred Handle
+ &tsExpiry); // (out) Lifetime (optional)
+
+ ReportSslError(scRet, __LINE__);
+ return scRet == SEC_E_OK;
+}
+
+static bool SSL_library_init(void)
+{
+ if (bSslInitDone) return true;
+
+ WaitForSingleObject(g_hSslMutex, INFINITE);
+
+ if (!bSslInitDone)
+ {
+ g_hSchannel = LoadLibraryA("schannel.dll");
+ if (g_hSchannel)
+ {
+ INIT_SECURITY_INTERFACE_A pInitSecurityInterface;
+ pInitSecurityInterface = (INIT_SECURITY_INTERFACE_A)GetProcAddress(g_hSchannel, SECURITY_ENTRYPOINT_ANSIA);
+ if (pInitSecurityInterface != NULL)
+ g_pSSPI = pInitSecurityInterface();
+
+ if (g_pSSPI)
+ {
+ HINSTANCE hCrypt = LoadLibraryA("crypt32.dll");
+ if (hCrypt)
+ {
+ fnCertGetCertificateChain = (pfnCertGetCertificateChain)GetProcAddress(hCrypt, "CertGetCertificateChain");
+ fnCertFreeCertificateChain = (pfnCertFreeCertificateChain)GetProcAddress(hCrypt, "CertFreeCertificateChain");
+ fnCertFreeCertificateContext = (pfnCertFreeCertificateContext)GetProcAddress(hCrypt, "CertFreeCertificateContext");
+ fnCertVerifyCertificateChainPolicy = (pfnCertVerifyCertificateChainPolicy)GetProcAddress(hCrypt, "CertVerifyCertificateChainPolicy");
+ }
+
+ MySslEmptyCache = (SSL_EMPTY_CACHE_FN_M)GetProcAddress(g_hSchannel, "SslEmptyCache");
+ AcquireCredentials();
+ bSslInitDone = true;
+ }
+ else
+ {
+ FreeLibrary(g_hSchannel);
+ g_hSchannel = NULL;
+ }
+ }
+ }
+
+ ReleaseMutex(g_hSslMutex);
+ return bSslInitDone;
+}
+
+void NetlibSslFree(SslHandle *ssl)
+{
+ if (ssl == NULL) return;
+
+ g_pSSPI->DeleteSecurityContext(&ssl->hContext);
+
+ mir_free(ssl->pbRecDataBuf);
+ mir_free(ssl->pbIoBuffer);
+ memset(ssl, 0, sizeof(SslHandle));
+ mir_free(ssl);
+}
+
+BOOL NetlibSslPending(SslHandle *ssl)
+{
+ return ssl != NULL && ( ssl->cbRecDataBuf != 0 || ssl->cbIoBuffer != 0 );
+}
+
+static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, DWORD dwCertFlags)
+{
+ if (!fnCertGetCertificateChain)
+ return true;
+
+ static LPSTR rgszUsages[] =
+ {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+
+ CERT_CHAIN_PARA ChainPara = {0};
+ HTTPSPolicyCallbackData polHttps = {0};
+ CERT_CHAIN_POLICY_PARA PolicyPara = {0};
+ CERT_CHAIN_POLICY_STATUS PolicyStatus = {0};
+ PCCERT_CHAIN_CONTEXT pChainContext = NULL;
+ PCCERT_CONTEXT pServerCert = NULL;
+ DWORD scRet;
+
+ PWSTR pwszServerName = mir_a2u(pszServerName);
+
+ scRet = g_pSSPI->QueryContextAttributesA(&ssl->hContext,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pServerCert);
+ if (scRet != SEC_E_OK)
+ goto cleanup;
+
+ if (pServerCert == NULL)
+ {
+ scRet = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ ChainPara.cbSize = sizeof(ChainPara);
+ ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ ChainPara.RequestedUsage.Usage.cUsageIdentifier = SIZEOF(rgszUsages);
+ ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (!fnCertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore,
+ &ChainPara, 0, NULL, &pChainContext))
+ {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
+ polHttps.dwAuthType = AUTHTYPE_SERVER;
+ polHttps.fdwChecks = dwCertFlags;
+ polHttps.pwszServerName = pwszServerName;
+
+ PolicyPara.cbSize = sizeof(PolicyPara);
+ PolicyPara.pvExtraPolicyPara = &polHttps;
+
+ PolicyStatus.cbSize = sizeof(PolicyStatus);
+
+ if (!fnCertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext,
+ &PolicyPara, &PolicyStatus))
+ {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ if (PolicyStatus.dwError)
+ {
+ scRet = PolicyStatus.dwError;
+ goto cleanup;
+ }
+
+ scRet = SEC_E_OK;
+
+cleanup:
+ if (pChainContext)
+ fnCertFreeCertificateChain(pChainContext);
+ if (pServerCert)
+ fnCertFreeCertificateContext(pServerCert);
+ mir_free(pwszServerName);
+
+ ReportSslError(scRet, __LINE__, true);
+ return scRet == SEC_E_OK;
+}
+
+static SECURITY_STATUS ClientHandshakeLoop(SslHandle *ssl, BOOL fDoInitialRead)
+{
+ SecBufferDesc InBuffer;
+ SecBuffer InBuffers[2];
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ BOOL fDoRead;
+
+ dwSSPIFlags =
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ ssl->cbIoBuffer = 0;
+
+ fDoRead = fDoInitialRead;
+
+ scRet = SEC_I_CONTINUE_NEEDED;
+
+ // Loop until the handshake is finished or an error occurs.
+ while (scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ // Read server data
+ if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (fDoRead)
+ {
+ static const TIMEVAL tv = {6, 0};
+ fd_set fd;
+
+ // If buffer not large enough reallocate buffer
+ if (ssl->sbIoBuffer <= ssl->cbIoBuffer)
+ {
+ ssl->sbIoBuffer += 4096;
+ ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer);
+ }
+
+ FD_ZERO(&fd);
+ FD_SET(ssl->s, &fd);
+ if (select(1, &fd, NULL, NULL, &tv) != 1)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure recieving data (timeout) (bytes %u)", ssl->cbIoBuffer);
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+
+ cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure recieving data (%d)", WSAGetLastError());
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+ if (cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL Negotiation connection gracefully closed");
+ scRet = ERROR_NOT_READY;
+ break;
+ }
+
+ NetlibDumpData(NULL, ssl->pbIoBuffer + ssl->cbIoBuffer, cbData, 0, MSG_DUMPSSL);
+ ssl->cbIoBuffer += cbData;
+ }
+ else fDoRead = TRUE;
+ }
+
+ // Set up the input buffers. Buffer 0 is used to pass in data
+ // received from the server. Schannel will consume some or all
+ // of this. Leftover data (if any) will be placed in buffer 1 and
+ // given a buffer type of SECBUFFER_EXTRA.
+
+ InBuffers[0].pvBuffer = ssl->pbIoBuffer;
+ InBuffers[0].cbBuffer = ssl->cbIoBuffer;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ InBuffers[1].pvBuffer = NULL;
+ InBuffers[1].cbBuffer = 0;
+ InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ InBuffer.cBuffers = 2;
+ InBuffer.pBuffers = InBuffers;
+ InBuffer.ulVersion = SECBUFFER_VERSION;
+
+ // Set up the output buffers. These are initialized to NULL
+ // so as to make it less likely we'll attempt to free random
+ // garbage later.
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType= SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ &ssl->hContext,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InBuffer,
+ 0,
+ NULL,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ // If success (or if the error was one of the special extended ones),
+ // send the contents of the output buffer to the server.
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)))
+ {
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL Negotiation failure sending data (%d)", WSAGetLastError());
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ // Free output buffer.
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ // we need to read more data from the server and try again.
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue;
+
+ // handshake completed successfully.
+ if (scRet == SEC_E_OK)
+ {
+ // Store remaining data for further use
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memmove(ssl->pbIoBuffer,
+ ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+ ssl->cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ ssl->cbIoBuffer = 0;
+ break;
+ }
+
+ // Check for fatal error.
+ if (FAILED(scRet)) break;
+
+ // server just requested client authentication.
+ if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ // Server has requested client authentication and
+ // GetNewClientCredentials(ssl);
+
+ // Go around again.
+ fDoRead = FALSE;
+ scRet = SEC_I_CONTINUE_NEEDED;
+ continue;
+ }
+
+
+ // Copy any leftover data from the buffer, and go around again.
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memmove(ssl->pbIoBuffer,
+ ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+
+ ssl->cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else ssl->cbIoBuffer = 0;
+ }
+
+ // Delete the security context in the case of a fatal error.
+ ReportSslError(scRet, __LINE__);
+
+ if (ssl->cbIoBuffer == 0)
+ {
+ mir_free(ssl->pbIoBuffer);
+ ssl->pbIoBuffer = NULL;
+ ssl->sbIoBuffer = 0;
+ }
+
+ return scRet;
+}
+
+static bool ClientConnect(SslHandle *ssl, const char *host)
+{
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ if (SecIsValidHandle(&ssl->hContext))
+ {
+ g_pSSPI->DeleteSecurityContext(&ssl->hContext);
+ SecInvalidateHandle(&ssl->hContext);
+ }
+
+ if (MySslEmptyCache) MySslEmptyCache();
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ // Initiate a ClientHello message and generate a token.
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ NULL,
+ (SEC_CHAR*)host,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &ssl->hContext,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ if (scRet != SEC_I_CONTINUE_NEEDED)
+ {
+ ReportSslError(scRet, __LINE__);
+ return 0;
+ }
+
+ // Send response to server if there is one.
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL failure sending connection data (%d %d)", ssl->s, WSAGetLastError());
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ return 0;
+ }
+
+ // Free output buffer.
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+
+ return ClientHandshakeLoop(ssl, TRUE) == SEC_E_OK;
+}
+
+
+SslHandle *NetlibSslConnect(SOCKET s, const char* host, int verify)
+{
+ SslHandle *ssl = (SslHandle*)mir_calloc(sizeof(SslHandle));
+ ssl->s = s;
+
+ SecInvalidateHandle(&ssl->hContext);
+
+ DWORD dwFlags = 0;
+
+ if (!host || inet_addr(host) != INADDR_NONE)
+ dwFlags |= 0x00001000;
+
+ bool res = SSL_library_init();
+
+ if (res) res = ClientConnect(ssl, host);
+ if (res && verify) res = VerifyCertificate(ssl, host, dwFlags);
+
+ if (!res)
+ {
+ NetlibSslFree(ssl);
+ ssl = NULL;
+ }
+ return ssl;
+}
+
+
+void NetlibSslShutdown(SslHandle *ssl)
+{
+ DWORD dwType;
+
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ DWORD scRet;
+
+ if (ssl == NULL || !SecIsValidHandle(&ssl->hContext))
+ return;
+
+ dwType = SCHANNEL_SHUTDOWN;
+
+ OutBuffers[0].pvBuffer = &dwType;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = sizeof(dwType);
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->ApplyControlToken(&ssl->hContext, &OutBuffer);
+ if (FAILED(scRet)) return;
+
+ //
+ // Build an SSL close notify message.
+ //
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = g_pSSPI->InitializeSecurityContextA(
+ &hCreds,
+ &ssl->hContext,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &ssl->hContext,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ if (FAILED(scRet)) return;
+
+ // Send the close notify message to the server.
+ if (OutBuffers[0].pvBuffer != NULL && OutBuffers[0].cbBuffer != 0)
+ {
+ NetlibDumpData(NULL, (unsigned char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 1, MSG_DUMPSSL);
+ send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
+ g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
+ }
+}
+
+static int NetlibSslReadSetResult(SslHandle *ssl, char *buf, int num, int peek)
+{
+ if (ssl->cbRecDataBuf == 0)
+ {
+ return (ssl->state == sockClosed ? 0: SOCKET_ERROR);
+ }
+
+ int bytes = min(num, ssl->cbRecDataBuf);
+ int rbytes = ssl->cbRecDataBuf - bytes;
+
+ memcpy(buf, ssl->pbRecDataBuf, bytes);
+ if (!peek)
+ {
+ memmove(ssl->pbRecDataBuf, ssl->pbRecDataBuf + bytes, rbytes);
+ ssl->cbRecDataBuf = rbytes;
+ }
+
+ return bytes;
+}
+
+int NetlibSslRead(SslHandle *ssl, char *buf, int num, int peek)
+{
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+ DWORD resNum = 0;
+ int i;
+
+ SecBufferDesc Message;
+ SecBuffer Buffers[4];
+ SecBuffer * pDataBuffer;
+ SecBuffer * pExtraBuffer;
+
+ if (ssl == NULL) return SOCKET_ERROR;
+
+ if (num <= 0) return 0;
+
+ if (ssl->state != sockOpen || (ssl->cbRecDataBuf != 0 && (!peek || ssl->cbRecDataBuf >= num)))
+ {
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ scRet = SEC_E_OK;
+
+ for (;;)
+ {
+ if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (ssl->sbIoBuffer <= ssl->cbIoBuffer)
+ {
+ ssl->sbIoBuffer += 2048;
+ ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer);
+ }
+
+ if (peek)
+ {
+ static const TIMEVAL tv = {0};
+ fd_set fd;
+ FD_ZERO(&fd);
+ FD_SET(ssl->s, &fd);
+
+ cbData = select(1, &fd, NULL, NULL, &tv);
+ if (cbData == SOCKET_ERROR)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (cbData == 0 && ssl->cbRecDataBuf)
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ NetlibLogf(NULL, "SSL failure recieving data (%d)", WSAGetLastError());
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL connection gracefully closed");
+ if (peek && ssl->cbRecDataBuf)
+ {
+ ssl->state = sockClosed;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ // Server disconnected.
+ if (ssl->cbIoBuffer)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ return 0;
+ }
+ else
+ {
+ NetlibDumpData(NULL, ssl->pbIoBuffer + ssl->cbIoBuffer, cbData, 0, MSG_DUMPSSL);
+ ssl->cbIoBuffer += cbData;
+ }
+ }
+
+ // Attempt to decrypt the received data.
+ Buffers[0].pvBuffer = ssl->pbIoBuffer;
+ Buffers[0].cbBuffer = ssl->cbIoBuffer;
+ Buffers[0].BufferType = SECBUFFER_DATA;
+
+ Buffers[1].BufferType = SECBUFFER_EMPTY;
+ Buffers[2].BufferType = SECBUFFER_EMPTY;
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+
+ if (g_pSSPI->DecryptMessage != NULL && g_pSSPI->DecryptMessage != PVOID(0x80000000))
+ scRet = g_pSSPI->DecryptMessage(&ssl->hContext, &Message, 0, NULL);
+ else
+ scRet = ((DECRYPT_MESSAGE_FN)g_pSSPI->Reserved4)(&ssl->hContext, &Message, 0, NULL);
+
+ // The input buffer contains only a fragment of an
+ // encrypted record. Loop around and read some more
+ // data.
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ continue;
+
+ if ( scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE && scRet != SEC_I_CONTEXT_EXPIRED)
+ {
+ ReportSslError(scRet, __LINE__);
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ // Locate data and (optional) extra buffers.
+ pDataBuffer = NULL;
+ pExtraBuffer = NULL;
+ for(i = 1; i < 4; i++)
+ {
+ if (pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
+ pDataBuffer = &Buffers[i];
+
+ if (pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
+ pExtraBuffer = &Buffers[i];
+ }
+
+ // Return decrypted data.
+ if (pDataBuffer)
+ {
+ DWORD bytes, rbytes;
+
+ bytes = peek ? 0 : min((DWORD)num, pDataBuffer->cbBuffer);
+ rbytes = pDataBuffer->cbBuffer - bytes;
+
+ NetlibDumpData(NULL, (PBYTE)pDataBuffer->pvBuffer, pDataBuffer->cbBuffer, 0, MSG_DUMPSSL);
+
+ if (rbytes > 0)
+ {
+ int nbytes = ssl->cbRecDataBuf + rbytes;
+ if (ssl->sbRecDataBuf < nbytes)
+ {
+ ssl->sbRecDataBuf = nbytes;
+ ssl->pbRecDataBuf = (PUCHAR)mir_realloc(ssl->pbRecDataBuf, nbytes);
+ }
+ memcpy(ssl->pbRecDataBuf + ssl->cbRecDataBuf, (char*)pDataBuffer->pvBuffer + bytes, rbytes);
+ ssl->cbRecDataBuf = nbytes;
+ }
+
+ if (peek)
+ {
+ resNum = bytes = min(num, ssl->cbRecDataBuf);
+ memcpy(buf, ssl->pbRecDataBuf, bytes);
+ }
+ else
+ {
+ resNum = bytes;
+ memcpy(buf, pDataBuffer->pvBuffer, bytes);
+ }
+ }
+
+ // Move any "extra" data to the input buffer.
+ if (pExtraBuffer)
+ {
+ memmove(ssl->pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+ ssl->cbIoBuffer = pExtraBuffer->cbBuffer;
+ }
+ else ssl->cbIoBuffer = 0;
+
+ if (pDataBuffer && resNum)
+ return resNum;
+
+ // Server signaled end of session
+ if (scRet == SEC_I_CONTEXT_EXPIRED)
+ {
+ NetlibLogf(NULL, "SSL Server signaled SSL Shutdown");
+ ssl->state = sockClosed;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+
+ if (scRet == SEC_I_RENEGOTIATE)
+ {
+ // The server wants to perform another handshake
+ // sequence.
+
+ scRet = ClientHandshakeLoop(ssl, FALSE);
+ if (scRet != SEC_E_OK)
+ {
+ ssl->state = sockError;
+ return NetlibSslReadSetResult(ssl, buf, num, peek);
+ }
+ }
+ }
+}
+
+int NetlibSslWrite(SslHandle *ssl, const char *buf, int num)
+{
+ SecPkgContext_StreamSizes Sizes;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ SecBufferDesc Message;
+ SecBuffer Buffers[4] = {0};
+
+ PUCHAR pbDataBuffer;
+
+ PUCHAR pbMessage;
+ DWORD cbMessage;
+
+ DWORD sendOff = 0;
+
+ if (ssl == NULL) return SOCKET_ERROR;
+
+ scRet = g_pSSPI->QueryContextAttributesA(&ssl->hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
+ if (scRet != SEC_E_OK) return scRet;
+
+ pbDataBuffer = (PUCHAR)mir_calloc(Sizes.cbMaximumMessage + Sizes.cbHeader + Sizes.cbTrailer);
+
+ pbMessage = pbDataBuffer + Sizes.cbHeader;
+
+ while (sendOff < (DWORD)num)
+ {
+ cbMessage = min(Sizes.cbMaximumMessage, (DWORD)num - sendOff);
+ CopyMemory(pbMessage, buf+sendOff, cbMessage);
+
+ Buffers[0].pvBuffer = pbDataBuffer;
+ Buffers[0].cbBuffer = Sizes.cbHeader;
+ Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+ Buffers[1].pvBuffer = pbMessage;
+ Buffers[1].cbBuffer = cbMessage;
+ Buffers[1].BufferType = SECBUFFER_DATA;
+
+ Buffers[2].pvBuffer = pbMessage + cbMessage;
+ Buffers[2].cbBuffer = Sizes.cbTrailer;
+ Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+
+ if (g_pSSPI->EncryptMessage != NULL)
+ scRet = g_pSSPI->EncryptMessage(&ssl->hContext, 0, &Message, 0);
+ else
+ scRet = ((ENCRYPT_MESSAGE_FN)g_pSSPI->Reserved3)(&ssl->hContext, 0, &Message, 0);
+
+ if (FAILED(scRet)) break;
+
+ // Calculate encrypted packet size
+ cbData = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
+
+ // Send the encrypted data to the server.
+ NetlibDumpData(NULL, pbDataBuffer, cbData, 1, MSG_DUMPSSL);
+ cbData = send(ssl->s, (char*)pbDataBuffer, cbData, 0);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ NetlibLogf(NULL, "SSL failure sending data (%d)", WSAGetLastError());
+ scRet = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+
+ sendOff += cbMessage;
+ }
+
+ mir_free(pbDataBuffer);
+ return scRet == SEC_E_OK ? num : SOCKET_ERROR;
+}
+
+static INT_PTR GetSslApi(WPARAM, LPARAM lParam)
+{
+ SSL_API* si = (SSL_API*)lParam;
+ if (si == NULL) return FALSE;
+
+ if (si->cbSize != sizeof(SSL_API))
+ return FALSE;
+
+ si->connect = (HSSL (__cdecl *)(SOCKET,const char *,int))NetlibSslConnect;
+ si->pending = (BOOL (__cdecl *)(HSSL))NetlibSslPending;
+ si->read = (int (__cdecl *)(HSSL,char *,int,int))NetlibSslRead;
+ si->write = (int (__cdecl *)(HSSL,const char *,int))NetlibSslWrite;
+ si->shutdown = (void (__cdecl *)(HSSL))NetlibSslShutdown;
+ si->sfree = (void (__cdecl *)(HSSL))NetlibSslFree;
+
+ return TRUE;
+}
+
+int LoadSslModule(void)
+{
+ CreateServiceFunction(MS_SYSTEM_GET_SI, GetSslApi);
+ g_hSslMutex = CreateMutex(NULL, FALSE, NULL);
+ SecInvalidateHandle(&hCreds);
+
+ return 0;
+}
+
+void UnloadSslModule(void)
+{
+ if (g_pSSPI && SecIsValidHandle(&hCreds))
+ g_pSSPI->FreeCredentialsHandle(&hCreds);
+ CloseHandle(g_hSslMutex);
+ if (g_hSchannel) FreeLibrary(g_hSchannel);
+}
diff --git a/src/modules/netlib/netlibupnp.cpp b/src/modules/netlib/netlibupnp.cpp
new file mode 100644
index 0000000000..935801d330
--- /dev/null
+++ b/src/modules/netlib/netlibupnp.cpp
@@ -0,0 +1,885 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "netlib.h"
+
+static const char search_request_msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 1\r\n"
+ "ST: urn:schemas-upnp-org:service:%s\r\n"
+ "\r\n";
+
+static const char xml_get_hdr[] =
+ "GET %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "ACCEPT-LANGUAGE: *\r\n\r\n";
+
+static const char soap_post_hdr[] =
+ "POST %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+ "SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char soap_post_hdr_m[] =
+ "M-POST %s URL HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+ "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n"
+ "01-SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char search_device[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u=\"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n";
+
+static bool gatewayFound;
+static SOCKADDR_IN locIP;
+static time_t lastDiscTime;
+static int expireTime = 120;
+
+static int retryCount;
+static SOCKET sock = INVALID_SOCKET;
+static char szConnHost[256];
+static unsigned short sConnPort;
+
+static WORD *portList;
+static unsigned numports, numportsAlloc;
+static HANDLE portListMutex;
+
+static char szCtlUrl[256], szDev[256];
+
+typedef enum
+{
+ DeviceGetReq,
+ ControlAction,
+ ControlQuery
+} ReqType;
+
+static bool txtParseParam(char* szData, char* presearch,
+ char* start, char* finish, char* param, size_t size)
+{
+ char *cp, *cp1;
+ size_t len;
+
+ *param = 0;
+
+ if (presearch != NULL) {
+ cp1 = strstr(szData, presearch);
+ if (cp1 == NULL) return false;
+ }
+ else
+ cp1 = szData;
+
+ cp = strstr(cp1, start);
+ if (cp == NULL) return false;
+ cp += strlen(start);
+ while (*cp == ' ') ++cp;
+
+ cp1 = strstr(cp, finish);
+ if (cp1 == NULL) return false;
+ while (*(cp1-1) == ' ' && cp1 > cp) --cp1;
+
+ len = min((size_t)(cp1 - cp), size-1);
+ strncpy(param, cp, len);
+ param[len] = 0;
+
+ return true;
+}
+
+void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath)
+{
+ char *ppath, *phost, *pport;
+ int sz;
+
+ phost = strstr(szUrl,"://");
+ if (phost == NULL) phost = szUrl;
+ else phost += 3;
+
+ ppath = strchr(phost,'/');
+ if (ppath == NULL) ppath = phost + strlen(phost);
+
+ pport = strchr(phost,':');
+ if (pport == NULL) pport = ppath;
+
+ if (szHost != NULL)
+ {
+ sz = pport - phost + 1;
+ if (sz>256) sz = 256;
+ strncpy(szHost, phost, sz);
+ szHost[sz-1] = 0;
+ }
+
+ if (sPort != NULL)
+ {
+ if (pport < ppath)
+ {
+ long prt = atol(pport+1);
+ *sPort = prt != 0 ? (unsigned short)prt : 80;
+ }
+ else
+ *sPort = 80;
+ }
+
+ if (szPath != NULL)
+ {
+ strncpy(szPath, ppath, 256);
+ szPath[255] = 0;
+ }
+}
+
+
+static void LongLog(char* szData)
+{
+ CallService(MS_NETLIB_LOG, 0, (LPARAM)szData);
+}
+
+static void closeRouterConnection(void)
+{
+ if (sock != INVALID_SOCKET)
+ {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+}
+
+static void validateSocket(void)
+{
+ static const TIMEVAL tv = { 0, 0 };
+ fd_set rfd;
+ char buf[4];
+ bool opened;
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ switch (select(1, &rfd, NULL, NULL, &tv))
+ {
+ case SOCKET_ERROR:
+ opened = false;
+ break;
+
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(sock, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ closeRouterConnection();
+}
+
+static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype)
+{
+ // Parse URL
+ char szHost[256], szPath[256], szRes[16];
+ int sz = 0, res = 0;
+ unsigned short sPort;
+ bool needClose;
+
+ const char* szPostHdr = soap_post_hdr;
+ char* szData = ( char* )mir_alloc(4096);
+ char* szReq = NULL;
+
+ parseURL(szUrl, szHost, &sPort, szPath);
+
+ if (sPort != sConnPort || _stricmp(szHost, szConnHost))
+ closeRouterConnection();
+ else
+ validateSocket();
+
+ for (;;)
+ {
+ retryCount = 0;
+ switch(reqtype)
+ {
+ case DeviceGetReq:
+ sz = mir_snprintf (szData, 4096, xml_get_hdr, szPath, szHost, sPort);
+ break;
+
+ case ControlAction:
+ {
+ char szData1[1024];
+
+ szReq = mir_strdup(szResult);
+ sz = mir_snprintf (szData1, sizeof(szData1),
+ soap_action, szActionName, szDev, szReq, szActionName);
+
+ sz = mir_snprintf (szData, 4096,
+ szPostHdr, szPath, szHost, sPort,
+ sz, szDev, szActionName, szData1);
+ }
+ break;
+
+ case ControlQuery:
+ {
+ char szData1[1024];
+
+ sz = mir_snprintf (szData1, sizeof(szData1),
+ soap_query, szActionName);
+
+ sz = mir_snprintf (szData, 4096,
+ szPostHdr, szPath, szHost, sPort,
+ sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1);
+ }
+ break;
+ }
+ szResult[0] = 0;
+ {
+ static const TIMEVAL tv = { 6, 0 };
+ static unsigned ttl = 4;
+ static u_long mode = 1;
+ fd_set rfd, wfd, efd;
+ SOCKADDR_IN enetaddr;
+
+retrycon:
+ if (sock == INVALID_SOCKET)
+ {
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(sPort);
+ enetaddr.sin_addr.s_addr = inet_addr(szHost);
+
+ // Resolve host name if needed
+ if (enetaddr.sin_addr.s_addr == INADDR_NONE)
+ {
+ PHOSTENT he = gethostbyname(szHost);
+ if (he)
+ enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0];
+ }
+
+ NetlibLogf(NULL, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort);
+
+ FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
+ FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd);
+
+ // Limit the scope of the connection (does not work for
+ setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned));
+
+ // Put socket into non-blocking mode for timeout on connect
+ ioctlsocket(sock, FIONBIO, &mode);
+
+ // Connect to the remote host
+ if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR)
+ {
+ int err = WSAGetLastError();
+
+ // Socket connection failed
+ if (err != WSAEWOULDBLOCK)
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP connect failed %d", err);
+ break;
+ }
+ // Wait for socket to connect
+ else if (select(1, &rfd, &wfd, &efd, &tv) != 1)
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP connect timeout");
+ break;
+ }
+ else if (!FD_ISSET(sock, &wfd))
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP connect failed");
+ break;
+ }
+ }
+ strcpy(szConnHost, szHost); sConnPort = sPort;
+ }
+
+ if (send(sock, szData, sz, 0) != SOCKET_ERROR)
+ {
+ char *hdrend = NULL;
+ int acksz = 0, pktsz = 0;
+
+ if (szActionName == NULL)
+ {
+ int len = sizeof(locIP);
+ getsockname(sock, (SOCKADDR*)&locIP, &len);
+ if (locIP.sin_addr.S_un.S_addr == 0x0100007f)
+ {
+ struct hostent *he;
+
+ gethostname(szPath, sizeof(szPath));
+ he = gethostbyname(szPath);
+ if (he != NULL)
+ locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
+ }
+ }
+
+ LongLog(szData);
+ sz = 0;
+ for(;;)
+ {
+ int bytesRecv;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ // Wait for the next packet
+ if (select(1, &rfd, NULL, NULL, &tv) != 1)
+ {
+ closeRouterConnection();
+ NetlibLogf(NULL, "UPnP recieve timeout");
+ break;
+ }
+
+ //
+ bytesRecv = recv(sock, &szResult[sz], resSize-sz, 0);
+
+ // Connection closed or aborted, all data received
+ if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR)
+ {
+ closeRouterConnection();
+ if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2)
+ {
+ ++retryCount;
+ goto retrycon;
+ }
+ break;
+ }
+
+ sz += bytesRecv;
+
+ // Insert null terminator to use string functions
+ if (sz >= (resSize-1))
+ {
+ szResult[resSize-1] = 0;
+ break;
+ }
+ else
+ szResult[sz] = 0;
+
+ // HTTP header found?
+ if (hdrend == NULL)
+ {
+ // Find HTTP header end
+ hdrend = strstr(szResult, "\r\n\r\n");
+ if (hdrend == NULL)
+ {
+ hdrend = strstr(szResult, "\n\n");
+ if (hdrend) hdrend += 2;
+ }
+
+ else
+ hdrend += 4;
+
+ if (hdrend != NULL)
+ {
+ // Get packet size if provided
+ if (txtParseParam(szResult, NULL, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
+ txtParseParam(szResult, NULL, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes)))
+ {
+ // Add size of HTTP header to the packet size to compute full transmission size
+ pktsz = atol(ltrimp(szRes)) + (hdrend - szResult);
+ }
+ // Get encoding type if provided
+ else if (txtParseParam(szResult, NULL, "Transfer-Encoding:", "\n", szRes, sizeof(szRes)))
+ {
+ if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
+ acksz = hdrend - szResult;
+ }
+ if (txtParseParam(szResult, NULL, "Connection:", "\n", szRes, sizeof(szRes)))
+ {
+ needClose = (_stricmp(lrtrimp(szRes), "close") == 0);
+ }
+ }
+ }
+
+ // Content-Length bytes reached, all data received
+ if (sz >= pktsz && pktsz != 0)
+ {
+ szResult[pktsz] = 0;
+ break;
+ }
+
+ // Chunked encoding processing
+ if (sz > acksz && acksz != 0)
+ {
+retry:
+ // Parse out chunk size
+ char* data = szResult + acksz;
+ char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n');
+ if (peol1 != NULL)
+ {
+ char *peol2 = strchr(++peol1, '\n');
+ if (peol2 != NULL)
+ {
+ // Get chunk size
+ int chunkBytes = strtol(peol1, NULL, 16);
+ acksz += chunkBytes;
+ peol2++;
+
+ memmove(data, peol2, strlen(peol2) + 1);
+ sz -= peol2 - data;
+
+ // Last chunk, all data received
+ if (chunkBytes == 0) break;
+ if (sz > acksz) goto retry;
+ }
+ }
+ }
+ }
+ LongLog(szResult);
+ }
+ else
+ {
+ if (retryCount < 2)
+ {
+ closeRouterConnection();
+ ++retryCount;
+ goto retrycon;
+ }
+ else
+ NetlibLogf(NULL, "UPnP send failed %d", WSAGetLastError());
+ }
+ }
+ txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
+ res = atol(szRes);
+ if (szActionName != NULL && res == 405 && szPostHdr == soap_post_hdr)
+ szPostHdr = soap_post_hdr_m;
+ else
+ break;
+ }
+
+ if (needClose)
+ closeRouterConnection();
+
+ mir_free(szData);
+ mir_free(szReq);
+ return res;
+}
+
+static unsigned getExtIP(void)
+{
+ char szExtIP[30];
+ char* szData = (char*)mir_alloc(4096); szData[0] = 0;
+
+ unsigned extip = 0;
+ int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction);
+ if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP)))
+ extip = ntohl(inet_addr(szExtIP));
+
+ mir_free(szData);
+ return extip;
+}
+
+static bool getUPnPURLs(char* szUrl, size_t sizeUrl)
+{
+ char* szData = (char*)mir_alloc(8192);
+
+ gatewayFound = httpTransact(szUrl, szData, 8192, NULL, DeviceGetReq) == 200;
+ if (gatewayFound)
+ {
+ char szTemp[256], *rpth;
+ size_t ctlLen;
+
+ txtParseParam(szData, NULL, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
+ strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
+ szCtlUrl[sizeof(szCtlUrl)-1] = 0;
+
+ mir_snprintf(szTemp, sizeof(szTemp), search_device, szDev);
+ txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
+
+ // URL combining per RFC 2396
+ if ( szUrl[0] != 0 )
+ {
+ if (strstr(szUrl, "://") != NULL) // absolute URI
+ rpth = szCtlUrl;
+ else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ if (rpth == NULL) rpth = szCtlUrl;
+ }
+ else if (szUrl[0] == '/') // relative URI abs_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ rpth = rpth ? rpth + 2 : szCtlUrl;
+
+ rpth = strchr(rpth, '/');
+ if (rpth == NULL) rpth = szCtlUrl + strlen(szCtlUrl);
+ }
+ else
+ { // relative URI rel_path
+ size_t ctlCLen = strlen(szCtlUrl);
+ rpth = szCtlUrl + ctlCLen;
+ if (ctlCLen != 0 && *(rpth-1) != '/')
+ strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen);
+ }
+
+ ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl);
+ strncpy(rpth, szUrl, ctlLen);
+ szCtlUrl[sizeof(szCtlUrl)-1] = 0;
+ }
+ else
+ {
+ szCtlUrl[0] = 0;
+ gatewayFound = false;
+ }
+ }
+ mir_free(szData);
+
+ return gatewayFound;
+}
+
+
+static void discoverUPnP(void)
+{
+ char* buf;
+ int buflen;
+ unsigned i, j, nip = 0;
+ unsigned* ips = NULL;
+
+ static const unsigned any = INADDR_ANY;
+ static const TIMEVAL tv = { 1, 600000 };
+
+ char szUrl[256] = "";
+ char hostname[256];
+ PHOSTENT he;
+ fd_set readfd;
+
+ SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ SOCKADDR_IN enetaddr;
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(1900);
+ enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250");
+
+ gethostname(hostname, sizeof(hostname));
+ he = gethostbyname(hostname);
+
+ if (he)
+ {
+ while(he->h_addr_list[nip]) ++nip;
+
+ ips = ( unsigned* )mir_alloc(nip * sizeof(unsigned));
+
+ for (j = 0; j < nip; ++j)
+ ips[j] = *(unsigned*)he->h_addr_list[j];
+ }
+
+ buf = (char*)mir_alloc(1500);
+
+ for(i = 3; --i && szUrl[0] == 0;)
+ {
+ for (j = 0; j < nip; ++j)
+ {
+ if (ips)
+ setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
+ sendto(sock, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
+ sendto(sock, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+ }
+
+ if (Miranda_Terminated()) break;
+
+ FD_ZERO(&readfd);
+ FD_SET(sock, &readfd);
+
+ while (select(1, &readfd, NULL, NULL, &tv) >= 1)
+ {
+ buflen = recv(sock, buf, 1500, 0);
+ if (buflen != SOCKET_ERROR)
+ {
+ buf[buflen] = 0;
+ LongLog(buf);
+
+ if (txtParseParam(buf, NULL, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
+ txtParseParam(buf, NULL, "Location:", "\n", szUrl, sizeof(szUrl)))
+ {
+ char age[30];
+ char szHostNew[256], szHostExist[256];
+
+ lrtrim(szUrl);
+
+ parseURL(szUrl, szHostNew, NULL, NULL);
+ parseURL(szCtlUrl, szHostExist, NULL, NULL);
+ if (strcmp(szHostNew, szHostExist) == 0)
+ {
+ gatewayFound = true;
+ break;
+ }
+
+ txtParseParam(buf, NULL, "ST:", "\n", szDev, sizeof(szDev));
+ txtParseParam(buf, "max-age", "=", "\n", age, sizeof(age));
+ expireTime = atoi(lrtrimp(age));
+ lrtrim(szDev);
+
+ if (getUPnPURLs(szUrl, sizeof(szUrl)))
+ {
+ gatewayFound = getExtIP() != 0;
+ if (gatewayFound) break;
+ }
+ }
+ }
+ FD_ZERO(&readfd);
+ FD_SET(sock, &readfd);
+ }
+ }
+
+ mir_free(buf);
+ mir_free(ips);
+ setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
+ closesocket(sock);
+}
+
+
+static bool findUPnPGateway(void)
+{
+ if ((time(NULL) - lastDiscTime) >= expireTime)
+ {
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ time_t curTime = time(NULL);
+
+ if ((curTime - lastDiscTime) >= expireTime)
+ {
+ gatewayFound = false;
+
+ discoverUPnP();
+ lastDiscTime = curTime;
+
+ NetlibLogf(NULL, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl);
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return gatewayFound;
+}
+
+bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search)
+{
+ int res = 0, i = 5;
+
+ if (findUPnPGateway())
+ {
+ char* szData = (char*)mir_alloc(4096);
+ char szExtIP[30];
+
+ *extport = intport - 1;
+ *extip = ntohl(locIP.sin_addr.S_un.S_addr);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ do
+ {
+ ++*extport;
+ mir_snprintf(szData, 4096, add_port_mapping,
+ *extport, proto, intport, inet_ntoa(locIP.sin_addr));
+ res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction);
+ txtParseParam(szData, NULL, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP));
+
+ }
+ while (search && res == 500 && atol(szExtIP) == 718 && --i);
+
+ mir_free(szData);
+
+ if (res == 200)
+ {
+ unsigned ip = getExtIP();
+ if (ip) *extip = ip;
+
+ if (numports >= numportsAlloc)
+ mir_realloc(portList, sizeof(WORD)*(numportsAlloc += 10));
+ portList[numports++] = *extport;
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return res == 200;
+}
+
+void NetlibUPnPDeletePortMapping(WORD extport, char* proto)
+{
+ if (extport == 0)
+ return;
+
+ // findUPnPGateway();
+
+ if (gatewayFound)
+ {
+ unsigned i;
+ char* szData = (char*)mir_alloc(4096);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+ mir_snprintf(szData, 4096, delete_port_mapping, extport, proto);
+ httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction);
+
+ for (i = 0; i < numports; ++i)
+ if (portList[i] == extport && --numports > 0)
+ memmove(&portList[i], &portList[i+1], (numports - i) * sizeof(WORD));
+
+ mir_free(szData);
+ ReleaseMutex(portListMutex);
+ }
+}
+
+void NetlibUPnPCleanup(void*)
+{
+ if (DBGetContactSettingByte(NULL,"Netlib","NLEnableUPnP",1)==0)
+ // upnp is disabled globally, no need for a cleanup
+ return;
+
+ {
+ int i, incoming = 0;
+ EnterCriticalSection(&csNetlibUser);
+ for (i = 0; i < netlibUser.getCount(); ++i)
+ {
+ if (netlibUser[i]->user.flags & NUF_INCOMING)
+ {
+ incoming = 1;
+ break;
+ }
+ }
+ LeaveCriticalSection(&csNetlibUser);
+ if (!incoming) return;
+ }
+
+ if (findUPnPGateway())
+ {
+ char* szData = (char*)alloca(4096);
+ char buf[50], lip[50];
+ unsigned i, j = 0, k, num = 100;
+
+ WORD ports[30];
+
+ strcpy(lip, inet_ntoa(locIP.sin_addr));
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
+ txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
+ num = atol(buf);
+
+ for (i=0; i<num && !Miranda_Terminated(); ++i)
+ {
+ mir_snprintf(szData, 4096, get_port_mapping, i);
+
+ ReleaseMutex(portListMutex);
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200)
+ break;
+
+ if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf)))
+ {
+ WORD mport = (WORD)atol(buf);
+
+ if (j >= SIZEOF(ports))
+ break;
+
+ for (k=0; k<numports; ++k)
+ if (portList[k] == mport)
+ break;
+
+ if (k >= numports)
+ ports[j++] = mport;
+ }
+ }
+
+ ReleaseMutex(portListMutex);
+
+ for (i=0; i<j && !Miranda_Terminated(); ++i)
+ NetlibUPnPDeletePortMapping(ports[i], "TCP");
+ }
+}
+
+void NetlibUPnPInit(void)
+{
+ numports = 0;
+ numportsAlloc = 10;
+ portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc);
+
+ portListMutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+void NetlibUPnPDestroy(void)
+{
+ mir_free(portList);
+ CloseHandle(portListMutex);
+}
diff --git a/src/modules/options/descbutton.cpp b/src/modules/options/descbutton.cpp
new file mode 100644
index 0000000000..2e5d55c7a4
--- /dev/null
+++ b/src/modules/options/descbutton.cpp
@@ -0,0 +1,332 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2007 Artem Shpynov
+Copyright 2000-2007 Miranda ICQ/IM project,
+
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "m_descbutton.h"
+
+extern HINSTANCE hMirandaInst;
+
+////////////////////////////////////////////////////////////////////////////////////
+// Internals
+
+#define DBC_BORDER_SIZE 7
+#define DBC_VSPACING 15
+#define DBC_HSPACING 10
+
+static LRESULT CALLBACK MDescButtonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// structure is used for storing list of tab info
+typedef struct {
+ HWND hwnd;
+ BOOL bSharedIcon;
+ HICON hIcon;
+ TCHAR *lpzTitle;
+ TCHAR *lpzDescription;
+
+ // UI info
+ BOOL bMouseInside;
+ RECT rc;
+ int width, height;
+ HFONT hfntTitle;
+
+ // control colors
+ RGBQUAD rgbBkgTop, rgbBkgBottom;
+ RGBQUAD rgbSelTop, rgbSelBottom;
+ RGBQUAD rgbHotTop, rgbHotBottom;
+ COLORREF clText, clBackground;
+ COLORREF clSelText, clSelBorder;
+ COLORREF clHotText, clHotBorder;
+
+ // fonts
+ HFONT hFont;
+} MDescButtonCtrl;
+
+int LoadDescButtonModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = MIRANDADESCBUTTONCLASS;
+ wc.lpfnWndProc = MDescButtonWndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_HAND);
+ wc.cbWndExtra = sizeof(MDescButtonCtrl *);
+ wc.hbrBackground = 0; //GetStockObject(WHITE_BRUSH);
+ wc.style = CS_GLOBALCLASS|CS_SAVEBITS;
+ RegisterClassEx(&wc);
+ return 0;
+}
+
+static void MDescButton_SetupColors(MDescButtonCtrl *dat)
+{
+ COLORREF cl;
+
+ cl = GetSysColor(COLOR_WINDOW);
+ dat->rgbBkgBottom.rgbRed = (dat->rgbBkgTop.rgbRed = GetRValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbGreen = (dat->rgbBkgTop.rgbGreen = GetGValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbBlue = (dat->rgbBkgTop.rgbBlue = GetBValue(cl)) * .95;
+
+ cl = GetSysColor(COLOR_HIGHLIGHT);
+ dat->rgbSelTop.rgbRed = (dat->rgbSelBottom.rgbRed = GetRValue(cl)) * .75;
+ dat->rgbSelTop.rgbGreen = (dat->rgbSelBottom.rgbGreen = GetGValue(cl)) * .75;
+ dat->rgbSelTop.rgbBlue = (dat->rgbSelBottom.rgbBlue = GetBValue(cl)) * .75;
+
+ dat->rgbHotTop.rgbRed = (dat->rgbSelTop.rgbRed + 255) / 2;
+ dat->rgbHotTop.rgbGreen = (dat->rgbSelTop.rgbGreen + 255) / 2;
+ dat->rgbHotTop.rgbBlue = (dat->rgbSelTop.rgbBlue + 255) / 2;
+
+ dat->rgbHotBottom.rgbRed = (dat->rgbSelBottom.rgbRed + 255) / 2;
+ dat->rgbHotBottom.rgbGreen = (dat->rgbSelBottom.rgbGreen + 255) / 2;
+ dat->rgbHotBottom.rgbBlue = (dat->rgbSelBottom.rgbBlue + 255) / 2;
+
+ dat->clBackground = GetSysColor(COLOR_3DFACE);
+ dat->clText = GetSysColor(COLOR_WINDOWTEXT);
+ dat->clSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ dat->clSelBorder = RGB(dat->rgbSelTop.rgbRed, dat->rgbSelTop.rgbGreen, dat->rgbSelTop.rgbBlue);
+ dat->clHotBorder = RGB(dat->rgbHotTop.rgbRed, dat->rgbHotTop.rgbGreen, dat->rgbHotTop.rgbBlue);
+
+ if (!dat->hFont) dat->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+}
+
+static void MDescButton_FillRect(HDC hdc, int x, int y, int width, int height, COLORREF cl)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, cl);
+
+ RECT rc; SetRect(&rc, x, y, x+width, y+height);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MDescButton_DrawGradient(HDC hdc, int x, int y, int width, int height, RGBQUAD *rgb0, RGBQUAD *rgb1)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, 0);
+
+ RECT rc; SetRect(&rc, x, 0, x+width, 0);
+ for (int i=y+height; --i >= y; ) {
+ COLORREF color = RGB(
+ ((height-i-1)*rgb0->rgbRed + i*rgb1->rgbRed) / height,
+ ((height-i-1)*rgb0->rgbGreen + i*rgb1->rgbGreen) / height,
+ ((height-i-1)*rgb0->rgbBlue + i*rgb1->rgbBlue) / height);
+ rc.top = rc.bottom = i;
+ ++rc.bottom;
+ SetBkColor(hdc, color);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+ }
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static LRESULT MDescButton_OnPaint(HWND hwndDlg, MDescButtonCtrl *dat, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ HBITMAP hBmp, hOldBmp;
+ RECT temprc;
+ HFONT hfntSave;
+
+ HDC hdc=BeginPaint(hwndDlg,&ps);
+ HDC tempDC=CreateCompatibleDC(hdc);
+
+ SIZE titleSize = {0};
+
+ hBmp=CreateCompatibleBitmap(hdc,dat->width, dat->height);
+ hOldBmp=(HBITMAP)SelectObject(tempDC,hBmp);
+
+ temprc.left=0;
+ temprc.right=dat->width;
+ temprc.top=0;
+
+ //Draw background
+ if (dat->bMouseInside || (GetFocus() == hwndDlg)) {
+ MDescButton_FillRect(tempDC, 0, 0, dat->width, dat->height, dat->clSelBorder);
+ MDescButton_DrawGradient(tempDC, 1, 1, dat->width-2, dat->height-2, &dat->rgbSelTop, &dat->rgbSelBottom);
+ SetTextColor(tempDC, dat->clSelText);
+ }
+ else {
+ MDescButton_FillRect(tempDC, 0, 0, dat->width, dat->height, dat->clBackground);
+ SetTextColor(tempDC, dat->clText);
+ }
+
+ if (dat->hIcon)
+ DrawIcon(tempDC, DBC_BORDER_SIZE, DBC_BORDER_SIZE, dat->hIcon);
+
+ hfntSave = (HFONT)SelectObject(tempDC, dat->hFont);
+ SetBkMode(tempDC, TRANSPARENT);
+
+ if (dat->lpzTitle) {
+ LOGFONT lf;
+ RECT textRect;
+ HFONT hfntSave;
+
+ GetObject(dat->hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ lf.lfHeight *= 1.5;
+ hfntSave = (HFONT)SelectObject(tempDC, CreateFontIndirect(&lf));
+
+ textRect.left = DBC_BORDER_SIZE + dat->hIcon ? 32 + DBC_VSPACING : 0;
+ textRect.right = dat->width - DBC_BORDER_SIZE;
+ textRect.top = DBC_BORDER_SIZE;
+ textRect.bottom = dat->height - DBC_BORDER_SIZE;
+ DrawText(tempDC, dat->lpzTitle, -1, &textRect, DT_TOP|DT_LEFT|DT_END_ELLIPSIS);
+ GetTextExtentPoint32(tempDC, dat->lpzTitle, lstrlen(dat->lpzTitle), &titleSize);
+
+ DeleteObject(SelectObject(tempDC, hfntSave));
+ }
+
+ if (dat->lpzDescription) {
+ RECT textRect;
+ textRect.left = DBC_BORDER_SIZE + dat->hIcon ? 32 + DBC_VSPACING : 0;
+ textRect.right = dat->width - DBC_BORDER_SIZE;
+ textRect.top = DBC_BORDER_SIZE + titleSize.cy ? titleSize.cy + DBC_HSPACING : 0;
+ textRect.bottom = dat->height - DBC_BORDER_SIZE;
+ DrawText(tempDC, dat->lpzDescription, -1, &textRect, DT_TOP|DT_LEFT|DT_WORDBREAK|DT_END_ELLIPSIS);
+ GetTextExtentPoint32(tempDC, dat->lpzTitle, lstrlen(dat->lpzTitle), &titleSize);
+ }
+
+ SelectObject(tempDC, hfntSave);
+
+ //Copy to output
+ BitBlt(hdc,dat->rc.left,dat->rc.top,dat->width,dat->height,tempDC,0,0,SRCCOPY);
+ SelectObject(tempDC,hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+ EndPaint(hwndDlg,&ps);
+
+ return TRUE;
+}
+
+static LRESULT CALLBACK MDescButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MDescButtonCtrl *dat = (MDescButtonCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch(msg) {
+ case WM_NCCREATE:
+ dat = (MDescButtonCtrl*)mir_alloc(sizeof(MDescButtonCtrl));
+ if (dat==NULL)
+ return FALSE;
+
+ memset(dat, 0, sizeof(MDescButtonCtrl));
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)dat);
+ MDescButton_SetupColors(dat);
+ return TRUE;
+
+ case WM_SETFONT:
+ dat->hFont = (HFONT)wParam;
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwndDlg,&dat->rc);
+ dat->width=dat->rc.right-dat->rc.left;
+ dat->height=dat->rc.bottom-dat->rc.top;
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_STYLECHANGED:
+ MDescButton_SetupColors(dat);
+ return TRUE;
+
+ case WM_MOUSEMOVE:
+ if (!dat->bMouseInside) {
+ TRACKMOUSEEVENT tme = {0};
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwndDlg;
+ _TrackMouseEvent(&tme);
+ dat->bMouseInside = TRUE;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ }
+ return 0;
+
+ case WM_MOUSELEAVE:
+ dat->bMouseInside = FALSE;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return 0;
+
+ case WM_LBUTTONUP:
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), 0), 0);
+ return 0;
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_NCPAINT:
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ break;
+
+ case WM_PAINT:
+ MDescButton_OnPaint(hwndDlg, dat, msg, wParam, lParam);
+ break;
+
+ case DBCM_SETTITLE:
+ if (dat->lpzTitle)
+ mir_free(dat->lpzTitle);
+ if (wParam & MDBCF_UNICODE)
+ dat->lpzTitle = mir_u2t((WCHAR *)lParam);
+ else
+ dat->lpzTitle = mir_a2t((char *)lParam);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return TRUE;
+
+ case DBCM_SETDESCRIPTION:
+ if (dat->lpzDescription)
+ mir_free(dat->lpzDescription);
+ if (wParam & MDBCF_UNICODE)
+ dat->lpzDescription = mir_u2t((WCHAR *)lParam);
+ else
+ dat->lpzDescription = mir_a2t((char *)lParam);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return TRUE;
+
+ case DBCM_SETICON:
+ if (dat->hIcon && !dat->bSharedIcon)
+ DestroyIcon(dat->hIcon);
+
+ if (wParam & MDBCF_SHAREDICON) {
+ dat->bSharedIcon = TRUE;
+ dat->hIcon = (HICON)lParam;
+ }
+ else {
+ dat->bSharedIcon = FALSE;
+ dat->hIcon = CopyIcon((HICON)lParam);
+ }
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return TRUE;
+
+ case WM_DESTROY:
+ if (dat->lpzTitle)
+ mir_free(dat->lpzTitle);
+ if (dat->lpzDescription)
+ mir_free(dat->lpzDescription);
+ if (dat->hIcon && !dat->bSharedIcon)
+ DestroyIcon(dat->hIcon);
+ mir_free(dat);
+ return TRUE;
+ }
+
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
diff --git a/src/modules/options/filter.cpp b/src/modules/options/filter.cpp
new file mode 100644
index 0000000000..116899d44e
--- /dev/null
+++ b/src/modules/options/filter.cpp
@@ -0,0 +1,217 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "filter.h"
+
+HANDLE hOptionsInitialize;
+
+int HookFilterEvents()
+{
+ hOptionsInitialize = HookEvent(ME_OPT_INITIALISE, OnOptionsInitialise);
+ return 0;
+}
+
+int UnhookFilterEvents()
+{
+ UnhookEvent(hOptionsInitialize);
+ return 0;
+}
+
+INT_PTR CALLBACK DlgProcOptSearch(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hWnd);
+
+ CheckDlgButton(hWnd, IDC_ENABLE_KEYWORDFILTERING, DBGetContactSettingWord(NULL, "Options", "EnableKeywordFiltering", TRUE) ? BST_CHECKED : BST_UNCHECKED);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_ENABLE_KEYWORDFILTERING:
+ SendMessage(GetParent(hWnd), PSM_CHANGED,0,0);
+ break;
+ }
+ break;
+
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hWnd, IDC_ENABLE_KEYWORDFILTERING));
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ DBWriteContactSettingWord(NULL, "Options", "EnableKeywordFiltering", IsDlgButtonChecked(hWnd, IDC_ENABLE_KEYWORDFILTERING));
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int OnOptionsInitialise(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+
+ odp.cbSize = sizeof(odp);
+ odp.position = -190000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_KEYWORDFILTER);
+ odp.ptszTitle = TranslateT("Options search");
+ odp.ptszGroup = TranslateT("Customize");
+ odp.groupPosition = 810000000;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcOptSearch;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0;
+}
+
+CPageList filterStrings(1);
+
+void AddFilterString(const PageHash key, TCHAR *data)
+{
+ if (ContainsFilterString(key, data)) return;
+
+ CPageKeywords * values = filterStrings[key];
+ if ( values == NULL ) {
+ values = new CPageKeywords( key );
+ filterStrings.insert( values );
+ }
+ values->AddKeyWord( data );
+}
+
+void ClearFilterStrings()
+{
+ filterStrings.destroy();
+}
+
+BOOL ContainsFilterString(const PageHash key, TCHAR *data)
+{
+ CPageKeywords* values = filterStrings[key];
+ return (values) ? values->ContainsString( data ) : FALSE;
+}
+
+void AddTreeViewNodes(HWND hWndDlg, PageHash key, HTREEITEM root)
+{
+ if (root) {
+ TCHAR title[2048] = {0};
+
+ TVITEM item = {0};
+ item.mask = TVIF_TEXT;
+ item.hItem = root;
+ item.pszText = title;
+ item.cchTextMax = SIZEOF(title);
+
+ if (TreeView_GetItem(hWndDlg, &item))
+ if (_tcslen(title) > 0)
+ AddFilterString(key, title);
+
+ HTREEITEM child = root;
+ while (child) {
+ child = TreeView_GetNextItem(hWndDlg, child, TVGN_CHILD);
+ AddTreeViewNodes(hWndDlg, key, child);
+ }
+
+ AddTreeViewNodes(hWndDlg, key, TreeView_GetNextSibling(hWndDlg, root));
+ }
+}
+
+void AddDialogString(HWND hWndDlg, const PageHash key)
+{
+ TCHAR title[2048];
+ GetWindowText(hWndDlg, title, SIZEOF( title ));
+ if (_tcslen(title) > 0)
+ AddFilterString(key, title);
+
+ TCHAR szClass[64];
+ GetClassName(hWndDlg,szClass, SIZEOF(szClass));
+
+ if (lstrcmpi(szClass, _T("SysTreeView32")) == 0) {
+ HTREEITEM hItem = TreeView_GetRoot(hWndDlg);
+ AddTreeViewNodes(hWndDlg, key, hItem);
+ }
+ else {
+ if (lstrcmpi(szClass, _T("listbox")) == 0) {
+ if (GetWindowStyle(hWndDlg) & LBS_HASSTRINGS) {
+ int count = ListBox_GetCount(hWndDlg);
+ for (int i = 0; i < count; i++) {
+ title[0] = 0; //safety
+ int res = ListBox_GetText(hWndDlg, i, title);
+ if (res != LB_ERR) {
+ title[SIZEOF(title) - 1] = 0;
+ if (_tcslen(title) > 0)
+ AddFilterString(key, title);
+ } } }
+ }
+ else {
+ if (lstrcmpi(szClass, _T("SysListView32")) == 0) {
+ int count = ListView_GetItemCount(hWndDlg);
+ for (int i = 0; i < count; i++) {
+ title[0] = 0; //safety
+ ListView_GetItemText(hWndDlg, i, 0, title, SIZEOF(title));
+
+ if (_tcslen(title) > 0)
+ AddFilterString(key, title);
+ } }
+
+ if (lstrcmpi(szClass, _T("combobox")) == 0) {
+ if (GetWindowStyle(hWndDlg) & CBS_HASSTRINGS) {
+ int count = ComboBox_GetCount(hWndDlg);
+ for (int i = 0; i < count; i++) {
+ title[0] = 0; //safety
+ int res = ComboBox_GetLBText(hWndDlg, i, title);
+ if (res != CB_ERR) {
+ title[SIZEOF(title) - 1] = 0;
+
+ if (_tcslen(title) > 0)
+ AddFilterString(key, title);
+} } } } } } }
+
+static BOOL CALLBACK GetDialogStringsCallback(HWND hWnd,LPARAM lParam)
+{
+ AddDialogString(hWnd, lParam);
+
+ return TRUE;
+}
+
+void GetDialogStrings(int enableKeywordFiltering, const PageHash key, TCHAR *pluginName, HWND hWnd, TCHAR * group, TCHAR * title, TCHAR * tab, TCHAR * name )
+{
+ AddFilterString(key, pluginName); //add the plugin name as keyword
+ if ( group ) AddFilterString(key, group);
+ if ( title ) AddFilterString(key, title);
+ if ( tab ) AddFilterString(key, tab);
+ if ( name ) AddFilterString(key, name);
+
+ if ((enableKeywordFiltering) && (hWnd != 0)) {
+ AddDialogString(hWnd, key);
+
+ EnumChildWindows(hWnd, GetDialogStringsCallback, (LPARAM) key);
+ }
+}
diff --git a/src/modules/options/filter.h b/src/modules/options/filter.h
new file mode 100644
index 0000000000..3db823fcdf
--- /dev/null
+++ b/src/modules/options/filter.h
@@ -0,0 +1,108 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_OPTIONS_FILTERING_H
+#define M_OPTIONS_FILTERING_H
+
+extern HANDLE hOptionsInitialize;
+
+int HookFilterEvents();
+int UnhookFilterEvents();
+int OnOptionsInitialise(WPARAM wParam, LPARAM lParam);
+
+typedef DWORD PageHash;
+
+void AddFilterString(const PageHash key, const TCHAR *data);
+BOOL ContainsFilterString(const PageHash key, TCHAR *data);
+void ClearFilterStrings();
+void GetDialogStrings(int enableKeywordFiltering, const PageHash key, TCHAR *pluginName, HWND hWnd, TCHAR * group, TCHAR * title, TCHAR * tab, TCHAR * name );
+
+_inline TCHAR * _tcslwr_locale( TCHAR * buf )
+{
+ LCMapString( LangPackGetDefaultLocale() , LCMAP_LOWERCASE, buf, (int)_tcslen( buf ), buf, (int)_tcslen( buf ) );
+ return buf;
+}
+
+typedef LIST<TCHAR> KeywordList;
+class CPageKeywords
+{
+ PageHash _pageHashKey;
+ KeywordList _pageKeyWords;
+ static int _KeyWordsSortFunc( const TCHAR* p1, const TCHAR* p2 ) { return _tcscmp( p1, p2 ); };
+
+public:
+ CPageKeywords( PageHash pageHashKey ) : _pageHashKey( pageHashKey ), _pageKeyWords( 1, _KeyWordsSortFunc ) {};
+ ~CPageKeywords()
+ {
+ for ( int j = 0; j < _pageKeyWords.getCount(); j++ )
+ {
+ TCHAR * data = _pageKeyWords[j];
+ mir_free( data );
+ }
+ _pageKeyWords.destroy();
+ };
+
+ void AddKeyWord( TCHAR * ptKeyWord )
+ {
+ TCHAR * plwrWord = _tcslwr_locale( mir_tstrdup( ptKeyWord ) );
+ if ( _pageKeyWords.getIndex( plwrWord ) == -1 )
+ _pageKeyWords.insert( plwrWord ) ;
+ else
+ mir_free( plwrWord );
+ };
+
+ BOOL ContainsString( TCHAR * data )
+ {
+ for ( int i = 0; i < _pageKeyWords.getCount(); i++)
+ if (_tcsstr(_pageKeyWords[i], data))
+ return TRUE;
+ return FALSE;
+ }
+ static int PageSortFunc( const CPageKeywords* p1, const CPageKeywords* p2 )
+ {
+ if (p1->_pageHashKey < p2->_pageHashKey) { return -1; }
+ else if (p1->_pageHashKey > p2->_pageHashKey) { return 1; }
+ return 0;
+ }
+};
+
+class CPageList : public OBJLIST<CPageKeywords>
+{
+ CPageList();
+public:
+ CPageList( int aincr, FTSortFunc afunc = CPageKeywords::PageSortFunc ) : OBJLIST<CPageKeywords>( aincr, afunc ) {};
+ CPageKeywords * operator[]( PageHash key )
+ {
+ CPageKeywords keyToSearch( key );
+ return this->find( &keyToSearch );
+ }
+ ~CPageList() {};
+};
+
+
+
+int LangPackGetDefaultLocale();
+
+
+#endif //M_OPTIONS_FILTERING_H
+
diff --git a/src/modules/options/headerbar.cpp b/src/modules/options/headerbar.cpp
new file mode 100644
index 0000000000..a2407d2aab
--- /dev/null
+++ b/src/modules/options/headerbar.cpp
@@ -0,0 +1,372 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2007 Artem Shpynov
+Copyright 2000-2007 Miranda ICQ/IM project,
+
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "m_iconheader.h"
+
+extern HINSTANCE hMirandaInst;
+
+
+static BOOL IsAeroMode()
+{
+ BOOL result;
+ return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
+
+static BOOL IsVSMode()
+{
+ return isThemeActive && IsWinVerVistaPlus() && isThemeActive();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Internals
+
+static LRESULT CALLBACK MHeaderbarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// structure is used for storing list of tab info
+struct MHeaderbarCtrl
+{
+ __inline void* operator new( size_t size )
+ { return mir_calloc( size );
+ }
+ __inline void operator delete( void* p )
+ { mir_free( p );
+ }
+
+ MHeaderbarCtrl() {}
+ ~MHeaderbarCtrl() { mir_free( controlsToRedraw ); }
+
+ HWND hwnd;
+
+ // UI info
+ RECT rc;
+ int width, height;
+ HICON hIcon;
+
+ // control colors
+ RGBQUAD rgbBkgTop, rgbBkgBottom;
+ COLORREF clText;
+
+ int nControlsToRedraw;
+ HWND *controlsToRedraw;
+
+ // fonts
+ HFONT hFont;
+};
+
+int LoadHeaderbarModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = _T("MHeaderbarCtrl"); //MIRANDAHEADERBARCLASS;
+ wc.lpfnWndProc = MHeaderbarWndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(MHeaderbarCtrl*);
+ wc.hbrBackground = 0; //GetStockObject(WHITE_BRUSH);
+ wc.style = CS_GLOBALCLASS|CS_SAVEBITS;
+ RegisterClassEx(&wc);
+ return 0;
+}
+
+static void MHeaderbar_SetupColors(MHeaderbarCtrl *dat)
+{
+ COLORREF cl;
+
+ cl = GetSysColor(COLOR_WINDOW);
+ dat->rgbBkgBottom.rgbRed = (dat->rgbBkgTop.rgbRed = GetRValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbGreen = (dat->rgbBkgTop.rgbGreen = GetGValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbBlue = (dat->rgbBkgTop.rgbBlue = GetBValue(cl)) * .95;
+
+ dat->clText = GetSysColor(COLOR_WINDOWTEXT);
+
+ if (!dat->hFont) dat->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+}
+
+static void MHeaderbar_FillRect(HDC hdc, int x, int y, int width, int height, COLORREF cl)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, cl);
+
+ RECT rc; SetRect(&rc, x, y, x+width, y+height);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MHeaderbar_DrawGradient(HDC hdc, int x, int y, int width, int height, RGBQUAD *rgb0, RGBQUAD *rgb1)
+{
+ int i;
+
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, 0);
+
+ RECT rc; SetRect(&rc, x, 0, x+width, 0);
+ for (i=y+height; --i >= y; )
+ {
+ COLORREF color = RGB(
+ ((height-i-1)*rgb0->rgbRed + i*rgb1->rgbRed) / height,
+ ((height-i-1)*rgb0->rgbGreen + i*rgb1->rgbGreen) / height,
+ ((height-i-1)*rgb0->rgbBlue + i*rgb1->rgbBlue) / height);
+ rc.top = rc.bottom = i;
+ ++rc.bottom;
+ SetBkColor(hdc, color);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+ }
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static LRESULT MHeaderbar_OnPaint(HWND hwndDlg, MHeaderbarCtrl *mit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int iTopSpace = IsAeroMode() ? 0 : 3;
+ PAINTSTRUCT ps;
+ HBITMAP hBmp, hOldBmp;
+
+ int titleLength = GetWindowTextLength(hwndDlg) + 1;
+ TCHAR *szTitle = (TCHAR *)mir_alloc(sizeof(TCHAR) * titleLength);
+ GetWindowText(hwndDlg, szTitle, titleLength);
+
+ TCHAR *szSubTitle = _tcschr(szTitle, _T('\n'));
+ if (szSubTitle) *szSubTitle++ = 0;
+
+ HDC hdc=BeginPaint(hwndDlg,&ps);
+ HDC tempDC=CreateCompatibleDC(hdc);
+
+ BITMAPINFO bmi;
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = mit->width;
+ bmi.bmiHeader.biHeight = -mit->height; // we need this for DrawThemeTextEx
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
+
+ hOldBmp=(HBITMAP)SelectObject(tempDC,hBmp);
+
+ if (IsAeroMode()) {
+ RECT temprc;
+ temprc.left=0;
+ temprc.right=mit->width;
+ temprc.top=0;
+ temprc.bottom=mit->width;
+ FillRect(tempDC, &temprc, (HBRUSH)GetStockObject(BLACK_BRUSH));
+
+ MARGINS margins = {0,0,mit->height,0};
+ dwmExtendFrameIntoClientArea(GetParent(hwndDlg), &margins);
+
+ WTA_OPTIONS opts;
+ opts.dwFlags = opts.dwMask = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
+ setWindowThemeAttribute(GetParent(hwndDlg), WTA_NONCLIENT, &opts, sizeof(opts));
+ }
+ else {
+ if (IsVSMode())
+ MHeaderbar_FillRect(tempDC, 0, 0, mit->width, mit->height, GetSysColor(COLOR_WINDOW));
+ else
+ MHeaderbar_DrawGradient(tempDC, 0, 0, mit->width, mit->height, &mit->rgbBkgTop, &mit->rgbBkgBottom);
+
+ MHeaderbar_FillRect(tempDC, 0, mit->height-2, mit->width, 1, GetSysColor(COLOR_BTNSHADOW));
+ MHeaderbar_FillRect(tempDC, 0, mit->height-1, mit->width, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
+ }
+
+ HFONT hFont = mit->hFont;
+ SetBkMode(tempDC, TRANSPARENT);
+ SetTextColor(tempDC, mit->clText);
+
+ LOGFONT lf;
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ HFONT hFntBold = CreateFontIndirect(&lf);
+
+ if (mit->hIcon)
+ DrawIcon(tempDC, 10, iTopSpace, mit->hIcon);
+ else {
+ HICON hIcon = (HICON)SendMessage(GetParent(hwndDlg), WM_GETICON, ICON_BIG, 0);
+ if (hIcon == NULL)
+ hIcon = (HICON)SendMessage(GetParent(hwndDlg), WM_GETICON, ICON_SMALL, 0);
+ DrawIcon(tempDC, 10, iTopSpace, hIcon);
+ }
+
+ RECT textRect;
+ textRect.left=50;
+ textRect.right=mit->width;
+ textRect.top=2 + iTopSpace;
+ textRect.bottom=GetSystemMetrics(SM_CYICON)-2 + iTopSpace;
+
+ if (IsAeroMode()) {
+ DTTOPTS dto = {0};
+ dto.dwSize = sizeof(dto);
+ dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE;
+ dto.iGlowSize = 10;
+
+ HANDLE hTheme = openThemeData(hwndDlg, L"Window");
+ textRect.left=50;
+ SelectObject(tempDC, hFntBold);
+
+ wchar_t *szTitleW = mir_t2u(szTitle);
+ drawThemeTextEx(hTheme, tempDC, WP_CAPTION, CS_ACTIVE, szTitleW, -1, DT_TOP|DT_LEFT|DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP|DT_END_ELLIPSIS, &textRect, &dto);
+ mir_free(szTitleW);
+
+ if (szSubTitle) {
+ textRect.left=66;
+ SelectObject(tempDC, hFont);
+
+ wchar_t *szSubTitleW = mir_t2u(szSubTitle);
+ drawThemeTextEx(hTheme, tempDC, WP_CAPTION, CS_ACTIVE, szSubTitleW, -1, DT_BOTTOM|DT_LEFT|DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP|DT_END_ELLIPSIS, &textRect, &dto);
+ mir_free(szSubTitleW);
+ }
+ closeThemeData(hTheme);
+ }
+ else {
+ textRect.left=50;
+ SelectObject(tempDC, hFntBold);
+ DrawText(tempDC, szTitle, -1, &textRect, DT_TOP|DT_LEFT|DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP|DT_END_ELLIPSIS);
+
+ if (szSubTitle) {
+ textRect.left=66;
+ SelectObject(tempDC, hFont);
+ DrawText(tempDC, szSubTitle, -1, &textRect, DT_BOTTOM|DT_LEFT|DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP|DT_END_ELLIPSIS);
+ } }
+
+ DeleteObject(hFntBold);
+
+ mir_free(szTitle);
+
+ //Copy to output
+ if (mit->nControlsToRedraw)
+ {
+ RECT temprc;
+ temprc.left=0;
+ temprc.right=mit->width;
+ temprc.top=0;
+ temprc.bottom=mit->width;
+ HRGN hRgn = CreateRectRgnIndirect(&temprc);
+
+ for (int i = 0; i < mit->nControlsToRedraw; ++i)
+ {
+ GetWindowRect(mit->controlsToRedraw[i], &temprc);
+ MapWindowPoints(NULL, hwndDlg, (LPPOINT)&temprc, 2);
+ HRGN hRgnTmp = CreateRectRgnIndirect(&temprc);
+ CombineRgn(hRgn, hRgn, hRgnTmp, RGN_DIFF);
+ DeleteObject(hRgnTmp);
+ }
+ SelectClipRgn(hdc,hRgn);
+ DeleteObject(hRgn);
+ }
+
+ BitBlt(hdc,mit->rc.left,mit->rc.top,mit->width,mit->height,tempDC,0,0,SRCCOPY);
+
+ SelectClipRgn(hdc,NULL);
+
+ SelectObject(tempDC,hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+
+ EndPaint(hwndDlg,&ps);
+
+ return TRUE;
+}
+
+static LRESULT CALLBACK MHeaderbarWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MHeaderbarCtrl* itc = (MHeaderbarCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch(msg) {
+ case WM_NCCREATE:
+ itc = new MHeaderbarCtrl; //(MHeaderbarCtrl*)mir_alloc(sizeof(MHeaderbarCtrl));
+ if (itc==NULL)
+ return FALSE;
+
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)itc);
+ MHeaderbar_SetupColors(itc);
+
+ { HWND hParent = GetParent(hwndDlg);
+ RECT rcWnd; GetWindowRect(hwndDlg, &rcWnd);
+ itc->controlsToRedraw = 0;
+ itc->nControlsToRedraw = 0;
+ for (HWND hChild = FindWindowEx(hParent, NULL, NULL, NULL); hChild; hChild = FindWindowEx(hParent, hChild, NULL, NULL))
+ {
+ if (hChild != hwndDlg)
+ {
+ RECT rcChild; GetWindowRect(hChild, &rcChild);
+ RECT rc;
+ IntersectRect(&rc, &rcChild, &rcWnd);
+ if (!IsRectEmpty(&rc))
+ {
+ ++itc->nControlsToRedraw;
+ itc->controlsToRedraw = (HWND *)mir_realloc(itc->controlsToRedraw, sizeof(HWND) * itc->nControlsToRedraw);
+ itc->controlsToRedraw[itc->nControlsToRedraw - 1] = hChild;
+ }
+ }
+ }
+ }
+
+ break;
+
+ case WM_SETFONT:
+ itc->hFont = (HFONT)wParam;
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwndDlg,&itc->rc);
+ itc->width=itc->rc.right-itc->rc.left;
+ itc->height=itc->rc.bottom-itc->rc.top;
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_STYLECHANGED:
+ MHeaderbar_SetupColors(itc);
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ SendMessage(GetParent(hwndDlg), WM_SYSCOMMAND, 0xF012, 0);
+ return 0;
+
+ case WM_SETICON:
+ if (wParam < 3) {
+ itc->hIcon = (HICON)lParam;
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_NCPAINT:
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ break;
+
+ case WM_PAINT:
+ MHeaderbar_OnPaint(hwndDlg, itc, msg, wParam, lParam);
+ break;
+
+ case WM_DESTROY:
+ delete itc;
+ break;
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
diff --git a/src/modules/options/iconheader.cpp b/src/modules/options/iconheader.cpp
new file mode 100644
index 0000000000..9ab44b0852
--- /dev/null
+++ b/src/modules/options/iconheader.cpp
@@ -0,0 +1,545 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2007 Artem Shpynov
+Copyright 2000-2007 Miranda ICQ/IM project,
+
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "m_iconheader.h"
+
+
+extern HINSTANCE hMirandaInst;
+
+static BOOL IsAeroMode()
+{
+ BOOL result;
+ return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
+
+static BOOL IsVSMode()
+{
+ return isThemeActive && IsWinVerVistaPlus() && isThemeActive();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Internals
+
+#define ITC_BORDER_SIZE 3
+
+static LRESULT CALLBACK MIcoTabWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// structure is used for storing list of tab info
+struct MIcoTabCtrl
+{
+ __inline void* operator new( size_t size )
+ { return mir_calloc( size );
+ }
+ __inline void operator delete( void* p )
+ { mir_free( p );
+ }
+
+ MIcoTabCtrl(): pList(1) {}
+
+ HWND hwnd;
+ int nSelectedIdx, nHotIdx;
+ LIST<MIcoTab> pList;
+
+ // UI info
+ BOOL bMouseInside;
+ RECT rc;
+ int width, height;
+ int itemWidth, itemHeight;
+
+ //background bitmap
+ HBITMAP hBkgBmp;
+ HBITMAP hBkgOldBmp;
+ HDC hBkgDC;
+ SIZE BkgSize;
+
+ // control colors
+ RGBQUAD rgbBkgTop, rgbBkgBottom;
+ RGBQUAD rgbSelTop, rgbSelBottom;
+ RGBQUAD rgbHotTop, rgbHotBottom;
+ COLORREF clText;
+ COLORREF clSelText, clSelBorder;
+ COLORREF clHotText, clHotBorder;
+
+ // fonts
+ HFONT hFont;
+};
+
+typedef void (*ItemDestuctor)(void*);
+
+static void MITListDestructor(void * adr)
+{
+ MIcoTab * mit=(MIcoTab *)adr;
+ mir_free(mit->tcsName);
+ if (mit->hIcon && !(mit->flag&MITCF_SHAREDICON))
+ DestroyIcon(mit->hIcon);
+ mir_free(adr);
+}
+
+void li_ListDestruct(LIST<MIcoTab> &pList, ItemDestuctor pItemDestructor)
+{
+ for (int i=0; i<pList.getCount(); i++) pItemDestructor(pList[i]);
+ pList.destroy();
+}
+
+int LoadIcoTabsModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = MIRANDAICOTABCLASS;
+ wc.lpfnWndProc = MIcoTabWndProc;
+// wc.hCursor = LoadCursor(NULL, IDC_HAND);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(MIcoTabCtrl*);
+ wc.hbrBackground = 0; //GetStockObject(WHITE_BRUSH);
+ wc.style = CS_GLOBALCLASS/*|CS_SAVEBITS*/;
+ RegisterClassEx(&wc);
+ return 0;
+}
+
+static void MIcoTab_SetupColors(MIcoTabCtrl *dat)
+{
+ COLORREF cl;
+
+ cl = GetSysColor(COLOR_WINDOW);
+ dat->rgbBkgBottom.rgbRed = (dat->rgbBkgTop.rgbRed = GetRValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbGreen = (dat->rgbBkgTop.rgbGreen = GetGValue(cl)) * .95;
+ dat->rgbBkgBottom.rgbBlue = (dat->rgbBkgTop.rgbBlue = GetBValue(cl)) * .95;
+
+ cl = GetSysColor(COLOR_HIGHLIGHT);
+ dat->rgbSelTop.rgbRed = (dat->rgbSelBottom.rgbRed = GetRValue(cl)) * .75;
+ dat->rgbSelTop.rgbGreen = (dat->rgbSelBottom.rgbGreen = GetGValue(cl)) * .75;
+ dat->rgbSelTop.rgbBlue = (dat->rgbSelBottom.rgbBlue = GetBValue(cl)) * .75;
+
+ dat->rgbHotTop.rgbRed = (dat->rgbSelTop.rgbRed + 255) / 2;
+ dat->rgbHotTop.rgbGreen = (dat->rgbSelTop.rgbGreen + 255) / 2;
+ dat->rgbHotTop.rgbBlue = (dat->rgbSelTop.rgbBlue + 255) / 2;
+
+ dat->rgbHotBottom.rgbRed = (dat->rgbSelBottom.rgbRed + 255) / 2;
+ dat->rgbHotBottom.rgbGreen = (dat->rgbSelBottom.rgbGreen + 255) / 2;
+ dat->rgbHotBottom.rgbBlue = (dat->rgbSelBottom.rgbBlue + 255) / 2;
+
+ dat->clText = GetSysColor(COLOR_WINDOWTEXT);
+ dat->clSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ dat->clSelBorder = RGB(dat->rgbSelTop.rgbRed, dat->rgbSelTop.rgbGreen, dat->rgbSelTop.rgbBlue);
+ dat->clHotBorder = RGB(dat->rgbHotTop.rgbRed, dat->rgbHotTop.rgbGreen, dat->rgbHotTop.rgbBlue);
+
+ if (!dat->hFont) dat->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+}
+
+static void MIcoTab_FillRect(HDC hdc, int x, int y, int width, int height, COLORREF cl)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, cl);
+
+ RECT rc; SetRect(&rc, x, y, x+width, y+height);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MIcoTab_DrawGradient(HDC hdc, int x, int y, int width, int height, RGBQUAD *rgb0, RGBQUAD *rgb1)
+{
+ int oldMode = SetBkMode(hdc, OPAQUE);
+ COLORREF oldColor = SetBkColor(hdc, 0);
+
+ RECT rc; SetRect(&rc, x, 0, x+width, 0);
+ for ( int i=y+height; --i >= y; ) {
+ COLORREF color = RGB(
+ ((height-i-1)*rgb0->rgbRed + i*rgb1->rgbRed) / height,
+ ((height-i-1)*rgb0->rgbGreen + i*rgb1->rgbGreen) / height,
+ ((height-i-1)*rgb0->rgbBlue + i*rgb1->rgbBlue) / height);
+ rc.top = rc.bottom = i;
+ ++rc.bottom;
+ SetBkColor(hdc, color);
+ ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0);
+ }
+
+ SetBkMode(hdc, oldMode);
+ SetBkColor(hdc, oldColor);
+}
+
+static void MIcoTab_DrawItem(HWND hwnd, HDC hdc, MIcoTabCtrl *dat, MIcoTab *tab, int i)
+{
+ int iTopSpace = IsAeroMode() ? 0 : ITC_BORDER_SIZE;
+ int itemX = ITC_BORDER_SIZE + dat->itemWidth * i;
+ int iconTop = iTopSpace + 5;
+ int textTop = iconTop + 32 + 3;
+
+ HFONT hFntSave = NULL;
+
+ if (dat->nSelectedIdx == i) {
+ LOGFONT lf;
+ GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFntSave = (HFONT)SelectObject(hdc, CreateFontIndirect(&lf));
+
+ if (IsVSMode()) {
+ RECT rc;
+ rc.left = itemX;
+ rc.top = iTopSpace;
+ rc.right = itemX + dat->itemWidth;
+ rc.bottom = iTopSpace + dat->itemHeight;
+ HANDLE hTheme = openThemeData(hwnd, L"ListView");
+ if (dat->nHotIdx == i || GetFocus() == hwnd)
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOTSELECTED, &rc, NULL);
+ else
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_SELECTED, &rc, NULL);
+
+ closeThemeData(hTheme);
+ }
+ else {
+ MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clSelBorder);
+ MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbSelTop, &dat->rgbSelBottom);
+ }
+ SetTextColor(hdc, dat->clSelText);
+ }
+ else if (dat->nHotIdx == i) {
+ if (IsVSMode()) {
+ RECT rc;
+ rc.left = itemX;
+ rc.top = iTopSpace;
+ rc.right = itemX + dat->itemWidth;
+ rc.bottom = iTopSpace + dat->itemHeight;
+ setWindowTheme(hwnd, L"explorer", NULL);
+ HANDLE hTheme = openThemeData(hwnd, L"ListView");
+ drawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOT, &rc, NULL);
+ closeThemeData(hTheme);
+ }
+ else {
+ MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clHotBorder);
+ MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbHotTop, &dat->rgbHotBottom);
+ }
+ SetTextColor(hdc, dat->clHotText);
+ }
+ else SetTextColor(hdc, dat->clText);
+
+ RECT textRect;
+ textRect.left=itemX;
+ textRect.right=itemX+dat->itemWidth;
+ textRect.top=textTop;
+ textRect.bottom=iconTop+dat->itemHeight;
+ DrawIcon(hdc,itemX+dat->itemWidth/2-16, iconTop, tab->hIcon);
+
+ if (IsVSMode()) {
+ DTTOPTS dto = {0};
+ dto.dwSize = sizeof(dto);
+ dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE;
+ dto.iGlowSize = 10;
+ HANDLE hTheme = openThemeData(hwnd, L"Window");
+ wchar_t *tcsNameW = mir_t2u(tab->tcsName);
+ drawThemeTextEx(hTheme, hdc, WP_CAPTION, CS_ACTIVE, tcsNameW, -1, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS, &textRect, &dto);
+ mir_free(tcsNameW);
+ closeThemeData(hTheme);
+ }
+ else DrawText(hdc,tab->tcsName,-1,&textRect, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS);
+
+ if (hFntSave)
+ DeleteObject(SelectObject(hdc, hFntSave));
+}
+
+static LRESULT MIcoTab_OnPaint(HWND hwndDlg, MIcoTabCtrl *mit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ HBITMAP hBmp, hOldBmp;
+ RECT temprc;
+ int i;
+
+ HDC hdc=BeginPaint(hwndDlg,&ps);
+ HDC tempDC=CreateCompatibleDC(hdc);
+
+ HFONT hFont = 0;
+
+ BITMAPINFO bmi;
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = mit->width;
+ bmi.bmiHeader.biHeight = -mit->height; // we need this for DrawThemeTextEx
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
+
+ hOldBmp=(HBITMAP)SelectObject(tempDC,hBmp);
+
+ if (IsAeroMode()) {
+ temprc.left=0;
+ temprc.right=mit->width;
+ temprc.top=0;
+ temprc.bottom=mit->width;
+ FillRect(tempDC, &temprc, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ }
+ else {
+ if (mit->hBkgBmp)
+ StretchBlt(tempDC,0,0,mit->width,mit->height,mit->hBkgDC,0,0,mit->BkgSize.cx,mit->BkgSize.cy,SRCCOPY);
+ else {
+ if (IsVSMode())
+ MIcoTab_FillRect(tempDC, 0, 0, mit->width, mit->height, GetSysColor(COLOR_WINDOW));
+ else
+ MIcoTab_DrawGradient(tempDC, 0, 0, mit->width, mit->height, &mit->rgbBkgTop, &mit->rgbBkgBottom);
+
+ MIcoTab_FillRect(tempDC, 0, mit->height-2, mit->width, 1, GetSysColor(COLOR_BTNSHADOW));
+ MIcoTab_FillRect(tempDC, 0, mit->height-1, mit->width, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
+ } }
+
+ //Draw Items
+ hFont = mit->hFont;
+ SelectObject(tempDC,hFont);
+ SetBkMode(tempDC,TRANSPARENT);
+
+ for (i=0; i<mit->pList.getCount(); i++) {
+ MIcoTab *tab = (MIcoTab *)mit->pList[i];
+ MIcoTab_DrawItem(hwndDlg, tempDC, mit, tab, i);
+ }
+
+ //Copy to output
+ BitBlt(hdc,mit->rc.left,mit->rc.top,mit->width,mit->height,tempDC,0,0,SRCCOPY);
+ SelectObject(tempDC,hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+
+ EndPaint(hwndDlg,&ps);
+
+ return TRUE;
+}
+
+static LRESULT CALLBACK MIcoTabWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MIcoTabCtrl* itc = (MIcoTabCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch(msg) {
+ case WM_NCCREATE:
+ itc = new MIcoTabCtrl; //(MIcoTabCtrl*)mir_alloc(sizeof(MIcoTabCtrl));
+ if (itc==NULL) return FALSE;
+ itc->nSelectedIdx=-1;
+ itc->nHotIdx=-1;
+ itc->bMouseInside = FALSE;
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)itc);
+ MIcoTab_SetupColors(itc);
+
+ if (IsAeroMode()) {
+ RECT rc; GetWindowRect(hwndDlg, &rc);
+ MARGINS margins = {0,0,rc.bottom-rc.top,0};
+ dwmExtendFrameIntoClientArea(GetParent(hwndDlg), &margins);
+ }
+
+ return TRUE;
+
+ case WM_SETFONT:
+ itc->hFont = (HFONT)wParam;
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwndDlg,&itc->rc);
+ itc->width=itc->rc.right-itc->rc.left;
+ itc->height=itc->rc.bottom-itc->rc.top;
+
+ if (itc->pList.getCount()) {
+ itc->itemWidth=(itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount();
+ itc->itemHeight=itc->height-2*ITC_BORDER_SIZE-2;
+ }
+ else itc->itemWidth = itc->itemHeight = 0;
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_STYLECHANGED:
+ MIcoTab_SetupColors(itc);
+ return TRUE;
+
+ case WM_MOUSEMOVE:
+ if (!itc->bMouseInside) {
+ TRACKMOUSEEVENT tme = {0};
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwndDlg;
+ _TrackMouseEvent(&tme);
+ itc->bMouseInside = TRUE;
+ }
+
+ itc->nHotIdx = (LOWORD(lParam) - ITC_BORDER_SIZE) / itc->itemWidth;
+ if (itc->nHotIdx >= itc->pList.getCount())
+ itc->nHotIdx = -1;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return 0;
+
+ case WM_MOUSELEAVE:
+ itc->bMouseInside = FALSE;
+ itc->nHotIdx = -1;
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return 0;
+
+ case WM_LBUTTONUP:
+ if ((itc->nHotIdx >= 0) && (itc->nHotIdx != itc->nSelectedIdx))
+ {
+ itc->nSelectedIdx = itc->nHotIdx;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED),
+ itc->nSelectedIdx);
+ }
+ return 0;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ break;
+
+ case WM_MOUSEACTIVATE:
+ SetFocus(hwndDlg);
+ return MA_ACTIVATE;
+
+ case WM_GETDLGCODE:
+ {
+ if (lParam)
+ {
+ MSG *msg = (MSG *) lParam;
+ if (msg->message == WM_KEYDOWN)
+ {
+ if (msg->wParam == VK_TAB)
+ return 0;
+ if (msg->wParam == VK_ESCAPE)
+ return 0;
+ } else
+ if (msg->message == WM_CHAR)
+ {
+ if (msg->wParam == '\t')
+ return 0;
+ if (msg->wParam == 27)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+ }
+
+ case WM_KEYDOWN:
+ {
+ int newIdx = itc->nSelectedIdx;
+ switch (wParam)
+ {
+ case VK_NEXT:
+ case VK_RIGHT:
+ newIdx++;
+ break;
+ case VK_PRIOR:
+ case VK_LEFT:
+ newIdx--;
+ break;
+ }
+ if ((newIdx >= 0) && (newIdx < itc->pList.getCount()) && (newIdx != itc->nSelectedIdx))
+ {
+ itc->nSelectedIdx = newIdx;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGEDKBD),
+ itc->nSelectedIdx);
+ }
+ return 0;
+ }
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_NCPAINT:
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ break;
+
+ case WM_PAINT:
+ MIcoTab_OnPaint(hwndDlg, itc, msg, wParam, lParam);
+ break;
+
+ case ITCM_SETBACKGROUND:
+ itc->hBkgBmp=(HBITMAP)lParam;
+ if (!itc->hBkgDC)
+ itc->hBkgDC = CreateCompatibleDC(NULL);
+ itc->hBkgOldBmp = (HBITMAP)SelectObject(itc->hBkgDC, itc->hBkgBmp);
+ {
+ BITMAPINFO bmp;
+ GetObject(itc->hBkgBmp, sizeof(bmp), &bmp);
+ itc->BkgSize.cx=bmp.bmiHeader.biWidth;
+ itc->BkgSize.cy=bmp.bmiHeader.biHeight;
+ }
+ return TRUE;
+
+ case ITCM_ADDITEM:
+ {
+ MIcoTab* pMit=(MIcoTab *)wParam;
+ if (!pMit)
+ return FALSE;
+
+ MIcoTab* pListMit=(MIcoTab *)mir_calloc(sizeof(MIcoTab));
+ pListMit->flag=pMit->flag;
+ pListMit->data=pMit->data;
+ if (pMit->flag & MITCF_UNICODE)
+ pListMit->tcsName=mir_u2t(pMit->lpwzName);
+ else
+ pListMit->tcsName=mir_a2t(pMit->lpzName);
+ if (pMit->hIcon) {
+ if (pListMit->flag&MITCF_SHAREDICON)
+ pListMit->hIcon=pMit->hIcon;
+ else
+ pListMit->hIcon=CopyIcon(pMit->hIcon);
+ }
+ itc->pList.insert(pListMit);
+
+ itc->itemWidth=(itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount();
+ itc->itemHeight=itc->height-2*ITC_BORDER_SIZE-2;
+
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ return TRUE;
+ }
+
+ case ITCM_SETSEL:
+ if ( wParam >= 0 && (int)wParam < itc->pList.getCount()) {
+ itc->nSelectedIdx = wParam;
+ SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,
+ MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED),
+ itc->nSelectedIdx);
+ }
+ return TRUE;
+
+ case ITCM_GETSEL:
+ return itc->nSelectedIdx;
+
+ case ITCM_GETITEMDATA:
+ if ( wParam >= 0 && (int)wParam < itc->pList.getCount())
+ return ((MIcoTab *)itc->pList[wParam])->data;
+ return 0;
+
+ case WM_DESTROY:
+ if (itc->hBkgDC) {
+ SelectObject(itc->hBkgDC, itc->hBkgOldBmp);
+ DeleteDC(itc->hBkgDC);
+ }
+ li_ListDestruct(itc->pList,MITListDestructor);
+ delete itc;
+ return TRUE;
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
diff --git a/src/modules/options/options.cpp b/src/modules/options/options.cpp
new file mode 100644
index 0000000000..0366d99062
--- /dev/null
+++ b/src/modules/options/options.cpp
@@ -0,0 +1,1521 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "filter.h"
+
+#define OPENOPTIONSDIALOG_OLD_SIZE 12
+
+#define FILTER_TIMEOUT_TIMER 10012
+
+#define ALL_MODULES_FILTER _T("<all modules>")
+#define CORE_MODULES_FILTER _T("<core modules>")
+
+static HANDLE hOptionsInitEvent;
+static HWND hwndOptions=NULL;
+static HWND hFilterSearchWnd = NULL;
+
+// Thread for search keywords in dialogs
+static BYTE bSearchState = 0; // 0 - not executed; 1 - in progress; 2 - completed;
+static int FilterPage = 0;
+static int FilterLoadProgress = 100;
+static int FilterTimerId = 0;
+
+char * GetPluginNameByInstance( HINSTANCE hInstance );
+
+struct OptionsPageInit
+{
+ int pageCount;
+ OPTIONSDIALOGPAGE *odp;
+};
+
+struct DlgTemplateExBegin
+{
+ WORD dlgVer;
+ WORD signature;
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ WORD cDlgItems;
+ short x;
+ short y;
+ short cx;
+ short cy;
+};
+
+struct OptionsPageData
+{
+ DLGTEMPLATE *pTemplate;
+ DLGPROC dlgProc;
+ HINSTANCE hInst;
+ HTREEITEM hTreeItem;
+ HWND hwnd;
+ int changed;
+ int simpleHeight,expertHeight;
+ int simpleWidth,expertWidth;
+ int simpleBottomControlId,simpleRightControlId;
+ int nExpertOnlyControls;
+ UINT *expertOnlyControls;
+ DWORD flags;
+ TCHAR *pszTitle, *pszGroup, *pszTab;
+ BOOL insideTab;
+ LPARAM dwInitParam;
+
+ int offsetX;
+ int offsetY;
+};
+
+struct OptionsDlgData
+{
+ int pageCount;
+ int currentPage;
+ HTREEITEM hCurrentPage;
+ struct OptionsPageData *opd;
+ RECT rcDisplay;
+ RECT rcTab;
+ HFONT hBoldFont;
+ TCHAR szFilterString[1024];
+};
+
+static HTREEITEM FindNamedTreeItemAtRoot(HWND hwndTree, const TCHAR* name)
+{
+ TVITEM tvi;
+ TCHAR str[128];
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF( str );
+ tvi.hItem = TreeView_GetRoot( hwndTree );
+ while( tvi.hItem != NULL ) {
+ SendMessage( hwndTree, TVM_GETITEM, 0, (LPARAM)&tvi );
+ if( !_tcsicmp( str,name ))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling( hwndTree, tvi.hItem );
+ }
+ return NULL;
+}
+
+static HTREEITEM FindNamedTreeItemAtChildren(HWND hwndTree, HTREEITEM hItem, const TCHAR* name)
+{
+ TVITEM tvi;
+ TCHAR str[128];
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF( str );
+ tvi.hItem = TreeView_GetChild( hwndTree, hItem );
+ while( tvi.hItem != NULL ) {
+ SendMessage( hwndTree, TVM_GETITEM, 0, (LPARAM)&tvi );
+ if( !_tcsicmp( str,name ))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling( hwndTree, tvi.hItem );
+ }
+ return NULL;
+}
+
+static BOOL CALLBACK BoldGroupTitlesEnumChildren(HWND hwnd,LPARAM lParam)
+{
+ TCHAR szClass[64];
+
+ GetClassName(hwnd,szClass,SIZEOF(szClass));
+ if(!lstrcmp(szClass,_T("Button")) && (GetWindowLongPtr(hwnd,GWL_STYLE)&0x0F)==BS_GROUPBOX)
+ SendMessage(hwnd,WM_SETFONT,lParam,0);
+ return TRUE;
+}
+
+struct MoveChildParam
+{
+ HWND hDlg;
+ POINT offset;
+};
+static BOOL CALLBACK MoveEnumChildren(HWND hwnd,LPARAM lParam)
+{
+ struct MoveChildParam * param = ( struct MoveChildParam *) lParam;
+
+ RECT rcWnd;
+ GetWindowRect( hwnd, &rcWnd);
+
+ HWND hwndParent = GetParent( hwnd );
+ if ( hwndParent != param->hDlg )
+ return TRUE; // Do not move subchilds
+
+ POINT pt; pt.x = 0; pt.y = 0;
+
+ ClientToScreen( hwndParent, &pt );
+ OffsetRect( &rcWnd, -pt.x, -pt.y );
+
+ SetWindowPos( hwnd, NULL, rcWnd.left + param->offset.x, rcWnd.top + param->offset.y, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE );
+
+ return TRUE;
+}
+
+#define OPTSTATE_PREFIX "s_"
+
+static void SaveOptionsTreeState(HWND hdlg)
+{
+ TVITEMA tvi;
+ char buf[130],str[128];
+ tvi.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF(str);
+ tvi.hItem = TreeView_GetRoot( GetDlgItem( hdlg, IDC_PAGETREE ));
+ while ( tvi.hItem != NULL ) {
+ if ( SendMessageA( GetDlgItem(hdlg,IDC_PAGETREE), TVM_GETITEMA, 0, (LPARAM)&tvi )) {
+ mir_snprintf(buf, SIZEOF(buf), "%s%s",OPTSTATE_PREFIX,str);
+ DBWriteContactSettingByte(NULL,"Options",buf,(BYTE)((tvi.state&TVIS_EXPANDED)?1:0));
+ }
+ tvi.hItem = TreeView_GetNextSibling( GetDlgItem( hdlg, IDC_PAGETREE ), tvi.hItem );
+} }
+
+#define DM_FOCUSPAGE (WM_USER+10)
+#define DM_REBUILDPAGETREE (WM_USER+11)
+
+static void ThemeDialogBackground(HWND hwnd, BOOL tabbed)
+{
+ if (enableThemeDialogTexture)
+ enableThemeDialogTexture(hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE);
+}
+
+static int lstrcmpnull(TCHAR *str1, TCHAR *str2)
+{
+ if ( str1 == NULL && str2 == NULL )
+ return 0;
+ if ( str1 != NULL && str2 == NULL )
+ return 1;
+ if ( str1 == NULL && str2 != NULL )
+ return -1;
+
+ return lstrcmp(str1, str2);
+}
+
+static TCHAR *GetPluginName(HINSTANCE hInstance, TCHAR *buffer, int size)
+{
+ TCHAR tszModuleName[MAX_PATH];
+ GetModuleFileName(hInstance, tszModuleName, SIZEOF(tszModuleName));
+ TCHAR *dllName = _tcsrchr(tszModuleName,'\\');
+ if (!dllName)
+ {
+ dllName = tszModuleName;
+ }
+ else {
+ dllName++;
+ }
+
+ _tcsncpy(buffer, dllName, size);
+
+ return buffer;
+}
+
+PageHash GetPluginPageHash(const OptionsPageData *page)
+{
+ return hashstr(page->pszGroup) + hashstr(page->pszTitle) + hashstr(page->pszTab);
+}
+
+static void FindFilterStrings(int enableKeywordFiltering, int current, HWND hWndParent, const OptionsPageData *page)
+{
+ TCHAR pluginName[MAX_PATH];
+ HWND hWnd = 0;
+ if (enableKeywordFiltering) {
+ if (current)
+ hWnd = page->hwnd;
+ else
+ {
+ hWnd = CreateDialogIndirectParamA(page->hInst, page->pTemplate, hWndParent, page->dlgProc, page->dwInitParam); //create the options dialog page so we can parse it
+ ShowWindow(hWnd, SW_HIDE); //make sure it's hidden
+ } }
+
+ DWORD key = GetPluginPageHash(page); //get the plugin page hash
+
+ TCHAR * PluginFullName = NULL;
+ char * temp = GetPluginNameByInstance( page->hInst );
+ if ( temp ) PluginFullName = mir_a2t( temp );
+ GetDialogStrings(enableKeywordFiltering, key, GetPluginName(page->hInst, pluginName, SIZEOF(pluginName)), hWnd, page->pszGroup, page->pszTitle, page->pszTab, PluginFullName );
+ if ( PluginFullName ) mir_free( PluginFullName ) ;
+
+ if (enableKeywordFiltering && !current)
+ DestroyWindow(hWnd); //destroy the page, we're done with it
+}
+
+static int MatchesFilter(const OptionsPageData *page, TCHAR *szFilterString)
+{
+ DWORD key = GetPluginPageHash(page);
+
+ return ContainsFilterString(key, szFilterString);
+}
+
+static WNDPROC OptionsFilterDefaultProc = NULL;
+
+static LRESULT CALLBACK OptionsFilterSubclassProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message != WM_PAINT && message != WM_PRINT)
+ return CallWindowProc(OptionsFilterDefaultProc, hWnd, message, wParam, lParam );
+
+ if ( GetFocus() == hWnd || GetWindowTextLength( hWnd ) )
+ return CallWindowProc(OptionsFilterDefaultProc, hWnd, message, wParam, lParam );
+
+ RECT rc;
+ GetClientRect( hWnd, &rc);
+ HDC hdc;
+ PAINTSTRUCT paint;
+
+ if (message == WM_PAINT)
+ hdc = BeginPaint( hWnd, &paint);
+ else
+ hdc = (HDC)wParam;
+
+ TCHAR buf[255];
+ if ( bSearchState==1 && FilterLoadProgress < 100 && FilterLoadProgress > 0 )
+ mir_sntprintf( buf, SIZEOF(buf), TranslateT("Loading... %d%%"), FilterLoadProgress );
+ else
+ mir_sntprintf( buf, SIZEOF(buf), TranslateT( "Search" ) );
+
+ BOOL bDrawnByTheme = FALSE;
+
+ int oldMode = SetBkMode( hdc, TRANSPARENT );
+
+ if ( openThemeData ) {
+ HTHEME hTheme = openThemeData( hWnd, L"EDIT");
+ if ( hTheme ) {
+ if ( isThemeBackgroundPartiallyTransparent( hTheme, EP_EDITTEXT, ETS_NORMAL ))
+ drawThemeParentBackground( hWnd, hdc, &rc );
+
+ RECT rc2;
+ getThemeBackgroundContentRect( hTheme, hdc, EP_EDITTEXT, ETS_NORMAL, &rc, &rc2 );
+ rc2.top = 2 * rc.top - rc2.top;
+ rc2.left = 2 * rc.left - rc2.left;
+ rc2.bottom = 2 * rc.bottom - rc2.bottom;
+ rc2.right = 2 * rc.right - rc2.right;
+
+ drawThemeBackground( hTheme, hdc, EP_EDITTEXT, ETS_NORMAL, &rc2, &rc );
+ HFONT hFont = (HFONT) SendMessage(hWnd, WM_GETFONT, 0, 0);
+ HFONT oldFont = (HFONT) SelectObject( hdc, hFont );
+
+ wchar_t *bufW = mir_t2u(buf);
+ drawThemeText( hTheme, hdc, EP_EDITTEXT, ETS_DISABLED, bufW, -1, 0, 0, &rc );
+ mir_free(bufW);
+
+ SelectObject( hdc, oldFont );
+ closeThemeData( hTheme );
+ bDrawnByTheme = TRUE;
+ }
+ }
+
+ SetBkMode( hdc, oldMode );
+
+ if ( !bDrawnByTheme ) {
+ HFONT hFont = (HFONT) SendMessage(hWnd, WM_GETFONT, 0, 0);
+ HFONT oldFont = (HFONT) SelectObject( hdc, hFont );
+ SetTextColor( hdc, GetSysColor(COLOR_GRAYTEXT) );
+ FillRect( hdc, &rc, GetSysColorBrush( COLOR_WINDOW ) );
+ int oldMode = SetBkMode( hdc, TRANSPARENT );
+ DrawText( hdc, buf, -1, &rc, 0 );
+ SetBkMode( hdc, oldMode );
+ SelectObject( hdc, oldFont );
+ }
+
+ if (message == WM_PAINT)
+ EndPaint( hWnd, &paint);
+
+ return 0;
+}
+
+static BOOL CheckPageShow( HWND hdlg, OptionsDlgData * dat, int i )
+{
+ if (dat->szFilterString && dat->szFilterString[0] && !MatchesFilter(&dat->opd[i], dat->szFilterString)) return FALSE;
+ if ((dat->opd[i].flags & ODPF_SIMPLEONLY) && IsDlgButtonChecked( hdlg, IDC_EXPERT)) return FALSE;
+ if ((dat->opd[i].flags & ODPF_EXPERTONLY) && !IsDlgButtonChecked( hdlg, IDC_EXPERT)) return FALSE;
+ return TRUE;
+}
+
+static BOOL IsAeroMode()
+{
+ BOOL result;
+ return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
+
+static void AeroPaintControl(HWND hwnd, HDC hdc, WNDPROC OldWndProc, UINT msg = WM_PRINT, LPARAM lpFlags = PRF_CLIENT|PRF_NONCLIENT)
+{
+ HBITMAP hBmp, hOldBmp;
+ RECT rc; GetClientRect(hwnd, &rc);
+ BYTE *pBits;
+
+ HDC tempDC = CreateCompatibleDC(hdc);
+
+ BITMAPINFO bmi;
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = rc.right;
+ bmi.bmiHeader.biHeight = -rc.bottom;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0);
+
+ hOldBmp = (HBITMAP)SelectObject(tempDC,hBmp);
+
+ //paint
+ SetPropA(hwnd, "Miranda.AeroRender.Active", (HANDLE)TRUE);
+ CallWindowProc(OldWndProc, hwnd, msg, (WPARAM)tempDC, lpFlags);
+ SetPropA(hwnd, "Miranda.AeroRender.Active", (HANDLE)FALSE);
+
+ // Fix alpha channel
+ GdiFlush();
+ for (int i = 0; i < rc.right*rc.bottom; ++i, pBits += 4)
+ if (!pBits[3]) pBits[3] = 255;
+
+ //Copy to output
+ BitBlt(hdc,0,0,rc.right,rc.bottom,tempDC,0,0,SRCCOPY);
+ SelectObject(tempDC,hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+}
+
+static LRESULT CALLBACK AeroPaintSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC OldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_CTLCOLOREDIT:
+ if (!GetPropA((HWND)lParam, "Miranda.AeroRender.Active"))
+ RedrawWindow((HWND)lParam, NULL, NULL, RDW_INVALIDATE);
+ break;
+
+ case WM_ERASEBKGND:
+ return TRUE;
+
+ case WM_PRINT:
+ case WM_PRINTCLIENT:
+ AeroPaintControl(hwnd, (HDC)wParam, OldWndProc, msg, lParam);
+ return TRUE;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hwnd, &ps);
+ AeroPaintControl(hwnd, hdc, OldWndProc);
+ EndPaint(hwnd, &ps);
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ RemovePropA(hwnd, "Miranda.AeroRender.Active");
+ break;
+ }
+ return CallWindowProc(OldWndProc, hwnd, msg, wParam, lParam);
+}
+
+static void CALLBACK FilterSearchTimerFunc( HWND hwnd, UINT, UINT_PTR, DWORD )
+{
+ struct OptionsDlgData* dat = (struct OptionsDlgData* )GetWindowLongPtr( hwnd, GWLP_USERDATA );
+ if ( !dat )
+ return;
+
+ if ( hFilterSearchWnd == NULL)
+ hFilterSearchWnd = CreateWindowA( "STATIC", "Test", WS_OVERLAPPED|WS_DISABLED, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), 0 ); // Fake window to keep option page focused
+
+ if ( FilterPage < dat->pageCount )
+ FindFilterStrings( TRUE, dat->currentPage == FilterPage, hFilterSearchWnd, &( dat->opd[FilterPage]) );
+
+ FilterPage++;
+ FilterLoadProgress = FilterPage*100/( (dat->pageCount) ? dat->pageCount : FilterPage );
+ if ( FilterPage >= dat->pageCount )
+ {
+ KillTimer( hwnd, FilterTimerId );
+ FilterTimerId = 0;
+ bSearchState = 2;
+ FilterLoadProgress = 100;
+ DestroyWindow( hFilterSearchWnd );
+ hFilterSearchWnd = NULL;
+ }
+ RedrawWindow( GetDlgItem(hwnd, IDC_KEYWORD_FILTER ), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE );
+}
+
+static void ExecuteFindFilterStringsTimer( HWND hdlg )
+{
+ bSearchState = 1;
+ FilterPage = 0;
+ if (FilterTimerId) KillTimer( hdlg, FilterTimerId );
+ FilterTimerId = SetTimer( hdlg, NULL, 1, FilterSearchTimerFunc );
+}
+
+static void FillFilterCombo(int enableKeywordFiltering, HWND hDlg, struct OptionsPageData * opd, int PageCount)
+{
+ int i;
+ int index;
+ HINSTANCE* KnownInstances = ( HINSTANCE* )alloca(sizeof(HINSTANCE)*PageCount);
+ int countKnownInst = 0;
+ SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_RESETCONTENT, 0,0);
+ index=SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_ADDSTRING,(WPARAM)0, (LPARAM)TranslateTS(ALL_MODULES_FILTER));
+ SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_SETITEMDATA,(WPARAM)index, (LPARAM)NULL);
+ index=SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_ADDSTRING,(WPARAM)0, (LPARAM)TranslateTS(CORE_MODULES_FILTER));
+ SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_SETITEMDATA,(WPARAM)index, (LPARAM)hMirandaInst);
+ TCHAR* tszModuleName = ( TCHAR* )alloca(MAX_PATH*sizeof(TCHAR));
+ for (i=0; i<PageCount; i++) {
+ TCHAR * dllName = NULL;
+ int j;
+ HINSTANCE inst=opd[i].hInst;
+
+ if ( !enableKeywordFiltering )
+ FindFilterStrings( enableKeywordFiltering, FALSE, hDlg, &opd[i]); // only modules name ( fast enougth )
+
+ if (inst==hMirandaInst) continue;
+ for (j=0; j<countKnownInst; j++)
+ if (KnownInstances[j]==inst) break;
+ if (j!=countKnownInst) continue;
+ KnownInstances[countKnownInst]=inst;
+ countKnownInst++;
+ GetModuleFileName(inst, tszModuleName, MAX_PATH);
+ {
+ char * name = GetPluginNameByInstance( inst );
+ if ( name )
+ dllName = mir_a2t( name );
+ }
+
+ if (!dllName) dllName = mir_tstrdup( _tcsrchr(tszModuleName,_T('\\')) );
+ if (!dllName) dllName = mir_tstrdup( tszModuleName );
+
+ if (dllName) {
+ index=SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_ADDSTRING,(WPARAM)0, (LPARAM)dllName);
+ SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER,(UINT) CB_SETITEMDATA,(WPARAM)index, (LPARAM)inst);
+ mir_free( dllName );
+ }
+ }
+
+ FilterLoadProgress = 100;
+ if ( enableKeywordFiltering)
+ ExecuteFindFilterStringsTimer( hDlg );
+}
+
+static BOOL IsInsideTab( HWND hdlg, OptionsDlgData * dat, int i )
+{
+ int pages = 0;
+ if (dat->opd[i].pszTab != NULL)
+ {
+ // Count tabs to calc position
+ for (int j=0; j < dat->pageCount && pages < 2; j++ )
+ {
+ if (!CheckPageShow( hdlg, dat, j ) ) continue;
+ //if (( dat->opd[j].flags & ODPF_SIMPLEONLY ) && IsDlgButtonChecked( hdlg, IDC_EXPERT )) continue;
+ //if (( dat->opd[j].flags & ODPF_EXPERTONLY ) && !IsDlgButtonChecked( hdlg, IDC_EXPERT )) continue;
+ if ( lstrcmp(dat->opd[j].pszTitle, dat->opd[i].pszTitle) || lstrcmpnull(dat->opd[j].pszGroup, dat->opd[i].pszGroup) ) continue;
+ pages++;
+ }
+ }
+ return (pages > 1);
+}
+
+static INT_PTR CALLBACK OptionsDlgProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ struct OptionsDlgData* dat = (struct OptionsDlgData* )GetWindowLongPtr( hdlg, GWLP_USERDATA );
+ HWND hwndTree = GetDlgItem(hdlg, IDC_PAGETREE);
+
+ switch ( message ) {
+ case WM_CTLCOLORSTATIC:
+ switch ( GetDlgCtrlID(( HWND )lParam ))
+ {
+ case IDC_WHITERECT:
+ case IDC_KEYWORD_FILTER:
+ SetBkColor(( HDC )wParam, GetSysColor( COLOR_WINDOW ));
+ return ( INT_PTR )GetSysColorBrush( COLOR_WINDOW );
+ }
+ break;
+
+ case WM_INITDIALOG:
+ {
+ PROPSHEETHEADER *psh=(PROPSHEETHEADER*)lParam;
+ OPENOPTIONSDIALOG *ood=(OPENOPTIONSDIALOG*)psh->pStartPage;
+ OPTIONSDIALOGPAGE *odp;
+ int i;
+ struct DlgTemplateExBegin *dte;
+ TCHAR *lastPage = NULL, *lastGroup = NULL, *lastTab = NULL;
+ DBVARIANT dbv;
+ TCITEM tie;
+ LOGFONT lf;
+
+ typedef BOOL (STDAPICALLTYPE *pfnGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
+ pfnGetComboBoxInfo getComboBoxInfo = (pfnGetComboBoxInfo)GetProcAddress(GetModuleHandleA("user32"), "GetComboBoxInfo");
+ if (getComboBoxInfo) {
+ COMBOBOXINFO cbi;
+ cbi.cbSize = sizeof(COMBOBOXINFO);
+ getComboBoxInfo(GetDlgItem( hdlg, IDC_KEYWORD_FILTER), &cbi);
+ OptionsFilterDefaultProc = (WNDPROC)SetWindowLongPtr( cbi.hwndItem, GWLP_WNDPROC, (LONG_PTR) OptionsFilterSubclassProc );
+
+ if (IsAeroMode()) {
+ SetWindowLongPtr(cbi.hwndCombo, GWLP_USERDATA, GetWindowLongPtr(cbi.hwndCombo, GWLP_WNDPROC));
+ SetWindowLongPtr(cbi.hwndCombo, GWLP_WNDPROC, (LONG_PTR)AeroPaintSubclassProc);
+ SetWindowLongPtr(cbi.hwndItem, GWLP_USERDATA, GetWindowLongPtr(cbi.hwndItem, GWLP_WNDPROC));
+ SetWindowLongPtr(cbi.hwndItem, GWLP_WNDPROC, (LONG_PTR)AeroPaintSubclassProc);
+ } }
+
+ Utils_RestoreWindowPositionNoSize(hdlg, NULL, "Options", "");
+ TranslateDialogDefault(hdlg);
+ Window_SetIcon_IcoLib(hdlg, SKINICON_OTHER_OPTIONS);
+ CheckDlgButton(hdlg,IDC_EXPERT,DBGetContactSettingByte(NULL,"Options","Expert",SETTING_SHOWEXPERT_DEFAULT)?BST_CHECKED:BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hdlg,IDC_APPLY),FALSE);
+ dat=(struct OptionsDlgData*)mir_alloc(sizeof(struct OptionsDlgData));
+ SetWindowLongPtr(hdlg,GWLP_USERDATA,(LONG_PTR)dat);
+ SetWindowText(hdlg,psh->pszCaption);
+
+ dat->hBoldFont=(HFONT)SendDlgItemMessage(hdlg,IDC_EXPERT,WM_GETFONT,0,0);
+ GetObject(dat->hBoldFont,sizeof(lf),&lf);
+ lf.lfWeight=FW_BOLD;
+ dat->hBoldFont=CreateFontIndirect(&lf);
+
+ dat->pageCount = psh->nPages;
+ dat->opd = ( struct OptionsPageData* )mir_alloc( sizeof(struct OptionsPageData) * dat->pageCount );
+ odp = ( OPTIONSDIALOGPAGE* )psh->ppsp;
+
+ dat->currentPage = -1;
+ if ( ood->pszPage == NULL ) {
+ if ( !DBGetContactSettingTString( NULL, "Options", "LastPage", &dbv )) {
+ lastPage = mir_tstrdup( dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+
+ if ( ood->pszGroup == NULL ) {
+ if ( !DBGetContactSettingTString( NULL, "Options", "LastGroup", &dbv )) {
+ lastGroup = mir_tstrdup( dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+ }
+ else lastGroup = LangPackPcharToTchar( ood->pszGroup );
+ }
+ else
+ {
+ lastPage = LangPackPcharToTchar( ood->pszPage );
+ lastGroup = ( ood->pszGroup == NULL ) ? NULL : LangPackPcharToTchar( ood->pszGroup );
+ }
+
+ if ( ood->pszTab == NULL ) {
+ if ( !DBGetContactSettingTString( NULL, "Options", "LastTab", &dbv )) {
+ lastTab = mir_tstrdup( dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+ }
+ else lastTab = LangPackPcharToTchar( ood->pszTab );
+
+ for ( i=0; i < dat->pageCount; i++, odp++ ) {
+ struct OptionsPageData* opd = &dat->opd[i];
+ HRSRC hrsrc=FindResourceA(odp->hInstance,odp->pszTemplate,MAKEINTRESOURCEA(5));
+ HGLOBAL hglb=LoadResource(odp->hInstance,hrsrc);
+ DWORD resSize=SizeofResource(odp->hInstance,hrsrc);
+ opd->pTemplate = ( DLGTEMPLATE* )mir_alloc(resSize);
+ memcpy(opd->pTemplate,LockResource(hglb),resSize);
+ dte=(struct DlgTemplateExBegin*)opd->pTemplate;
+ if ( dte->signature == 0xFFFF ) {
+ //this feels like an access violation, and is according to boundschecker
+ //...but it works - for now
+ //may well have to remove and sort out the original dialogs
+ dte->style&=~(WS_VISIBLE|WS_CHILD|WS_POPUP|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME|DS_CENTER);
+ dte->style|=WS_CHILD;
+ }
+ else {
+ opd->pTemplate->style&=~(WS_VISIBLE|WS_CHILD|WS_POPUP|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME|DS_CENTER);
+ opd->pTemplate->style|=WS_CHILD;
+ }
+ opd->dlgProc=odp->pfnDlgProc;
+ opd->hInst=odp->hInstance;
+ opd->hwnd=NULL;
+ opd->changed=0;
+ opd->simpleHeight=opd->expertHeight=0;
+ opd->simpleBottomControlId=odp->nIDBottomSimpleControl;
+ opd->simpleWidth=opd->expertWidth=0;
+ opd->simpleRightControlId=odp->nIDRightSimpleControl;
+ opd->nExpertOnlyControls=odp->nExpertOnlyControls;
+ opd->expertOnlyControls=odp->expertOnlyControls;
+ opd->flags=odp->flags;
+ opd->dwInitParam=odp->dwInitParam;
+ if ( odp->pszTitle == NULL )
+ opd->pszTitle = NULL;
+ else if ( odp->flags & ODPF_UNICODE ) {
+ #if defined ( _UNICODE )
+ opd->pszTitle = ( TCHAR* )mir_wstrdup( odp->ptszTitle );
+ #else
+ opd->pszTitle = mir_u2a(( WCHAR* )odp->ptszTitle );
+ #endif
+ }
+ else opd->pszTitle = ( TCHAR* )mir_strdup( odp->pszTitle );
+
+ if ( odp->pszGroup == NULL )
+ opd->pszGroup = NULL;
+ else if ( odp->flags & ODPF_UNICODE ) {
+ #if defined ( _UNICODE )
+ opd->pszGroup = ( TCHAR* )mir_wstrdup( odp->ptszGroup );
+ #else
+ opd->pszGroup = mir_u2a(( WCHAR* )odp->ptszGroup );
+ #endif
+ }
+ else opd->pszGroup = ( TCHAR* )mir_strdup( odp->pszGroup );
+
+ if ( odp->pszTab == NULL )
+ opd->pszTab = NULL;
+ else if ( odp->flags & ODPF_UNICODE ) {
+ #if defined ( _UNICODE )
+ opd->pszTab = ( TCHAR* )mir_wstrdup( odp->ptszTab );
+ #else
+ opd->pszTab = mir_u2a(( WCHAR* )odp->ptszTab );
+ #endif
+ }
+ else opd->pszTab = ( TCHAR* )mir_strdup( odp->pszTab );
+
+ if ( !lstrcmp( lastPage, odp->ptszTitle ) &&
+ !lstrcmpnull( lastGroup, odp->ptszGroup ) &&
+ (( ood->pszTab == NULL && dat->currentPage == -1 ) || !lstrcmpnull( lastTab, odp->ptszTab )))
+ dat->currentPage = i;
+ }
+ mir_free( lastGroup );
+ mir_free( lastPage );
+ mir_free( lastTab );
+
+ GetWindowRect(GetDlgItem(hdlg,IDC_STNOPAGE),&dat->rcDisplay);
+ MapWindowPoints(NULL, hdlg, (LPPOINT)&dat->rcDisplay, 2);
+
+ // Add an item to count in height
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+ tie.iImage = -1;
+ tie.pszText = _T("X");
+ TabCtrl_InsertItem(GetDlgItem(hdlg,IDC_TAB), 0, &tie);
+
+ GetWindowRect(GetDlgItem(hdlg,IDC_TAB), &dat->rcTab);
+ MapWindowPoints(NULL, hdlg, (LPPOINT)&dat->rcTab, 2);
+ TabCtrl_AdjustRect(GetDlgItem(hdlg,IDC_TAB), FALSE, &dat->rcTab);
+
+ //!!!!!!!!!! int enableKeywordFiltering = DBGetContactSettingWord(NULL, "Options", "EnableKeywordFiltering", TRUE);
+ FillFilterCombo( 0, //!!!!!!!!!! enableKeywordFiltering,
+ hdlg, dat->opd, dat->pageCount);
+ SendMessage(hdlg,DM_REBUILDPAGETREE,0,0);
+
+ return TRUE;
+ }
+ case DM_REBUILDPAGETREE:
+ {
+ int i;
+ TVINSERTSTRUCT tvis;
+ TVITEMA tvi;
+ BOOL bRemoveFocusFromFilter = FALSE;
+ char str[128],buf[130];
+
+ HINSTANCE FilterInst=NULL;
+
+ LPARAM oldSel = SendDlgItemMessage(hdlg, IDC_KEYWORD_FILTER, CB_GETEDITSEL, 0, 0);
+
+ GetDlgItemText(hdlg, IDC_KEYWORD_FILTER, dat->szFilterString, SIZEOF(dat->szFilterString));
+
+ //if filter string is set to all modules then make the filter string empty (this will return all modules)
+ if (_tcscmp(dat->szFilterString, TranslateTS( ALL_MODULES_FILTER ) ) == 0) {
+ dat->szFilterString[0] = 0;
+ bRemoveFocusFromFilter = TRUE;
+ }
+ //if filter string is set to core modules replace it with the name of the executable (this will return all core modules)
+ else if (_tcscmp(dat->szFilterString, TranslateTS( CORE_MODULES_FILTER) ) == 0) {
+ //replace string with process name - that will show core settings
+ TCHAR szFileName[300];
+ GetModuleFileName(NULL, szFileName, SIZEOF(szFileName));
+ TCHAR *pos = _tcsrchr(szFileName, _T('\\'));
+ if (pos)
+ pos++;
+ else
+ pos = szFileName;
+
+ _tcsncpy(dat->szFilterString, pos, SIZEOF(dat->szFilterString));
+ }
+ else {
+ int sel = SendMessage( GetDlgItem(hdlg, IDC_KEYWORD_FILTER ), (UINT) CB_GETCURSEL, 0,0 );
+ if (sel != -1) {
+ HINSTANCE hinst = (HINSTANCE)SendMessage( GetDlgItem(hdlg, IDC_KEYWORD_FILTER ), (UINT) CB_GETITEMDATA, sel ,0 );
+ TCHAR szFileName[300];
+ GetModuleFileName(hinst, szFileName, SIZEOF(szFileName));
+ TCHAR *pos = _tcsrchr(szFileName, _T('\\'));
+ if (pos) pos++;
+ else pos = szFileName;
+ _tcsncpy(dat->szFilterString, pos, SIZEOF(dat->szFilterString));
+ } }
+
+ _tcslwr_locale(dat->szFilterString); //all strings are stored as lowercase ... make sure filter string is lowercase too
+
+ ShowWindow(hwndTree, SW_HIDE); //deleteall is annoyingly visible
+
+ HWND oldWnd = NULL;
+ HWND oldTab = NULL;
+ if ( dat->currentPage != (-1)) {
+ oldWnd = dat->opd[dat->currentPage].hwnd;
+ if ( dat->opd[dat->currentPage].insideTab )
+ oldTab = GetDlgItem( hdlg,IDC_TAB );
+ }
+
+ dat->hCurrentPage = NULL;
+
+ TreeView_SelectItem(hwndTree, NULL);
+
+ TreeView_DeleteAllItems(hwndTree);
+
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED;
+ for ( i=0; i < dat->pageCount; i++ ) {
+ static TCHAR *fullTitle=NULL;
+ TCHAR * useTitle;
+ if (fullTitle) mir_free(fullTitle);
+ fullTitle=NULL;
+ if (! CheckPageShow( hdlg, dat, i ) ) continue;
+ tvis.hParent = NULL;
+ if ( FilterInst!=NULL ) {
+ size_t sz=dat->opd[i].pszGroup?_tcslen(dat->opd[i].pszGroup)+1:0;
+ if (sz) sz+=3;
+ sz+=dat->opd[i].pszTitle?_tcslen(dat->opd[i].pszTitle)+1:0;
+ fullTitle = ( TCHAR* )mir_alloc(sz*sizeof(TCHAR));
+ mir_sntprintf(fullTitle,sz,(dat->opd[i].pszGroup && dat->opd[i].pszTitle)?_T("%s - %s"):_T("%s%s"),dat->opd[i].pszGroup?dat->opd[i].pszGroup:_T(""),dat->opd[i].pszTitle?dat->opd[i].pszTitle:_T("") );
+ }
+ useTitle=fullTitle?fullTitle:dat->opd[i].pszTitle;
+ if(dat->opd[i].pszGroup != NULL && FilterInst==NULL) {
+ tvis.hParent = FindNamedTreeItemAtRoot(hwndTree, dat->opd[i].pszGroup);
+ if(tvis.hParent == NULL) {
+ tvis.item.lParam = -1;
+ tvis.item.pszText = dat->opd[i].pszGroup;
+ tvis.hParent = TreeView_InsertItem(hwndTree, &tvis);
+ }
+ }
+ else {
+ TVITEM tvi;
+ tvi.hItem = FindNamedTreeItemAtRoot(hwndTree,useTitle);
+ if( tvi.hItem != NULL ) {
+ if ( i == dat->currentPage ) dat->hCurrentPage=tvi.hItem;
+ tvi.mask = TVIF_PARAM;
+ TreeView_GetItem(hwndTree,&tvi);
+ if ( tvi.lParam == -1 ) {
+ tvi.lParam = i;
+ TreeView_SetItem(hwndTree,&tvi);
+ continue;
+ } } }
+
+ if ( dat->opd[i].pszTab != NULL ) {
+ HTREEITEM hItem;
+ if (tvis.hParent == NULL)
+ hItem = FindNamedTreeItemAtRoot(hwndTree,useTitle);
+ else
+ hItem = FindNamedTreeItemAtChildren(hwndTree,tvis.hParent,useTitle);
+ if( hItem != NULL ) {
+ if ( i == dat->currentPage ) {
+ TVITEM tvi;
+ tvi.hItem=hItem;
+ tvi.mask=TVIF_PARAM;
+ tvi.lParam=dat->currentPage;
+ TreeView_SetItem(hwndTree,&tvi);
+ dat->hCurrentPage=hItem;
+ }
+ continue;
+ }
+ }
+
+ tvis.item.pszText = useTitle;
+ tvis.item.lParam = i;
+ dat->opd[i].hTreeItem = TreeView_InsertItem(hwndTree, &tvis);
+ if ( i == dat->currentPage )
+ dat->hCurrentPage = dat->opd[i].hTreeItem;
+
+ if (fullTitle) mir_free(fullTitle);
+ fullTitle=NULL;
+ }
+ tvi.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF(str);
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while ( tvi.hItem != NULL ) {
+ if ( SendMessageA( hwndTree, TVM_GETITEMA, 0, (LPARAM)&tvi )) {
+ mir_snprintf(buf, SIZEOF(buf), "%s%s",OPTSTATE_PREFIX,str);
+ if ( !DBGetContactSettingByte( NULL, "Options", buf, 1 ))
+ TreeView_Expand( hwndTree, tvi.hItem, TVE_COLLAPSE );
+ }
+ tvi.hItem = TreeView_GetNextSibling( hwndTree, tvi.hItem );
+ }
+ if(dat->hCurrentPage==NULL) {
+ dat->hCurrentPage=TreeView_GetRoot(hwndTree);
+ dat->currentPage=-1;
+ }
+ TreeView_SelectItem(hwndTree,dat->hCurrentPage);
+
+ if ( oldWnd ) {
+ if ( dat->currentPage == (-1) || oldWnd != dat->opd[dat->currentPage].hwnd ) {
+ ShowWindow( oldWnd, SW_HIDE);
+ if ( oldTab && ( dat->currentPage ==-1 || !dat->opd[dat->currentPage].insideTab ) )
+ ShowWindow( oldTab, SW_HIDE );
+ }
+ }
+
+ if ( dat->szFilterString[0] == 0 ) // Clear the keyword combo box
+ SetWindowText( GetDlgItem(hdlg, IDC_KEYWORD_FILTER), _T("") );
+ if ( !bRemoveFocusFromFilter )
+ SetFocus(GetDlgItem(hdlg, IDC_KEYWORD_FILTER)); //set the focus back to the combo box
+
+ SendDlgItemMessage(hdlg, IDC_KEYWORD_FILTER, CB_SETEDITSEL, 0, oldSel ); //but don't select any of the text
+
+ ShowWindow(hwndTree,SW_SHOW);
+ }
+ break;
+
+ case PSM_CHANGED:
+ EnableWindow(GetDlgItem(hdlg,IDC_APPLY),TRUE);
+ if(dat->currentPage != (-1)) dat->opd[dat->currentPage].changed=1;
+ return TRUE;
+
+ case PSM_ISEXPERT:
+ SetWindowLongPtr(hdlg,DWLP_MSGRESULT,IsDlgButtonChecked(hdlg,IDC_EXPERT));
+ return TRUE;
+
+ case PSM_GETBOLDFONT:
+ SetWindowLongPtr(hdlg,DWLP_MSGRESULT,(LONG_PTR)dat->hBoldFont);
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch(wParam) {
+ case IDC_TAB:
+ case IDC_PAGETREE:
+ switch(((LPNMHDR)lParam)->code) {
+ case TVN_ITEMEXPANDING:
+ SetWindowLongPtr(hdlg,DWLP_MSGRESULT,FALSE);
+ return TRUE;
+ case TCN_SELCHANGING:
+ case TVN_SELCHANGING:
+ { PSHNOTIFY pshn;
+ if(dat->currentPage==-1 || dat->opd[dat->currentPage].hwnd==NULL) break;
+ pshn.hdr.code=PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom=dat->opd[dat->currentPage].hwnd;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=0;
+ if(SendMessage(dat->opd[dat->currentPage].hwnd,WM_NOTIFY,0,(LPARAM)&pshn)) {
+ SetWindowLongPtr(hdlg,DWLP_MSGRESULT,TRUE);
+ return TRUE;
+ }
+ break;
+ }
+ case TCN_SELCHANGE:
+ case TVN_SELCHANGED:
+ { BOOL tabChange = (wParam == IDC_TAB);
+ ShowWindow(GetDlgItem(hdlg,IDC_STNOPAGE),SW_HIDE);
+ if(dat->currentPage!=-1 && dat->opd[dat->currentPage].hwnd!=NULL) ShowWindow(dat->opd[dat->currentPage].hwnd,SW_HIDE);
+ if (!tabChange) {
+ TVITEM tvi;
+ tvi.hItem=dat->hCurrentPage=TreeView_GetSelection(hwndTree);
+ if(tvi.hItem==NULL) break;
+ tvi.mask=TVIF_HANDLE|TVIF_PARAM;
+ TreeView_GetItem(hwndTree,&tvi);
+ dat->currentPage=tvi.lParam;
+ ShowWindow(GetDlgItem(hdlg,IDC_TAB),SW_HIDE);
+ }
+ else {
+ TCITEM tie;
+ TVITEM tvi;
+
+ tie.mask = TCIF_PARAM;
+ TabCtrl_GetItem(GetDlgItem(hdlg,IDC_TAB),TabCtrl_GetCurSel(GetDlgItem(hdlg,IDC_TAB)),&tie);
+ dat->currentPage=tie.lParam;
+
+ tvi.hItem=dat->hCurrentPage;
+ tvi.mask=TVIF_PARAM;
+ tvi.lParam=dat->currentPage;
+ TreeView_SetItem(hwndTree,&tvi);
+ }
+ if ( dat->currentPage != -1 ) {
+ if ( dat->opd[dat->currentPage].hwnd == NULL ) {
+ RECT rcPage;
+ RECT rcControl,rc;
+ int w,h;
+
+ dat->opd[dat->currentPage].hwnd=CreateDialogIndirectParamA(dat->opd[dat->currentPage].hInst,dat->opd[dat->currentPage].pTemplate,hdlg,dat->opd[dat->currentPage].dlgProc,dat->opd[dat->currentPage].dwInitParam);
+ if(dat->opd[dat->currentPage].flags&ODPF_BOLDGROUPS)
+ EnumChildWindows(dat->opd[dat->currentPage].hwnd,BoldGroupTitlesEnumChildren,(LPARAM)dat->hBoldFont);
+ GetClientRect(dat->opd[dat->currentPage].hwnd,&rcPage);
+ dat->opd[dat->currentPage].expertWidth=rcPage.right;
+ dat->opd[dat->currentPage].expertHeight=rcPage.bottom;
+ GetWindowRect(dat->opd[dat->currentPage].hwnd,&rc);
+
+ if(dat->opd[dat->currentPage].simpleBottomControlId) {
+ GetWindowRect(GetDlgItem(dat->opd[dat->currentPage].hwnd,dat->opd[dat->currentPage].simpleBottomControlId),&rcControl);
+ dat->opd[dat->currentPage].simpleHeight=rcControl.bottom-rc.top;
+ }
+ else dat->opd[dat->currentPage].simpleHeight=dat->opd[dat->currentPage].expertHeight;
+
+ if(dat->opd[dat->currentPage].simpleRightControlId) {
+ GetWindowRect(GetDlgItem(dat->opd[dat->currentPage].hwnd,dat->opd[dat->currentPage].simpleRightControlId),&rcControl);
+ dat->opd[dat->currentPage].simpleWidth=rcControl.right-rc.left;
+ }
+ else dat->opd[dat->currentPage].simpleWidth=dat->opd[dat->currentPage].expertWidth;
+
+ if(IsDlgButtonChecked(hdlg,IDC_EXPERT)) {
+ w=dat->opd[dat->currentPage].expertWidth;
+ h=dat->opd[dat->currentPage].expertHeight;
+ }
+ else {
+ int i;
+ for(i=0;i<dat->opd[dat->currentPage].nExpertOnlyControls;i++)
+ ShowWindow(GetDlgItem(dat->opd[dat->currentPage].hwnd,dat->opd[dat->currentPage].expertOnlyControls[i]),SW_HIDE);
+ w=dat->opd[dat->currentPage].simpleWidth;
+ h=dat->opd[dat->currentPage].simpleHeight;
+ }
+
+ dat->opd[dat->currentPage].offsetX = 0;
+ dat->opd[dat->currentPage].offsetY = 0;
+
+ dat->opd[dat->currentPage].insideTab = IsInsideTab( hdlg, dat, dat->currentPage );
+ if (dat->opd[dat->currentPage].insideTab) {
+ SetWindowPos(dat->opd[dat->currentPage].hwnd,HWND_TOP,(dat->rcTab.left+dat->rcTab.right-w)>>1,dat->rcTab.top,w,h,0);
+ ThemeDialogBackground(dat->opd[dat->currentPage].hwnd,TRUE);
+ } else {
+ SetWindowPos(dat->opd[dat->currentPage].hwnd,HWND_TOP,(dat->rcDisplay.left+dat->rcDisplay.right-w)>>1,(dat->rcDisplay.top+dat->rcDisplay.bottom-h)>>1,w,h,0);
+ ThemeDialogBackground(dat->opd[dat->currentPage].hwnd,FALSE);
+ }
+ }
+ if ( !tabChange )
+ {
+ dat->opd[ dat->currentPage].insideTab = IsInsideTab( hdlg, dat, dat->currentPage );
+ if ( dat->opd[dat->currentPage].insideTab ) {
+ // Make tabbed pane
+ int i,pages=0,sel=0;
+ TCITEM tie;
+ HWND hwndTab = GetDlgItem(hdlg,IDC_TAB);
+
+ TabCtrl_DeleteAllItems(hwndTab);
+ tie.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
+ tie.iImage = -1;
+ for ( i=0; i < dat->pageCount; i++ ) {
+ if (!CheckPageShow( hdlg, dat, i ) ) continue;
+ //if (( dat->opd[i].flags & ODPF_SIMPLEONLY ) && IsDlgButtonChecked( hdlg, IDC_EXPERT )) continue;
+ //if (( dat->opd[i].flags & ODPF_EXPERTONLY ) && !IsDlgButtonChecked( hdlg, IDC_EXPERT )) continue;
+ if ( lstrcmp(dat->opd[i].pszTitle, dat->opd[dat->currentPage].pszTitle) || lstrcmpnull(dat->opd[i].pszGroup, dat->opd[dat->currentPage].pszGroup) ) continue;
+
+ tie.pszText = dat->opd[i].pszTab;
+ tie.lParam = i;
+ TabCtrl_InsertItem(hwndTab, pages, &tie);
+ if ( !lstrcmp(dat->opd[i].pszTab,dat->opd[dat->currentPage].pszTab) )
+ sel = pages;
+ pages++;
+ }
+ TabCtrl_SetCurSel(hwndTab,sel);
+ ShowWindow(hwndTab, dat->opd[dat->currentPage].insideTab ? SW_SHOW : SW_HIDE );
+ }
+
+ if (dat->opd[dat->currentPage].insideTab)
+ ThemeDialogBackground(dat->opd[dat->currentPage].hwnd,TRUE);
+ else
+ ThemeDialogBackground(dat->opd[dat->currentPage].hwnd,FALSE);
+ }
+
+ // Resizing
+ if (!dat->opd[dat->currentPage].simpleBottomControlId)
+ {
+ int pageWidth, pageHeight;
+
+ if(IsDlgButtonChecked(hdlg,IDC_EXPERT)) {
+ pageWidth=dat->opd[dat->currentPage].expertWidth;
+ pageHeight=dat->opd[dat->currentPage].expertHeight;
+ }
+ else {
+ pageWidth=dat->opd[dat->currentPage].simpleWidth;
+ pageHeight=dat->opd[dat->currentPage].simpleHeight;
+ }
+
+ RECT * parentPageRect = &dat->rcDisplay;
+
+ if ( dat->opd[dat->currentPage].insideTab )
+ parentPageRect = &dat->rcTab;
+
+ pageHeight = min( pageHeight, parentPageRect->bottom - parentPageRect->top );
+ pageWidth = min( pageWidth, parentPageRect->right - parentPageRect->left );
+
+ int newOffsetX = ( parentPageRect->right - parentPageRect->left - pageWidth ) >> 1;
+ int newOffsetY = dat->opd[dat->currentPage].insideTab ? 0 : ( parentPageRect->bottom - parentPageRect->top - pageHeight ) >> 1;
+
+ struct MoveChildParam mcp;
+ mcp.hDlg = dat->opd[dat->currentPage].hwnd;
+ mcp.offset.x = newOffsetX - dat->opd[dat->currentPage].offsetX;
+ mcp.offset.y = newOffsetY - dat->opd[dat->currentPage].offsetY;
+
+
+ if ( mcp.offset.x || mcp.offset.y )
+ {
+ EnumChildWindows(dat->opd[dat->currentPage].hwnd,MoveEnumChildren,(LPARAM)(&mcp));
+
+ SetWindowPos( dat->opd[dat->currentPage].hwnd, NULL,
+ parentPageRect->left, parentPageRect->top,
+ parentPageRect->right - parentPageRect->left,
+ parentPageRect->bottom - parentPageRect->top,
+ SWP_NOZORDER | SWP_NOACTIVATE );
+ dat->opd[dat->currentPage].offsetX = newOffsetX;
+ dat->opd[dat->currentPage].offsetY = newOffsetY;
+ }
+
+ }
+
+ ShowWindow(dat->opd[dat->currentPage].hwnd,SW_SHOW);
+ if(((LPNMTREEVIEW)lParam)->action==TVC_BYMOUSE) PostMessage(hdlg,DM_FOCUSPAGE,0,0);
+ else SetFocus(hwndTree);
+ }
+ else ShowWindow(GetDlgItem(hdlg,IDC_STNOPAGE),SW_SHOW);
+ break;
+ } } }
+ break;
+
+ case DM_FOCUSPAGE:
+ if (dat->currentPage != -1)
+ SetFocus(dat->opd[dat->currentPage].hwnd);
+ break;
+
+ case WM_TIMER:
+ if (wParam == FILTER_TIMEOUT_TIMER) {
+ SaveOptionsTreeState(hdlg);
+ SendMessage(hdlg,DM_REBUILDPAGETREE,0,0);
+
+ KillTimer(hdlg, FILTER_TIMEOUT_TIMER);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_KEYWORD_FILTER:
+ //add a timer - when the timer elapses filter the option pages
+ if ( (HIWORD(wParam)==CBN_SELCHANGE) || (HIWORD(wParam) == CBN_EDITCHANGE))
+ if (!SetTimer(hdlg, FILTER_TIMEOUT_TIMER, 400, NULL))
+ MessageBeep(MB_ICONSTOP);
+
+ break;
+
+ case IDC_EXPERT:
+ {
+ int expert=IsDlgButtonChecked(hdlg,IDC_EXPERT);
+ int i,j;
+ PSHNOTIFY pshn;
+ RECT rcPage;
+ int neww,newh;
+
+ DBWriteContactSettingByte(NULL,"Options","Expert",(BYTE)expert);
+ pshn.hdr.idFrom=0;
+ pshn.lParam=expert;
+ pshn.hdr.code=PSN_EXPERTCHANGED;
+ for(i=0;i<dat->pageCount;i++) {
+ if(dat->opd[i].hwnd==NULL) continue;
+ if (!CheckPageShow( hdlg, dat, i ) ) continue;
+ //if (( dat->opd[i].flags & ODPF_SIMPLEONLY ) && expert) continue;
+ //if (( dat->opd[i].flags & ODPF_EXPERTONLY ) && !expert) continue;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+
+ for(j=0;j<dat->opd[i].nExpertOnlyControls;j++)
+ ShowWindow(GetDlgItem(dat->opd[i].hwnd,dat->opd[i].expertOnlyControls[j]),expert?SW_SHOW:SW_HIDE);
+
+ dat->opd[i].insideTab = IsInsideTab( hdlg, dat, i );
+
+ GetWindowRect(dat->opd[i].hwnd,&rcPage);
+ if(dat->opd[i].simpleBottomControlId) newh=expert?dat->opd[i].expertHeight:dat->opd[i].simpleHeight;
+ else newh=rcPage.bottom-rcPage.top;
+ if(dat->opd[i].simpleRightControlId) neww=expert?dat->opd[i].expertWidth:dat->opd[i].simpleWidth;
+ else neww=rcPage.right-rcPage.left;
+ if(i==dat->currentPage) {
+ POINT ptStart,ptEnd,ptNow;
+ DWORD thisTick,startTick;
+ RECT rc;
+
+ ptNow.x=ptNow.y=0;
+ ClientToScreen(hdlg,&ptNow);
+ GetWindowRect(dat->opd[i].hwnd,&rc);
+ ptStart.x=rc.left-ptNow.x;
+ ptStart.y=rc.top-ptNow.y;
+ if (dat->opd[i].insideTab) {
+ ptEnd.x=(dat->rcTab.left+dat->rcTab.right-neww)>>1;
+ ptEnd.y=dat->rcTab.top;
+ } else {
+ ptEnd.x=(dat->rcDisplay.left+dat->rcDisplay.right-neww)>>1;
+ ptEnd.y=(dat->rcDisplay.top+dat->rcDisplay.bottom-newh)>>1;
+ }
+ if(abs(ptEnd.x-ptStart.x)>5 || abs(ptEnd.y-ptStart.y)>5) {
+ startTick=GetTickCount();
+ SetWindowPos(dat->opd[i].hwnd,HWND_TOP,0,0,min(neww,rcPage.right),min(newh,rcPage.bottom),SWP_NOMOVE);
+ UpdateWindow(dat->opd[i].hwnd);
+ for(;;) {
+ thisTick=GetTickCount();
+ if(thisTick>startTick+100) break;
+ ptNow.x=ptStart.x+(ptEnd.x-ptStart.x)*(int)(thisTick-startTick)/100;
+ ptNow.y=ptStart.y+(ptEnd.y-ptStart.y)*(int)(thisTick-startTick)/100;
+ SetWindowPos(dat->opd[i].hwnd,0,ptNow.x,ptNow.y,0,0,SWP_NOZORDER|SWP_NOSIZE);
+ }
+ }
+ if (dat->opd[i].insideTab)
+ ShowWindow(GetDlgItem(hdlg,IDC_TAB),SW_SHOW);
+ else
+ ShowWindow(GetDlgItem(hdlg,IDC_TAB),SW_HIDE);
+ }
+
+ if (dat->opd[i].insideTab) {
+ SetWindowPos(dat->opd[i].hwnd,HWND_TOP,(dat->rcTab.left+dat->rcTab.right-neww)>>1,dat->rcTab.top,neww,newh,0);
+ ThemeDialogBackground(dat->opd[i].hwnd,TRUE);
+ } else {
+ SetWindowPos(dat->opd[i].hwnd,HWND_TOP,(dat->rcDisplay.left+dat->rcDisplay.right-neww)>>1,(dat->rcDisplay.top+dat->rcDisplay.bottom-newh)>>1,neww,newh,0);
+ ThemeDialogBackground(dat->opd[i].hwnd,FALSE);
+ }
+ }
+ SaveOptionsTreeState(hdlg);
+ SendMessage(hdlg,DM_REBUILDPAGETREE,0,0);
+ break;
+ }
+ case IDCANCEL:
+ { int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=0;
+ pshn.hdr.code=PSN_RESET;
+ for(i=0;i<dat->pageCount;i++) {
+ if(dat->opd[i].hwnd==NULL || !dat->opd[i].changed) continue;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ DestroyWindow(hdlg);
+ break;
+ }
+ case IDOK:
+ case IDC_APPLY:
+ {
+ int i;
+ PSHNOTIFY pshn;
+
+ if (LOWORD(wParam) == IDOK && GetParent(GetFocus()) == GetDlgItem(hdlg, IDC_KEYWORD_FILTER))
+ return TRUE;
+
+ EnableWindow(GetDlgItem(hdlg,IDC_APPLY),FALSE);
+ SetFocus(hwndTree);
+ if(dat->currentPage!=(-1)) {
+ pshn.hdr.idFrom=0;
+ pshn.lParam=0;
+ pshn.hdr.code=PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom=dat->opd[dat->currentPage].hwnd;
+ if(SendMessage(dat->opd[dat->currentPage].hwnd,WM_NOTIFY,0,(LPARAM)&pshn))
+ break;
+ }
+
+ pshn.hdr.code=PSN_APPLY;
+ for(i=0;i<dat->pageCount;i++) {
+ if(dat->opd[i].hwnd==NULL || !dat->opd[i].changed) continue;
+ dat->opd[i].changed=0;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ if(SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn)==PSNRET_INVALID_NOCHANGEPAGE) {
+ dat->hCurrentPage=dat->opd[i].hTreeItem;
+ TreeView_SelectItem(hwndTree,dat->hCurrentPage);
+ if(dat->currentPage!=(-1)) ShowWindow(dat->opd[dat->currentPage].hwnd,SW_HIDE);
+ dat->currentPage=i;
+ if (dat->currentPage != (-1)) ShowWindow(dat->opd[dat->currentPage].hwnd,SW_SHOW);
+ return 0;
+ } }
+
+ if ( LOWORD( wParam ) == IDOK )
+ DestroyWindow(hdlg);
+ break;
+ } }
+ break;
+
+ case WM_DESTROY:
+ if ( FilterTimerId ) KillTimer ( hdlg, FilterTimerId );
+ DestroyWindow ( hFilterSearchWnd );
+ ClearFilterStrings();
+ dat->szFilterString[0]=0;
+
+ SaveOptionsTreeState( hdlg );
+ Window_FreeIcon_IcoLib( hdlg );
+
+ if ( dat->currentPage != -1 ) {
+ if ( dat->opd[dat->currentPage].pszTab )
+ DBWriteContactSettingTString( NULL, "Options", "LastTab", dat->opd[dat->currentPage].pszTab );
+ else DBDeleteContactSetting( NULL, "Options", "LastTab" );
+ if ( dat->opd[dat->currentPage].pszGroup )
+ DBWriteContactSettingTString( NULL, "Options", "LastGroup", dat->opd[dat->currentPage].pszGroup );
+ else DBDeleteContactSetting( NULL, "Options", "LastGroup" );
+ DBWriteContactSettingTString( NULL, "Options", "LastPage", dat->opd[dat->currentPage].pszTitle );
+ }
+ else {
+ DBDeleteContactSetting(NULL,"Options","LastTab");
+ DBDeleteContactSetting(NULL,"Options","LastGroup");
+ DBDeleteContactSetting(NULL,"Options","LastPage");
+ }
+ Utils_SaveWindowPosition(hdlg, NULL, "Options", "");
+ {
+ int i;
+ for ( i=0; i < dat->pageCount; i++ ) {
+ if ( dat->opd[i].hwnd != NULL )
+ DestroyWindow(dat->opd[i].hwnd);
+ mir_free(dat->opd[i].pszGroup);
+ mir_free(dat->opd[i].pszTab);
+ mir_free(dat->opd[i].pszTitle);
+ mir_free(dat->opd[i].pTemplate);
+ } }
+ mir_free( dat->opd );
+ DeleteObject( dat->hBoldFont );
+ mir_free( dat );
+ hwndOptions = NULL;
+
+ CallService(MS_MODERNOPT_RESTORE, 0, 0);
+ break;
+ }
+ return FALSE;
+}
+
+static void FreeOptionsData( struct OptionsPageInit* popi )
+{
+ int i;
+ for ( i=0; i < popi->pageCount; i++ ) {
+ mir_free(( char* )popi->odp[i].pszTitle );
+ mir_free( popi->odp[i].pszGroup );
+ mir_free( popi->odp[i].pszTab );
+ if (( DWORD_PTR )popi->odp[i].pszTemplate & 0xFFFF0000 )
+ mir_free((char*)popi->odp[i].pszTemplate);
+ }
+ mir_free(popi->odp);
+}
+
+void OpenAccountOptions( PROTOACCOUNT* pa )
+{
+ struct OptionsPageInit opi = { 0 };
+ if ( pa->ppro == NULL )
+ return;
+
+ pa->ppro->OnEvent( EV_PROTO_ONOPTIONS, ( WPARAM )&opi, 0 );
+ if ( opi.pageCount > 0 ) {
+ TCHAR tszTitle[ 100 ];
+ OPENOPTIONSDIALOG ood = { 0 };
+ PROPSHEETHEADER psh = { 0 };
+
+ mir_sntprintf( tszTitle, SIZEOF(tszTitle), TranslateT("%s options"), pa->tszAccountName );
+
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = LPGEN("Network");
+ ood.pszPage = mir_t2a( pa->tszAccountName );
+
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_PROPSHEETPAGE|PSH_NOAPPLYNOW;
+ psh.hwndParent = NULL;
+ psh.nPages = opi.pageCount;
+ psh.pStartPage = (LPCTSTR)&ood;
+ psh.pszCaption = tszTitle;
+ psh.ppsp = (PROPSHEETPAGE*)opi.odp;
+ hwndOptions = CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_OPTIONSPAGE),NULL,OptionsDlgProc,(LPARAM)&psh);
+ mir_free(( void* )ood.pszPage );
+ FreeOptionsData( &opi );
+} }
+
+static void OpenOptionsNow(const char *pszGroup,const char *pszPage,const char *pszTab, bool bSinglePage=false)
+{
+ if ( IsWindow( hwndOptions )) {
+ ShowWindow( hwndOptions, SW_RESTORE );
+ SetForegroundWindow( hwndOptions );
+ if ( pszPage != NULL) {
+ TCHAR *ptszPage = LangPackPcharToTchar(pszPage);
+ HTREEITEM hItem = NULL;
+ if (pszGroup != NULL) {
+ TCHAR *ptszGroup = LangPackPcharToTchar(pszGroup);
+ hItem = FindNamedTreeItemAtRoot(GetDlgItem(hwndOptions,IDC_PAGETREE),ptszGroup);
+ if (hItem != NULL) {
+ hItem = FindNamedTreeItemAtChildren(GetDlgItem(hwndOptions,IDC_PAGETREE),hItem,ptszPage);
+ }
+ mir_free(ptszGroup);
+ } else {
+ hItem = FindNamedTreeItemAtRoot(GetDlgItem(hwndOptions,IDC_PAGETREE),ptszPage);
+ }
+ if (hItem != NULL) {
+ TreeView_SelectItem(GetDlgItem(hwndOptions,IDC_PAGETREE),hItem);
+ }
+ mir_free(ptszPage);
+ }
+ } else {
+ struct OptionsPageInit opi = { 0 };
+ NotifyEventHooks( hOptionsInitEvent, ( WPARAM )&opi, 0 );
+ if ( opi.pageCount > 0 ) {
+ OPENOPTIONSDIALOG ood = { 0 };
+ PROPSHEETHEADER psh = { 0 };
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
+ psh.nPages = opi.pageCount;
+ ood.pszGroup = pszGroup;
+ ood.pszPage = pszPage;
+ ood.pszTab = pszTab;
+ psh.pStartPage = (LPCTSTR)&ood; //more structure misuse
+ psh.pszCaption = TranslateT("Miranda IM Options");
+ psh.ppsp = (PROPSHEETPAGE*)opi.odp; //blatent misuse of the structure, but what the hell
+
+ hwndOptions = CreateDialogParam(hMirandaInst,
+ MAKEINTRESOURCE(bSinglePage ? IDD_OPTIONSPAGE : IDD_OPTIONS),
+ NULL, OptionsDlgProc, (LPARAM)&psh);
+
+ FreeOptionsData( &opi );
+} } }
+
+static INT_PTR OpenOptions(WPARAM, LPARAM lParam)
+{
+ OPENOPTIONSDIALOG *ood=(OPENOPTIONSDIALOG*)lParam;
+ if ( ood == NULL )
+ return 1;
+
+ if ( ood->cbSize == OPENOPTIONSDIALOG_OLD_SIZE )
+ OpenOptionsNow( ood->pszGroup, ood->pszPage, NULL );
+ else if (ood->cbSize == sizeof(OPENOPTIONSDIALOG))
+ OpenOptionsNow( ood->pszGroup, ood->pszPage, ood->pszTab );
+ else
+ return 1;
+
+ return 0;
+}
+
+static INT_PTR OpenOptionsPage(WPARAM, LPARAM lParam)
+{
+ OPENOPTIONSDIALOG *ood=(OPENOPTIONSDIALOG*)lParam;
+ if ( ood == NULL )
+ return 1;
+
+ if ( ood->cbSize == OPENOPTIONSDIALOG_OLD_SIZE )
+ OpenOptionsNow( ood->pszGroup, ood->pszPage, NULL, true );
+ else if (ood->cbSize == sizeof(OPENOPTIONSDIALOG))
+ OpenOptionsNow( ood->pszGroup, ood->pszPage, ood->pszTab, true );
+ else
+ return 1;
+
+ return (INT_PTR)hwndOptions;
+}
+
+static INT_PTR OpenOptionsDialog(WPARAM, LPARAM)
+{
+ if (hwndOptions || GetAsyncKeyState(VK_CONTROL) || !ServiceExists(MS_MODERNOPT_SHOW))
+ OpenOptionsNow(NULL,NULL,NULL);
+ else
+ CallService(MS_MODERNOPT_SHOW, 0, 0);
+ return 0;
+}
+
+static INT_PTR AddOptionsPage(WPARAM wParam,LPARAM lParam)
+{ OPTIONSDIALOGPAGE *odp=(OPTIONSDIALOGPAGE*)lParam, *dst;
+ struct OptionsPageInit *opi=(struct OptionsPageInit*)wParam;
+
+ if(odp==NULL||opi==NULL) return 1;
+ if(odp->cbSize!=sizeof(OPTIONSDIALOGPAGE)
+ && odp->cbSize != OPTIONPAGE_OLD_SIZE
+ && odp->cbSize != OPTIONPAGE_OLD_SIZE2
+ && odp->cbSize != OPTIONPAGE_OLD_SIZE3)
+ return 1;
+
+ opi->odp=(OPTIONSDIALOGPAGE*)mir_realloc(opi->odp,sizeof(OPTIONSDIALOGPAGE)*(opi->pageCount+1));
+ dst = opi->odp + opi->pageCount;
+ memset( dst, 0, sizeof( OPTIONSDIALOGPAGE ));
+ memcpy( dst, odp, odp->cbSize );
+
+ if ( odp->ptszTitle != NULL ) {
+ if ( odp->flags & ODPF_DONTTRANSLATE ) {
+ #if defined( _UNICODE )
+ if ( odp->flags & ODPF_UNICODE )
+ dst->ptszTitle = mir_wstrdup( odp->ptszTitle );
+ else {
+ dst->ptszTitle = mir_a2u( odp->pszTitle );
+ dst->flags |= ODPF_UNICODE;
+ }
+ #else
+ dst->pszTitle = mir_strdup( odp->pszTitle );
+ #endif
+ }
+ else {
+ #if defined( _UNICODE )
+ if ( odp->flags & ODPF_UNICODE )
+ dst->ptszTitle = mir_wstrdup( TranslateW( odp->ptszTitle ));
+ else {
+ dst->ptszTitle = LangPackPcharToTchar( odp->pszTitle );
+ dst->flags |= ODPF_UNICODE;
+ }
+ #else
+ dst->pszTitle = mir_strdup( Translate( odp->pszTitle ));
+ #endif
+ }
+ }
+
+ if ( odp->ptszGroup != NULL ) {
+ #if defined( _UNICODE )
+ if ( odp->flags & ODPF_UNICODE )
+ dst->ptszGroup = mir_wstrdup( TranslateW( odp->ptszGroup ));
+ else {
+ dst->ptszGroup = LangPackPcharToTchar( odp->pszGroup );
+ dst->flags |= ODPF_UNICODE;
+ }
+ #else
+ dst->pszGroup = mir_strdup( Translate( odp->pszGroup ));
+ #endif
+ }
+
+ if ( odp->cbSize > OPTIONPAGE_OLD_SIZE2 && odp->ptszTab != NULL ) {
+ #if defined( _UNICODE )
+ if ( odp->flags & ODPF_UNICODE )
+ dst->ptszTab = mir_wstrdup( TranslateW( odp->ptszTab ));
+ else {
+ dst->ptszTab = LangPackPcharToTchar( odp->pszTab );
+ dst->flags |= ODPF_UNICODE;
+ }
+ #else
+ dst->pszTab = mir_strdup( Translate( odp->pszTab ));
+ #endif
+ }
+
+ if (( DWORD_PTR )odp->pszTemplate & 0xFFFF0000 )
+ dst->pszTemplate = mir_strdup( odp->pszTemplate );
+
+ opi->pageCount++;
+ return 0;
+}
+
+static int OptModulesLoaded(WPARAM, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_OPTIONS );
+ mi.position = 1900000000;
+ mi.pszName = LPGEN("&Options...");
+ mi.pszService = "Options/OptionsCommand";
+ CallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ return 0;
+}
+
+int ShutdownOptionsModule(WPARAM, LPARAM)
+{
+ if (IsWindow(hwndOptions)) DestroyWindow(hwndOptions);
+ hwndOptions=NULL;
+
+ //!!!!!!!!!! UnhookFilterEvents();
+
+ return 0;
+}
+
+int LoadOptionsModule(void)
+{
+ hwndOptions=NULL;
+ hOptionsInitEvent=CreateHookableEvent(ME_OPT_INITIALISE);
+ CreateServiceFunction(MS_OPT_ADDPAGE,AddOptionsPage);
+ CreateServiceFunction(MS_OPT_OPENOPTIONS,OpenOptions);
+ CreateServiceFunction(MS_OPT_OPENOPTIONSPAGE,OpenOptionsPage);
+ CreateServiceFunction("Options/OptionsCommand",OpenOptionsDialog);
+ HookEvent(ME_SYSTEM_MODULESLOADED,OptModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,ShutdownOptionsModule);
+
+ //!!!!!!!!!! HookFilterEvents();
+ return 0;
+}
diff --git a/src/modules/plugins/newplugins.cpp b/src/modules/plugins/newplugins.cpp
new file mode 100644
index 0000000000..80dddc00dd
--- /dev/null
+++ b/src/modules/plugins/newplugins.cpp
@@ -0,0 +1,1204 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+// block these plugins
+#define DEFMOD_REMOVED_UIPLUGINOPTS 21
+#define DEFMOD_REMOVED_PROTOCOLNETLIB 22
+
+// basic export prototypes
+typedef int (__cdecl * Miranda_Plugin_Load) ( PLUGINLINK * );
+typedef int (__cdecl * Miranda_Plugin_Unload) ( void );
+// version control
+typedef PLUGININFO * (__cdecl * Miranda_Plugin_Info) ( DWORD mirandaVersion );
+typedef PLUGININFOEX * (__cdecl * Miranda_Plugin_InfoEx) ( DWORD mirandaVersion );
+// prototype for databases
+typedef DATABASELINK * (__cdecl * Database_Plugin_Info) ( void * reserved );
+// prototype for clists
+typedef int (__cdecl * CList_Initialise) ( PLUGINLINK * );
+// Interface support
+typedef MUUID * (__cdecl * Miranda_Plugin_Interfaces) ( void );
+
+typedef struct { // can all be NULL
+ HINSTANCE hInst;
+ Miranda_Plugin_Load Load;
+ Miranda_Plugin_Unload Unload;
+ Miranda_Plugin_Info Info;
+ Miranda_Plugin_InfoEx InfoEx;
+ Miranda_Plugin_Interfaces Interfaces;
+ Database_Plugin_Info DbInfo;
+ CList_Initialise clistlink;
+ PLUGININFOEX * pluginInfo; // must be freed if hInst==NULL then its a copy
+ DATABASELINK * dblink; // only valid during module being in memory
+} BASIC_PLUGIN_INFO;
+
+#define PCLASS_FAILED 0x1 // not a valid plugin, or API is invalid, pluginname is valid
+#define PCLASS_BASICAPI 0x2 // has Load, Unload, MirandaPluginInfo() -> PLUGININFO seems valid, this dll is in memory.
+#define PCLASS_DB 0x4 // has DatabasePluginInfo() and is valid as can be, and PCLASS_BASICAPI has to be set too
+#define PCLASS_LAST 0x8 // this plugin should be unloaded after everything else
+#define PCLASS_OK 0x10 // plugin should be loaded, if DB means nothing
+#define PCLASS_LOADED 0x20 // Load() has been called, Unload() should be called.
+#define PCLASS_STOPPED 0x40 // wasn't loaded cos plugin name not on white list
+#define PCLASS_CLIST 0x80 // a CList implementation
+#define PCLASS_SERVICE 0x100 // has Service Mode implementation
+
+typedef struct pluginEntry {
+ TCHAR pluginname[64];
+ unsigned int pclass; // PCLASS_*
+ BASIC_PLUGIN_INFO bpi;
+ struct pluginEntry * nextclass;
+} pluginEntry;
+
+static int sttComparePlugins( const pluginEntry* p1, const pluginEntry* p2 )
+{ return ( int )( p1->bpi.hInst - p2->bpi.hInst );
+}
+
+static int sttComparePluginsByName( const pluginEntry* p1, const pluginEntry* p2 )
+{ return lstrcmp( p1->pluginname, p2->pluginname );
+}
+
+LIST<pluginEntry> pluginList( 10, sttComparePluginsByName ), pluginListAddr( 10, sttComparePlugins );
+
+/////////////////////////////////////////////////////////////////////////////////
+
+#define MAX_MIR_VER ULONG_MAX
+
+struct PluginUUIDList {
+ MUUID uuid;
+ DWORD maxVersion;
+}
+static const pluginBannedList[] =
+{
+ {{0x7f65393b, 0x7771, 0x4f3f, { 0xa9, 0xeb, 0x5d, 0xba, 0xf2, 0xb3, 0x61, 0xf1 }}, MAX_MIR_VER}, // png2dib
+ {{0xe00f1643, 0x263c, 0x4599, { 0xb8, 0x4b, 0x5, 0x3e, 0x5c, 0x51, 0x1d, 0x28 }}, MAX_MIR_VER}, // loadavatars (unicode)
+ {{0xc9e01eb0, 0xa119, 0x42d2, { 0xb3, 0x40, 0xe8, 0x67, 0x8f, 0x5f, 0xea, 0xd9 }}, MAX_MIR_VER}, // loadavatars (ansi)
+ {{0xb4ef58c4, 0x4458, 0x4e47, { 0xa7, 0x67, 0x5c, 0xae, 0xe5, 0xe7, 0xc, 0x81 }}, MAX_MIR_VER}, // 0.7.x AIM Protocol
+ {{0xb529402b, 0x53ba, 0x4c81, { 0x9e, 0x27, 0xd4, 0x31, 0xeb, 0xe8, 0xec, 0x36 }}, MAX_MIR_VER}, // 0.7.x IRC Protocol
+ {{0x847bb03c, 0x408c, 0x4f9b, { 0xaa, 0x5a, 0xf5, 0xc0, 0xb7, 0xb5, 0x60, 0x1e }}, MAX_MIR_VER}, // 0.7.x ICQ Protocol
+ {{0x1ee5af12, 0x26b0, 0x4290, { 0x8f, 0x97, 0x16, 0x77, 0xcb, 0xe, 0xfd, 0x2b }}, MAX_MIR_VER}, // 0.7.x Jabber Protocol (Unicode)
+ {{0xf7f5861d, 0x988d, 0x479d, { 0xa5, 0xbb, 0x80, 0xc7, 0xfa, 0x8a, 0xd0, 0xef }}, MAX_MIR_VER}, // 0.7.x Jabber Protocol (Ansi)
+ {{0xdc39da8a, 0x8385, 0x4cd9, { 0xb2, 0x98, 0x80, 0x67, 0x7b, 0x8f, 0xe6, 0xe4 }}, MAX_MIR_VER}, // 0.7.x MSN Protocol (Unicode)
+ {{0x29aa3a80, 0x3368, 0x4b78, { 0x82, 0xc1, 0xdf, 0xc7, 0x29, 0x6a, 0x58, 0x99 }}, MAX_MIR_VER}, // 0.7.x MSN Protocol (Ansi)
+ {{0xa6648b6c, 0x6fb8, 0x4551, { 0xb4, 0xe7, 0x1, 0x36, 0xf9, 0x16, 0xd4, 0x85 }}, MAX_MIR_VER}, // 0.7.x Yahoo Protocol
+ {{0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 }}, PLUGIN_MAKE_VERSION(3, 0, 4, 0)}, // 0.8.x TabSRMM (Unicode)
+ {{0x5889a3ef, 0x7c95, 0x4249, { 0x98, 0xbb, 0x34, 0xe9, 0x5, 0x3a, 0x6e, 0xa0 }}, PLUGIN_MAKE_VERSION(3, 0, 4, 0)}, // 0.8.x TabSRMM (ANSI)
+ {{0x84636f78, 0x2057, 0x4302, { 0x8a, 0x65, 0x23, 0xa1, 0x6d, 0x46, 0x84, 0x4c }}, PLUGIN_MAKE_VERSION(2, 9, 0, 4)}, // 0.8.x Scriver (Unicode)
+ {{0x1e91b6c9, 0xe040, 0x4a6f, { 0xab, 0x56, 0xdf, 0x76, 0x98, 0xfa, 0xcb, 0xf1 }}, PLUGIN_MAKE_VERSION(2, 9, 0, 4)}, // 0.8.x Scriver (ANSI)
+ {{0x240a91dc, 0x9464, 0x457a, { 0x97, 0x87, 0xff, 0x1e, 0xa8, 0x8e, 0x77, 0xe3 }}, PLUGIN_MAKE_VERSION(0, 9, 0, 0)}, // 0.8.x CList Classic (Unicode)
+ {{0x552cf71a, 0x249f, 0x4650, { 0xbb, 0x2b, 0x7c, 0xdb, 0x1f, 0xe7, 0xd1, 0x78 }}, PLUGIN_MAKE_VERSION(0, 9, 0, 0)}, // 0.8.x CList Classic (ANSI)
+ {{0x8f79b4ee, 0xeb48, 0x4a03, { 0x87, 0x3e, 0x27, 0xbe, 0x6b, 0x7e, 0x9a, 0x25 }}, PLUGIN_MAKE_VERSION(0, 9, 1, 0)}, // 0.8.x Clist Nicer (Unicode)
+ {{0x5a070cec, 0xb2ab, 0x4bbe, { 0x8e, 0x48, 0x9c, 0x8d, 0xcd, 0xda, 0x14, 0xc3 }}, PLUGIN_MAKE_VERSION(0, 9, 1, 0)}, // 0.8.x Clist Nicer (ANSI)
+ {{0x43909b6, 0xaad8, 0x4d82, { 0x8e, 0xb5, 0x9f, 0x64, 0xcf, 0xe8, 0x67, 0xcd }}, PLUGIN_MAKE_VERSION(0, 9, 0, 8)}, // 0.8.x Clist Modern (Unicode)
+ {{0xf6588c56, 0x15dc, 0x4cd7, { 0x8c, 0xf9, 0x48, 0xab, 0x6c, 0x5f, 0xd2, 0xf }}, PLUGIN_MAKE_VERSION(0, 9, 0, 8)}, // 0.8.x Clist Modern (ANSI)
+ {{0x2a417ab9, 0x16f2, 0x472d, { 0x9a, 0xe3, 0x41, 0x51, 0x3, 0xc7, 0x8a, 0x64 }}, PLUGIN_MAKE_VERSION(0, 9, 0, 0)}, // 0.8.x Clist MW (Unicode)
+ {{0x7ab05d31, 0x9972, 0x4406, { 0x82, 0x3e, 0xe, 0xd7, 0x45, 0xef, 0x7c, 0x56 }}, PLUGIN_MAKE_VERSION(0, 9, 0, 0)} // 0.8.x Clist MW (ANSI)
+};
+const int pluginBannedListCount = SIZEOF(pluginBannedList);
+
+static BOOL bModuleInitialized = FALSE;
+
+PLUGINLINK pluginCoreLink;
+TCHAR mirandabootini[MAX_PATH];
+static DWORD mirandaVersion;
+static int serviceModeIdx = -1;
+static pluginEntry * pluginListSM;
+static pluginEntry * pluginListDb;
+static pluginEntry * pluginListUI;
+static pluginEntry * pluginList_freeimg;
+static pluginEntry * pluginList_crshdmp;
+static HANDLE hPluginListHeap = NULL;
+static pluginEntry * pluginDefModList[DEFMOD_HIGHEST+1]; // do not free this memory
+static int askAboutIgnoredPlugins;
+
+int InitIni(void);
+void UninitIni(void);
+
+#define PLUGINDISABLELIST "PluginDisable"
+
+int CallHookSubscribers( HANDLE hEvent, WPARAM wParam, LPARAM lParam );
+
+int LoadDatabaseModule(void);
+
+char * GetPluginNameByInstance( HINSTANCE hInstance )
+{
+ int i = 0;
+ if ( pluginList.getCount() == 0) return NULL;
+ for (i = 0; i < pluginList.getCount(); i++)
+ {
+ pluginEntry* pe = pluginList[i];
+ if (pe->bpi.pluginInfo && pe->bpi.hInst == hInstance)
+ return pe->bpi.pluginInfo->shortName;
+ }
+ return NULL;
+}
+
+HINSTANCE GetInstByAddress( void* codePtr )
+{
+ int idx;
+ HINSTANCE result;
+ pluginEntry p; p.bpi.hInst = ( HINSTANCE )codePtr;
+
+ if ( pluginListAddr.getCount() == 0 )
+ return NULL;
+
+ List_GetIndex(( SortedList* )&pluginListAddr, &p, &idx );
+ if ( idx > 0 )
+ idx--;
+
+ result = pluginListAddr[idx]->bpi.hInst;
+
+ if (result < hMirandaInst && codePtr > hMirandaInst)
+ result = hMirandaInst;
+ else if ( idx == 0 && codePtr < ( void* )result )
+ result = NULL;
+
+ return result;
+}
+
+static int uuidToString(const MUUID uuid, char *szStr, int cbLen)
+{
+ if (cbLen<1||!szStr) return 0;
+ mir_snprintf(szStr, cbLen, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ uuid.a, uuid.b, uuid.c, uuid.d[0], uuid.d[1], uuid.d[2], uuid.d[3], uuid.d[4], uuid.d[5], uuid.d[6], uuid.d[7]);
+ return 1;
+}
+
+static int equalUUID(MUUID u1, MUUID u2)
+{
+ return memcmp(&u1, &u2, sizeof(MUUID))?0:1;
+}
+
+static MUUID miid_last = MIID_LAST;
+static MUUID miid_servicemode = MIID_SERVICEMODE;
+
+static int validInterfaceList(Miranda_Plugin_Interfaces ifaceProc)
+{
+ MUUID *piface = ( ifaceProc ) ? ifaceProc() : NULL;
+ int i = 0/*, j*/;
+
+ if (!piface)
+ return 0;
+ if (equalUUID(miid_last, piface[0]))
+ return 0;
+ /*while (!equalUUID(miid_last, piface[i]) ) {
+ for (j=0; j<interfaceBannedListCount; j++) {
+ if (equalUUID(interfaceBannedList[j].uuid, piface[i]))
+ return 0;
+ i++;
+ }
+ break;
+ }*/
+ return 1;
+}
+
+static int isPluginBanned(MUUID u1, DWORD dwVersion) {
+ int i;
+
+ for (i=0; i<pluginBannedListCount; i++) {
+ if (equalUUID(pluginBannedList[i].uuid, u1)) {
+ if (dwVersion<pluginBannedList[i].maxVersion)
+ return 1;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+// returns true if the API exports were good, otherwise, passed in data is returned
+#define CHECKAPI_NONE 0
+#define CHECKAPI_DB 1
+#define CHECKAPI_CLIST 2
+
+/*
+ * historyeditor added by nightwish - plugin is problematic and can ruin database as it does not understand UTF-8 message
+ * storage
+ */
+
+static const TCHAR* modulesToSkip[] =
+{
+ _T("autoloadavatars.dll"), _T("multiwindow.dll"), _T("fontservice.dll"),
+ _T("icolib.dll"), _T("historyeditor.dll")
+};
+
+// The following plugins will be checked for a valid MUUID or they will not be loaded
+static const TCHAR* expiredModulesToSkip[] =
+{
+ _T("scriver.dll"), _T("nconvers.dll"), _T("tabsrmm.dll"), _T("nhistory.dll"),
+ _T("historypp.dll"), _T("help.dll"), _T("loadavatars.dll"), _T("tabsrmm_unicode.dll"),
+ _T("clist_nicer_plus.dll"), _T("changeinfo.dll"), _T("png2dib.dll"), _T("dbx_mmap.dll"),
+ _T("dbx_3x.dll"), _T("sramm.dll"), _T("srmm_mod.dll"), _T("srmm_mod (no Unicode).dll"),
+ _T("singlemodeSRMM.dll"), _T("msg_export.dll"), _T("clist_modern.dll"),
+ _T("clist_nicer.dll")
+};
+
+static int checkPI( BASIC_PLUGIN_INFO* bpi, PLUGININFOEX* pi )
+{
+ int bHasValidInfo = FALSE;
+
+ if ( pi == NULL )
+ return FALSE;
+
+ if ( bpi->InfoEx ) {
+ if ( pi->cbSize == sizeof(PLUGININFOEX))
+ if ( !validInterfaceList(bpi->Interfaces) || isPluginBanned( pi->uuid, pi->version ))
+ return FALSE;
+
+ bHasValidInfo = TRUE;
+ }
+
+ if ( !bHasValidInfo )
+ if ( bpi->Info && pi->cbSize != sizeof(PLUGININFO))
+ return FALSE;
+
+ if ( pi->shortName == NULL || pi->description == NULL || pi->author == NULL ||
+ pi->authorEmail == NULL || pi->copyright == NULL || pi->homepage == NULL )
+ return FALSE;
+
+ if ( pi->replacesDefaultModule > DEFMOD_HIGHEST ||
+ pi->replacesDefaultModule == DEFMOD_REMOVED_UIPLUGINOPTS ||
+ pi->replacesDefaultModule == DEFMOD_REMOVED_PROTOCOLNETLIB )
+ return FALSE;
+
+ return TRUE;
+}
+
+static int checkAPI(TCHAR* plugin, BASIC_PLUGIN_INFO* bpi, DWORD mirandaVersion, int checkTypeAPI, int* exports)
+{
+ HINSTANCE h = NULL;
+ // this is evil but these plugins are buggy/old and people are blaming Miranda
+ // fontservice plugin is built into the core now
+ {
+ TCHAR * p = _tcsrchr(plugin, '\\');
+ if ( p != NULL && ++p ) {
+ int i;
+ for ( i = 0; i < SIZEOF(modulesToSkip); i++ )
+ if ( lstrcmpi( p, modulesToSkip[i] ) == 0 )
+ return 0;
+ } }
+
+ h = LoadLibrary(plugin);
+ if ( h == NULL ) return 0;
+ // loaded, check for exports
+ bpi->Load = (Miranda_Plugin_Load) GetProcAddress(h, "Load");
+ bpi->Unload = (Miranda_Plugin_Unload) GetProcAddress(h, "Unload");
+ bpi->Info = (Miranda_Plugin_Info) GetProcAddress(h, "MirandaPluginInfo");
+ bpi->InfoEx = (Miranda_Plugin_InfoEx) GetProcAddress(h, "MirandaPluginInfoEx");
+ bpi->Interfaces = (Miranda_Plugin_Interfaces) GetProcAddress(h, "MirandaPluginInterfaces");
+
+ // if they were present
+ if ( bpi->Load && bpi->Unload && ( bpi->Info || ( bpi->InfoEx && bpi->Interfaces ))) {
+ PLUGININFOEX* pi = 0;
+ if (bpi->InfoEx)
+ pi = bpi->InfoEx(mirandaVersion);
+ else
+ pi = (PLUGININFOEX*)bpi->Info(mirandaVersion);
+ {
+ // similar to the above hack but these plugins are checked for a valid interface first (in case there are updates to the plugin later)
+ TCHAR* p = _tcsrchr(plugin, '\\');
+ if ( pi != NULL && p != NULL && ++p ) {
+ if ( !bpi->InfoEx || pi->cbSize != sizeof(PLUGININFOEX)) {
+ int i;
+ for ( i = 0; i < SIZEOF(expiredModulesToSkip); i++ ) {
+ if ( lstrcmpi( p, expiredModulesToSkip[i] ) == 0 ) {
+ FreeLibrary(h);
+ return 0;
+ } } } } }
+
+ if ( checkPI( bpi, pi )) {
+ bpi->pluginInfo = pi;
+ // basic API is present
+ if ( checkTypeAPI == CHECKAPI_NONE ) {
+ bpi->hInst=h;
+ return 1;
+ }
+ // check for DB?
+ if ( checkTypeAPI == CHECKAPI_DB ) {
+ bpi->DbInfo = (Database_Plugin_Info) GetProcAddress(h, "DatabasePluginInfo");
+ if ( bpi->DbInfo ) {
+ // fetch internal database function pointers
+ bpi->dblink = bpi->DbInfo(NULL);
+ // validate returned link structure
+ if ( bpi->dblink && bpi->dblink->cbSize==sizeof(DATABASELINK) ) {
+ bpi->hInst=h;
+ return 1;
+ }
+ // had DB exports
+ if ( exports != NULL ) *exports=1;
+ } //if
+ } //if
+
+ // check clist ?
+ if ( checkTypeAPI == CHECKAPI_CLIST ) {
+ bpi->clistlink = (CList_Initialise) GetProcAddress(h, "CListInitialise");
+ #if defined( _UNICODE )
+ if ( pi->flags & UNICODE_AWARE )
+ #endif
+ if ( bpi->clistlink ) {
+ // nothing more can be done here, this export is a load function
+ bpi->hInst=h;
+ if ( exports != NULL ) *exports=1;
+ return 1;
+ }
+ }
+
+ } // if
+ if ( exports != NULL ) *exports=1;
+ } //if
+ // not found, unload
+ FreeLibrary(h);
+ return 0;
+}
+
+// returns true if the given file is <anything>.dll exactly
+static int valid_library_name(TCHAR *name)
+{
+ TCHAR * dot = _tcsrchr(name, '.');
+ if ( dot != NULL && lstrcmpi(dot + 1, _T("dll")) == 0)
+ if (dot[4] == 0)
+ return 1;
+
+ return 0;
+}
+
+// returns true if the given file matches dbx_*.dll, which is used to LoadLibrary()
+static int validguess_db_name(TCHAR * name)
+{
+ int rc = 0;
+ // this is ONLY SAFE because name -> ffd.cFileName == MAX_PATH
+ TCHAR x = name[4];
+ name[4]=0;
+ rc = lstrcmpi(name, _T("dbx_")) == 0 || lstrcmpi(name, _T("dbrw")) == 0;
+ name[4] = x;
+ return rc;
+}
+
+// returns true if the given file matches clist_*.dll
+static int validguess_clist_name(TCHAR * name)
+{
+ int rc=0;
+ // argh evil
+ TCHAR x = name[6];
+ name[6] = 0;
+ rc = lstrcmpi(name, _T("clist_")) == 0;
+ name[6] = x;
+ return rc;
+}
+
+// returns true if the given file matches svc_*.dll
+static int validguess_servicemode_name(TCHAR * name)
+{
+ int rc = 0;
+ // argh evil
+ TCHAR x = name[4];
+ name[4]=0;
+ rc = lstrcmpi(name, _T("svc_")) == 0;
+ name[4] = x;
+ return rc;
+}
+
+// perform any API related tasks to freeing
+static void Plugin_Uninit(pluginEntry * p)
+{
+ // if it was an installed database plugin, call its unload
+ if ( p->pclass & PCLASS_DB )
+ p->bpi.dblink->Unload( p->pclass & PCLASS_OK );
+
+ // if the basic API check had passed, call Unload if Load() was ever called
+ if ( p->pclass & PCLASS_LOADED )
+ p->bpi.Unload();
+
+ // release the library
+ if ( p->bpi.hInst != NULL ) {
+ // we need to kill all resources which belong to that DLL before calling FreeLibrary
+ KillModuleEventHooks( p->bpi.hInst );
+ KillModuleServices( p->bpi.hInst );
+
+ FreeLibrary( p->bpi.hInst );
+ ZeroMemory( &p->bpi, sizeof( p->bpi ));
+ }
+ pluginList.remove( p );
+ pluginListAddr.remove( p );
+}
+
+typedef BOOL (*SCAN_PLUGINS_CALLBACK) ( WIN32_FIND_DATA * fd, TCHAR * path, WPARAM wParam, LPARAM lParam );
+
+static void enumPlugins(SCAN_PLUGINS_CALLBACK cb, WPARAM wParam, LPARAM lParam)
+{
+ TCHAR exe[MAX_PATH];
+ TCHAR search[MAX_PATH];
+ TCHAR * p = 0;
+ // get miranda's exe path
+ GetModuleFileName(NULL, exe, SIZEOF(exe));
+ // find the last \ and null it out, this leaves no trailing slash
+ p = _tcsrchr(exe, '\\'); if (p) *p = 0;
+ // create the search filter
+ mir_sntprintf(search, SIZEOF(search), _T("%s\\Plugins\\*.dll"), exe);
+ {
+ // FFFN will return filenames for things like dot dll+ or dot dllx
+ HANDLE hFind=INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA ffd;
+ hFind = FindFirstFile(search, &ffd);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do {
+ if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && valid_library_name(ffd.cFileName))
+ {
+ cb(&ffd, exe, wParam, lParam);
+ } //if
+ } while (FindNextFile(hFind, &ffd));
+ FindClose(hFind);
+ } //if
+ }
+}
+
+// this is called by the db module to return all DBs plugins, then when it finds the one it likes the others are unloaded
+static INT_PTR PluginsEnum(WPARAM, LPARAM lParam)
+{
+ PLUGIN_DB_ENUM * de = (PLUGIN_DB_ENUM *) lParam;
+ pluginEntry * x = pluginListDb;
+ if ( de == NULL || de->cbSize != sizeof(PLUGIN_DB_ENUM) || de->pfnEnumCallback == NULL ) return 1;
+ while ( x != NULL )
+ {
+ int rc = de->pfnEnumCallback(StrConvA(x->pluginname), x->bpi.dblink, de->lParam);
+ if (rc == DBPE_DONE)
+ {
+ // this db has been picked, get rid of all the others
+ pluginEntry * y = pluginListDb, * n;
+ while ( y != NULL )
+ {
+ n = y->nextclass;
+ if ( x != y )
+ Plugin_Uninit(y);
+ y = n;
+ } // while
+ x->pclass |= PCLASS_LOADED | PCLASS_OK | PCLASS_LAST;
+ return 0;
+ }
+ else if ( rc == DBPE_HALT ) return 1;
+ x = x->nextclass;
+ } // while
+ return pluginListDb != NULL ? 1 : -1;
+}
+
+static INT_PTR PluginsGetDefaultArray(WPARAM, LPARAM)
+{
+ return (INT_PTR)&pluginDefModList;
+}
+
+// called in the first pass to create pluginEntry* structures and validate database plugins
+static BOOL scanPluginsDir (WIN32_FIND_DATA * fd, TCHAR * path, WPARAM, LPARAM)
+{
+ int isdb = validguess_db_name(fd->cFileName);
+ BASIC_PLUGIN_INFO bpi;
+ pluginEntry* p = (pluginEntry*)HeapAlloc(hPluginListHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, sizeof(pluginEntry));
+ _tcsncpy(p->pluginname, fd->cFileName, SIZEOF(p->pluginname));
+ // plugin name suggests its a db module, load it right now
+ if ( isdb )
+ {
+ TCHAR buf[MAX_PATH];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\\Plugins\\%s"), path, fd->cFileName);
+ if (checkAPI(buf, &bpi, mirandaVersion, CHECKAPI_DB, NULL))
+ {
+ // db plugin is valid
+ p->pclass |= (PCLASS_DB | PCLASS_BASICAPI);
+ // copy the dblink stuff
+ p->bpi=bpi;
+ // keep a faster list.
+ if ( pluginListDb != NULL ) p->nextclass = pluginListDb;
+ pluginListDb=p;
+ }
+ else
+ // didn't have basic APIs or DB exports - failed.
+ p->pclass |= PCLASS_FAILED;
+ }
+ else if (validguess_clist_name(fd->cFileName))
+ {
+ // keep a note of this plugin for later
+ if ( pluginListUI != NULL ) p->nextclass=pluginListUI;
+ pluginListUI=p;
+ p->pclass |= PCLASS_CLIST;
+ }
+ else if (validguess_servicemode_name(fd->cFileName))
+ {
+ TCHAR buf[MAX_PATH];
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\\Plugins\\%s"), path, fd->cFileName);
+ if (checkAPI(buf, &bpi, mirandaVersion, CHECKAPI_NONE, NULL))
+ {
+ p->pclass |= (PCLASS_OK | PCLASS_BASICAPI);
+ p->bpi = bpi;
+ if (bpi.Interfaces)
+ {
+ int i = 0;
+ MUUID *piface = bpi.Interfaces();
+ while (!equalUUID(miid_last, piface[i]))
+ {
+ if (!equalUUID(miid_servicemode, piface[i++]))
+ continue;
+ p->pclass |= (PCLASS_SERVICE);
+ if ( pluginListSM != NULL ) p->nextclass = pluginListSM;
+ pluginListSM=p;
+ if (pluginList_crshdmp == NULL && lstrcmpi(fd->cFileName, _T("svc_crshdmp.dll")) == 0)
+ {
+ pluginList_crshdmp = p;
+ p->pclass |= PCLASS_LAST;
+ }
+ break;
+ }
+ }
+ }
+ else
+ // didn't have basic APIs or DB exports - failed.
+ p->pclass |= PCLASS_FAILED;
+ }
+ else if (pluginList_freeimg == NULL && lstrcmpi(fd->cFileName, _T("advaimg.dll")) == 0)
+ pluginList_freeimg = p;
+
+ // add it to the list
+ pluginList.insert( p );
+ return TRUE;
+}
+
+static void SetPluginOnWhiteList(TCHAR * pluginname, int allow)
+{
+ DBWriteContactSettingByte(NULL, PLUGINDISABLELIST, StrConvA(pluginname), allow == 0);
+}
+
+// returns 1 if the plugin should be enabled within this profile, filename is always lower case
+static int isPluginOnWhiteList(TCHAR * pluginname)
+{
+ char* pluginnameA = _strlwr(mir_t2a(pluginname));
+ int rc = DBGetContactSettingByte(NULL, PLUGINDISABLELIST, pluginnameA, 0);
+ mir_free(pluginnameA);
+ if ( rc != 0 && askAboutIgnoredPlugins )
+ {
+ TCHAR buf[256];
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("'%s' is disabled, re-enable?"), pluginname);
+ if (MessageBox(NULL, buf, TranslateT("Re-enable Miranda plugin?"), MB_YESNO | MB_ICONQUESTION) == IDYES)
+ {
+ SetPluginOnWhiteList(pluginname, 1);
+ rc = 0;
+ }
+ }
+
+ return rc == 0;
+}
+
+static pluginEntry* getCListModule(TCHAR * exe, TCHAR * slice, int useWhiteList)
+{
+ pluginEntry * p = pluginListUI;
+ BASIC_PLUGIN_INFO bpi;
+ while ( p != NULL )
+ {
+ mir_sntprintf(slice, &exe[MAX_PATH] - slice, _T("\\Plugins\\%s"), p->pluginname);
+ CharLower(p->pluginname);
+ if ( useWhiteList ? isPluginOnWhiteList(p->pluginname) : 1 ) {
+ if ( checkAPI(exe, &bpi, mirandaVersion, CHECKAPI_CLIST, NULL) ) {
+ p->bpi = bpi;
+ p->pclass |= PCLASS_LAST | PCLASS_OK | PCLASS_BASICAPI;
+ pluginListAddr.insert( p );
+ if ( bpi.clistlink(&pluginCoreLink) == 0 ) {
+ p->bpi = bpi;
+ p->pclass |= PCLASS_LOADED;
+ return p;
+ }
+ else Plugin_Uninit( p );
+ } //if
+ } //if
+ p = p->nextclass;
+ }
+ return NULL;
+}
+
+int UnloadPlugin(TCHAR* buf, int bufLen)
+{
+ int i;
+ for ( i = pluginList.getCount()-1; i >= 0; i-- )
+ {
+ pluginEntry* p = pluginList[i];
+ if (!_tcsicmp( p->pluginname, buf))
+ {
+ GetModuleFileName( p->bpi.hInst, buf, bufLen);
+ Plugin_Uninit( p );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Service plugins functions
+
+char **GetSeviceModePluginsList(void)
+{
+ int i = 0;
+ char **list = NULL;
+ pluginEntry * p = pluginListSM;
+ while ( p != NULL ) {
+ i++;
+ p = p->nextclass;
+ }
+ if ( i ) {
+ list = (char**)mir_calloc( (i + 1) * sizeof(char*) );
+ p = pluginListSM;
+ i = 0;
+ while ( p != NULL ) {
+ list[i++] = p->bpi.pluginInfo->shortName;
+ p = p->nextclass;
+ }
+ }
+ return list;
+}
+
+void SetServiceModePlugin( int idx )
+{
+ serviceModeIdx = idx;
+}
+
+int LoadServiceModePlugin(void)
+{
+ int i = 0;
+ pluginEntry * p = pluginListSM;
+
+ if ( serviceModeIdx < 0 )
+ return 0;
+
+ while ( p != NULL ) {
+ if ( serviceModeIdx == i++ ) {
+ if ( p->bpi.Load(&pluginCoreLink) == 0 ) {
+ p->pclass |= PCLASS_LOADED;
+ if ( CallService( MS_SERVICEMODE_LAUNCH, 0, 0 ) != CALLSERVICE_NOTFOUND )
+ return 1;
+ else {
+ MessageBox(NULL, TranslateT("Unable to load plugin in Service Mode!"), p->pluginname, 0);
+ return -1;
+ }
+ }
+ Plugin_Uninit( p );
+ return -1;
+ }
+ p = p->nextclass;
+ }
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Event hook to unload all non-core plugins
+// hooked very late, after all the internal plugins, blah
+
+void UnloadNewPlugins(void)
+{
+ int i;
+
+ // unload everything but the special db/clist plugins
+ for ( i = pluginList.getCount()-1; i >= 0; i-- ) {
+ pluginEntry* p = pluginList[i];
+ if ( !(p->pclass & PCLASS_LAST) && (p->pclass & PCLASS_OK))
+ Plugin_Uninit( p );
+} }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Plugins options page dialog
+
+typedef struct
+{
+ int flags;
+ char* author;
+ char* authorEmail;
+ char* description;
+ char* copyright;
+ char* homepage;
+ MUUID uuid;
+}
+ PluginListItemData;
+
+static BOOL dialogListPlugins(WIN32_FIND_DATA * fd, TCHAR * path, WPARAM, LPARAM lParam)
+{
+ LVITEM it;
+ int iRow;
+ HWND hwndList=(HWND)lParam;
+ BASIC_PLUGIN_INFO pi;
+ int exports=0;
+ TCHAR buf[MAX_PATH];
+ int isdb = 0;
+ HINSTANCE gModule;
+ PluginListItemData* dat;
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s\\Plugins\\%s"), path, fd->cFileName);
+ CharLower(fd->cFileName);
+ gModule = GetModuleHandle(buf);
+ if ( checkAPI(buf, &pi, mirandaVersion, CHECKAPI_NONE, &exports) == 0 ) {
+ // failed to load anything, but if exports were good, show some info.
+ return TRUE;
+ }
+ isdb = pi.pluginInfo->replacesDefaultModule == DEFMOD_DB;
+ ZeroMemory(&it, sizeof(it));
+ it.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
+ it.pszText = fd->cFileName;
+ it.iImage = ( pi.pluginInfo->flags & 1 ) ? 0 : 1;
+ it.lParam = (LPARAM)( dat = (PluginListItemData*)mir_alloc( sizeof( PluginListItemData )));
+ iRow=SendMessage( hwndList, LVM_INSERTITEM, 0, (LPARAM)&it );
+ if ( isPluginOnWhiteList(fd->cFileName) )
+ ListView_SetItemState(hwndList, iRow, !isdb ? 0x2000 : 0x3000, LVIS_STATEIMAGEMASK);
+ if ( iRow != -1 ) {
+ dat->flags = pi.pluginInfo->replacesDefaultModule;
+ dat->author = mir_strdup( pi.pluginInfo->author );
+ dat->authorEmail = mir_strdup( pi.pluginInfo->authorEmail );
+ dat->copyright = mir_strdup( pi.pluginInfo->copyright );
+ dat->description = mir_strdup( pi.pluginInfo->description );
+ dat->homepage = mir_strdup( pi.pluginInfo->homepage );
+ if ( pi.pluginInfo->cbSize == sizeof( PLUGININFOEX ))
+ dat->uuid = pi.pluginInfo->uuid;
+ else
+ memset( &dat->uuid, 0, sizeof(dat->uuid));
+
+ TCHAR *shortNameT = mir_a2t(pi.pluginInfo->shortName);
+ ListView_SetItemText(hwndList, iRow, 1, shortNameT);
+ mir_free(shortNameT);
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%d.%d.%d.%d"), HIBYTE(HIWORD(pi.pluginInfo->version)),
+ LOBYTE(HIWORD(pi.pluginInfo->version)), HIBYTE(LOWORD(pi.pluginInfo->version)),
+ LOBYTE(LOWORD(pi.pluginInfo->version)));
+ ListView_SetItemText(hwndList, iRow, 2, buf);
+
+ it.mask = LVIF_IMAGE;
+ it.iItem = iRow;
+ it.iSubItem = 3;
+ it.iImage = ( gModule != NULL ) ? 2 : 3;
+ ListView_SetItem( hwndList, &it );
+ }
+ else mir_free( dat );
+ FreeLibrary(pi.hInst);
+ return TRUE;
+}
+
+static void RemoveAllItems( HWND hwnd )
+{
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = 0;
+ while ( ListView_GetItem( hwnd, &lvi )) {
+ PluginListItemData* dat = ( PluginListItemData* )lvi.lParam;
+ mir_free( dat->author );
+ mir_free( dat->authorEmail );
+ mir_free( dat->copyright );
+ mir_free( dat->description );
+ mir_free( dat->homepage );
+ mir_free( dat );
+ lvi.iItem ++;
+} }
+
+INT_PTR CALLBACK DlgPluginOpt(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ HWND hwndList=GetDlgItem(hwndDlg,IDC_PLUGLIST);
+ LVCOLUMN col;
+ HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | (IsWinVerXPPlus()? ILC_COLOR32 : ILC_COLOR16), 4, 0);
+ ImageList_AddIcon_IconLibLoaded( hIml, SKINICON_OTHER_UNICODE );
+ ImageList_AddIcon_IconLibLoaded( hIml, SKINICON_OTHER_ANSI );
+ ImageList_AddIcon_IconLibLoaded( hIml, SKINICON_OTHER_LOADED );
+ ImageList_AddIcon_IconLibLoaded( hIml, SKINICON_OTHER_NOTLOADED );
+ ListView_SetImageList( hwndList, hIml, LVSIL_SMALL );
+
+ TranslateDialogDefault(hwndDlg);
+
+ col.mask = LVCF_TEXT | LVCF_WIDTH;
+ col.pszText = TranslateT("Plugin");
+ col.cx = 70;//max = 140;
+ ListView_InsertColumn(hwndList,0,&col);
+
+ col.pszText=TranslateT("Name");
+ col.cx = 70;//max = 220;
+ ListView_InsertColumn(hwndList,1,&col);
+
+ col.pszText=TranslateT("Version");
+ col.cx=55;
+ ListView_InsertColumn(hwndList,2,&col);
+
+ col.pszText=_T("");
+ col.cx=20;
+ ListView_InsertColumn(hwndList,3,&col);
+ //ListView_InsertColumn(hwndList,4,&col);
+
+ // XXX: Won't work on windows 95 without IE3+ or 4.70
+ ListView_SetExtendedListViewStyleEx( hwndList, 0, LVS_EX_SUBITEMIMAGES | LVS_EX_CHECKBOXES | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT );
+ // scan the plugin dir for plugins, cos
+ enumPlugins( dialogListPlugins, ( WPARAM )hwndDlg, ( LPARAM )hwndList );
+ // sort out the headers
+ {
+ int w, max;
+
+ ListView_SetColumnWidth( hwndList, 0, LVSCW_AUTOSIZE ); // dll name
+ w = ListView_GetColumnWidth( hwndList, 0 );
+ if (w>140) {
+ ListView_SetColumnWidth( hwndList, 0, 140 );
+ w = 140;
+ }
+ max = w<140? 220+140-w:220;
+ ListView_SetColumnWidth( hwndList, 1, LVSCW_AUTOSIZE ); // short name
+ w = ListView_GetColumnWidth( hwndList, 1 );
+ if (w>max)
+ ListView_SetColumnWidth( hwndList, 1, max );
+ }
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ NMLISTVIEW * hdr = (NMLISTVIEW *) lParam;
+ if ( hdr && hdr->hdr.code == LVN_ITEMCHANGED && hdr->uOldState != 0
+ && (hdr->uNewState == 0x1000 || hdr->uNewState == 0x2000 ) && IsWindowVisible(hdr->hdr.hwndFrom) ) {
+ HWND hwndList = GetDlgItem(hwndDlg,IDC_PLUGLIST);
+ PluginListItemData* dat;
+ int iRow;
+ LVITEM it;
+ it.mask=LVIF_PARAM | LVIF_STATE;
+ it.iItem = hdr->iItem;
+ if ( !ListView_GetItem( hwndList, &it ))
+ break;
+
+ dat = ( PluginListItemData* )it.lParam;
+ if ( dat->flags == DEFMOD_DB ) {
+ ListView_SetItemState(hwndList, hdr->iItem, 0x3000, LVIS_STATEIMAGEMASK);
+ return FALSE;
+ }
+ // if enabling and replaces, find all other replaces and toggle off
+ if ( hdr->uNewState & 0x2000 && dat->flags != 0 ) {
+ for ( iRow=0; iRow != -1; ) {
+ if ( iRow != hdr->iItem ) {
+ LVITEM dt;
+ dt.mask = LVIF_PARAM;
+ dt.iItem = iRow;
+ if ( ListView_GetItem( hwndList, &dt )) {
+ PluginListItemData* dat2 = ( PluginListItemData* )dt.lParam;
+ if ( dat2->flags == dat->flags ) {
+ // the lParam is unset, so when the check is unset the clist block doesnt trigger
+ int lParam = dat2->flags;
+ dat2->flags = 0;
+ ListView_SetItemState(hwndList, iRow, 0x1000, LVIS_STATEIMAGEMASK );
+ dat2->flags = lParam;
+ } } }
+
+ iRow = ListView_GetNextItem( hwndList, iRow, LVNI_ALL );
+ } }
+
+ ShowWindow( GetDlgItem(hwndDlg, IDC_RESTART ), TRUE );
+ SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 );
+ break;
+ }
+
+ if ( hdr && hdr->hdr.code == LVN_ITEMCHANGED && IsWindowVisible(hdr->hdr.hwndFrom) && hdr->iItem != -1 ) {
+ TCHAR buf[1024];
+ int sel = hdr->uNewState & LVIS_SELECTED;
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_PLUGLIST);
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = hdr->iItem;
+ if ( ListView_GetItem( hwndList, &lvi )) {
+ PluginListItemData* dat = ( PluginListItemData* )lvi.lParam;
+
+ ListView_GetItemText(hwndList, hdr->iItem, 1, buf, SIZEOF(buf));
+ SetWindowText(GetDlgItem(hwndDlg,IDC_PLUGININFOFRAME),sel ? buf : _T(""));
+
+ SetWindowTextA(GetDlgItem(hwndDlg,IDC_PLUGINAUTHOR), sel ? dat->author : "" );
+ SetWindowTextA(GetDlgItem(hwndDlg,IDC_PLUGINEMAIL), sel ? dat->authorEmail : "" );
+ {
+ TCHAR* p = LangPackPcharToTchar( dat->description );
+ SetWindowText(GetDlgItem(hwndDlg,IDC_PLUGINLONGINFO), sel ? p : _T(""));
+ mir_free( p );
+ }
+ SetWindowTextA(GetDlgItem(hwndDlg,IDC_PLUGINCPYR), sel ? dat->copyright : "" );
+ SetWindowTextA(GetDlgItem(hwndDlg,IDC_PLUGINURL), sel ? dat->homepage : "" );
+ if(equalUUID(miid_last, dat->uuid))
+ SetWindowText(GetDlgItem(hwndDlg,IDC_PLUGINPID), sel ? TranslateT("<none>") : _T(""));
+ else
+ {
+ char szUID[128];
+ uuidToString( dat->uuid, szUID, sizeof(szUID));
+ SetWindowTextA(GetDlgItem(hwndDlg,IDC_PLUGINPID), sel ? szUID : "" );
+ }
+ } }
+
+ if ( hdr && hdr->hdr.code == PSN_APPLY ) {
+ HWND hwndList=GetDlgItem(hwndDlg,IDC_PLUGLIST);
+ int iRow;
+ int iState;
+ TCHAR buf[1024];
+ for (iRow=0 ; iRow != (-1) ; ) {
+ ListView_GetItemText(hwndList, iRow, 0, buf, SIZEOF(buf));
+ iState=ListView_GetItemState(hwndList, iRow, LVIS_STATEIMAGEMASK);
+ SetPluginOnWhiteList(buf, iState&0x2000 ? 1 : 0);
+ iRow=ListView_GetNextItem(hwndList, iRow, LVNI_ALL);
+ } }
+ break;
+ }
+
+ case WM_COMMAND:
+ if ( HIWORD(wParam) == STN_CLICKED ) {
+ switch (LOWORD(wParam)) {
+ case IDC_PLUGINEMAIL:
+ case IDC_PLUGINURL:
+ {
+ char buf[512];
+ char * p = &buf[7];
+ lstrcpyA(buf,"mailto:");
+ if ( GetWindowTextA(GetDlgItem(hwndDlg, LOWORD(wParam)), p, SIZEOF(buf) - 7) ) {
+ CallService(MS_UTILS_OPENURL,0,(LPARAM) (LOWORD(wParam)==IDC_PLUGINEMAIL ? buf : p) );
+ }
+ break;
+ }
+ case IDC_GETMOREPLUGINS:
+ {
+ CallService(MS_UTILS_OPENURL,0,(LPARAM) "http://addons.miranda-im.org/index.php?action=display&id=1" );
+ break;
+ }
+ } }
+ break;
+
+ case WM_DESTROY:
+ RemoveAllItems( GetDlgItem( hwndDlg, IDC_PLUGLIST ));
+ break;
+ }
+ return FALSE;
+}
+
+static int PluginOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hMirandaInst;
+ odp.pfnDlgProc = DlgPluginOpt;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_PLUGINS);
+ odp.position = 1300000000;
+ odp.pszTitle = LPGEN("Plugins");
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Loads all plugins
+
+int LoadNewPluginsModule(void)
+{
+ TCHAR exe[MAX_PATH];
+ TCHAR* slice;
+ pluginEntry* p;
+ pluginEntry* clist = NULL;
+ int useWhiteList, i;
+ bool msgModule = false;
+
+ // make full path to the plugin
+ GetModuleFileName(NULL, exe, SIZEOF(exe));
+ slice = _tcsrchr(exe, '\\');
+ if (slice) *slice = 0;
+
+ // remember some useful options
+ askAboutIgnoredPlugins=(UINT) GetPrivateProfileInt( _T("PluginLoader"), _T("AskAboutIgnoredPlugins"), 0, mirandabootini);
+
+ // if Crash Dumper is present, load it to provide Crash Reports
+ if (pluginList_crshdmp != NULL && isPluginOnWhiteList(pluginList_crshdmp->pluginname))
+ {
+ if ( pluginList_crshdmp->bpi.Load(&pluginCoreLink) == 0 )
+ pluginList_crshdmp->pclass |= PCLASS_LOADED | PCLASS_LAST;
+ else
+ Plugin_Uninit( pluginList_crshdmp );
+ }
+
+ // if freeimage is present, load it to provide the basic core functions
+ if ( pluginList_freeimg != NULL ) {
+ BASIC_PLUGIN_INFO bpi;
+ mir_sntprintf(slice, &exe[SIZEOF(exe)] - slice, _T("\\Plugins\\%s"), pluginList_freeimg->pluginname);
+ if ( checkAPI(exe, &bpi, mirandaVersion, CHECKAPI_NONE, NULL) ) {
+ pluginList_freeimg->bpi = bpi;
+ pluginList_freeimg->pclass |= PCLASS_OK | PCLASS_BASICAPI;
+ if ( bpi.Load(&pluginCoreLink) == 0 )
+ pluginList_freeimg->pclass |= PCLASS_LOADED;
+ else
+ Plugin_Uninit( pluginList_freeimg );
+ } }
+
+ // first load the clist cos alot of plugins need that to be present at Load()
+ for ( useWhiteList = 1; useWhiteList >= 0 && clist == NULL; useWhiteList-- )
+ clist=getCListModule(exe, slice, useWhiteList);
+ /* the loop above will try and get one clist DLL to work, if all fail then just bail now */
+ if ( clist == NULL ) {
+ // result = 0, no clist_* can be found
+ if ( pluginListUI )
+ MessageBox(NULL, TranslateT("Unable to start any of the installed contact list plugins, I even ignored your preferences for which contact list couldn't load any."), _T("Miranda IM"), MB_OK | MB_ICONINFORMATION);
+ else
+ MessageBox(NULL, TranslateT("Can't find a contact list plugin! you need clist_classic or any other clist plugin.") , _T("Miranda IM"), MB_OK | MB_ICONINFORMATION);
+ return 1;
+ }
+
+ /* enable and disable as needed */
+ p = pluginListUI;
+ while ( p != NULL ) {
+ SetPluginOnWhiteList(p->pluginname, clist != p ? 0 : 1 );
+ p = p->nextclass;
+ }
+ /* now loop thru and load all the other plugins, do this in one pass */
+
+ for ( i=0; i < pluginList.getCount(); i++ ) {
+ p = pluginList[i];
+ CharLower(p->pluginname);
+ if (!(p->pclass & (PCLASS_LOADED | PCLASS_DB | PCLASS_CLIST)))
+ {
+ if (isPluginOnWhiteList(p->pluginname))
+ {
+ BASIC_PLUGIN_INFO bpi;
+ mir_sntprintf(slice, &exe[SIZEOF(exe)] - slice, _T("\\Plugins\\%s"), p->pluginname);
+ if ( checkAPI(exe, &bpi, mirandaVersion, CHECKAPI_NONE, NULL) ) {
+ int rm = bpi.pluginInfo->replacesDefaultModule;
+ p->bpi = bpi;
+ p->pclass |= PCLASS_OK | PCLASS_BASICAPI;
+
+ if ( pluginDefModList[rm] == NULL ) {
+ pluginListAddr.insert( p );
+ if ( bpi.Load(&pluginCoreLink) == 0 ) {
+ p->pclass |= PCLASS_LOADED;
+ msgModule |= (bpi.pluginInfo->replacesDefaultModule == DEFMOD_SRMESSAGE);
+ }
+ else {
+ Plugin_Uninit( p );
+ i--;
+ }
+ if ( rm ) pluginDefModList[rm]=p;
+ } //if
+ else {
+ SetPluginOnWhiteList( p->pluginname, 0 );
+ Plugin_Uninit( p );
+ i--;
+ }
+ }
+ else p->pclass |= PCLASS_FAILED;
+ }
+ else {
+ Plugin_Uninit( p );
+ i--;
+ }
+ }
+ else if ( p->bpi.hInst != NULL )
+ {
+ pluginListAddr.insert( p );
+ p->pclass |= PCLASS_LOADED;
+ }
+ }
+ if (!msgModule)
+ MessageBox(NULL, TranslateT("No messaging plugins loaded. Please install/enable one of the messaging plugins, for instance, \"srmm.dll\""), _T("Miranda IM"), MB_OK | MB_ICONINFORMATION);
+
+ HookEvent(ME_OPT_INITIALISE, PluginOptionsInit);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Plugins module initialization
+// called before anything real is loaded, incl. database
+
+int LoadNewPluginsModuleInfos(void)
+{
+ bModuleInitialized = TRUE;
+
+ hPluginListHeap=HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
+ mirandaVersion = (DWORD)CallService(MS_SYSTEM_GETVERSION, 0, 0);
+ //
+ CreateServiceFunction(MS_PLUGINS_ENUMDBPLUGINS, PluginsEnum);
+ CreateServiceFunction(MS_PLUGINS_GETDISABLEDEFAULTARRAY, PluginsGetDefaultArray);
+ // make sure plugins can get internal core APIs
+ pluginCoreLink.CallService = CallService;
+ pluginCoreLink.ServiceExists = ServiceExists;
+ pluginCoreLink.CreateServiceFunction = CreateServiceFunction;
+ pluginCoreLink.CreateServiceFunctionParam = CreateServiceFunctionParam;
+ pluginCoreLink.CreateServiceFunctionObj = CreateServiceFunctionObj;
+ pluginCoreLink.CreateServiceFunctionObjParam = CreateServiceFunctionObjParam;
+ pluginCoreLink.CreateTransientServiceFunction = CreateServiceFunction;
+ pluginCoreLink.DestroyServiceFunction = DestroyServiceFunction;
+ pluginCoreLink.CreateHookableEvent = CreateHookableEvent;
+ pluginCoreLink.DestroyHookableEvent = DestroyHookableEvent;
+ pluginCoreLink.HookEvent = HookEvent;
+ pluginCoreLink.HookEventParam = HookEventParam;
+ pluginCoreLink.HookEventObj = HookEventObj;
+ pluginCoreLink.HookEventObjParam = HookEventObjParam;
+ pluginCoreLink.HookEventMessage = HookEventMessage;
+ pluginCoreLink.UnhookEvent = UnhookEvent;
+ pluginCoreLink.NotifyEventHooks = NotifyEventHooks;
+ pluginCoreLink.SetHookDefaultForHookableEvent = SetHookDefaultForHookableEvent;
+ pluginCoreLink.CallServiceSync = CallServiceSync;
+ pluginCoreLink.CallFunctionAsync = CallFunctionAsync;
+ pluginCoreLink.NotifyEventHooksDirect = CallHookSubscribers;
+ pluginCoreLink.CallProtoService = CallProtoService;
+ pluginCoreLink.CallContactService = CallContactService;
+ pluginCoreLink.KillObjectServices = KillObjectServices;
+ pluginCoreLink.KillObjectEventHooks = KillObjectEventHooks;
+
+ // remember where the mirandaboot.ini goes
+ pathToAbsoluteT(_T("mirandaboot.ini"), mirandabootini, NULL);
+ // look for all *.dll's
+ enumPlugins(scanPluginsDir, 0, 0);
+ // the database will select which db plugin to use, or fail if no profile is selected
+ if (LoadDatabaseModule()) return 1;
+ InitIni();
+ // could validate the plugin entries here but internal modules arent loaded so can't call Load() in one pass
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//
+// Plugins module unloading
+// called at the end of module chain unloading, just modular engine left at this point
+
+void UnloadNewPluginsModule(void)
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ // unload everything but the DB
+ for ( i = pluginList.getCount()-1; i >= 0; i-- ) {
+ pluginEntry* p = pluginList[i];
+ if ( !(p->pclass & PCLASS_DB) && p != pluginList_crshdmp )
+ Plugin_Uninit( p );
+ }
+
+ if ( pluginList_crshdmp )
+ Plugin_Uninit( pluginList_crshdmp );
+
+ // unload the DB
+ for ( i = pluginList.getCount()-1; i >= 0; i-- ) {
+ pluginEntry* p = pluginList[i];
+ Plugin_Uninit( p );
+ }
+
+ if ( hPluginListHeap ) HeapDestroy(hPluginListHeap);
+ hPluginListHeap=0;
+
+ pluginList.destroy();
+ pluginListAddr.destroy();
+ UninitIni();
+}
diff --git a/src/modules/protocols/protoaccs.cpp b/src/modules/protocols/protoaccs.cpp
new file mode 100644
index 0000000000..b4a2b64f4f
--- /dev/null
+++ b/src/modules/protocols/protoaccs.cpp
@@ -0,0 +1,638 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+#include "../clist/clc.h"
+
+bool CheckProtocolOrder(void);
+void BuildProtoMenus();
+
+static BOOL bModuleInitialized = FALSE;
+
+static int CompareAccounts( const PROTOACCOUNT* p1, const PROTOACCOUNT* p2 )
+{
+ return lstrcmpA( p1->szModuleName, p2->szModuleName );
+}
+
+LIST<PROTOACCOUNT> accounts( 10, CompareAccounts );
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int EnumDbModules(const char *szModuleName, DWORD ofsModuleName, LPARAM lParam)
+{
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingString( NULL, szModuleName, "AM_BaseProto", &dbv )) {
+ if (!Proto_GetAccount( szModuleName )) {
+ PROTOACCOUNT* pa = ( PROTOACCOUNT* )mir_calloc( sizeof( PROTOACCOUNT ));
+ pa->cbSize = sizeof( *pa );
+ pa->type = PROTOTYPE_PROTOCOL;
+ pa->szModuleName = mir_strdup( szModuleName );
+ pa->szProtoName = mir_strdup( dbv.pszVal );
+ pa->tszAccountName = mir_a2t( szModuleName );
+ pa->bIsVisible = TRUE;
+ pa->bIsEnabled = FALSE;
+ pa->iOrder = accounts.getCount();
+ accounts.insert( pa );
+ }
+ DBFreeVariant( &dbv );
+ }
+ return 0;
+}
+
+void LoadDbAccounts(void)
+{
+ DBVARIANT dbv;
+ int ver = DBGetContactSettingDword( NULL, "Protocols", "PrVer", -1 );
+ int count = DBGetContactSettingDword( NULL, "Protocols", "ProtoCount", 0 ), i;
+
+ for ( i=0; i < count; i++ ) {
+ char buf[10];
+ _itoa( i, buf, 10 );
+ if ( DBGetContactSettingString( NULL, "Protocols", buf, &dbv ))
+ continue;
+
+ PROTOACCOUNT* pa = ( PROTOACCOUNT* )mir_calloc( sizeof( PROTOACCOUNT ));
+ if ( pa == NULL ) {
+ DBFreeVariant( &dbv );
+ continue;
+ }
+ pa->cbSize = sizeof( *pa );
+ pa->type = PROTOTYPE_PROTOCOL;
+ pa->szModuleName = mir_strdup( dbv.pszVal );
+ DBFreeVariant( &dbv );
+
+ _itoa( OFFSET_VISIBLE+i, buf, 10 );
+ pa->bIsVisible = DBGetContactSettingDword( NULL, "Protocols", buf, 1 );
+
+ _itoa( OFFSET_PROTOPOS+i, buf, 10 );
+ pa->iOrder = DBGetContactSettingDword( NULL, "Protocols", buf, 1 );
+
+ if ( ver >= 4 ) {
+ DBFreeVariant( &dbv );
+ _itoa( OFFSET_NAME+i, buf, 10 );
+ if ( !DBGetContactSettingTString( NULL, "Protocols", buf, &dbv )) {
+ pa->tszAccountName = mir_tstrdup( dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+
+ _itoa( OFFSET_ENABLED+i, buf, 10 );
+ pa->bIsEnabled = DBGetContactSettingDword( NULL, "Protocols", buf, 1 );
+
+ if ( !DBGetContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", &dbv )) {
+ pa->szProtoName = mir_strdup( dbv.pszVal );
+ DBFreeVariant( &dbv );
+ }
+ }
+ else pa->bIsEnabled = TRUE;
+
+ if ( !pa->szProtoName ) {
+ pa->szProtoName = mir_strdup( pa->szModuleName );
+ DBWriteContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", pa->szProtoName );
+ }
+
+ if ( !pa->tszAccountName )
+ pa->tszAccountName = mir_a2t( pa->szModuleName );
+
+ accounts.insert( pa );
+ }
+
+ if (CheckProtocolOrder()) WriteDbAccounts();
+
+ int anum = accounts.getCount();
+ CallService(MS_DB_MODULES_ENUM, 0, (LPARAM)EnumDbModules);
+ if (anum != accounts.getCount()) WriteDbAccounts();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+ int arrlen;
+ char **pszSettingName;
+}
+ enumDB_ProtoProcParam;
+
+static int enumDB_ProtoProc( const char* szSetting, LPARAM lParam )
+{
+ if ( szSetting ) {
+ enumDB_ProtoProcParam* p = ( enumDB_ProtoProcParam* )lParam;
+
+ p->arrlen++;
+ p->pszSettingName = ( char** )mir_realloc( p->pszSettingName, p->arrlen*sizeof( char* ));
+ p->pszSettingName[ p->arrlen-1 ] = mir_strdup( szSetting );
+ }
+ return 0;
+}
+
+void WriteDbAccounts()
+{
+ int i;
+
+ // enum all old settings to delete
+ enumDB_ProtoProcParam param = { 0, NULL };
+
+ DBCONTACTENUMSETTINGS dbces;
+ dbces.pfnEnumProc = enumDB_ProtoProc;
+ dbces.szModule = "Protocols";
+ dbces.ofsSettings = 0;
+ dbces.lParam = ( LPARAM )&param;
+ CallService( MS_DB_CONTACT_ENUMSETTINGS, 0, ( LPARAM )&dbces );
+
+ // delete all settings
+ if ( param.arrlen ) {
+ int i;
+ for ( i=0; i < param.arrlen; i++ ) {
+ DBDeleteContactSetting( 0, "Protocols", param.pszSettingName[i] );
+ mir_free( param.pszSettingName[i] );
+ }
+ mir_free( param.pszSettingName );
+ }
+
+ // write new data
+ for ( i=0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+
+ char buf[ 20 ];
+ _itoa( i, buf, 10 );
+ DBWriteContactSettingString( NULL, "Protocols", buf, pa->szModuleName );
+
+ _itoa( OFFSET_PROTOPOS+i, buf, 10 );
+ DBWriteContactSettingDword( NULL, "Protocols", buf, pa->iOrder );
+
+ _itoa( OFFSET_VISIBLE+i, buf, 10 );
+ DBWriteContactSettingDword( NULL, "Protocols", buf, pa->bIsVisible );
+
+ _itoa( OFFSET_ENABLED+i, buf, 10 );
+ DBWriteContactSettingDword( NULL, "Protocols", buf, pa->bIsEnabled );
+
+ _itoa( OFFSET_NAME+i, buf, 10 );
+ DBWriteContactSettingTString( NULL, "Protocols", buf, pa->tszAccountName );
+ }
+
+ DBDeleteContactSetting( 0, "Protocols", "ProtoCount" );
+ DBWriteContactSettingDword( 0, "Protocols", "ProtoCount", accounts.getCount() );
+ DBWriteContactSettingDword( 0, "Protocols", "PrVer", 4 );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+static int OnContactDeleted(WPARAM wParam, LPARAM lParam)
+{
+ const HANDLE hContact = (HANDLE)wParam;
+ if (hContact)
+ {
+ PROTOACCOUNT* pa = Proto_GetAccount(hContact);
+
+ if (Proto_IsAccountEnabled(pa) && pa->ppro)
+ pa->ppro->OnEvent(EV_PROTO_ONCONTACTDELETED, wParam, lParam);
+ }
+ return 0;
+}
+
+static int OnDbSettingsChanged(WPARAM wParam, LPARAM lParam)
+{
+ const HANDLE hContact = (HANDLE)wParam;
+ if (hContact)
+ {
+ PROTOACCOUNT* pa = Proto_GetAccount(hContact);
+ if (Proto_IsAccountEnabled(pa) && pa->ppro)
+ pa->ppro->OnEvent(EV_PROTO_DBSETTINGSCHANGED, wParam, lParam);
+ }
+ return 0;
+}
+
+static int InitializeStaticAccounts( WPARAM, LPARAM )
+{
+ int count = 0;
+
+ for ( int i = 0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if ( !pa->ppro || !Proto_IsAccountEnabled( pa ))
+ continue;
+
+ pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 );
+
+ if ( !pa->bOldProto )
+ count++;
+ }
+
+ BuildProtoMenus();
+
+ if ( count == 0 && !DBGetContactSettingByte( NULL, "FirstRun", "AccManager", 0 )) {
+ DBWriteContactSettingByte( NULL, "FirstRun", "AccManager", 1 );
+ CallService( MS_PROTO_SHOWACCMGR, 0, 0 );
+ }
+ return 0;
+}
+
+static int UninitializeStaticAccounts( WPARAM, LPARAM )
+{
+ for ( int i = 0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ if ( !pa->ppro || !Proto_IsAccountEnabled( pa ))
+ continue;
+
+ pa->ppro->OnEvent( EV_PROTO_ONREADYTOEXIT, 0, 0 );
+ pa->ppro->OnEvent( EV_PROTO_ONEXIT, 0, 0 );
+ }
+ return 0;
+}
+
+int LoadAccountsModule( void )
+{
+ int i;
+
+ bModuleInitialized = TRUE;
+
+ for ( i = 0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[i];
+ pa->bDynDisabled = !Proto_IsProtocolLoaded( pa->szProtoName );
+ if ( pa->ppro )
+ continue;
+
+ if (!Proto_IsAccountEnabled( pa )) {
+ pa->type = PROTOTYPE_DISPROTO;
+ continue;
+ }
+
+ if ( !ActivateAccount( pa )) {
+ pa->bDynDisabled = TRUE;
+ pa->type = PROTOTYPE_DISPROTO;
+ } }
+
+ HookEvent( ME_SYSTEM_MODULESLOADED, InitializeStaticAccounts );
+ HookEvent( ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts );
+ HookEvent( ME_DB_CONTACT_DELETED, OnContactDeleted );
+ HookEvent( ME_DB_CONTACT_SETTINGCHANGED, OnDbSettingsChanged );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR stub1( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam );
+}
+
+static INT_PTR stub2( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->AddToListByEvent( HIWORD(wParam), LOWORD(wParam), (HANDLE)lParam );
+}
+
+static INT_PTR stub3( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM )
+{ return ( INT_PTR )ppi->Authorize(( HANDLE )wParam );
+}
+
+static INT_PTR stub4( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvT(( const char* )lParam ));
+}
+
+static INT_PTR stub7( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->ChangeInfo( wParam, ( void* )lParam );
+}
+
+static INT_PTR stub11( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam;
+ return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action, (const PROTOCHAR**)&pfr->szFilename );
+}
+
+static INT_PTR stub12( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->GetCaps( wParam, (HANDLE)lParam );
+}
+
+static INT_PTR stub13( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM )
+{ return ( INT_PTR )ppi->GetIcon( wParam );
+}
+
+static INT_PTR stub15( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ return ( INT_PTR )ppi->SearchBasic( StrConvT(( char* )lParam ));
+}
+
+static INT_PTR stub16( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ return ( INT_PTR )ppi->SearchByEmail( StrConvT(( char* )lParam ));
+}
+
+static INT_PTR stub17( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam;
+ return ( INT_PTR )ppi->SearchByName( StrConvT(( char* )psbn->pszNick ),
+ StrConvT(( char* )psbn->pszFirstName ), StrConvT(( char* )psbn->pszLastName ));
+}
+
+static INT_PTR stub18( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ return ( INT_PTR )ppi->SearchAdvanced(( HWND )lParam );
+}
+
+static INT_PTR stub19( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ return ( INT_PTR )ppi->CreateExtendedSearchUI (( HWND )lParam );
+}
+
+static INT_PTR stub22( PROTO_INTERFACE* ppi, WPARAM, LPARAM lParam )
+{ CCSDATA *ccs = ( CCSDATA* )lParam;
+ ppi->RecvMsg( ccs->hContact, ( PROTORECVEVENT* )ccs->lParam );
+ return 0;
+}
+
+static INT_PTR stub29( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM )
+{ return ( INT_PTR )ppi->SetStatus( wParam );
+}
+
+static INT_PTR stub33( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvT(( const char* )lParam ));
+}
+
+static INT_PTR stub41( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ lstrcpynA(( char* )lParam, ppi->m_szModuleName, wParam );
+ return 0;
+}
+
+static INT_PTR stub42( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{ return ppi->m_iStatus;
+}
+
+#ifdef _UNICODE
+
+static INT_PTR stub43( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{
+ PROTO_AVATAR_INFORMATION* p = ( PROTO_AVATAR_INFORMATION* )lParam;
+
+ PROTO_AVATAR_INFORMATIONW tmp = { 0 };
+ tmp.cbSize = sizeof( tmp );
+ tmp.hContact = p->hContact;
+ int result = CallProtoService( ppi->m_szModuleName, PS_GETAVATARINFOW, wParam, ( LPARAM )&tmp );
+
+ p->format = tmp.format;
+
+ wchar_t filename[MAX_PATH];
+ wcscpy(filename, tmp.filename);
+ GetShortPathNameW(tmp.filename, filename, SIZEOF(filename));
+
+ WideCharToMultiByte( CP_ACP, 0, filename, -1, p->filename, MAX_PATH, 0, 0 );
+ return result;
+}
+
+static INT_PTR stub44( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{
+ wchar_t* buf = ( wchar_t* )_alloca( sizeof(wchar_t) * (lParam + 1));
+ int result = CallProtoService( ppi->m_szModuleName, PS_GETMYAVATARW, WPARAM( buf ), lParam );
+ if ( result == 0 )
+ {
+ wchar_t* filename = ( wchar_t* )_alloca( sizeof(wchar_t) * (lParam + 1));
+ wcscpy(filename, buf);
+ GetShortPathNameW(buf, filename, lParam + 1);
+
+ WideCharToMultiByte( CP_ACP, 0, filename, -1, ( char* )wParam, lParam, 0, 0 );
+ }
+
+ return result;
+}
+
+static INT_PTR stub45( PROTO_INTERFACE* ppi, WPARAM wParam, LPARAM lParam )
+{
+ return CallProtoService( ppi->m_szModuleName, PS_SETMYAVATARW, wParam, ( LPARAM )( LPCTSTR )StrConvT(( char* )lParam ));
+}
+
+#endif
+
+static HANDLE CreateProtoServiceEx( const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param )
+{
+ char tmp[100];
+ mir_snprintf( tmp, sizeof( tmp ), "%s%s", szModule, szService );
+ return CreateServiceFunctionObj( tmp, pFunc, param );
+}
+
+BOOL ActivateAccount( PROTOACCOUNT* pa )
+{
+ PROTO_INTERFACE* ppi;
+ PROTOCOLDESCRIPTOR* ppd = Proto_IsProtocolLoaded( pa->szProtoName );
+ if ( ppd == NULL )
+ return FALSE;
+
+ if ( ppd->fnInit == NULL )
+ return FALSE;
+
+ ppi = ppd->fnInit( pa->szModuleName, pa->tszAccountName );
+ if ( ppi == NULL )
+ return FALSE;
+
+ pa->type = PROTOTYPE_PROTOCOL;
+ pa->ppro = ppi;
+ ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE;
+ CreateProtoServiceEx( pa->szModuleName, PS_ADDTOLIST, (MIRANDASERVICEOBJ)stub1, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_ADDTOLISTBYEVENT, (MIRANDASERVICEOBJ)stub2, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_AUTHALLOW, (MIRANDASERVICEOBJ)stub3, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_AUTHDENY, (MIRANDASERVICEOBJ)stub4, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_CHANGEINFO, (MIRANDASERVICEOBJ)stub7, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_FILERESUME, (MIRANDASERVICEOBJ)stub11, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_GETCAPS, (MIRANDASERVICEOBJ)stub12, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_LOADICON, (MIRANDASERVICEOBJ)stub13, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_BASICSEARCH, (MIRANDASERVICEOBJ)stub15, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYEMAIL, (MIRANDASERVICEOBJ)stub16, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYNAME, (MIRANDASERVICEOBJ)stub17, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_SEARCHBYADVANCED, (MIRANDASERVICEOBJ)stub18, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_CREATEADVSEARCHUI, (MIRANDASERVICEOBJ)stub19, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PSR_MESSAGE, (MIRANDASERVICEOBJ)stub22, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_SETSTATUS, (MIRANDASERVICEOBJ)stub29, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_SETAWAYMSG, (MIRANDASERVICEOBJ)stub33, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_GETNAME, (MIRANDASERVICEOBJ)stub41, pa->ppro );
+ CreateProtoServiceEx( pa->szModuleName, PS_GETSTATUS, (MIRANDASERVICEOBJ)stub42, pa->ppro );
+
+#ifdef _UNICODE
+ char szServiceName[ 200 ];
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETAVATARINFO );
+ if ( !ServiceExists( szServiceName )) {
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETAVATARINFOW );
+ if ( ServiceExists( szServiceName ))
+ CreateProtoServiceEx( pa->szModuleName, PS_GETAVATARINFO, (MIRANDASERVICEOBJ)stub43, pa->ppro );
+ }
+
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETMYAVATAR );
+ if ( !ServiceExists( szServiceName )) {
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_GETMYAVATARW );
+ if ( ServiceExists( szServiceName ))
+ CreateProtoServiceEx( pa->szModuleName, PS_GETMYAVATAR, (MIRANDASERVICEOBJ)stub44, pa->ppro );
+ }
+
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_SETMYAVATAR );
+ if ( !ServiceExists( szServiceName )) {
+ mir_snprintf( szServiceName, SIZEOF(szServiceName), "%s%s", pa->szModuleName, PS_SETMYAVATARW );
+ if ( ServiceExists( szServiceName ))
+ CreateProtoServiceEx( pa->szModuleName, PS_SETMYAVATAR, (MIRANDASERVICEOBJ)stub45, pa->ppro );
+ }
+ #endif
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct DeactivationThreadParam
+{
+ tagPROTO_INTERFACE* ppro;
+ pfnUninitProto fnUninit;
+ bool bIsDynamic;
+ bool bErase;
+};
+
+pfnUninitProto GetProtocolDestructor( char* szProto );
+
+static int DeactivationThread( DeactivationThreadParam* param )
+{
+ tagPROTO_INTERFACE* p = ( tagPROTO_INTERFACE* )param->ppro;
+ p->SetStatus(ID_STATUS_OFFLINE);
+
+ char * szModuleName = NEWSTR_ALLOCA(p->m_szModuleName);
+
+ if ( param->bIsDynamic ) {
+ p->OnEvent( EV_PROTO_ONREADYTOEXIT, 0, 0 );
+ p->OnEvent( EV_PROTO_ONEXIT, 0, 0 );
+ }
+
+ KillObjectThreads( p ); // waits for them before terminating
+ KillObjectEventHooks( p ); // untie an object from the outside world
+
+ if ( param->bErase )
+ p->OnEvent( EV_PROTO_ONERASE, 0, 0 );
+
+ if ( param->fnUninit )
+ param->fnUninit( p );
+
+ KillObjectServices( p );
+
+ if ( param->bErase )
+ EraseAccount( szModuleName );
+
+ delete param;
+ return 0;
+}
+
+void DeactivateAccount( PROTOACCOUNT* pa, bool bIsDynamic, bool bErase )
+{
+ if ( pa->ppro == NULL ) {
+ if ( bErase )
+ EraseAccount( pa->szModuleName );
+ return;
+ }
+
+ if ( pa->hwndAccMgrUI ) {
+ DestroyWindow(pa->hwndAccMgrUI);
+ pa->hwndAccMgrUI = NULL;
+ pa->bAccMgrUIChanged = FALSE;
+ }
+
+ DeactivationThreadParam* param = new DeactivationThreadParam;
+ param->ppro = pa->ppro;
+ param->fnUninit = GetProtocolDestructor( pa->szProtoName );
+ param->bIsDynamic = bIsDynamic;
+ param->bErase = bErase;
+ pa->ppro = NULL;
+ pa->type = PROTOTYPE_DISPROTO;
+ if ( bIsDynamic )
+ mir_forkthread(( pThreadFunc )DeactivationThread, param );
+ else
+ DeactivationThread( param );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void EraseAccount( const char* pszModuleName )
+{
+ DBVARIANT dbv;
+ DBCONTACTGETSETTING dbcgs;
+ char szProtoName[32];
+
+ dbcgs.pValue = &dbv;
+ dbcgs.szModule = "Protocol";
+ dbcgs.szSetting = "p";
+
+ // remove protocol contacts first
+ HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 );
+ while ( hContact != NULL ) {
+ HANDLE h1 = hContact;
+ hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM )h1, 0 );
+
+ dbv.type = DBVT_ASCIIZ;
+ dbv.pszVal = szProtoName;
+ dbv.cchVal = SIZEOF(szProtoName);
+
+ if ( CallService( MS_DB_CONTACT_GETSETTINGSTATIC, ( WPARAM )h1, ( LPARAM )&dbcgs ))
+ continue;
+
+ if ( !lstrcmpA( szProtoName, pszModuleName ))
+ CallService( MS_DB_CONTACT_DELETE, ( WPARAM )h1, 0 );
+ }
+
+ // remove all protocol settings
+ CallService( MS_DB_MODULE_DELETE, 0, ( LPARAM )pszModuleName );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void UnloadAccount( PROTOACCOUNT* pa, bool bIsDynamic, bool bErase )
+{
+ DeactivateAccount( pa, bIsDynamic, bErase );
+
+ mir_free( pa->tszAccountName );
+ mir_free( pa->szProtoName );
+ // szModuleName should be freed only on a program's exit.
+ // otherwise many plugins dependand on static protocol names will crash!
+ // do NOT fix this 'leak', please
+ if ( !bIsDynamic ) {
+ mir_free( pa->szModuleName );
+ mir_free( pa );
+ }
+}
+
+void UnloadAccountsModule()
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ for( i=accounts.getCount()-1; i >= 0; i-- ) {
+ PROTOACCOUNT* pa = accounts[ i ];
+ UnloadAccount( pa, false, false );
+ accounts.remove(i);
+ }
+
+ accounts.destroy();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void BuildProtoMenus()
+{
+ for ( int i = 0; i < accounts.getCount(); i++ ) {
+ PROTOACCOUNT* pa = accounts[ i ];
+ if ( cli.pfnGetProtocolVisibility( pa->szModuleName ) == 0 )
+ continue;
+
+ if ( pa->ppro )
+ pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 );
+ }
+}
+
+void RebuildProtoMenus( int iNewValue )
+{
+ DBWriteContactSettingByte( NULL, "CList", "MoveProtoMenus", iNewValue );
+
+ RebuildMenuOrder();
+ BuildProtoMenus();
+}
diff --git a/src/modules/protocols/protochains.cpp b/src/modules/protocols/protochains.cpp
new file mode 100644
index 0000000000..f7f3729806
--- /dev/null
+++ b/src/modules/protocols/protochains.cpp
@@ -0,0 +1,274 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <m_protomod.h>
+
+//Protocol chain is list of integers "0".."n", with network protocol named "p"
+INT_PTR Proto_CallContactService(WPARAM wParam,LPARAM lParam)
+//note that this is ChainSend() too, due to a quirk of function definitions
+{
+ CCSDATA *ccs=(CCSDATA*)lParam;
+ int i;
+ char str[10];
+ DBVARIANT dbv;
+ INT_PTR ret;
+ PROTOACCOUNT* pa;
+
+ if ( wParam == (WPARAM)(-1))
+ return 1;
+
+ for ( i = wParam;; i++ ) {
+ _itoa( i, str, 10 );
+ if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv ))
+ break;
+
+ if (( ret = CallProtoService( dbv.pszVal, ccs->szProtoService, i+1, lParam )) != CALLSERVICE_NOTFOUND ) {
+ //chain was started, exit
+ mir_free( dbv.pszVal );
+ return ret;
+ }
+ mir_free( dbv.pszVal );
+ }
+ if ( DBGetContactSettingString( ccs->hContact, "Protocol", "p", &dbv ))
+ return 1;
+
+ pa = Proto_GetAccount( dbv.pszVal );
+ if ( pa == NULL || pa->ppro == NULL )
+ ret = 1;
+ else {
+ if ( pa->bOldProto )
+ ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, (WPARAM)(-1), ( LPARAM)ccs );
+ else
+ ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, ccs->wParam, ccs->lParam );
+ if ( ret == CALLSERVICE_NOTFOUND )
+ ret = 1;
+ }
+
+ mir_free(dbv.pszVal);
+ return ret;
+}
+
+static INT_PTR CallRecvChain(WPARAM wParam,LPARAM lParam)
+{
+ CCSDATA *ccs=(CCSDATA*)lParam;
+ int i;
+ INT_PTR ret;
+ char str[10];
+ DBVARIANT dbv;
+ PROTOACCOUNT* pa;
+
+ if ( wParam == (WPARAM)(-1)) return 1; //shouldn't happen - sanity check
+ if ( wParam == 0 ) { //begin processing by finding end of chain
+ for( ;;wParam++ ) {
+ _itoa( wParam, str, 10 );
+ if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv ))
+ break;
+ mir_free(dbv.pszVal);
+ }
+ }
+ else wParam--;
+
+ for ( i = wParam-1; i >= 0; i-- ) {
+ _itoa( i, str, 10 );
+ if ( DBGetContactSettingString( ccs->hContact, "_Filter", str, &dbv )) //never happens
+ return 1;
+
+ if (( ret = CallProtoService( dbv.pszVal, ccs->szProtoService, i+1, lParam )) != CALLSERVICE_NOTFOUND ) {
+ //chain was started, exit
+ mir_free( dbv.pszVal );
+ return ret;
+ }
+ mir_free( dbv.pszVal );
+ }
+
+ //end of chain, call network protocol again
+ if ( DBGetContactSettingString( ccs->hContact, "Protocol", "p", &dbv ))
+ return 1;
+
+ pa = Proto_GetAccount( dbv.pszVal );
+ if ( pa == NULL || pa->ppro == NULL )
+ ret = 1;
+ else {
+ if ( pa->bOldProto )
+ ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, (WPARAM)(-1), ( LPARAM)ccs );
+ else
+ ret = CallProtoServiceInt( ccs->hContact, dbv.pszVal, ccs->szProtoService, ccs->wParam, ccs->lParam );
+ if ( ret == CALLSERVICE_NOTFOUND )
+ ret = 1;
+ }
+
+ mir_free( dbv.pszVal );
+ return ret;
+}
+
+static INT_PTR Proto_ChainRecv(WPARAM wParam,LPARAM lParam)
+{
+ /* this will switch threads just like before */
+ return CallServiceSync(MS_PROTO_CHAINRECV "ThreadSafe",wParam,lParam);
+}
+
+PROTOACCOUNT* __fastcall Proto_GetAccount(HANDLE hContact)
+{
+ DBVARIANT dbv;
+ DBCONTACTGETSETTING dbcgs;
+ char name[32];
+
+ dbv.type = DBVT_ASCIIZ;
+ dbv.pszVal = name;
+ dbv.cchVal = SIZEOF(name);
+ dbcgs.pValue = &dbv;
+ dbcgs.szModule = "Protocol";
+ dbcgs.szSetting = "p";
+ if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&dbcgs))
+ return 0;
+
+ return Proto_GetAccount((char* )dbv.pszVal);
+}
+
+static INT_PTR Proto_GetContactBaseProto(WPARAM wParam, LPARAM)
+{
+ PROTOACCOUNT* pa = Proto_GetAccount((HANDLE)wParam);
+ return (INT_PTR)(Proto_IsAccountEnabled( pa ) ? pa->szModuleName : NULL);
+}
+
+static INT_PTR Proto_GetContactBaseAccount(WPARAM wParam, LPARAM)
+{
+ PROTOACCOUNT* pa = Proto_GetAccount((HANDLE)wParam);
+ return (INT_PTR)(pa ? pa->szModuleName : NULL);
+}
+
+static INT_PTR Proto_IsProtoOnContact(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ char str[10];
+ DBVARIANT dbv;
+
+ if (!lParam) return 0;
+
+ if(!DBGetContactSettingString((HANDLE)wParam,"Protocol","p",&dbv)) {
+ if(!_stricmp((char*)lParam,dbv.pszVal)) {
+ mir_free(dbv.pszVal);
+ return -1;
+ }
+ mir_free(dbv.pszVal);
+ }
+ for(i=0;;i++) {
+ _itoa(i,str,10);
+ if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) break;
+ if(!strcmp((char*)lParam,dbv.pszVal)) {
+ mir_free(dbv.pszVal);
+ return i+1;
+ }
+ mir_free(dbv.pszVal);
+ }
+ return 0;
+}
+
+static INT_PTR Proto_AddToContact(WPARAM wParam,LPARAM lParam)
+{
+ PROTOCOLDESCRIPTOR *pd,*pdCompare;
+
+ pd = Proto_IsProtocolLoaded(( char* )lParam );
+ if ( pd == NULL ) {
+ PROTOACCOUNT* pa = Proto_GetAccount(( char* )lParam );
+ if ( pa ) {
+ DBWriteContactSettingString((HANDLE)wParam,"Protocol","p",(char*)lParam);
+ return 0;
+ }
+ return 1;
+ }
+
+ if ( pd->type == PROTOTYPE_PROTOCOL ) {
+ DBWriteContactSettingString((HANDLE)wParam,"Protocol","p",(char*)lParam);
+ return 0;
+ }
+ if(Proto_IsProtoOnContact(wParam,lParam)) return 1;
+ { /* v:0.3.3 + PROTO FILTERS ARE NOW KEPT IN THEIR OWN DB MODULE! */
+ int i;
+ char str[10],*lastProto;
+ DBVARIANT dbv;
+
+ for(i=0;;i++) {
+ _itoa(i,str,10);
+ if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) break;
+ pdCompare = Proto_IsProtocolLoaded(( char* )dbv.pszVal );
+ mir_free(dbv.pszVal);
+ if(pdCompare==NULL) continue;
+ if(pd->type > pdCompare->type) break;
+ }
+ //put the new module at position i
+ lastProto=mir_strdup((char*)lParam);
+ for(;;i++) {
+ _itoa(i,str,10);
+ if(DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) {
+ DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,lastProto);
+ mir_free(lastProto);
+ break;
+ }
+ DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,lastProto);
+ mir_free(lastProto);
+ lastProto=dbv.pszVal;
+ }
+ }
+ return 0;
+}
+
+static INT_PTR Proto_RemoveFromContact(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ DBVARIANT dbv;
+ char str[10];
+
+ i = Proto_IsProtoOnContact(wParam,lParam);
+ if(!i) return 1;
+ if(i==-1)
+ DBDeleteContactSetting((HANDLE)wParam,"Protocol","p");
+ else {
+ for(i--;;i++) { //we have to decrease i, as Proto_IsOnContact returns +1 more number than read from database
+ _itoa(i+1,str,10);
+ if(0!=DBGetContactSettingString((HANDLE)wParam,"_Filter",str,&dbv)) {
+ _itoa(i,str,10);
+ DBDeleteContactSetting((HANDLE)wParam,"_Filter",str);
+ break;
+ }
+ _itoa(i,str,10);
+ DBWriteContactSettingString((HANDLE)wParam,"_Filter",str,dbv.pszVal);
+ mir_free(dbv.pszVal);
+ }
+ }
+ return 0;
+}
+
+int LoadProtoChains(void)
+{
+ CreateServiceFunction(MS_PROTO_CALLCONTACTSERVICE,Proto_CallContactService);
+ CreateServiceFunction(MS_PROTO_CHAINSEND,Proto_CallContactService);
+ CreateServiceFunction(MS_PROTO_CHAINRECV,Proto_ChainRecv);
+ CreateServiceFunction(MS_PROTO_CHAINRECV "ThreadSafe",CallRecvChain);
+ CreateServiceFunction(MS_PROTO_GETCONTACTBASEPROTO,Proto_GetContactBaseProto);
+ CreateServiceFunction(MS_PROTO_GETCONTACTBASEACCOUNT,Proto_GetContactBaseAccount);
+ CreateServiceFunction(MS_PROTO_ISPROTOONCONTACT,Proto_IsProtoOnContact);
+ CreateServiceFunction(MS_PROTO_ADDTOCONTACT,Proto_AddToContact);
+ CreateServiceFunction(MS_PROTO_REMOVEFROMCONTACT,Proto_RemoveFromContact);
+ return 0;
+}
diff --git a/src/modules/protocols/protocols.cpp b/src/modules/protocols/protocols.cpp
new file mode 100644
index 0000000000..60b53aeaf4
--- /dev/null
+++ b/src/modules/protocols/protocols.cpp
@@ -0,0 +1,844 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+int LoadProtoChains(void);
+int LoadProtoOptions( void );
+
+HANDLE hAccListChanged;
+static HANDLE hAckEvent,hTypeEvent;
+static BOOL bModuleInitialized = FALSE;
+
+typedef struct
+{
+ const char* name;
+ int id;
+}
+ TServiceListItem;
+
+static int CompareServiceItems( const TServiceListItem* p1, const TServiceListItem* p2 )
+{ return strcmp( p1->name, p2->name );
+}
+
+static LIST<TServiceListItem> serviceItems( 10, CompareServiceItems );
+
+//------------------------------------------------------------------------------------
+
+static int CompareProtos( const PROTOCOLDESCRIPTOR* p1, const PROTOCOLDESCRIPTOR* p2 )
+{ return strcmp( p1->szName, p2->szName );
+}
+
+static LIST<PROTOCOLDESCRIPTOR> protos( 10, CompareProtos );
+
+static INT_PTR Proto_BroadcastAck(WPARAM wParam, LPARAM lParam)
+{
+#ifdef _UNICODE
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack && ack->type == ACKTYPE_AVATAR && ack->hProcess)
+ {
+ PROTO_AVATAR_INFORMATION* ai = (PROTO_AVATAR_INFORMATION*)ack->hProcess;
+ if (ai->cbSize == sizeof(PROTO_AVATAR_INFORMATION))
+ {
+ PROTO_AVATAR_INFORMATIONW aiw = { sizeof(aiw), ai->hContact, ai->format };
+ MultiByteToWideChar(CP_ACP, 0, ai->filename, -1, aiw.filename, SIZEOF(aiw.filename));
+
+ ack->hProcess = &aiw;
+ }
+ }
+#endif
+
+ return NotifyEventHooks(hAckEvent, wParam, lParam);
+}
+
+INT_PTR __fastcall MyCallProtoService( const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam );
+void FreeFilesMatrix( TCHAR ***files );
+
+PROTOCOLDESCRIPTOR* __fastcall Proto_IsProtocolLoaded( const char* szProtoName )
+{
+ if ( szProtoName ) {
+ PROTOCOLDESCRIPTOR tmp;
+ tmp.szName = ( char* )szProtoName;
+ return protos.find( &tmp );
+ }
+ return NULL;
+}
+
+INT_PTR srvProto_IsLoaded(WPARAM, LPARAM lParam)
+{
+ return (INT_PTR)Proto_GetAccount(( char* )lParam );
+}
+
+INT_PTR Proto_EnumProtocols(WPARAM wParam,LPARAM lParam)
+{
+ *( int* )wParam = protos.getCount();
+ *( PROTOCOLDESCRIPTOR*** )lParam = protos.getArray();
+ return 0;
+}
+
+static PROTO_INTERFACE* defInitProto( const char* szModuleName, const TCHAR* )
+{
+ return AddDefaultAccount( szModuleName );
+}
+
+static INT_PTR Proto_RegisterModule(WPARAM, LPARAM lParam)
+{
+ PROTOCOLDESCRIPTOR* pd = ( PROTOCOLDESCRIPTOR* )lParam, *p;
+ if ( pd->cbSize != sizeof( PROTOCOLDESCRIPTOR ) && pd->cbSize != PROTOCOLDESCRIPTOR_V3_SIZE )
+ return 1;
+
+ p = ( PROTOCOLDESCRIPTOR* )mir_alloc( sizeof( PROTOCOLDESCRIPTOR ));
+ if ( !p )
+ return 2;
+
+ if ( pd->cbSize == PROTOCOLDESCRIPTOR_V3_SIZE ) {
+ memset( p, 0, sizeof( PROTOCOLDESCRIPTOR ));
+ p->cbSize = PROTOCOLDESCRIPTOR_V3_SIZE;
+ p->type = pd->type;
+ if ( p->type == PROTOTYPE_PROTOCOL ) {
+ // let's create a new container
+ PROTO_INTERFACE* ppi = AddDefaultAccount( pd->szName );
+ if ( ppi ) {
+ PROTOACCOUNT* pa = Proto_GetAccount( pd->szName );
+ if ( pa == NULL ) {
+ pa = (PROTOACCOUNT*)mir_calloc( sizeof( PROTOACCOUNT ));
+ pa->cbSize = sizeof(PROTOACCOUNT);
+ pa->type = PROTOTYPE_PROTOCOL;
+ pa->szModuleName = mir_strdup( pd->szName );
+ pa->szProtoName = mir_strdup( pd->szName );
+ pa->tszAccountName = mir_a2t( pd->szName );
+ pa->bIsVisible = pa->bIsEnabled = TRUE;
+ pa->iOrder = accounts.getCount();
+ accounts.insert( pa );
+ }
+ pa->bOldProto = TRUE;
+ pa->ppro = ppi;
+ p->fnInit = defInitProto;
+ p->fnUninit = FreeDefaultAccount;
+ }
+ }
+ }
+ else *p = *pd;
+ p->szName = mir_strdup( pd->szName );
+ protos.insert( p );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic core services
+
+static INT_PTR Proto_RecvFile(WPARAM,LPARAM lParam)
+{
+ CCSDATA* ccs = ( CCSDATA* )lParam;
+ PROTORECVEVENT* pre = ( PROTORECVEVENT* )ccs->lParam;
+ char* szFile = pre->szMessage + sizeof( DWORD );
+ char* szDescr = szFile + strlen( szFile ) + 1;
+
+ // Suppress the standard event filter
+ if ( pre->lParam != NULL )
+ *( DWORD* )pre->szMessage = 0;
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof( dbei );
+ dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0);
+ dbei.timestamp = pre->timestamp;
+ dbei.flags = ( pre->flags & PREF_CREATEREAD ) ? DBEF_READ : 0;
+ dbei.flags |= ( pre->flags & PREF_UTF ) ? DBEF_UTF : 0;
+ dbei.eventType = EVENTTYPE_FILE;
+ dbei.cbBlob = (DWORD)(sizeof( DWORD ) + strlen( szFile ) + strlen( szDescr ) + 2);
+ dbei.pBlob = ( PBYTE )pre->szMessage;
+ HANDLE hdbe = ( HANDLE )CallService( MS_DB_EVENT_ADD, ( WPARAM )ccs->hContact, ( LPARAM )&dbei );
+
+ if ( pre->lParam != NULL )
+ PushFileEvent( ccs->hContact, hdbe, pre->lParam );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void sttRecvCreateBlob( DBEVENTINFO& dbei, int fileCount, char** pszFiles, char* szDescr )
+{
+ dbei.cbBlob = sizeof( DWORD );
+ {
+ for ( int i=0; i < fileCount; i++ )
+ dbei.cbBlob += lstrlenA( pszFiles[i] ) + 1;
+ }
+
+ dbei.cbBlob += lstrlenA( szDescr ) + 1;
+
+ if (( dbei.pBlob = ( BYTE* )mir_alloc( dbei.cbBlob )) == 0 )
+ return;
+
+ *( DWORD* )dbei.pBlob = 0;
+ BYTE* p = dbei.pBlob + sizeof( DWORD );
+ for ( int i=0; i < fileCount; i++ ) {
+ strcpy(( char* )p, pszFiles[i] );
+ p += lstrlenA( pszFiles[i] ) + 1;
+ }
+ strcpy(( char* )p, ( szDescr == NULL ) ? "" : szDescr );
+}
+
+static INT_PTR Proto_RecvFileT(WPARAM,LPARAM lParam)
+{
+ CCSDATA* ccs = ( CCSDATA* )lParam;
+ PROTORECVFILET* pre = ( PROTORECVFILET* )ccs->lParam;
+ if ( pre->fileCount == 0 )
+ return 0;
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof( dbei );
+ dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0);
+ dbei.timestamp = pre->timestamp;
+ dbei.flags = ( pre->flags & PREF_CREATEREAD ) ? DBEF_READ : 0;
+ dbei.eventType = EVENTTYPE_FILE;
+
+ char** pszFiles = ( char** )alloca( pre->fileCount * sizeof(char*));
+ {
+ for ( int i=0; i < pre->fileCount; i++ )
+ pszFiles[i] = Utf8EncodeT( pre->ptszFiles[i] );
+ }
+ char* szDescr = Utf8EncodeT( pre->tszDescription );
+ dbei.flags |= DBEF_UTF;
+ sttRecvCreateBlob( dbei, pre->fileCount, pszFiles, szDescr );
+ {
+ for ( int i=0; i < pre->fileCount; i++ )
+ mir_free( pszFiles[i] );
+ }
+ mir_free( szDescr );
+
+ HANDLE hdbe = ( HANDLE )CallService( MS_DB_EVENT_ADD, ( WPARAM )ccs->hContact, ( LPARAM )&dbei );
+
+ PushFileEvent( ccs->hContact, hdbe, pre->lParam );
+ mir_free( dbei.pBlob );
+ return 0;
+}
+
+static INT_PTR Proto_RecvMessage(WPARAM,LPARAM lParam)
+{
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam;
+
+ if (pre->szMessage == NULL) return NULL;
+
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof( dbei );
+ dbei.szModule = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0);
+ dbei.timestamp = pre->timestamp;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = (DWORD)strlen( pre->szMessage ) + 1;
+ if ( pre->flags & PREF_CREATEREAD )
+ dbei.flags |= DBEF_READ;
+ if ( pre->flags & PREF_UTF )
+ dbei.flags |= DBEF_UTF;
+ if ( pre->flags & PREF_UNICODE )
+ dbei.cbBlob += sizeof( wchar_t )*( (DWORD)wcslen(( wchar_t* )&pre->szMessage[dbei.cbBlob+1] )+1 );
+
+ dbei.pBlob = ( PBYTE ) pre->szMessage;
+ return CallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// User Typing Notification services
+
+static int Proto_ValidTypingContact(HANDLE hContact, char *szProto)
+{
+ if ( !hContact || !szProto )
+ return 0;
+
+ return ( CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_4,0) & PF4_SUPPORTTYPING ) ? 1 : 0;
+}
+
+static INT_PTR Proto_SelfIsTyping(WPARAM wParam,LPARAM lParam)
+{
+ if ( lParam == PROTOTYPE_SELFTYPING_OFF || lParam == PROTOTYPE_SELFTYPING_ON ) {
+ char* szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 );
+ if ( !szProto )
+ return 0;
+
+ if ( Proto_ValidTypingContact(( HANDLE )wParam, szProto ))
+ CallProtoService( szProto, PSS_USERISTYPING, wParam, lParam );
+ }
+
+ return 0;
+}
+
+static INT_PTR Proto_ContactIsTyping(WPARAM wParam,LPARAM lParam)
+{
+ int type = (int)lParam;
+ char *szProto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 );
+ if ( !szProto )
+ return 0;
+
+ if ( CallService( MS_IGNORE_ISIGNORED, wParam, IGNOREEVENT_TYPINGNOTIFY ))
+ return 0;
+
+ if ( type < PROTOTYPE_CONTACTTYPING_OFF )
+ return 0;
+
+ if ( Proto_ValidTypingContact(( HANDLE )wParam, szProto ))
+ NotifyEventHooks( hTypeEvent, wParam, lParam );
+
+ return 0;
+}
+
+void Proto_SetStatus(const char* szProto, unsigned status)
+{
+ if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND)
+ {
+ TCHAR* awayMsg = (TCHAR* )CallService(MS_AWAYMSG_GETSTATUSMSGW, (WPARAM) status, (LPARAM) szProto);
+ if ((INT_PTR)awayMsg == CALLSERVICE_NOTFOUND)
+ {
+ char* awayMsgA = (char*)CallService(MS_AWAYMSG_GETSTATUSMSG, (WPARAM) status, (LPARAM) szProto);
+ if ((INT_PTR)awayMsgA != CALLSERVICE_NOTFOUND)
+ {
+ awayMsg = mir_a2t(awayMsgA);
+ mir_free(awayMsgA);
+ }
+ }
+ if ((INT_PTR)awayMsg != CALLSERVICE_NOTFOUND)
+ {
+ CallProtoService(szProto, PS_SETAWAYMSGT, status, (LPARAM) awayMsg);
+ mir_free(awayMsg);
+ } }
+ CallProtoService(szProto, PS_SETSTATUS, status, 0);
+}
+
+#ifdef _UNICODE
+char** __fastcall Proto_FilesMatrixA( wchar_t **files )
+{
+ if ( files == NULL ) return NULL;
+
+ int count = 0;
+ while( files[ count++ ] );
+
+ char** filesA = ( char** )mir_alloc( count * sizeof( char* ));
+ for( int i = 0; i < count; ++i )
+ filesA[ i ] = mir_u2a( files[ i ] );
+
+ return filesA;
+}
+
+static wchar_t** __fastcall Proto_FilesMatrixU( char **files )
+{
+ if ( files == NULL ) return NULL;
+
+ int count = 0;
+ while( files[ count++ ] );
+
+ wchar_t** filesU = ( wchar_t** )mir_alloc( count * sizeof( wchar_t* ));
+ for( int i = 0; i < count; ++i )
+ filesU[ i ] = mir_a2u( files[ i ] );
+
+ return filesU;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 0.8.0+ - accounts
+
+PROTOACCOUNT* __fastcall Proto_GetAccount( const char* accName )
+{
+ int idx;
+ PROTOACCOUNT temp;
+ temp.szModuleName = ( char* )accName;
+ if (( idx = accounts.getIndex( &temp )) == -1 )
+ return NULL;
+
+ return accounts[idx];
+}
+
+static INT_PTR srvProto_GetAccount(WPARAM, LPARAM lParam)
+{
+ return ( INT_PTR )Proto_GetAccount(( char* )lParam );
+}
+
+static INT_PTR Proto_EnumAccounts(WPARAM wParam, LPARAM lParam)
+{
+ *( int* )wParam = accounts.getCount();
+ *( PROTOACCOUNT*** )lParam = accounts.getArray();
+ return 0;
+}
+
+bool __fastcall Proto_IsAccountEnabled( PROTOACCOUNT* pa )
+{
+ return pa && (( pa->bIsEnabled && !pa->bDynDisabled ) || pa->bOldProto );
+}
+
+static INT_PTR srvProto_IsAccountEnabled(WPARAM, LPARAM lParam)
+{
+ return ( INT_PTR )Proto_IsAccountEnabled(( PROTOACCOUNT* )lParam);
+}
+
+bool __fastcall Proto_IsAccountLocked( PROTOACCOUNT* pa )
+{
+ return pa && DBGetContactSettingByte(NULL, pa->szModuleName, "LockMainStatus", 0) != 0;
+}
+
+static INT_PTR srvProto_IsAccountLocked(WPARAM, LPARAM lParam)
+{
+ return ( INT_PTR )Proto_IsAccountLocked( Proto_GetAccount(( char* )lParam ));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CallProtoServiceInt( HANDLE hContact, const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam )
+{
+ PROTOACCOUNT* pa = Proto_GetAccount( szModule );
+ if ( pa && !pa->bOldProto ) {
+ PROTO_INTERFACE* ppi;
+ if (( ppi = pa->ppro ) == NULL )
+ return CALLSERVICE_NOTFOUND;
+ else {
+ TServiceListItem *item = serviceItems.find(( TServiceListItem* )&szService );
+ if ( item ) {
+ switch( item->id ) {
+ case 1:
+#ifdef _UNICODE
+ if ( ppi->m_iVersion > 1 || !((( PROTOSEARCHRESULT* )lParam)->flags & PSR_UNICODE))
+ return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam );
+ else {
+ PROTOSEARCHRESULT *psr = ( PROTOSEARCHRESULT* )lParam;
+ PROTOSEARCHRESULT *psra =( PROTOSEARCHRESULT* )mir_alloc( psr->cbSize );
+ memcpy( psra, psr, psr->cbSize );
+ psra->nick = ( PROTOCHAR* )mir_u2a( psr->nick );
+ psra->firstName = ( PROTOCHAR* )mir_u2a( psr->firstName );
+ psra->lastName = ( PROTOCHAR* )mir_u2a( psr->lastName );
+ psra->email = ( PROTOCHAR* )mir_u2a( psr->email );
+
+ INT_PTR res = ( INT_PTR )ppi->AddToList( wParam, psra );
+
+ mir_free( psra->nick );
+ mir_free( psra->firstName );
+ mir_free( psra->lastName );
+ mir_free( psra->email );
+ mir_free( psra );
+
+ return res;
+ }
+#else
+ return ( INT_PTR )ppi->AddToList( wParam, (PROTOSEARCHRESULT*)lParam );
+#endif
+ case 2: return ( INT_PTR )ppi->AddToListByEvent( LOWORD(wParam), HIWORD(wParam), (HANDLE)lParam );
+ case 3: return ( INT_PTR )ppi->Authorize( ( HANDLE )wParam );
+ case 4:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, ( PROTOCHAR* )lParam );
+ case 5: return ( INT_PTR )ppi->AuthRecv( hContact, ( PROTORECVEVENT* )lParam );
+ case 6:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->AuthRequest( hContact, StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->AuthRequest( hContact, ( PROTOCHAR* )lParam );
+ case 7: return ( INT_PTR )ppi->ChangeInfo( wParam, ( void* )lParam );
+ case 8:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, ( PROTOCHAR* )lParam );
+ case 9: return ( INT_PTR )ppi->FileCancel( hContact, ( HANDLE )wParam );
+ case 10:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, ( PROTOCHAR* )lParam );
+ case 11: {
+ PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam;
+#ifdef _UNICODE
+ if ( ppi->m_iVersion > 1 ) {
+ PROTOCHAR* szFname = mir_a2t(( char* )pfr->szFilename );
+ INT_PTR res = ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action,
+ ( const PROTOCHAR** )&szFname);
+ mir_free(( PROTOCHAR* )pfr->szFilename );
+ pfr->szFilename = ( PROTOCHAR* )mir_t2a( szFname ); mir_free( szFname );
+ }
+ else
+#endif
+ return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action,
+ ( const PROTOCHAR** )&pfr->szFilename );
+ }
+ case 12: return ( INT_PTR )ppi->GetCaps( wParam, (HANDLE)lParam );
+ case 13: return ( INT_PTR )ppi->GetIcon( wParam );
+ case 14: return ( INT_PTR )ppi->GetInfo( hContact, wParam );;
+ case 15:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchBasic( StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->SearchBasic(( TCHAR* )lParam );
+ case 16:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchByEmail( StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->SearchByEmail(( TCHAR* )lParam );
+ case 17: {
+ PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam;
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchByName( StrConvT(( char* )psbn->pszNick ),
+ StrConvT(( char* )psbn->pszFirstName ), StrConvT(( char* )psbn->pszLastName ));
+ else
+ return ( INT_PTR )ppi->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName );
+ }
+ case 18: return ( INT_PTR )ppi->SearchAdvanced( ( HWND )lParam );
+ case 19: return ( INT_PTR )ppi->CreateExtendedSearchUI ( ( HWND )lParam );
+ case 20: return ( INT_PTR )ppi->RecvContacts( hContact, ( PROTORECVEVENT* )lParam );
+ case 21: return ( INT_PTR )ppi->RecvFile( hContact, ( PROTOFILEEVENT* )lParam );
+ case 22: return ( INT_PTR )ppi->RecvMsg( hContact, ( PROTORECVEVENT* )lParam );
+ case 23: return ( INT_PTR )ppi->RecvUrl( hContact, ( PROTORECVEVENT* )lParam );
+ case 24: return ( INT_PTR )ppi->SendContacts( hContact, LOWORD( wParam ), HIWORD( wParam ),
+ ( HANDLE* )lParam );
+ case 25:
+#ifdef _UNICODE
+ if ( ppi->m_iVersion > 1 ) {
+ TCHAR** files = Proto_FilesMatrixU(( char** )lParam );
+ INT_PTR res = ( INT_PTR )ppi->SendFile( hContact, StrConvT(( char* )wParam ), ( TCHAR** )files );
+ if ( res == 0 ) FreeFilesMatrix( &files );
+ return res;
+ }
+ else
+#endif
+ return ( INT_PTR )ppi->SendFile( hContact, ( TCHAR* )wParam, ( TCHAR** )lParam );
+ case 26: return ( INT_PTR )ppi->SendMsg( hContact, wParam, ( const char* )lParam );
+ case 27: return ( INT_PTR )ppi->SendUrl( hContact, wParam, ( const char* )lParam );
+ case 28: return ( INT_PTR )ppi->SetApparentMode( hContact, wParam );
+ case 29: return ( INT_PTR )ppi->SetStatus( wParam );
+ case 30: return ( INT_PTR )ppi->GetAwayMsg( hContact );
+ case 31: return ( INT_PTR )ppi->RecvAwayMsg( hContact, wParam, ( PROTORECVEVENT* )lParam );
+ case 32: return ( INT_PTR )ppi->SendAwayMsg( hContact, ( HANDLE )wParam, ( const char* )lParam );
+ case 33:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvT(( char* )lParam ));
+ else
+ return ( INT_PTR )ppi->SetAwayMsg( wParam, ( TCHAR* )lParam );
+ case 34: return ( INT_PTR )ppi->UserIsTyping( ( HANDLE )wParam, lParam );
+ case 35: lstrcpynA(( char* )lParam, ppi->m_szModuleName, wParam ); return 0;
+ case 36: return ppi->m_iStatus;
+
+#ifdef _UNICODE
+ case 100:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SetAwayMsg( wParam, ( TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->SetAwayMsg( wParam, StrConvA(( TCHAR* )lParam ));
+ case 102:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SendFile( hContact, ( TCHAR* )wParam, ( TCHAR** )lParam );
+ else {
+ char** files = Proto_FilesMatrixA(( TCHAR** )lParam );
+ INT_PTR res = ( INT_PTR )ppi->SendFile( hContact, StrConvA(( TCHAR* )wParam ), ( TCHAR** )files );
+ if ( res == 0 ) FreeFilesMatrix(( TCHAR*** )&files );
+ return res;
+ }
+ case 103:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, ( TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->FileAllow( hContact, ( HANDLE )wParam, StrConvA(( TCHAR* )lParam ));
+ case 104:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, ( TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->FileDeny( hContact, ( HANDLE )wParam, StrConvA(( TCHAR* )lParam ));
+ case 105: {
+ PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam;
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action,
+ ( const PROTOCHAR** )&pfr->szFilename );
+ else {
+ char* szFname = mir_t2a( pfr->szFilename );
+ INT_PTR res = ( INT_PTR )ppi->FileResume(( HANDLE )wParam, &pfr->action,
+ ( const PROTOCHAR** )&szFname);
+ mir_free( szFname );
+ } }
+ case 106:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->AuthRequest( hContact, ( const TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->AuthRequest( hContact, StrConvA(( const TCHAR* )lParam ));
+ case 107:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, ( const TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->AuthDeny(( HANDLE )wParam, StrConvA(( const TCHAR* )lParam ));
+ case 108:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchBasic(( const TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->SearchBasic(StrConvA(( const TCHAR* )lParam ));
+ case 109: {
+ PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam;
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName );
+ else
+ return ( INT_PTR )ppi->SearchByName( StrConvA(( TCHAR* )psbn->pszNick ),
+ StrConvA(( TCHAR* )psbn->pszFirstName ), StrConvA(( TCHAR* )psbn->pszLastName ));
+ }
+ case 110:
+ if ( ppi->m_iVersion > 1 )
+ return ( INT_PTR )ppi->SearchByEmail(( const TCHAR* )lParam );
+ else
+ return ( INT_PTR )ppi->SearchByEmail(StrConvA(( const TCHAR* )lParam ));
+#endif
+ } } } }
+
+#ifdef _UNICODE
+ if ( strcmp( szService, PS_ADDTOLIST ) == 0 ) {
+ PROTOSEARCHRESULT *psr = ( PROTOSEARCHRESULT* )lParam;
+ PROTOSEARCHRESULT *psra =( PROTOSEARCHRESULT* )mir_alloc( psr->cbSize );
+ memcpy( psra, psr, psr->cbSize );
+ psra->nick = ( PROTOCHAR* )mir_u2a( psr->nick );
+ psra->firstName = ( PROTOCHAR* )mir_u2a( psr->firstName );
+ psra->lastName = ( PROTOCHAR* )mir_u2a( psr->lastName );
+ psra->email = ( PROTOCHAR* )mir_u2a( psr->email );
+
+ INT_PTR res = MyCallProtoService( szModule, szService, wParam, ( LPARAM )psra );
+
+ mir_free( psra->nick );
+ mir_free( psra->firstName );
+ mir_free( psra->lastName );
+ mir_free( psra->email );
+ mir_free( psra );
+
+ return res;
+ }
+#endif
+
+ INT_PTR res = MyCallProtoService( szModule, szService, wParam, lParam );
+
+#ifdef _UNICODE
+ if ( res == CALLSERVICE_NOTFOUND && pa && pa->bOldProto && pa->ppro && strchr( szService, 'W' )) {
+ TServiceListItem *item = serviceItems.find(( TServiceListItem* )&szService );
+ if ( !item ) return res;
+
+ switch( item->id ) {
+ case 100:
+ return ( INT_PTR )pa->ppro->SetAwayMsg( wParam, ( TCHAR* )lParam );
+ case 102: {
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ return ( INT_PTR )pa->ppro->SendFile( ccs->hContact, ( TCHAR* )ccs->wParam, ( TCHAR** )ccs->lParam );
+ }
+ case 103: {
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ return ( INT_PTR )pa->ppro->FileAllow( ccs->hContact, ( HANDLE )ccs->wParam, ( TCHAR* )ccs->lParam );
+ }
+ case 104: {
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ return ( INT_PTR )pa->ppro->FileDeny( ccs->hContact, ( HANDLE )ccs->wParam, ( TCHAR* )ccs->lParam );
+ }
+ case 105: {
+ PROTOFILERESUME* pfr = ( PROTOFILERESUME* )lParam;
+ return ( INT_PTR )pa->ppro->FileResume(( HANDLE )wParam, &pfr->action, &pfr->szFilename );
+ }
+ case 106: {
+ CCSDATA *ccs = ( CCSDATA* )lParam;
+ return ( INT_PTR )pa->ppro->AuthRequest( ccs->hContact, ( const TCHAR* )ccs->lParam );
+ }
+ case 107:
+ return ( INT_PTR )pa->ppro->AuthDeny(( HANDLE )wParam, ( const TCHAR* )lParam );
+ case 108:
+ return ( INT_PTR )pa->ppro->SearchBasic(( const TCHAR* )lParam );
+ case 109: {
+ PROTOSEARCHBYNAME* psbn = ( PROTOSEARCHBYNAME* )lParam;
+ return ( INT_PTR )pa->ppro->SearchByName( psbn->pszNick, psbn->pszFirstName, psbn->pszLastName );
+ }
+ case 110:
+ return ( INT_PTR )pa->ppro->SearchByEmail(( const TCHAR* )lParam );
+ } }
+#endif
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CallContactService( HANDLE hContact, const char *szProtoService, WPARAM wParam, LPARAM lParam )
+{
+ int i;
+ DBVARIANT dbv;
+ INT_PTR ret;
+ PROTOACCOUNT* pa;
+ CCSDATA ccs = { hContact, szProtoService, wParam, lParam };
+
+ for ( i = 0;; i++ ) {
+ char str[10];
+ _itoa( i, str, 10 );
+ if ( DBGetContactSettingString( hContact, "_Filter", str, &dbv ))
+ break;
+
+ if (( ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, i+1, ( LPARAM)&ccs )) != CALLSERVICE_NOTFOUND ) {
+ //chain was started, exit
+ mir_free( dbv.pszVal );
+ return ret;
+ }
+ mir_free( dbv.pszVal );
+ }
+ if ( DBGetContactSettingString( hContact, "Protocol", "p", &dbv ))
+ return 1;
+
+ pa = Proto_GetAccount( dbv.pszVal );
+ if ( pa == NULL || pa->ppro == NULL )
+ ret = 1;
+ else {
+ if ( pa->bOldProto )
+ ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, (WPARAM)(-1), ( LPARAM)&ccs );
+ else
+ ret = CallProtoServiceInt( hContact, dbv.pszVal, szProtoService, wParam, lParam );
+ if ( ret == CALLSERVICE_NOTFOUND )
+ ret = 1;
+ }
+
+ mir_free( dbv.pszVal );
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void InsertServiceListItem( int id, const char* szName )
+{
+ TServiceListItem* p = ( TServiceListItem* )mir_alloc( sizeof( TServiceListItem ));
+ p->id = id;
+ p->name = szName;
+ serviceItems.insert( p );
+}
+
+int LoadProtocolsModule(void)
+{
+ bModuleInitialized = TRUE;
+
+ if ( LoadProtoChains() )
+ return 1;
+
+ InsertServiceListItem( 1, PS_ADDTOLIST );
+ InsertServiceListItem( 2, PS_ADDTOLISTBYEVENT );
+ InsertServiceListItem( 3, PS_AUTHALLOW );
+ InsertServiceListItem( 4, PS_AUTHDENY );
+ InsertServiceListItem( 5, PSR_AUTH );
+ InsertServiceListItem( 6, PSS_AUTHREQUEST );
+ InsertServiceListItem( 7, PS_CHANGEINFO );
+ InsertServiceListItem( 8, PSS_FILEALLOW );
+ InsertServiceListItem( 9, PSS_FILECANCEL );
+ InsertServiceListItem( 10, PSS_FILEDENY );
+ InsertServiceListItem( 11, PS_FILERESUME );
+ InsertServiceListItem( 12, PS_GETCAPS );
+ InsertServiceListItem( 13, PS_LOADICON );
+ InsertServiceListItem( 14, PSS_GETINFO );
+ InsertServiceListItem( 15, PS_BASICSEARCH );
+ InsertServiceListItem( 16, PS_SEARCHBYEMAIL );
+ InsertServiceListItem( 17, PS_SEARCHBYNAME );
+ InsertServiceListItem( 18, PS_SEARCHBYADVANCED );
+ InsertServiceListItem( 19, PS_CREATEADVSEARCHUI );
+ InsertServiceListItem( 20, PSR_CONTACTS );
+ InsertServiceListItem( 21, PSR_FILE );
+ InsertServiceListItem( 22, PSR_MESSAGE );
+ InsertServiceListItem( 23, PSR_URL );
+ InsertServiceListItem( 24, PSS_CONTACTS );
+ InsertServiceListItem( 25, PSS_FILE );
+ InsertServiceListItem( 26, PSS_MESSAGE );
+ InsertServiceListItem( 27, PSS_URL );
+ InsertServiceListItem( 28, PSS_SETAPPARENTMODE );
+ InsertServiceListItem( 29, PS_SETSTATUS );
+ InsertServiceListItem( 30, PSS_GETAWAYMSG );
+ InsertServiceListItem( 31, PSR_AWAYMSG );
+ InsertServiceListItem( 32, PSS_AWAYMSG );
+ InsertServiceListItem( 33, PS_SETAWAYMSG );
+ InsertServiceListItem( 34, PSS_USERISTYPING );
+ InsertServiceListItem( 35, PS_GETNAME );
+ InsertServiceListItem( 36, PS_GETSTATUS );
+
+#ifdef _UNICODE
+ InsertServiceListItem( 100, PS_SETAWAYMSGW );
+ InsertServiceListItem( 102, PSS_FILEW );
+ InsertServiceListItem( 103, PSS_FILEALLOWW );
+ InsertServiceListItem( 104, PSS_FILEDENYW );
+ InsertServiceListItem( 105, PS_FILERESUMEW );
+ InsertServiceListItem( 106, PSS_AUTHREQUESTW );
+ InsertServiceListItem( 107, PS_AUTHDENYW );
+ InsertServiceListItem( 108, PS_BASICSEARCHW );
+ InsertServiceListItem( 109, PS_SEARCHBYNAMEW );
+ InsertServiceListItem( 110, PS_SEARCHBYEMAILW );
+#endif
+
+ hAckEvent = CreateHookableEvent(ME_PROTO_ACK);
+ hTypeEvent = CreateHookableEvent(ME_PROTO_CONTACTISTYPING);
+ hAccListChanged = CreateHookableEvent(ME_PROTO_ACCLISTCHANGED);
+
+ CreateServiceFunction( MS_PROTO_BROADCASTACK, Proto_BroadcastAck );
+ CreateServiceFunction( MS_PROTO_ISPROTOCOLLOADED, srvProto_IsLoaded );
+ CreateServiceFunction( MS_PROTO_ENUMPROTOS, Proto_EnumProtocols );
+ CreateServiceFunction( MS_PROTO_REGISTERMODULE, Proto_RegisterModule );
+ CreateServiceFunction( MS_PROTO_SELFISTYPING, Proto_SelfIsTyping );
+ CreateServiceFunction( MS_PROTO_CONTACTISTYPING, Proto_ContactIsTyping );
+
+ CreateServiceFunction( MS_PROTO_RECVFILE, Proto_RecvFile );
+ CreateServiceFunction( MS_PROTO_RECVFILET, Proto_RecvFileT );
+ CreateServiceFunction( MS_PROTO_RECVMSG, Proto_RecvMessage );
+
+ CreateServiceFunction( "Proto/EnumProtocols", Proto_EnumAccounts );
+ CreateServiceFunction( MS_PROTO_ENUMACCOUNTS, Proto_EnumAccounts );
+ CreateServiceFunction( MS_PROTO_GETACCOUNT, srvProto_GetAccount );
+
+ CreateServiceFunction( MS_PROTO_ISACCOUNTENABLED, srvProto_IsAccountEnabled );
+ CreateServiceFunction( MS_PROTO_ISACCOUNTLOCKED, srvProto_IsAccountLocked );
+
+ return LoadProtoOptions();
+}
+
+void UnloadProtocolsModule()
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ if ( hAckEvent ) {
+ DestroyHookableEvent(hAckEvent);
+ hAckEvent = NULL;
+ }
+ if ( hAccListChanged ) {
+ DestroyHookableEvent(hAccListChanged);
+ hAccListChanged = NULL;
+ }
+
+ if ( protos.getCount() ) {
+ for( i=0; i < protos.getCount(); i++ ) {
+ mir_free( protos[i]->szName);
+ mir_free( protos[i] );
+ }
+ protos.destroy();
+ }
+
+ for ( i=0; i < serviceItems.getCount(); i++ )
+ mir_free( serviceItems[i] );
+ serviceItems.destroy();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+pfnUninitProto GetProtocolDestructor( char* szProto )
+{
+ int idx;
+ PROTOCOLDESCRIPTOR temp;
+ temp.szName = szProto;
+ if (( idx = protos.getIndex( &temp )) != -1 )
+ return protos[idx]->fnUninit;
+
+ return NULL;
+}
diff --git a/src/modules/protocols/protodir.cpp b/src/modules/protocols/protodir.cpp
new file mode 100644
index 0000000000..7a110268ef
--- /dev/null
+++ b/src/modules/protocols/protodir.cpp
@@ -0,0 +1,248 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2005 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+#if 0
+
+extern HANDLE hCacheHeap;
+
+/*
+
+ the id cache has id/proto against hContact to lookup ID's fast to resolve to hContact,
+ the protoBaseCache has hContact's sorted, so can lookup hContact->proto fast, these two caches
+ share the same data, they're indexes, each entry might not have an "id" set.
+
+ There is a small cache which maintains a protocol list
+
+*/
+
+/*
+
+ The information we need to cache is not readily available, data has to be gathered at startup
+ when a new contact is created/deleted, when a new proto registers and so on.
+
+ The information we get at startup includes walking the contact chain and reading Protocol/p which
+ will give us the protocol the contact is on, all this info is stored in contactEntry's within
+ protoCache ONLY - contactCache is EMPTY at this point.
+
+ We can not fetch the id of the contact because this information is only in SOME protocol plugins,
+ this is a problem but we'll hook all proto registrations and ask each proto if it supports the
+ returning this ID name, if not - it won't even use our id <-> contact look so no biggie!
+
+*/
+
+typedef struct {
+ char * proto; // within proto cache
+ char * id; // optional
+ HANDLE hContact;
+} contactEntry;
+
+typedef struct {
+ CRITICAL_SECTION csLock;
+ SortedList contactCache; // index for id/proto -> hContact
+ SortedList protoCache; // index for hContact -> proto/id
+ SortedList protoNameCache; // index of protocol names
+} contactDir;
+
+static contactDir condir;
+
+// compare's id/proto and return's hContact's
+int contactCacheCompare(void * a, void * b)
+{
+ contactEntry * x = (contactEntry *) a;
+ contactEntry * y = (contactEntry *) b;
+ int rc=0;
+ // same protocol?
+ rc = strcmp(x->proto, y->proto);
+ if ( rc == 0 ) {
+ // same id? id's might be missing
+ if ( x->id && y->id ) rc = strcmp(x->id, y->id);
+ }
+ return rc;
+}
+
+// compares hContact's and returns associated data
+int protoCacheCompare(void * a, void * b)
+{
+ contactEntry * x = (contactEntry *) a;
+ contactEntry * y = (contactEntry *) b;
+ if ( x->hContact < y->hContact ) return -1;
+ if ( x->hContact > y->hContact ) return 1;
+ return 0;
+}
+
+// keeps a list of protocol names
+int protoNameCacheCompare(void * a, void * b)
+{
+ return strcmp( (char *)a, (char*)b );
+}
+
+// cache the protocol string so that its not allocated per contact but shared
+char * contactDir_Proto_Add(contactDir * cd, char * proto)
+{
+ int index = 0 ;
+ char * szCache = 0;
+ EnterCriticalSection(&cd->csLock);
+ if ( List_GetIndex(&cd->protoNameCache, proto, &index) ) szCache = cd->protoNameCache.items[index];
+ else {
+ szCache = HeapAlloc(hCacheHeap, HEAP_NO_SERIALIZE, strlen(proto)+1);
+ strcpy(szCache, proto);
+ List_Insert(&cd->protoNameCache, szCache, index);
+ }
+ LeaveCriticalSection(&cd->csLock);
+ return szCache;
+}
+
+// thread safe
+char * contactDir_Proto_Get(contactDir * cd, HANDLE hContact)
+{
+ char * szCache = 0;
+ int index = 0;
+ contactEntry e;
+ e.hContact=hContact;
+ e.proto="";
+ e.id="";
+ EnterCriticalSection(&cd->csLock);
+ if ( List_GetIndex(&cd->protoCache, &e, &index) ) {
+ contactEntry * p = cd->protoCache.items[index];
+ szCache = p->proto;
+ }
+ LeaveCriticalSection(&cd->csLock);
+ return szCache;
+}
+
+// thread tolerant, if updating id dont pass proto, if updating proto dont pass id
+void contactDir_Contact_Add(contactDir * cd, HANDLE hContact, char * proto, char * id)
+{
+ // if a contact is gonna exist anywhere it's going to be in the ->protoCache which has a key of hContact
+ // if id is not null then the contact should be indexed via the ->contactCache instead
+ if ( id == NULL ) {
+ int index = 0;
+ contactEntry e;
+ e.hContact=hContact;
+ e.proto = proto;
+ e.id = "";
+ EnterCriticalSection(&cd->csLock);
+ if ( List_GetIndex(&cd->protoCache, &e, &index) ) {
+ contactEntry * p = cd->protoCache.items[index];
+ // this hContact is in the cache, protcol changing?
+ p->proto = contactDir_Proto_Add(cd, proto); // just replace the old pointer
+ } else {
+ contactEntry * p = 0;
+ // this hContact isn't in the cache, add it
+ p = HeapAlloc(hCacheHeap, HEAP_NO_SERIALIZE, sizeof(contactEntry));
+ p->proto = contactDir_Proto_Add(cd, proto);
+ p->id = 0;
+ p->hContact = hContact;
+ // add it
+ List_Insert(&cd->protoCache, p, index);
+ }
+ LeaveCriticalSection(&cd->csLock);
+ } else {
+ // this contact HAS to be in ->protoCache since it was added during startup
+ // need to find the contactEntry* that should already exist for it
+ } //if
+}
+
+// only expected to be called at startup.
+void contactDir_Proto_Walk(contactDir * cd)
+{
+ HANDLE hContact;
+ char buf[128];
+ DBCONTACTGETSETTING gsProto;
+ DBVARIANT dbvProto;
+ // setup the read structure
+ gsProto.szModule="Protocol";
+ gsProto.szSetting="p";
+ gsProto.pValue = &dbvProto;
+ // this might not work
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while ( hContact ) {
+ // and how we'll get the reset
+ dbvProto.type=DBVT_ASCIIZ;
+ dbvProto.pszVal = (char *) &buf;
+ dbvProto.cchVal = SIZEOF(buf);
+ // figure out what hContact/Protocol/p is
+ if ( CallService(MS_DB_CONTACT_GETSETTINGSTATIC,(WPARAM)hContact, (LPARAM)&gsProto) == 0 ) {
+ contactDir_Contact_Add(cd, hContact, buf, NULL);
+ }
+ // find next
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+// ctor/dtor
+
+void contactDir_Init(contactDir * cd)
+{
+ InitializeCriticalSection(&cd->csLock);
+ cd->contactCache.increment=50;
+ cd->contactCache.sortFunc=contactCacheCompare;
+ cd->protoCache.increment=50;
+ cd->protoCache.sortFunc=protoCacheCompare;
+ cd->protoNameCache.increment=5;
+ cd->protoNameCache.sortFunc=protoNameCacheCompare;
+ // build a list of all hContact's and what proto's they are on
+ contactDir_Proto_Walk(cd);
+}
+
+void contactDir_Deinit(contactDir * cd)
+{
+ List_Destroy(&cd->contactCache);
+ List_Destroy(&cd->protoCache);
+ List_Destroy(&cd->protoNameCache);
+ DeleteCriticalSection(&cd->csLock);
+}
+
+static int contactDirGetProto(WPARAM wParam, LPARAM lParam)
+{
+ return (int) contactDir_Proto_Get(&condir,(HANDLE)wParam);
+}
+
+#endif
+
+void InitContactDir(void)
+{
+ return;
+ //contactDir_Init(&condir);
+ //CreateServiceFunction(MS_PROTODIR_PROTOFROMCONTACT, contactDirGetProto);
+}
+
+
+void UninitContactDir(void)
+{
+ return;
+#if 0
+ {
+ int j;
+ for ( j = 0; j< condir.protoCache.realCount; j++) {
+ char buf[128];
+ contactEntry * p = condir.protoCache.items[j];
+ mir_snprintf(buf,SIZEOF(buf)," [%s] %s @ %x \n", p->proto, p->id ? p->id : "", p->hContact);
+ OutputDebugString(buf);
+ }
+ }
+ contactDir_Deinit(&condir);
+#endif
+}
diff --git a/src/modules/protocols/protoint.cpp b/src/modules/protocols/protoint.cpp
new file mode 100644
index 0000000000..dec59cce98
--- /dev/null
+++ b/src/modules/protocols/protoint.cpp
@@ -0,0 +1,271 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+char** __fastcall Proto_FilesMatrixA( TCHAR **files );
+void FreeFilesMatrix( TCHAR ***files );
+
+INT_PTR __fastcall MyCallProtoService( const char *szModule, const char *szService, WPARAM wParam, LPARAM lParam )
+{
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf( str, sizeof(str), "%s%s", szModule, szService );
+ return CallService(str,wParam,lParam);
+}
+
+struct DEFAULT_PROTO_INTERFACE : public PROTO_INTERFACE
+{
+ HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr )
+ { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_ADDTOLIST, flags, (LPARAM)psr );
+ }
+
+ HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent )
+ { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_ADDTOLISTBYEVENT, MAKELONG(flags, iContact), (LPARAM)hDbEvent );
+ }
+
+ int __cdecl Authorize( HANDLE hContact )
+ { return ( int )MyCallProtoService( m_szModuleName, PS_AUTHALLOW, (WPARAM)hContact, 0 );
+ }
+
+ int __cdecl AuthDeny( HANDLE hContact, const TCHAR* szReason )
+ { return ( int )MyCallProtoService( m_szModuleName, PS_AUTHDENY, (WPARAM)hContact, (LPARAM)StrConvA(szReason));
+ }
+
+ int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_AUTH, 0, (LPARAM)evt };
+ return ( int )MyCallProtoService( m_szModuleName, PSR_AUTH, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl AuthRequest( HANDLE hContact, const TCHAR* szMessage )
+ { CCSDATA ccs = { hContact, PSS_AUTHREQUEST, 0, (LPARAM)szMessage };
+ ccs.lParam = ( LPARAM )mir_t2a( szMessage );
+ int res = ( int )MyCallProtoService( m_szModuleName, PSS_AUTHREQUEST, 0, (LPARAM)&ccs );
+ mir_free(( char* )ccs.lParam );
+ return res;
+ }
+
+ HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData )
+ { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_CHANGEINFO, iInfoType, ( LPARAM )pInfoData );
+ }
+
+ HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath )
+ { CCSDATA ccs = { hContact, PSS_FILEALLOW, (WPARAM)hTransfer, (LPARAM)szPath };
+#ifdef _UNICODE
+ ccs.lParam = ( LPARAM )mir_t2a( szPath );
+ HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILEALLOW, 0, (LPARAM)&ccs );
+ mir_free(( char* )ccs.lParam );
+ return res;
+#else
+ return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILEALLOW, 0, (LPARAM)&ccs );
+#endif
+ }
+
+ int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer )
+ { CCSDATA ccs = { hContact, PSS_FILECANCEL, (WPARAM)hTransfer, 0 };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_FILECANCEL, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason )
+ { CCSDATA ccs = { hContact, PSS_FILEDENY, (WPARAM)hTransfer, (LPARAM)szReason };
+#ifdef _UNICODE
+ ccs.lParam = ( LPARAM )mir_t2a( szReason );
+ int res = ( int )MyCallProtoService( m_szModuleName, PSS_FILEDENY, 0, (LPARAM)&ccs );
+ mir_free(( char* )ccs.lParam );
+ return res;
+#else
+ return ( int )MyCallProtoService( m_szModuleName, PSS_FILEDENY, 0, (LPARAM)&ccs );
+#endif
+ }
+
+ int __cdecl FileResume( HANDLE hTransfer, int* action, const PROTOCHAR** szFilename )
+ { PROTOFILERESUME pfr = { *action, *szFilename };
+#ifdef _UNICODE
+ pfr.szFilename = ( PROTOCHAR* )mir_t2a( pfr.szFilename );
+ int res = ( int )MyCallProtoService( m_szModuleName, PS_FILERESUME, ( WPARAM )hTransfer, ( LPARAM )&pfr);
+ mir_free(( PROTOCHAR* )*szFilename );
+ *action = pfr.action; *szFilename = (PROTOCHAR*)pfr.szFilename;
+#else
+ int res = ( int )MyCallProtoService( m_szModuleName, PS_FILERESUME, ( WPARAM )hTransfer, ( LPARAM )&pfr );
+ *action = pfr.action; *szFilename = (PROTOCHAR*)pfr.szFilename;
+#endif
+ return res;
+ }
+
+ DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact )
+ { return ( DWORD_PTR )MyCallProtoService( m_szModuleName, PS_GETCAPS, type, (LPARAM)hContact );
+ }
+
+ HICON __cdecl GetIcon( int iconIndex )
+ { return ( HICON )MyCallProtoService( m_szModuleName, PS_LOADICON, iconIndex, 0 );
+ }
+
+ int __cdecl GetInfo( HANDLE hContact, int flags )
+ { CCSDATA ccs = { hContact, PSS_GETINFO, flags, 0 };
+ return MyCallProtoService( m_szModuleName, PSS_GETINFO, 0, (LPARAM)&ccs );
+ }
+
+ HANDLE __cdecl SearchBasic( const PROTOCHAR* id )
+ { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_BASICSEARCH, 0, ( LPARAM )StrConvA( id ));
+ }
+
+ HANDLE __cdecl SearchByEmail( const PROTOCHAR* email )
+ { return ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYEMAIL, 0, ( LPARAM )StrConvA( email ));
+ }
+
+ HANDLE __cdecl SearchByName( const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName )
+ { PROTOSEARCHBYNAME psn;
+#ifdef _UNICODE
+ psn.pszNick = ( PROTOCHAR* )mir_t2a( nick );
+ psn.pszFirstName = ( PROTOCHAR* )mir_t2a( firstName );
+ psn.pszLastName = ( PROTOCHAR* )mir_t2a( lastName );
+ HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYNAME, 0, ( LPARAM )&psn );
+ mir_free( psn.pszNick );
+ mir_free( psn.pszFirstName );
+ mir_free( psn.pszLastName );
+ return res;
+#else
+ psn.pszNick = ( char* )nick;
+ psn.pszFirstName = ( char* )firstName;
+ psn.pszLastName = ( char* )lastName;
+ return ( HANDLE )MyCallProtoService( m_szModuleName, PS_SEARCHBYNAME, 0, ( LPARAM )&psn );
+#endif
+ }
+
+ HWND __cdecl SearchAdvanced( HWND owner )
+ { return ( HWND )MyCallProtoService( m_szModuleName, PS_SEARCHBYADVANCED, 0, ( LPARAM )owner );
+ }
+
+ HWND __cdecl CreateExtendedSearchUI( HWND owner )
+ { return ( HWND )MyCallProtoService( m_szModuleName, PS_CREATEADVSEARCHUI, 0, ( LPARAM )owner );
+ }
+
+ int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_CONTACTS, 0, (LPARAM)evt };
+ return ( int )MyCallProtoService( m_szModuleName, PSR_CONTACTS, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl RecvFile( HANDLE hContact, PROTOFILEEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)evt };
+ return MyCallProtoService( m_szModuleName, PSR_FILE, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_MESSAGE, 0, (LPARAM)evt };
+ return ( int )MyCallProtoService( m_szModuleName, PSR_MESSAGE, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_URL, 0, (LPARAM)evt };
+ return ( int )MyCallProtoService( m_szModuleName, PSR_URL, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList )
+ { CCSDATA ccs = { hContact, PSS_CONTACTS, MAKEWPARAM(flags,nContacts), (LPARAM)hContactsList };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_CONTACTS, 0, (LPARAM)&ccs );
+ }
+
+ HANDLE __cdecl SendFile( HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles )
+ { CCSDATA ccs = { hContact, PSS_FILE, (WPARAM)szDescription, (LPARAM)ppszFiles };
+#ifdef _UNICODE
+ ccs.wParam = ( WPARAM )mir_t2a( szDescription );
+ ccs.lParam = ( LPARAM )Proto_FilesMatrixA( ppszFiles );
+ HANDLE res = ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILE, 0, ( LPARAM )&ccs );
+ if ( res == 0 ) FreeFilesMatrix(( TCHAR*** )&ccs.lParam );
+ mir_free(( char* )ccs.wParam );
+ return res;
+#else
+ return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_FILE, 0, (LPARAM)&ccs );
+#endif
+ }
+
+ int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg )
+ { CCSDATA ccs = { hContact, PSS_MESSAGE, flags, (LPARAM)msg };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_MESSAGE, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SendUrl( HANDLE hContact, int flags, const char* url )
+ { CCSDATA ccs = { hContact, PSS_URL, flags, (LPARAM)url };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_URL, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SetApparentMode( HANDLE hContact, int mode )
+ { CCSDATA ccs = { hContact, PSS_SETAPPARENTMODE, mode, 0 };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_SETAPPARENTMODE, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SetStatus( int iNewStatus )
+ { return ( int )MyCallProtoService( m_szModuleName, PS_SETSTATUS, iNewStatus, 0 );
+ }
+
+ HANDLE __cdecl GetAwayMsg( HANDLE hContact )
+ { CCSDATA ccs = { hContact, PSS_GETAWAYMSG, 0, 0 };
+ return ( HANDLE )MyCallProtoService( m_szModuleName, PSS_GETAWAYMSG, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl RecvAwayMsg( HANDLE hContact, int statusMode, PROTORECVEVENT* evt )
+ { CCSDATA ccs = { hContact, PSR_AWAYMSG, statusMode, (LPARAM)evt };
+ return ( int )MyCallProtoService( m_szModuleName, PSR_AWAYMSG, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg )
+ { CCSDATA ccs = { hContact, PSS_AWAYMSG, (WPARAM)hProcess, (LPARAM)msg };
+ return ( int )MyCallProtoService( m_szModuleName, PSS_AWAYMSG, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl SetAwayMsg( int iStatus, const TCHAR* msg )
+ { return ( int )MyCallProtoService( m_szModuleName, PS_SETAWAYMSG, iStatus, (LPARAM)StrConvA(msg));
+ }
+
+ int __cdecl UserIsTyping( HANDLE hContact, int type )
+ { CCSDATA ccs = { hContact, PSS_USERISTYPING, (WPARAM)hContact, type };
+ return MyCallProtoService( m_szModuleName, PSS_USERISTYPING, 0, (LPARAM)&ccs );
+ }
+
+ int __cdecl OnEvent( PROTOEVENTTYPE, WPARAM, LPARAM )
+ {
+ return 0;
+ }
+};
+
+// creates the default protocol container for compatibility with the old plugins
+
+PROTO_INTERFACE* AddDefaultAccount( const char* szProtoName )
+{
+ PROTO_INTERFACE* ppi = new DEFAULT_PROTO_INTERFACE;
+ if ( ppi != NULL ) {
+ ppi->m_iVersion = 1;
+ ppi->m_szModuleName = mir_strdup( szProtoName );
+ ppi->m_szProtoName = mir_strdup( szProtoName );
+ ppi->m_tszUserName = mir_a2t( szProtoName );
+ }
+ return ppi;
+}
+
+int FreeDefaultAccount( PROTO_INTERFACE* ppi )
+{
+ mir_free( ppi->m_szModuleName );
+ mir_free( ppi->m_szProtoName );
+ mir_free( ppi->m_tszUserName );
+ delete ppi;
+ return 0;
+}
diff --git a/src/modules/protocols/protoopts.cpp b/src/modules/protocols/protoopts.cpp
new file mode 100644
index 0000000000..3d9c0a2b36
--- /dev/null
+++ b/src/modules/protocols/protoopts.cpp
@@ -0,0 +1,1084 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+#define LBN_MY_CHECK 0x1000
+#define LBN_MY_RENAME 0x1001
+
+#define WM_MY_REFRESH (WM_USER+0x1000)
+#define WM_MY_RENAME (WM_USER+0x1001)
+
+INT_PTR Proto_EnumProtocols( WPARAM, LPARAM );
+bool CheckProtocolOrder(void);
+
+#define errMsg \
+"WARNING! The account is going to be deleted. It means that all its \
+settings, contacts and histories will be also erased.\n\n\
+Are you absolutely sure?"
+
+#define upgradeMsg \
+"Your account was successfully upgraded. \
+To activate it, restart of Miranda is needed.\n\n\
+If you want to restart Miranda now, press Yes, if you want to upgrade another account, press No"
+
+#define legacyMsg \
+"This account uses legacy protocol plugin. \
+Use Miranda IM options dialogs to change it's preferences."
+
+#define welcomeMsg \
+"Welcome to Miranda IM's account manager!\n \
+Here you can set up your IM accounts.\n\n \
+Select an account from the list on the left to see the available options. \
+Alternatively, just click on the Plus sign underneath the list to set up a new IM account."
+
+static HWND hAccMgr = NULL;
+
+extern HANDLE hAccListChanged;
+
+int UnloadPlugin( TCHAR* buf, int bufLen );
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Account edit form
+// Gets PROTOACCOUNT* as a parameter, or NULL to edit a new one
+
+typedef struct
+{
+ int action;
+ PROTOACCOUNT* pa;
+}
+ AccFormDlgParam;
+
+static INT_PTR CALLBACK AccFormDlgProc(HWND hwndDlg,UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch( message ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ PROTOCOLDESCRIPTOR** proto;
+ int protoCount, i, cnt = 0;
+ Proto_EnumProtocols(( WPARAM )&protoCount, ( LPARAM )&proto );
+ for ( i=0; i < protoCount; i++ ) {
+ PROTOCOLDESCRIPTOR* pd = proto[i];
+ if ( pd->type == PROTOTYPE_PROTOCOL && pd->cbSize == sizeof( *pd )) {
+ SendDlgItemMessageA( hwndDlg, IDC_PROTOTYPECOMBO, CB_ADDSTRING, 0, (LPARAM)proto[i]->szName );
+ ++cnt;
+ }
+ }
+ SendDlgItemMessage( hwndDlg, IDC_PROTOTYPECOMBO, CB_SETCURSEL, 0, 0 );
+ EnableWindow( GetDlgItem( hwndDlg, IDOK ), cnt != 0 );
+
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, lParam );
+ AccFormDlgParam* param = ( AccFormDlgParam* )lParam;
+
+ if ( param->action == PRAC_ADDED ) // new account
+ SetWindowText( hwndDlg, TranslateT( "Create new account" ));
+ else {
+ TCHAR str[200];
+ if ( param->action == PRAC_CHANGED ) { // update
+ EnableWindow( GetDlgItem( hwndDlg, IDC_PROTOTYPECOMBO ), FALSE );
+ mir_sntprintf( str, SIZEOF(str), _T("%s: %s"), TranslateT( "Editing account" ), param->pa->tszAccountName );
+ }
+ else mir_sntprintf( str, SIZEOF(str), _T("%s: %s"), TranslateT( "Upgrading account" ), param->pa->tszAccountName );
+
+ SetWindowText( hwndDlg, str );
+ SetDlgItemText( hwndDlg, IDC_ACCNAME, param->pa->tszAccountName );
+ SetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, param->pa->szModuleName );
+ SendDlgItemMessageA( hwndDlg, IDC_PROTOTYPECOMBO, CB_SELECTSTRING, -1, (LPARAM)param->pa->szProtoName );
+
+ EnableWindow( GetDlgItem( hwndDlg, IDC_ACCINTERNALNAME ), FALSE );
+ }
+ SendDlgItemMessage( hwndDlg, IDC_ACCINTERNALNAME, EM_LIMITTEXT, 40, 0 );
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch( LOWORD(wParam)) {
+ case IDOK:
+ {
+ AccFormDlgParam* param = ( AccFormDlgParam* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA );
+ PROTOACCOUNT* pa = param->pa;
+
+ if ( param->action == PRAC_ADDED ) {
+ char buf[200];
+ GetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, buf, SIZEOF( buf ));
+ rtrim( buf );
+ if ( buf[0] ) {
+ for (int i = 0; i < accounts.getCount(); ++i)
+ if (_stricmp(buf, accounts[i]->szModuleName) == 0)
+ return FALSE;
+ } }
+
+ switch( param->action ) {
+ case PRAC_UPGRADED:
+ {
+ int idx;
+ BOOL oldProto = pa->bOldProto;
+ TCHAR szPlugin[MAX_PATH];
+ mir_sntprintf(szPlugin, SIZEOF(szPlugin), _T("%s.dll"), StrConvT(pa->szProtoName));
+ idx = accounts.getIndex(pa);
+ UnloadAccount(pa, false, false);
+ accounts.remove(idx);
+ if (oldProto && UnloadPlugin(szPlugin, SIZEOF(szPlugin)))
+ {
+ TCHAR szNewName[MAX_PATH];
+ mir_sntprintf(szNewName, SIZEOF(szNewName), _T("%s~"), szPlugin);
+ MoveFile(szPlugin, szNewName);
+ }
+ }
+ // fall through
+
+ case PRAC_ADDED:
+ pa = (PROTOACCOUNT*)mir_calloc( sizeof( PROTOACCOUNT ));
+ pa->cbSize = sizeof( PROTOACCOUNT );
+ pa->bIsEnabled = TRUE;
+ pa->bIsVisible = TRUE;
+
+ pa->iOrder = accounts.getCount();
+ pa->type = PROTOTYPE_PROTOCOL;
+ break;
+ }
+ {
+ TCHAR buf[256];
+ GetDlgItemText( hwndDlg, IDC_ACCNAME, buf, SIZEOF( buf ));
+ mir_free(pa->tszAccountName);
+ pa->tszAccountName = mir_tstrdup( buf );
+ }
+ if ( param->action == PRAC_ADDED || param->action == PRAC_UPGRADED )
+ {
+ char buf[200];
+ GetDlgItemTextA( hwndDlg, IDC_PROTOTYPECOMBO, buf, SIZEOF( buf ));
+ pa->szProtoName = mir_strdup( buf );
+ GetDlgItemTextA( hwndDlg, IDC_ACCINTERNALNAME, buf, SIZEOF( buf ));
+ rtrim( buf );
+ if ( buf[0] == 0 ) {
+ int count = 1;
+ for( ;; ) {
+ DBVARIANT dbv;
+ mir_snprintf( buf, SIZEOF(buf), "%s_%d", pa->szProtoName, count++ );
+ if ( DBGetContactSettingString( NULL, buf, "AM_BaseProto", &dbv ))
+ break;
+ DBFreeVariant( &dbv );
+ } }
+ pa->szModuleName = mir_strdup( buf );
+
+ if ( !pa->tszAccountName[0] ) {
+ mir_free(pa->tszAccountName);
+ pa->tszAccountName = mir_a2t(buf);
+ }
+
+ DBWriteContactSettingString( NULL, pa->szModuleName, "AM_BaseProto", pa->szProtoName );
+ accounts.insert( pa );
+
+ if ( ActivateAccount( pa )) {
+ pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 );
+ if (!DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", FALSE))
+ pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 );
+ }
+ else pa->type = PROTOTYPE_DISPROTO;
+ }
+
+ WriteDbAccounts();
+ NotifyEventHooks( hAccListChanged, param->action, ( LPARAM )pa );
+
+ SendMessage( GetParent(hwndDlg), WM_MY_REFRESH, 0, 0 );
+ }
+
+ EndDialog( hwndDlg, TRUE );
+ break;
+
+ case IDCANCEL:
+ EndDialog( hwndDlg, FALSE );
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Accounts manager
+
+struct TAccMgrData
+{
+ HFONT hfntTitle, hfntText;
+ int titleHeight, textHeight;
+ int selectedHeight, normalHeight;
+ int iSelected;
+};
+
+struct TAccListData
+{
+ WNDPROC oldWndProc;
+ int iItem;
+ RECT rcCheck;
+
+ HWND hwndEdit;
+ WNDPROC oldEditProc;
+};
+
+static void sttClickButton(HWND hwndDlg, int idcButton)
+{
+ if (IsWindowEnabled(GetDlgItem(hwndDlg, idcButton)))
+ PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(idcButton, BN_CLICKED), (LPARAM)GetDlgItem(hwndDlg, idcButton));
+}
+
+static LRESULT CALLBACK sttEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_RETURN:
+ DestroyWindow(hwnd);
+ return 0;
+
+ case VK_ESCAPE:
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ DestroyWindow(hwnd);
+ return 0;
+ }
+ break;
+
+ case WM_GETDLGCODE:
+ if (wParam == VK_RETURN || wParam == VK_ESCAPE)
+ return DLGC_WANTMESSAGE;
+ break;
+
+ case WM_KILLFOCUS:
+ {
+ int length = GetWindowTextLength(hwnd) + 1;
+ TCHAR *str = ( TCHAR* )mir_alloc(sizeof(TCHAR) * length);
+ GetWindowText(hwnd, str, length);
+ SendMessage(GetParent(GetParent(hwnd)), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(GetParent(hwnd), GWL_ID), LBN_MY_RENAME), (LPARAM)str);
+ }
+ DestroyWindow(hwnd);
+ return 0;
+ }
+ return CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK AccListWndProc(HWND hwnd,UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TAccListData *dat = (struct TAccListData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if ( !dat )
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ {
+ POINT pt = {LOWORD(lParam), HIWORD(lParam)};
+ int iItem = LOWORD(SendMessage(hwnd, LB_ITEMFROMPOINT, 0, lParam));
+ ListBox_GetItemRect(hwnd, iItem, &dat->rcCheck);
+
+ dat->rcCheck.right = dat->rcCheck.left + GetSystemMetrics(SM_CXSMICON) + 4;
+ dat->rcCheck.bottom = dat->rcCheck.top + GetSystemMetrics(SM_CYSMICON) + 4;
+ if (PtInRect(&dat->rcCheck, pt))
+ dat->iItem = iItem;
+ else
+ dat->iItem = -1;
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ POINT pt = {LOWORD(lParam), HIWORD(lParam)};
+ if ((dat->iItem >= 0) && PtInRect(&dat->rcCheck, pt))
+ PostMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwnd, GWL_ID), LBN_MY_CHECK), (LPARAM)dat->iItem);
+ dat->iItem = -1;
+ }
+ break;
+
+ case WM_CHAR:
+ if (wParam == ' ') {
+ int iItem = ListBox_GetCurSel(hwnd);
+ if (iItem >= 0)
+ PostMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwnd, GWL_ID), LBN_MY_CHECK), (LPARAM)iItem);
+ return 0;
+ }
+
+ if (wParam == 10 /* enter */)
+ return 0;
+
+ break;
+
+ case WM_GETDLGCODE:
+ if (wParam == VK_RETURN)
+ return DLGC_WANTMESSAGE;
+ break;
+
+ case WM_MY_RENAME:
+ {
+ RECT rc;
+ struct TAccMgrData *parentDat = (struct TAccMgrData *)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwnd, ListBox_GetCurSel(hwnd));
+ if (!pa || pa->bOldProto || pa->bDynDisabled)
+ return 0;
+
+ ListBox_GetItemRect(hwnd, ListBox_GetCurSel(hwnd), &rc);
+ rc.left += 2*GetSystemMetrics(SM_CXSMICON) + 4;
+ rc.bottom = rc.top + max(GetSystemMetrics(SM_CXSMICON), parentDat->titleHeight) + 4 - 1;
+ ++rc.top; --rc.right;
+
+ dat->hwndEdit = CreateWindow(_T("EDIT"), pa->tszAccountName, WS_CHILD|WS_BORDER|ES_AUTOHSCROLL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, hwnd, NULL, hMirandaInst, NULL);
+ SetWindowLongPtr(dat->hwndEdit, GWLP_USERDATA, SetWindowLongPtr(dat->hwndEdit, GWLP_WNDPROC, (LONG_PTR)sttEditSubclassProc));
+ SendMessage(dat->hwndEdit, WM_SETFONT, (WPARAM)parentDat->hfntTitle, 0);
+ SendMessage(dat->hwndEdit, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN|EC_USEFONTINFO, 0);
+ SendMessage(dat->hwndEdit, EM_SETSEL, 0, (LPARAM) (-1));
+ ShowWindow(dat->hwndEdit, SW_SHOW);
+ }
+ SetFocus(dat->hwndEdit);
+ break;
+
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_F2:
+ PostMessage(hwnd, WM_MY_RENAME, 0, 0);
+ return 0;
+
+ case VK_INSERT:
+ sttClickButton(GetParent(hwnd), IDC_ADD);
+ return 0;
+
+ case VK_DELETE:
+ sttClickButton(GetParent(hwnd), IDC_REMOVE);
+ return 0;
+
+ case VK_RETURN:
+ if (GetAsyncKeyState(VK_CONTROL))
+ sttClickButton(GetParent(hwnd), IDC_EDIT);
+ else
+ sttClickButton(GetParent(hwnd), IDOK);
+ return 0;
+ }
+ break;
+ }
+
+ return CallWindowProc(dat->oldWndProc, hwnd, msg, wParam, lParam);
+}
+
+static void sttSubclassAccList(HWND hwnd, BOOL subclass)
+{
+ if (subclass) {
+ struct TAccListData *dat = (struct TAccListData *)mir_alloc(sizeof(struct TAccListData));
+ dat->iItem = -1;
+ dat->oldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat);
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)AccListWndProc);
+ }
+ else {
+ struct TAccListData *dat = (struct TAccListData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)dat->oldWndProc);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(dat);
+} }
+
+static void sttSelectItem(struct TAccMgrData *dat, HWND hwndList, int iItem)
+{
+ if ((dat->iSelected != iItem) && (dat->iSelected >= 0))
+ ListBox_SetItemHeight(hwndList, dat->iSelected, dat->normalHeight);
+
+ dat->iSelected = iItem;
+ ListBox_SetItemHeight(hwndList, dat->iSelected, dat->selectedHeight);
+ RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
+}
+
+static void sttUpdateAccountInfo(HWND hwndDlg, struct TAccMgrData *dat)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_ACCLIST);
+ int curSel = ListBox_GetCurSel( hwndList );
+ if ( curSel != LB_ERR ) {
+ HWND hwnd;
+ char svc[MAXMODULELABELLENGTH];
+
+ PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, curSel);
+ if ( pa ) {
+ EnableWindow( GetDlgItem( hwndDlg, IDC_UPGRADE ), pa->bOldProto || pa->bDynDisabled );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT ), !pa->bOldProto && !pa->bDynDisabled );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_REMOVE ), TRUE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_OPTIONS ), pa->ppro != 0 );
+
+ if ( dat->iSelected >= 0 ) {
+ PROTOACCOUNT *pa_old = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, dat->iSelected);
+ if (pa_old && pa_old != pa && pa_old->hwndAccMgrUI)
+ ShowWindow(pa_old->hwndAccMgrUI, SW_HIDE);
+ }
+
+ if ( pa->hwndAccMgrUI ) {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_HIDE);
+ ShowWindow(pa->hwndAccMgrUI, SW_SHOW);
+ }
+ else if ( !pa->ppro ) {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT("Account is disabled. Please activate it to access options."));
+ }
+ else {
+ mir_snprintf(svc, SIZEOF(svc), "%s%s", pa->szModuleName, PS_CREATEACCMGRUI);
+ hwnd = (HWND)CallService(svc, 0, (LPARAM)hwndDlg);
+ if (hwnd && (hwnd != (HWND)CALLSERVICE_NOTFOUND)) {
+ RECT rc;
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_HIDE);
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_TXT_INFO), &rc);
+ MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2);
+ SetWindowPos(hwnd, hwndList, rc.left, rc.top, 0, 0, SWP_NOSIZE|SWP_SHOWWINDOW);
+
+ pa->hwndAccMgrUI = hwnd;
+ }
+ else {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT(legacyMsg));
+ } }
+ return;
+ } }
+
+ EnableWindow( GetDlgItem( hwndDlg, IDC_UPGRADE ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_REMOVE ), FALSE );
+ EnableWindow( GetDlgItem( hwndDlg, IDC_OPTIONS ), FALSE );
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TXT_INFO), SW_SHOW);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_TXT_INFO), TranslateT(welcomeMsg));
+}
+
+INT_PTR CALLBACK AccMgrDlgProc(HWND hwndDlg,UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct TAccMgrData *dat = (struct TAccMgrData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(message) {
+ case WM_INITDIALOG:
+ {
+ TAccMgrData *dat = (TAccMgrData *)mir_alloc(sizeof(TAccMgrData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib( hwndDlg, SKINICON_OTHER_ACCMGR );
+
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("New account"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_EDIT, SKINICON_OTHER_RENAME, LPGEN("Edit"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_REMOVE, SKINICON_OTHER_DELETE, LPGEN("Remove account"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_OPTIONS, SKINICON_OTHER_OPTIONS, LPGEN( "Configure..."));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_UPGRADE, SKINICON_OTHER_ACCMGR, LPGEN("Upgrade account"));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_UPGRADE), FALSE);
+
+ {
+ LOGFONT lf;
+ HDC hdc;
+ HFONT hfnt;
+ TEXTMETRIC tm;
+
+ GetObject((HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ dat->hfntText = CreateFontIndirect(&lf);
+
+ GetObject((HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ dat->hfntTitle = CreateFontIndirect(&lf);
+
+ hdc = GetDC(hwndDlg);
+ hfnt = ( HFONT )SelectObject(hdc, dat->hfntTitle);
+ GetTextMetrics(hdc, &tm);
+ dat->titleHeight = tm.tmHeight;
+ SelectObject(hdc, dat->hfntText);
+ GetTextMetrics(hdc, &tm);
+ dat->textHeight = tm.tmHeight;
+ SelectObject(hdc, hfnt);
+ ReleaseDC(hwndDlg, hdc);
+
+ dat->normalHeight = 4 + max(dat->titleHeight, GetSystemMetrics(SM_CYSMICON));
+ dat->selectedHeight = dat->normalHeight + 4 + 2 * dat->textHeight;
+
+ SendDlgItemMessage(hwndDlg, IDC_NAME, WM_SETFONT, (WPARAM)dat->hfntTitle, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ACCOUNT, WM_SETFONT, (WPARAM)dat->hfntTitle, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TXT_ADDITIONAL, WM_SETFONT, (WPARAM)dat->hfntTitle, 0);
+ }
+
+ dat->iSelected = -1;
+ sttSubclassAccList(GetDlgItem(hwndDlg, IDC_ACCLIST), TRUE);
+ SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 );
+
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, "AccMgr", "");
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ switch ( GetDlgCtrlID(( HWND )lParam )) {
+ case IDC_WHITERECT:
+ case IDC_NAME:
+ SetBkColor(( HDC )wParam, GetSysColor( COLOR_WINDOW ));
+ return ( INT_PTR )GetSysColorBrush( COLOR_WINDOW );
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lps = (LPMEASUREITEMSTRUCT)lParam;
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)lps->itemData;
+
+ if ((lps->CtlID != IDC_ACCLIST) || !acc)
+ break;
+
+ lps->itemWidth = 10;
+ lps->itemHeight = dat->normalHeight;
+ }
+ return TRUE;
+
+ case WM_DRAWITEM:
+ {
+ int tmp, size, length;
+ TCHAR *text;
+ HICON hIcon;
+ HBRUSH hbrBack;
+ SIZE sz;
+
+ int cxIcon = GetSystemMetrics(SM_CXSMICON);
+ int cyIcon = GetSystemMetrics(SM_CYSMICON);
+
+ LPDRAWITEMSTRUCT lps = (LPDRAWITEMSTRUCT)lParam;
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)lps->itemData;
+
+ if ((lps->CtlID != IDC_ACCLIST) || (lps->itemID == -1) || !acc)
+ break;
+
+ SetBkMode(lps->hDC, TRANSPARENT);
+ if (lps->itemState & ODS_SELECTED) {
+ hbrBack = GetSysColorBrush(COLOR_HIGHLIGHT);
+ SetTextColor(lps->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else {
+ hbrBack = GetSysColorBrush(COLOR_WINDOW);
+ SetTextColor(lps->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+ FillRect(lps->hDC, &lps->rcItem, hbrBack);
+
+ lps->rcItem.left += 2;
+ lps->rcItem.top += 2;
+ lps->rcItem.bottom -= 2;
+
+ if ( acc->bOldProto )
+ tmp = SKINICON_OTHER_ON;
+ else if ( acc->bDynDisabled )
+ tmp = SKINICON_OTHER_OFF;
+ else
+ tmp = acc->bIsEnabled ? SKINICON_OTHER_TICK : SKINICON_OTHER_NOTICK;
+
+ hIcon = LoadSkinnedIcon(tmp);
+ DrawIconEx(lps->hDC, lps->rcItem.left, lps->rcItem.top, hIcon, cxIcon, cyIcon, 0, hbrBack, DI_NORMAL);
+ IconLib_ReleaseIcon(hIcon, 0);
+
+ lps->rcItem.left += cxIcon + 2;
+
+ if (acc->ppro) {
+ hIcon = acc->ppro->GetIcon( PLI_PROTOCOL | PLIF_SMALL );
+ DrawIconEx(lps->hDC, lps->rcItem.left, lps->rcItem.top, hIcon, cxIcon, cyIcon, 0, hbrBack, DI_NORMAL);
+ DestroyIcon(hIcon);
+ }
+ lps->rcItem.left += cxIcon + 2;
+
+ length = SendDlgItemMessage(hwndDlg, IDC_ACCLIST, LB_GETTEXTLEN, lps->itemID, 0);
+ size = max(length+1, 256);
+ text = (TCHAR *)_alloca(sizeof(TCHAR) * size);
+ SendDlgItemMessage(hwndDlg, IDC_ACCLIST, LB_GETTEXT, lps->itemID, (LPARAM)text);
+
+ SelectObject(lps->hDC, dat->hfntTitle);
+ tmp = lps->rcItem.bottom;
+ lps->rcItem.bottom = lps->rcItem.top + max(cyIcon, dat->titleHeight);
+ DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS|DT_VCENTER);
+ lps->rcItem.bottom = tmp;
+ GetTextExtentPoint32(lps->hDC, text, length, &sz);
+ lps->rcItem.top += max(cxIcon, sz.cy) + 2;
+
+ if (lps->itemID == (unsigned)dat->iSelected) {
+ SelectObject(lps->hDC, dat->hfntText);
+ mir_sntprintf(text, size, _T("%s: ") _T(TCHAR_STR_PARAM), TranslateT("Protocol"), acc->szProtoName);
+ length = lstrlen(text);
+ DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS);
+ GetTextExtentPoint32(lps->hDC, text, length, &sz);
+ lps->rcItem.top += sz.cy + 2;
+
+ if (acc->ppro && Proto_IsProtocolLoaded(acc->szProtoName)) {
+ char *szIdName;
+ TCHAR *tszIdName;
+ CONTACTINFO ci = { 0 };
+
+ szIdName = (char *)acc->ppro->GetCaps( PFLAG_UNIQUEIDTEXT, 0 );
+ tszIdName = szIdName ? mir_a2t(szIdName) : mir_tstrdup(TranslateT("Account ID"));
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = NULL;
+ ci.szProto = acc->szModuleName;
+ ci.dwFlag = CNF_UNIQUEID | CNF_TCHAR;
+ if ( !CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ switch (ci.type) {
+ case CNFT_ASCIIZ:
+ mir_sntprintf( text, size, _T("%s: %s"), tszIdName, ci.pszVal );
+ mir_free(ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ mir_sntprintf( text, size, _T("%s: %d"), tszIdName, ci.dVal );
+ break;
+ }
+ }
+ else mir_sntprintf(text, size, _T("%s: %s"), tszIdName, TranslateT("<unknown>"));
+ mir_free(tszIdName);
+ }
+ else mir_sntprintf(text, size, TranslateT("Protocol is not loaded."));
+
+ length = lstrlen(text);
+ DrawText(lps->hDC, text, -1, &lps->rcItem, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS);
+ GetTextExtentPoint32(lps->hDC, text, length, &sz);
+ lps->rcItem.top += sz.cy + 2;
+ } }
+ return TRUE;
+
+ case WM_MY_REFRESH:
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_ACCLIST);
+ int i = ListBox_GetCurSel(hList);
+ PROTOACCOUNT *acc = (i == LB_ERR) ? NULL : (PROTOACCOUNT *)ListBox_GetItemData(hList, i);
+
+ dat->iSelected = -1;
+ SendMessage( hList, LB_RESETCONTENT, 0, 0 );
+ for (i = 0; i < accounts.getCount(); ++i) {
+ int iItem = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)accounts[i]->tszAccountName);
+ SendMessage(hList, LB_SETITEMDATA, iItem, (LPARAM)accounts[i]);
+
+ if (accounts[i] == acc)
+ ListBox_SetCurSel(hList, iItem);
+ }
+
+ dat->iSelected = ListBox_GetCurSel(hList); // -1 if error => nothing selected in our case
+ if (dat->iSelected >= 0)
+ sttSelectItem(dat, hList, dat->iSelected);
+ else if (acc && acc->hwndAccMgrUI)
+ ShowWindow(acc->hwndAccMgrUI, SW_HIDE);
+
+ sttUpdateAccountInfo(hwndDlg, dat);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if ( GetWindowLongPtr(( HWND )wParam, GWL_ID ) == IDC_ACCLIST ) {
+ HWND hwndList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) };
+ int iItem = ListBox_GetCurSel( hwndList );
+
+ if (( pt.x == -1 ) && ( pt.y == -1 )) {
+ if (iItem != LB_ERR) {
+ RECT rc;
+ ListBox_GetItemRect( hwndList, iItem, &rc );
+ pt.x = rc.left + GetSystemMetrics(SM_CXSMICON) + 4;
+ pt.y = rc.top + 4 + max(GetSystemMetrics(SM_CXSMICON), dat->titleHeight);
+ ClientToScreen( hwndList, &pt );
+ }
+ }
+ else {
+ // menu was activated with mouse => find item under cursor & set focus to our control.
+ POINT ptItem = pt;
+ ScreenToClient( hwndList, &ptItem );
+ iItem = (short)LOWORD(SendMessage(hwndList, LB_ITEMFROMPOINT, 0, MAKELPARAM(ptItem.x, ptItem.y)));
+ if (iItem != LB_ERR)
+ {
+ ListBox_SetCurSel(hwndList, iItem);
+ sttUpdateAccountInfo(hwndDlg, dat);
+ sttSelectItem(dat, hwndList, iItem);
+ SetFocus(hwndList);
+ }
+ }
+
+ if ( iItem != LB_ERR ) {
+ PROTOACCOUNT* pa = (PROTOACCOUNT*)ListBox_GetItemData(hwndList, iItem);
+ HMENU hMenu = CreatePopupMenu();
+ if ( !pa->bOldProto && !pa->bDynDisabled )
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Rename"));
+
+ AppendMenu(hMenu, MF_STRING, 3, TranslateT("Delete"));
+
+ if ( Proto_IsAccountEnabled( pa ))
+ AppendMenu(hMenu, MF_STRING, 4, TranslateT("Configure"));
+
+ if ( pa->bOldProto || pa->bDynDisabled )
+ AppendMenu(hMenu, MF_STRING, 5, TranslateT("Upgrade"));
+
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING, 0, TranslateT("Cancel"));
+ switch (TrackPopupMenu( hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL )) {
+ case 1:
+ PostMessage(hwndList, WM_MY_RENAME, 0, 0);
+ break;
+
+ case 2:
+ sttClickButton(hwndDlg, IDC_EDIT);
+ break;
+
+ case 3:
+ sttClickButton(hwndDlg, IDC_REMOVE);
+ break;
+
+ case 4:
+ sttClickButton(hwndDlg, IDC_OPTIONS);
+ break;
+
+ case 5:
+ sttClickButton(hwndDlg, IDC_UPGRADE);
+ break;
+ }
+ DestroyMenu( hMenu );
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch( LOWORD(wParam)) {
+ case IDC_ACCLIST:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_ACCLIST);
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ sttUpdateAccountInfo(hwndDlg, dat);
+ sttSelectItem(dat, hwndList, ListBox_GetCurSel(hwndList));
+ SetFocus(hwndList);
+ break;
+
+ case LBN_DBLCLK:
+ PostMessage(hwndList, WM_MY_RENAME, 0, 0);
+ break;
+
+ case LBN_MY_CHECK:
+ {
+ PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, lParam);
+ if ( pa ) {
+ if ( pa->bOldProto || pa->bDynDisabled)
+ break;
+
+ pa->bIsEnabled = !pa->bIsEnabled;
+ if ( pa->bIsEnabled ) {
+ if ( ActivateAccount( pa )) {
+ pa->ppro->OnEvent( EV_PROTO_ONLOAD, 0, 0 );
+ if (!DBGetContactSettingByte(NULL, "CList", "MoveProtoMenus", FALSE))
+ pa->ppro->OnEvent( EV_PROTO_ONMENU, 0, 0 );
+ }
+ else pa->type = PROTOTYPE_DISPROTO;
+ }
+ else {
+ DWORD dwStatus = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0);
+ if (dwStatus >= ID_STATUS_ONLINE) {
+ if (IDCANCEL == ::MessageBox(hwndDlg,
+ TranslateT("Account is online. Disable account?"),
+ TranslateT("Accounts"), MB_OKCANCEL)) {
+ pa->bIsEnabled = 1; //stay enabled
+ }
+ }
+
+ if ( !pa->bIsEnabled )
+ DeactivateAccount( pa, true, false );
+ }
+
+ WriteDbAccounts();
+ NotifyEventHooks( hAccListChanged, PRAC_CHECKED, ( LPARAM )pa );
+ sttUpdateAccountInfo(hwndDlg, dat);
+ RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
+ } }
+ break;
+
+ case LBN_MY_RENAME:
+ {
+ int iItem = ListBox_GetCurSel(hwndList);
+ PROTOACCOUNT *pa = (PROTOACCOUNT *)ListBox_GetItemData(hwndList, iItem);
+ if ( pa ) {
+ mir_free(pa->tszAccountName);
+ pa->tszAccountName = (TCHAR*)lParam;
+ WriteDbAccounts();
+ NotifyEventHooks(hAccListChanged, PRAC_CHANGED, (LPARAM)pa);
+
+ ListBox_DeleteString(hwndList, iItem);
+ iItem = ListBox_AddString(hwndList, pa->tszAccountName);
+ ListBox_SetItemData(hwndList, iItem, (LPARAM)pa);
+ ListBox_SetCurSel(hwndList, iItem);
+
+ sttSelectItem(dat, hwndList, iItem);
+
+ RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
+ }
+ else mir_free((TCHAR*)lParam);
+ }
+ break;
+ } }
+ break;
+
+ case IDC_ADD:
+ {
+ AccFormDlgParam param = { PRAC_ADDED, NULL };
+ if ( IDOK == DialogBoxParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCFORM), hwndDlg, AccFormDlgProc, (LPARAM)&param ))
+ SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 );
+ }
+ break;
+
+ case IDC_EDIT:
+ {
+ HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ int idx = ListBox_GetCurSel( hList );
+ if ( idx != -1 )
+ PostMessage(hList, WM_MY_RENAME, 0, 0);
+ }
+ break;
+
+ case IDC_REMOVE:
+ {
+ HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ int idx = ListBox_GetCurSel( hList );
+ if ( idx != -1 ) {
+ PROTOACCOUNT* pa = ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx );
+ TCHAR buf[ 200 ];
+ mir_sntprintf( buf, SIZEOF(buf), TranslateT( "Account %s is being deleted" ), pa->tszAccountName );
+ if (pa->bOldProto) {
+ MessageBox( NULL, TranslateT( "You need to disable plugin to delete this account" ), buf,
+ MB_ICONERROR | MB_OK );
+ break;
+ }
+ if ( IDYES == MessageBox( NULL, TranslateT( errMsg ), buf, MB_ICONSTOP | MB_DEFBUTTON2 | MB_YESNO )) {
+ // lock controls to avoid changes during remove process
+ ListBox_SetCurSel( hList, -1 );
+ sttUpdateAccountInfo( hwndDlg, dat );
+ EnableWindow( hList, FALSE );
+ EnableWindow( GetDlgItem(hwndDlg, IDC_ADD), FALSE );
+
+ ListBox_SetItemData( hList, idx, 0 );
+
+ accounts.remove( pa );
+
+ CheckProtocolOrder();
+
+ WriteDbAccounts();
+ NotifyEventHooks( hAccListChanged, PRAC_REMOVED, ( LPARAM )pa );
+
+ UnloadAccount( pa, true, true );
+ SendMessage( hwndDlg, WM_MY_REFRESH, 0, 0 );
+
+ EnableWindow( hList, TRUE );
+ EnableWindow( GetDlgItem(hwndDlg, IDC_ADD), TRUE );
+ } } }
+ break;
+
+ case IDC_OPTIONS:
+ {
+ HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ int idx = ListBox_GetCurSel( hList );
+ if ( idx != -1 ) {
+ PROTOACCOUNT* pa = ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx );
+ if ( pa->bOldProto ) {
+ OPENOPTIONSDIALOG ood;
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = "Network";
+ ood.pszPage = pa->szModuleName;
+ ood.pszTab = NULL;
+ CallService( MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood );
+ }
+ else OpenAccountOptions( pa );
+ } }
+ break;
+
+ case IDC_UPGRADE:
+ {
+ HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ int idx = ListBox_GetCurSel( hList );
+ if ( idx != -1 ) {
+ AccFormDlgParam param = { PRAC_UPGRADED, ( PROTOACCOUNT* )ListBox_GetItemData( hList, idx ) };
+ DialogBoxParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCFORM), hwndDlg, AccFormDlgProc, (LPARAM)&param );
+ } }
+ break;
+
+ case IDC_LNK_NETWORK:
+ {
+ PSHNOTIFY pshn = {0};
+ pshn.hdr.code = PSN_APPLY;
+ pshn.hdr.hwndFrom = hwndDlg;
+ SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn);
+
+ OPENOPTIONSDIALOG ood = {0};
+ ood.cbSize = sizeof(ood);
+ ood.pszPage = "Network";
+ CallService( MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood );
+ break;
+ }
+
+ case IDC_LNK_ADDONS:
+ CallService(MS_UTILS_OPENURL, TRUE, (LPARAM)"http://addons.miranda-im.org/");
+ break;
+
+ case IDOK:
+ {
+ PSHNOTIFY pshn = {0};
+ pshn.hdr.code = PSN_APPLY;
+ pshn.hdr.hwndFrom = hwndDlg;
+ SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn);
+ DestroyWindow(hwndDlg);
+ break;
+ }
+
+ case IDCANCEL:
+ {
+ PSHNOTIFY pshn = {0};
+ pshn.hdr.code = PSN_RESET;
+ pshn.hdr.hwndFrom = hwndDlg;
+ SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&pshn);
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ }
+ case PSM_CHANGED:
+ {
+ HWND hList = GetDlgItem( hwndDlg, IDC_ACCLIST );
+ int idx = ListBox_GetCurSel( hList );
+ if ( idx != -1 ) {
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)ListBox_GetItemData(hList, idx);
+ if (acc)
+ {
+ acc->bAccMgrUIChanged = TRUE;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ {
+ int i;
+ PSHNOTIFY pshn = {0};
+ pshn.hdr.code = PSN_APPLY;
+ for (i = 0; i < accounts.getCount(); ++i) {
+ if ( accounts[i]->hwndAccMgrUI && accounts[i]->bAccMgrUIChanged ) {
+ pshn.hdr.hwndFrom = accounts[i]->hwndAccMgrUI;
+ SendMessage(accounts[i]->hwndAccMgrUI, WM_NOTIFY, 0, (LPARAM)&pshn);
+ accounts[i]->bAccMgrUIChanged = FALSE;
+ } }
+ return TRUE;
+ }
+ case PSN_RESET:
+ {
+ int i;
+ PSHNOTIFY pshn = {0};
+ pshn.hdr.code = PSN_RESET;
+ for (i = 0; i < accounts.getCount(); ++i) {
+ if ( accounts[i]->hwndAccMgrUI && accounts[i]->bAccMgrUIChanged ) {
+ pshn.hdr.hwndFrom = accounts[i]->hwndAccMgrUI;
+ SendMessage(accounts[i]->hwndAccMgrUI, WM_NOTIFY, 0, (LPARAM)&pshn);
+ accounts[i]->bAccMgrUIChanged = FALSE;
+ } }
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ {
+ for (int i = 0; i < accounts.getCount(); ++i) {
+ accounts[i]->bAccMgrUIChanged = FALSE;
+ if (accounts[i]->hwndAccMgrUI) {
+ DestroyWindow(accounts[i]->hwndAccMgrUI);
+ accounts[i]->hwndAccMgrUI = NULL;
+ } } }
+
+ Window_FreeIcon_IcoLib( hwndDlg );
+ Button_FreeIcon_IcoLib( hwndDlg, IDC_ADD );
+ Button_FreeIcon_IcoLib( hwndDlg, IDC_EDIT );
+ Button_FreeIcon_IcoLib( hwndDlg, IDC_REMOVE );
+ Button_FreeIcon_IcoLib( hwndDlg, IDC_OPTIONS );
+ Button_FreeIcon_IcoLib( hwndDlg, IDC_UPGRADE );
+ Utils_SaveWindowPosition( hwndDlg, NULL, "AccMgr", "");
+ sttSubclassAccList(GetDlgItem(hwndDlg, IDC_ACCLIST), FALSE);
+ DeleteObject(dat->hfntTitle);
+ DeleteObject(dat->hfntText);
+ mir_free(dat);
+ hAccMgr = NULL;
+ break;
+ }
+
+ return FALSE;
+}
+
+static INT_PTR OptProtosShow(WPARAM, LPARAM)
+{
+ if ( !hAccMgr )
+ hAccMgr = CreateDialogParam( hMirandaInst, MAKEINTRESOURCE(IDD_ACCMGR), NULL, AccMgrDlgProc, 0 );
+
+ ShowWindow( hAccMgr, SW_RESTORE );
+ SetForegroundWindow( hAccMgr );
+ SetActiveWindow( hAccMgr );
+ return 0;
+}
+
+int OptProtosLoaded(WPARAM, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_ACCMGR );
+ mi.position = 1900000000;
+ mi.pszName = LPGEN("&Accounts...");
+ mi.pszService = MS_PROTO_SHOWACCMGR;
+ CallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+ return 0;
+}
+
+static int OnAccListChanged( WPARAM eventCode, LPARAM lParam )
+{
+ PROTOACCOUNT* pa = (PROTOACCOUNT*)lParam;
+
+ switch( eventCode ) {
+ case PRAC_CHANGED:
+ if ( pa->ppro ) {
+ mir_free( pa->ppro->m_tszUserName );
+ pa->ppro->m_tszUserName = mir_tstrdup( pa->tszAccountName );
+ pa->ppro->OnEvent( EV_PROTO_ONRENAME, 0, lParam );
+ }
+ }
+
+ return 0;
+}
+
+static int ShutdownAccMgr(WPARAM, LPARAM)
+{
+ if ( IsWindow( hAccMgr ))
+ DestroyWindow( hAccMgr );
+ hAccMgr = NULL;
+ return 0;
+}
+
+int LoadProtoOptions( void )
+{
+ CreateServiceFunction( MS_PROTO_SHOWACCMGR, OptProtosShow );
+
+ HookEvent( ME_SYSTEM_MODULESLOADED, OptProtosLoaded );
+ HookEvent( ME_PROTO_ACCLISTCHANGED, OnAccListChanged );
+ HookEvent( ME_SYSTEM_PRESHUTDOWN, ShutdownAccMgr );
+ return 0;
+}
diff --git a/src/modules/skin/hotkeys.cpp b/src/modules/skin/hotkeys.cpp
new file mode 100644
index 0000000000..cd87fda1a7
--- /dev/null
+++ b/src/modules/skin/hotkeys.cpp
@@ -0,0 +1,1465 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <m_hotkeys.h>
+
+#define DBMODULENAME "SkinHotKeys"
+#define WM_HOTKEYUNREGISTERED (WM_USER+721)
+
+typedef enum { HKT_GLOBAL, HKT_LOCAL, HKT_MANUAL, HKT_COUNT } THotkeyType;
+
+typedef struct _THotkeyItem THotkeyItem;
+struct _THotkeyItem
+{
+ THotkeyType type;
+ char *pszService, *pszName; // pszName is valid _only_ for "root" hotkeys
+ TCHAR *ptszSection, *ptszDescription;
+ TCHAR *ptszSection_tr, *ptszDescription_tr;
+ LPARAM lParam;
+ WORD DefHotkey, Hotkey;
+ bool Enabled;
+ ATOM idHotkey;
+
+ THotkeyItem *rootHotkey;
+ int nSubHotkeys;
+ bool allowSubHotkeys;
+
+ bool OptChanged, OptDeleted, OptNew;
+ WORD OptHotkey;
+ THotkeyType OptType;
+ bool OptEnabled;
+
+ bool UnregisterHotkey; // valid only during WM_APP message in options UI, used to remove unregistered hotkeys from options
+};
+
+static int sttCompareHotkeys(const THotkeyItem *p1, const THotkeyItem *p2)
+{
+ int res;
+ if ( res = lstrcmp( p1->ptszSection_tr, p2->ptszSection_tr ))
+ return res;
+ if ( res = lstrcmp( p1->ptszDescription_tr, p2->ptszDescription_tr ))
+ return res;
+ if (!p1->rootHotkey && p2->rootHotkey)
+ return -1;
+ if (p1->rootHotkey && !p2->rootHotkey)
+ return 1;
+ return 0;
+}
+
+static LIST<THotkeyItem> hotkeys( 10, sttCompareHotkeys );
+
+static void sttFreeHotkey(THotkeyItem *item);
+
+static BOOL bModuleInitialized = FALSE;
+static HWND g_hwndHotkeyHost = NULL;
+static HWND g_hwndOptions = NULL;
+static DWORD g_pid = 0;
+static int g_hotkeyCount = 0;
+static HANDLE hEvChanged = 0;
+
+static LRESULT CALLBACK sttHotkeyHostWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static TCHAR *sttHokeyVkToName(WORD vkKey);
+static void sttHotkeyEditCreate(HWND hwnd);
+static void sttHotkeyEditDestroy(HWND hwnd);
+static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+static void sttRegisterHotkeys();
+static void sttUnregisterHotkeys();
+
+static INT_PTR svcHotkeySubclass(WPARAM wParam, LPARAM lParam);
+static INT_PTR svcHotkeyUnsubclass(WPARAM wParam, LPARAM lParam);
+static INT_PTR svcHotkeyRegister(WPARAM wParam, LPARAM lParam);
+static INT_PTR svcHotkeyUnregister(WPARAM wParam, LPARAM lParam);
+static INT_PTR svcHotkeyCheck(WPARAM wParam, LPARAM lParam);
+
+HHOOK hhkKeyboard = NULL;
+static LRESULT CALLBACK sttKeyboardProc(int code, WPARAM wParam, LPARAM lParam);
+
+static void sttWordToModAndVk(WORD w, BYTE *mod, BYTE *vk)
+{
+ *mod = 0;
+ if (HIBYTE(w) & HOTKEYF_CONTROL) *mod |= MOD_CONTROL;
+ if (HIBYTE(w) & HOTKEYF_SHIFT) *mod |= MOD_SHIFT;
+ if (HIBYTE(w) & HOTKEYF_ALT) *mod |= MOD_ALT;
+ if (HIBYTE(w) & HOTKEYF_EXT) *mod |= MOD_WIN;
+ *vk = LOBYTE(w);
+}
+
+static LRESULT CALLBACK sttHotkeyHostWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_HOTKEY ) {
+ int i;
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ THotkeyItem *item = hotkeys[i];
+ if (item->type != HKT_GLOBAL) continue;
+ if (!item->Enabled) continue;
+ if (item->pszService && (wParam == item->idHotkey)) {
+ CallService(item->pszService, 0, item->lParam);
+ break;
+ } }
+
+ return FALSE;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK sttKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code == HC_ACTION && !(HIWORD(lParam) & KF_UP)) {
+ int i;
+ BYTE mod=0, vk=wParam;
+
+ if ( vk ) {
+ if (GetAsyncKeyState(VK_CONTROL)) mod |= MOD_CONTROL;
+ if (GetAsyncKeyState(VK_MENU)) mod |= MOD_ALT;
+ if (GetAsyncKeyState(VK_SHIFT)) mod |= MOD_SHIFT;
+ if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) mod |= MOD_WIN;
+
+ for ( i = 0; i < hotkeys.getCount(); i++ ) {
+ THotkeyItem *item = hotkeys[i];
+ BYTE hkMod, hkVk;
+ if (item->type != HKT_LOCAL) continue;
+ sttWordToModAndVk(item->Hotkey, &hkMod, &hkVk);
+ if (!hkVk) continue;
+ if (!item->Enabled) continue;
+ if (item->pszService && (vk == hkVk) && (mod == hkMod)) {
+ CallService(item->pszService, 0, item->lParam);
+ return TRUE;
+ } } } }
+
+ return CallNextHookEx(hhkKeyboard, code, wParam, lParam);
+}
+
+static INT_PTR svcHotkeySubclass(WPARAM wParam, LPARAM)
+{
+ sttHotkeyEditCreate((HWND)wParam);
+ return 0;
+}
+
+static INT_PTR svcHotkeyUnsubclass(WPARAM wParam, LPARAM)
+{
+ sttHotkeyEditDestroy((HWND)wParam);
+ return 0;
+}
+
+static INT_PTR svcHotkeyRegister(WPARAM wParam, LPARAM lParam)
+{
+ HOTKEYDESC *desc = (HOTKEYDESC *)lParam;
+ if ( desc->cbSize != sizeof(HOTKEYDESC) && desc->cbSize != HOTKEYDESC_SIZE_V1 )
+ return 0;
+
+ THotkeyItem *item = ( THotkeyItem* )mir_alloc(sizeof(THotkeyItem));
+ #if defined( _UNICODE )
+ DWORD dwFlags = ( desc->cbSize >= sizeof(HOTKEYDESC)) ? desc->dwFlags : 0;
+ if ( dwFlags & HKD_UNICODE ) {
+ item->ptszSection = mir_tstrdup( desc->ptszSection );
+ item->ptszDescription = mir_tstrdup( desc->ptszDescription );
+ }
+ else {
+ item->ptszSection = mir_a2u( desc->pszSection );
+ item->ptszDescription = mir_a2u( desc->pszDescription );
+ }
+ #else
+ item->ptszSection = mir_tstrdup( desc->pszSection );
+ item->ptszDescription = mir_tstrdup( desc->pszDescription );
+ #endif
+ item->ptszSection_tr = TranslateTS(item->ptszSection);
+ item->ptszDescription_tr = TranslateTS(item->ptszDescription);
+ item->allowSubHotkeys = TRUE;
+ item->rootHotkey = NULL;
+ item->nSubHotkeys = 0;
+
+ if ( item->rootHotkey = hotkeys.find( item )) {
+ if (item->rootHotkey->allowSubHotkeys) {
+ char nameBuf[MAXMODULELABELLENGTH];
+ mir_snprintf(nameBuf, SIZEOF(nameBuf), "%s$%d", item->rootHotkey->pszName, item->rootHotkey->nSubHotkeys);
+ item->pszName = mir_strdup(nameBuf);
+ item->Enabled = TRUE;
+
+ item->rootHotkey->nSubHotkeys++;
+ }
+ else {
+ mir_free(item->ptszSection);
+ mir_free(item->ptszDescription);
+ mir_free(item);
+ return 0;
+ }
+ }
+ else {
+ item->pszName = mir_strdup(desc->pszName);
+ item->Enabled = !DBGetContactSettingByte(NULL, DBMODULENAME "Off", item->pszName, 0);
+ }
+
+ item->pszService = desc->pszService ? mir_strdup(desc->pszService) : 0;
+ item->DefHotkey = desc->DefHotKey & ~HKF_MIRANDA_LOCAL;
+ item->Hotkey = DBGetContactSettingWord(NULL, DBMODULENAME, item->pszName, item->DefHotkey);
+ item->type = item->pszService ?
+ ( THotkeyType )DBGetContactSettingByte(NULL, DBMODULENAME "Types", item->pszName,
+ (desc->DefHotKey & HKF_MIRANDA_LOCAL) ? HKT_LOCAL : HKT_GLOBAL) : HKT_MANUAL;
+ item->lParam = desc->lParam;
+
+ char buf[256];
+ mir_snprintf(buf, SIZEOF(buf), "mir_hotkey_%d_%d", g_pid, g_hotkeyCount++);
+ item->idHotkey = GlobalAddAtomA(buf);
+ if (item->type == HKT_GLOBAL) {
+ if (item->Enabled) {
+ BYTE mod, vk;
+ sttWordToModAndVk(item->Hotkey, &mod, &vk);
+ if (vk) RegisterHotKey(g_hwndHotkeyHost, item->idHotkey, mod, vk);
+ } }
+
+ hotkeys.insert( item );
+
+ if ( !item->rootHotkey ) {
+ /* try to load alternatives from db */
+ int count, i;
+ mir_snprintf(buf, SIZEOF(buf), "%s$count", item->pszName);
+ count = (int)DBGetContactSettingDword(NULL, DBMODULENAME, buf, -1);
+ for (i = 0; i < count; i++) {
+ mir_snprintf(buf, SIZEOF(buf), "%s$%d", item->pszName, i);
+ if (!DBGetContactSettingWord(NULL, DBMODULENAME, buf, 0))
+ continue;
+
+ svcHotkeyRegister(wParam, lParam);
+ }
+ item->allowSubHotkeys = count < 0;
+ }
+ else {
+ mir_free( item->pszName );
+ item->pszName = NULL;
+ }
+
+ return item->idHotkey;
+}
+
+static INT_PTR svcHotkeyUnregister(WPARAM, LPARAM lParam)
+{
+ int i;
+ char *pszName = (char *)lParam;
+ char pszNamePrefix[MAXMODULELABELLENGTH];
+ size_t cbNamePrefix;
+ mir_snprintf(pszNamePrefix, SIZEOF(pszNamePrefix), "%s$", pszName);
+ cbNamePrefix = strlen(pszNamePrefix);
+
+ for (i = 0; i < hotkeys.getCount(); ++i)
+ {
+ char *pszCurrentName = hotkeys[i]->rootHotkey ?
+ hotkeys[i]->rootHotkey->pszName :
+ hotkeys[i]->pszName;
+ if (!pszCurrentName) continue;
+
+ hotkeys[i]->UnregisterHotkey =
+ !lstrcmpA(pszCurrentName, pszName) ||
+ !strncmp(pszCurrentName, pszNamePrefix, cbNamePrefix);
+ }
+
+ if (g_hwndOptions)
+ SendMessage(g_hwndOptions, WM_HOTKEYUNREGISTERED, 0, 0);
+
+ for (i = 0; i < hotkeys.getCount(); ++i)
+ if (hotkeys[i]->UnregisterHotkey) {
+ sttFreeHotkey(hotkeys[i]);
+ List_Remove((SortedList *)&hotkeys, i);
+ --i;
+ }
+
+ return 0;
+}
+
+static INT_PTR svcHotkeyCheck(WPARAM wParam, LPARAM lParam)
+{
+ MSG *msg = (MSG *)wParam;
+ TCHAR *pszSection = mir_a2t((char *)lParam);
+
+ if ((msg->message == WM_KEYDOWN) || (msg->message == WM_SYSKEYDOWN)) {
+ int i;
+ BYTE mod=0, vk=msg->wParam;
+
+ if (vk) {
+ if (GetAsyncKeyState(VK_CONTROL)) mod |= MOD_CONTROL;
+ if (GetAsyncKeyState(VK_MENU)) mod |= MOD_ALT;
+ if (GetAsyncKeyState(VK_SHIFT)) mod |= MOD_SHIFT;
+ if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) mod |= MOD_WIN;
+
+ for ( i = 0; i < hotkeys.getCount(); i++ ) {
+ THotkeyItem *item = hotkeys[i];
+ BYTE hkMod, hkVk;
+ if ((item->type != HKT_MANUAL) || lstrcmp(pszSection, item->ptszSection)) continue;
+ sttWordToModAndVk(item->Hotkey, &hkMod, &hkVk);
+ if (!hkVk) continue;
+ if (!item->Enabled) continue;
+ if ((vk == hkVk) && (mod == hkMod)) {
+ mir_free(pszSection);
+ return item->lParam;
+ } } } }
+
+ mir_free(pszSection);
+ return 0;
+}
+
+static void sttFreeHotkey(THotkeyItem *item)
+{
+ if ( item->type == HKT_GLOBAL && item->Enabled )
+ UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey);
+ GlobalDeleteAtom(item->idHotkey);
+ mir_free(item->pszName);
+ mir_free(item->pszService);
+ mir_free(item->ptszDescription);
+ mir_free(item->ptszSection);
+ mir_free(item);
+}
+
+static void sttRegisterHotkeys()
+{
+ int i;
+ for ( i = 0; i < hotkeys.getCount(); i++ ) {
+ THotkeyItem *item = hotkeys[i];
+ UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey);
+ if (item->type != HKT_GLOBAL) continue;
+ if (item->Enabled) {
+ BYTE mod, vk;
+ sttWordToModAndVk(item->Hotkey, &mod, &vk);
+ if (vk) RegisterHotKey(g_hwndHotkeyHost, item->idHotkey, mod, vk);
+} } }
+
+static void sttUnregisterHotkeys()
+{
+ int i;
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ THotkeyItem *item = hotkeys[i];
+ if ( item->type == HKT_GLOBAL && item->Enabled )
+ UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Hotkey control
+typedef struct
+{
+ WNDPROC oldWndProc;
+ BYTE shift;
+ BYTE key;
+}
+ THotkeyBoxData;
+
+static TCHAR *sttHokeyVkToName(WORD vkKey)
+{
+ static TCHAR buf[256] = {0};
+ DWORD code = MapVirtualKey(vkKey, 0) << 16;
+
+ switch (vkKey)
+ {
+ case 0:
+ case VK_CONTROL:
+ case VK_SHIFT:
+ case VK_MENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_PAUSE:
+ case VK_CANCEL:
+ case VK_NUMLOCK:
+ case VK_CAPITAL:
+ case VK_SCROLL:
+ return _T("");
+
+ case VK_DIVIDE:
+ case VK_INSERT:
+ case VK_HOME:
+ case VK_PRIOR:
+ case VK_DELETE:
+ case VK_END:
+ case VK_NEXT:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ code |= (1UL << 24);
+ }
+
+ GetKeyNameText(code, buf, 256);
+ return buf;
+}
+
+void HotkeyToName(TCHAR *buf, int size, BYTE shift, BYTE key)
+{
+ mir_sntprintf(buf, size, _T("%s%s%s%s%s"),
+ (shift & HOTKEYF_CONTROL) ? _T("Ctrl + ") : _T(""),
+ (shift & HOTKEYF_ALT) ? _T("Alt + ") : _T(""),
+ (shift & HOTKEYF_SHIFT) ? _T("Shift + ") : _T(""),
+ (shift & HOTKEYF_EXT) ? _T("Win + ") : _T(""),
+ sttHokeyVkToName(key));
+}
+
+WORD GetHotkeyValue(INT_PTR idHotkey)
+{
+ for (int i = 0; i < hotkeys.getCount(); i++)
+ if (hotkeys[i]->idHotkey == idHotkey)
+ return hotkeys[i]->Enabled ? hotkeys[i]->Hotkey : 0;
+
+ return 0;
+}
+
+static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ BOOL bKeyDown = FALSE;
+ if (!data) return 0;
+
+ switch (msg) {
+ case HKM_GETHOTKEY:
+ return data->key ? MAKEWORD(data->key, data->shift) : 0;
+
+ case HKM_SETHOTKEY:
+ {
+ TCHAR buf[256] = {0};
+ data->key = (BYTE)LOWORD(wParam);
+ data->shift = (BYTE)HIWORD(wParam);
+ HotkeyToName(buf, SIZEOF(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+ return 0;
+ }
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ case WM_KILLFOCUS:
+ break;
+
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_PASTE:
+ case WM_CONTEXTMENU:
+ return TRUE;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ bKeyDown = TRUE;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ TCHAR buf[256] = {0};
+
+ BYTE shift = 0;
+ BYTE key = wParam;
+ TCHAR *name = sttHokeyVkToName(key);
+ if (!*name || !bKeyDown) key = 0;
+
+ if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL;
+ if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT;
+ if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT;
+ if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT;
+
+ if (bKeyDown || !data->key) {
+ data->shift = shift;
+ data->key = key;
+ }
+
+ HotkeyToName(buf, SIZEOF(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+
+ if (bKeyDown && data->key)
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd);
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ WNDPROC saveOldWndProc = data->oldWndProc;
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)data->oldWndProc);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+ return CallWindowProc(saveOldWndProc, hwnd, msg, wParam, lParam);
+ } }
+
+ return CallWindowProc(data->oldWndProc, hwnd, msg, wParam, lParam);
+}
+
+static void sttHotkeyEditCreate(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data);
+ data->oldWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)sttHotkeyEditProc);
+}
+
+static void sttHotkeyEditDestroy(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)data->oldWndProc);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Options
+
+enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE };
+
+static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item)
+{
+ TCHAR buf[256];
+ LVITEM lvi = {0};
+ lvi.iItem = idx;
+
+ if ( !item->rootHotkey ) {
+ lvi.mask = LVIF_TEXT|LVIF_IMAGE;
+ lvi.iSubItem = COL_NAME;
+ lvi.pszText = item->ptszDescription_tr;
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+
+ ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled);
+ }
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = COL_KEY;
+ HotkeyToName(buf, SIZEOF(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey));
+ lvi.pszText = buf;
+ ListView_SetItem(hwndList, &lvi);
+
+ if ( item->rootHotkey ) {
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_TYPE;
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+ }
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.mask = LVIF_IMAGE|LVIF_TEXT;
+ lvi.iSubItem = COL_ADDREMOVE;
+ if (item->rootHotkey) {
+ lvi.iImage = 4;
+ lvi.pszText = TranslateT("Remove shortcut");
+ }
+ else {
+ lvi.iImage = 3;
+ lvi.pszText = TranslateT("Add another shortcut");
+ }
+ ListView_SetItem(hwndList, &lvi);
+}
+
+static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item)
+{
+ item->OptDeleted = TRUE;
+ ListView_DeleteItem(hwndList, idx);
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = TRUE;
+}
+
+static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ TCHAR title1[256] = {0}, title2[256] = {0};
+ THotkeyItem *item1 = NULL, *item2 = NULL;
+ LVITEM lvi = {0};
+ int res;
+
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.iItem = lParam1;
+ lvi.pszText = title1;
+ lvi.cchTextMax = SIZEOF(title1);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item1 = (THotkeyItem *)lvi.lParam;
+
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.iItem = lParam2;
+ lvi.pszText = title2;
+ lvi.cchTextMax = SIZEOF(title2);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item2 = (THotkeyItem *)lvi.lParam;
+
+ if (!item1 && !item2)
+ return lstrcmp(title1, title2);
+
+ if (!item1) {
+ if (res = lstrcmp(title1, item2->ptszSection_tr))
+ return res;
+ return -1;
+ }
+
+ if (!item2) {
+ if (res = lstrcmp(item1->ptszSection_tr, title2))
+ return res;
+ return 1;
+ }
+ return sttCompareHotkeys(item1, item2);
+}
+
+static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item)
+{
+ char buf[256];
+ LVITEM lvi = {0};
+
+ THotkeyItem *newItem = (THotkeyItem *)mir_alloc(sizeof(THotkeyItem));
+ newItem->pszName = NULL;
+ newItem->pszService = item->pszService ? mir_strdup(item->pszService) : NULL;
+ newItem->ptszSection = mir_tstrdup(item->ptszSection);
+ newItem->ptszDescription = mir_tstrdup(item->ptszDescription);
+ newItem->ptszSection_tr = item->ptszSection_tr;
+ newItem->ptszDescription_tr = item->ptszDescription_tr;
+ newItem->lParam = item->lParam;
+ mir_snprintf(buf, SIZEOF(buf), "mir_hotkey_%d_%d", g_pid, g_hotkeyCount++);
+ newItem->idHotkey = GlobalAddAtomA(buf);
+ newItem->rootHotkey = item;
+ newItem->Hotkey = newItem->DefHotkey = newItem->OptHotkey = 0;
+ newItem->type = newItem->OptType = item->OptType;
+ newItem->Enabled = newItem->OptEnabled = TRUE;
+ newItem->OptChanged = newItem->OptDeleted = FALSE;
+ newItem->OptNew = TRUE;
+
+ hotkeys.insert( newItem );
+
+ SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
+
+ lvi.mask |= LVIF_PARAM;
+ lvi.lParam = (LPARAM)newItem;
+ sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem);
+ ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList);
+
+ SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE);
+
+ item->OptChanged = TRUE;
+}
+
+static void sttOptionsSetChanged(THotkeyItem *item)
+{
+ item->OptChanged = TRUE;
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = TRUE;
+}
+
+static void sttOptionsSaveItem(THotkeyItem *item)
+{
+ int i;
+ char buf[MAXMODULELABELLENGTH];
+
+ if (item->rootHotkey) return;
+ if (!item->OptChanged) return;
+
+ item->Hotkey = item->OptHotkey;
+ item->type = item->OptType;
+ item->Enabled = item->OptEnabled;
+
+ DBWriteContactSettingWord(NULL, DBMODULENAME, item->pszName, item->Hotkey);
+ DBWriteContactSettingByte(NULL, DBMODULENAME "Off", item->pszName, (BYTE)!item->Enabled);
+ if (item->type != HKT_MANUAL)
+ DBWriteContactSettingByte(NULL, DBMODULENAME "Types", item->pszName, (BYTE)item->type);
+
+ item->nSubHotkeys = 0;
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ THotkeyItem *subItem = hotkeys[i];
+ if (subItem->rootHotkey == item) {
+ subItem->Hotkey = subItem->OptHotkey;
+ subItem->type = subItem->OptType;
+
+ mir_snprintf(buf, SIZEOF(buf), "%s$%d", item->pszName, item->nSubHotkeys);
+ DBWriteContactSettingWord(NULL, DBMODULENAME, buf, subItem->Hotkey);
+ if (subItem->type != HKT_MANUAL)
+ DBWriteContactSettingByte(NULL, DBMODULENAME "Types", buf, (BYTE)subItem->type);
+
+ ++item->nSubHotkeys;
+ } }
+
+ mir_snprintf(buf, SIZEOF(buf), "%s$count", item->pszName);
+ DBWriteContactSettingDword(NULL, DBMODULENAME, buf, item->nSubHotkeys);
+}
+
+static void sttBuildHotkeyList(HWND hwndList, TCHAR *section)
+{
+ int i, nItems=0;
+ ListView_DeleteAllItems(hwndList);
+
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ LVITEM lvi = {0};
+ THotkeyItem *item = hotkeys[i];
+
+ if (item->OptDeleted) continue;
+ if (section && lstrcmp(section, item->ptszSection)) continue;
+
+ if ( !section && (!i || lstrcmp(item->ptszSection, ((THotkeyItem *)hotkeys[i-1])->ptszSection ))) {
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.iItem = nItems++;
+ lvi.iSubItem = 0;
+ lvi.lParam = 0;
+ lvi.pszText = item->ptszSection_tr;
+ ListView_InsertItem(hwndList, &lvi);
+ ListView_SetCheckState(hwndList, lvi.iItem, TRUE);
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ lvi.pszText = item->ptszSection;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.iSubItem = 0;
+ }
+
+ lvi.mask = LVIF_PARAM;
+ if (!section) {
+ lvi.mask |= LVIF_INDENT;
+ lvi.iIndent = 1;
+ }
+ lvi.iItem = nItems++;
+ lvi.lParam = (LPARAM)item;
+ ListView_InsertItem(hwndList, &lvi);
+ sttOptionsSetupItem(hwndList, nItems-1, item);
+ }
+}
+
+static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey)
+{
+ LVITEM lvi;
+ THotkeyItem *item;
+ int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (iItem < 0) return;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ if (item = (THotkeyItem *)lvi.lParam) {
+ RECT rc;
+ ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc);
+ MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2);
+ SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0);
+
+ SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_SHOWWINDOW);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), NULL, NULL, RDW_INVALIDATE);
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY));
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), NULL, NULL, RDW_INVALIDATE);
+ }
+}
+
+static void sttOptionsDrawTextChunk(HDC hdc, TCHAR *text, RECT *rc)
+{
+ SIZE sz;
+ DrawText(hdc, text, lstrlen(text), rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS);
+ GetTextExtentPoint32(hdc, text, lstrlen(text), &sz);
+ rc->left += sz.cx;
+}
+
+static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOL initialized = FALSE;
+ static int colWidth = 0;
+ static WORD currentLanguage = 0;
+
+ HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ int i;
+ LVCOLUMN lvc;
+ RECT rc;
+ HIMAGELIST hIml;
+
+ initialized = FALSE;
+
+ TranslateDialogDefault(hwndDlg);
+
+ sttHotkeyEditCreate(GetDlgItem(hwndDlg, IDC_HOTKEY));
+
+ hIml = ImageList_Create(16, 16, ILC_MASK + (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16), 3, 1);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_WINDOWS);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_MIRANDA);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_WINDOW);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_ADDCONTACT);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_DELETE);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_UNDO);
+
+ // This is added to use for drawing operation only
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_GROUPOPEN);
+ ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_GROUPSHUT);
+
+ ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL);
+
+ ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES|LVS_EX_SUBITEMIMAGES|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_INFOTIP);
+
+ GetClientRect(hwndHotkey, &rc);
+ colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3*GetSystemMetrics(SM_CXSMICON) - 5;
+
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = colWidth * 2 / 3;
+ ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc);
+ lvc.cx = GetSystemMetrics(SM_CXSMICON);
+ ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc);
+ lvc.cx = colWidth / 3;
+ ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc);
+ lvc.cx = GetSystemMetrics(SM_CXSMICON);
+ ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc);
+ lvc.cx = GetSystemMetrics(SM_CXSMICON);
+ ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc);
+
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ THotkeyItem *item = hotkeys[i];
+
+ item->OptChanged = FALSE;
+ item->OptDeleted = item->OptNew = FALSE;
+ item->OptEnabled = item->Enabled;
+ item->OptHotkey = item->Hotkey;
+ item->OptType = item->type;
+ }
+
+ currentLanguage = LOWORD(GetKeyboardLayout(0));
+ sttBuildHotkeyList(hwndHotkey, NULL);
+ SetTimer(hwndDlg, 1024, 1000, NULL);
+
+ initialized = TRUE;
+
+ { /* load group states */
+ int count = ListView_GetItemCount(hwndHotkey);
+ TCHAR buf[128];
+ LVITEM lvi = {0};
+ lvi.pszText = buf;
+ lvi.cchTextMax = SIZEOF(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ char *szSetting;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam) continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ szSetting = mir_t2a(lvi.pszText);
+
+ ListView_SetCheckState(hwndHotkey, lvi.iItem,
+ DBGetContactSettingByte(NULL, DBMODULENAME "UI", szSetting, TRUE));
+
+ mir_free(szSetting);
+ }
+ }
+
+ g_hwndOptions = hwndDlg;
+
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ int count = ListView_GetItemCount(hwndHotkey);
+ TCHAR buf[128];
+ LVITEM lvi = {0};
+
+ g_hwndOptions = NULL;
+
+ KillTimer(hwndDlg, 1024);
+
+ lvi.pszText = buf;
+ lvi.cchTextMax = SIZEOF(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ char *szSetting;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam) continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ szSetting = mir_t2a(lvi.pszText);
+
+ DBWriteContactSettingByte(NULL, DBMODULENAME "UI", szSetting,
+ (BYTE) ListView_GetCheckState(hwndHotkey, lvi.iItem));
+
+ mir_free(szSetting);
+ }
+ break;
+ }
+
+ case WM_TIMER:
+ {
+ WORD newLanguage;
+ int count;
+ LVITEM lvi = {0};
+
+ if (!initialized) break;
+
+ newLanguage = LOWORD(GetKeyboardLayout(0));
+ if (newLanguage == currentLanguage) break;
+
+ count = ListView_GetItemCount(hwndHotkey);
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (!lvi.lParam) continue;
+
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam);
+ }
+
+ currentLanguage = newLanguage;
+ break;
+ }
+
+ case WM_HOTKEYUNREGISTERED:
+ {
+ int count;
+ LVITEM lvi = {0};
+
+ count = ListView_GetItemCount(hwndHotkey);
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (!lvi.lParam) continue;
+
+ if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) {
+ ListView_DeleteItem(hwndHotkey, lvi.iItem);
+ --lvi.iItem;
+ --count;
+ }
+ }
+ break;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ RECT rc = lpdis->rcItem;
+ int prefix = 65;
+ int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3;
+ rc.left += 5;
+
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+
+ if (lpdis->CtlID == IDC_CANVAS2) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc);
+
+ rc.left = prefix + width * 0;
+ ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc);
+
+ rc.left = prefix + width * 1;
+ ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc);
+
+ return TRUE;
+ }
+
+ if (lpdis->CtlID == IDC_CANVAS) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc);
+ rc.left += 10;
+
+ rc.left = prefix + width * 0;
+ ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc);
+
+ rc.left = prefix + width * 1;
+ ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc);
+
+ return TRUE;
+ }
+
+ break;
+ }
+
+ case WM_COMMAND:
+ if (( LOWORD( wParam ) == IDC_HOTKEY) && (( HIWORD( wParam ) == EN_KILLFOCUS) || (HIWORD(wParam) == 0 ))) {
+ LVITEM lvi;
+ THotkeyItem *item;
+ WORD wHotkey = (WORD)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE);
+ SetFocus(hwndHotkey);
+ if ( !wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0 ))
+ break;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem >= 0) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (item = (THotkeyItem *)lvi.lParam) {
+ item->OptHotkey = wHotkey;
+
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ } } }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS)
+ {
+ HWND hwndList = (HWND)wParam;
+ POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) };
+ LVITEM lvi = {0};
+ THotkeyItem *item = NULL;
+
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) return FALSE;
+
+ lvi.mask = LVIF_PARAM;
+ ListView_GetItem(hwndList, &lvi);
+ if (!(item = (THotkeyItem *)lvi.lParam)) return FALSE;
+
+ if (( pt.x == -1 ) && ( pt.y == -1 )) {
+ RECT rc;
+ ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL);
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndList, &pt);
+ }
+
+ {
+ enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT };
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_CHANGE, TranslateT("Modify"));
+ SetMenuItemInfo(hMenu, (UINT_PTR)MI_CHANGE, FALSE, &mii);
+ if (item->type != HKT_MANUAL) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING|
+ ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_SYSTEM, TranslateT("System scope"));
+ AppendMenu(hMenu, MF_STRING|
+ ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope"));
+ }
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ if (!item->rootHotkey)
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_ADD, TranslateT("Add binding"));
+ else
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_REMOVE, TranslateT("Remove"));
+ if (item->Hotkey != item->OptHotkey) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_REVERT, TranslateT("Undo"));
+ }
+
+ switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) {
+ case MI_CHANGE:
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ break;
+ case MI_SYSTEM:
+ item->OptType = HKT_GLOBAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_LOCAL:
+ item->OptType = HKT_LOCAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_ADD:
+ initialized = FALSE;
+ sttOptionsAddHotkey(hwndList, item);
+ initialized = FALSE;
+ break;
+ case MI_REMOVE:
+ sttOptionsDeleteHotkey(hwndList, lvi.iItem, item);
+ break;
+ case MI_REVERT:
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ break;
+ }
+ DestroyMenu( hMenu );
+ }
+
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+ switch (lpnmhdr->idFrom) {
+ case 0:
+ {
+ int i;
+
+ if (( lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET ))
+ break;
+
+ sttUnregisterHotkeys();
+
+ for (i = 0; i < hotkeys.getCount(); i++) {
+ THotkeyItem *item = hotkeys[i];
+ if (item->OptNew && item->OptDeleted ||
+ item->rootHotkey && !item->OptHotkey ||
+ (lpnmhdr->code == PSN_APPLY) && item->OptDeleted ||
+ (lpnmhdr->code == PSN_RESET) && item->OptNew)
+ {
+ sttFreeHotkey(item);
+ hotkeys.remove( i-- );
+ }
+ }
+
+ if (lpnmhdr->code == PSN_APPLY) {
+ LVITEM lvi = {0};
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ for (i = 0; i < hotkeys.getCount(); i++)
+ sttOptionsSaveItem(hotkeys[i]);
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = -1;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem)
+ ListView_SetItem(hwndHotkey, &lvi);
+ }
+
+ sttRegisterHotkeys();
+
+ NotifyEventHooks( hEvChanged, 0, 0 );
+ break;
+ }
+ case IDC_LV_HOTKEYS:
+ switch (lpnmhdr->code) {
+ case NM_CLICK:
+ {
+ THotkeyItem *item = NULL;
+ LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
+ LVHITTESTINFO lvhti = {0};
+ LVITEM lvi = {0};
+
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ lvi.iItem = lpnmia->iItem;
+ ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi);
+ item = (THotkeyItem *)lvi.lParam;
+
+ lvhti.pt = lpnmia->ptAction;
+ lvhti.iItem = lpnmia->iItem;
+ lvhti.iSubItem = lpnmia->iSubItem;
+ ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti);
+
+ if (item &&
+ (!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) ||
+ item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) &&
+ ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL)))
+ {
+ item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (item && (lpnmia->iSubItem == COL_RESET)) {
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ }
+ else if (item && (lpnmia->iSubItem == COL_ADDREMOVE)) {
+ if (item->rootHotkey)
+ sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ else {
+ initialized = FALSE;
+ sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item);
+ initialized = TRUE;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ }
+ case LVN_KEYDOWN:
+ {
+ LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam;
+ if ((param->wVKey == VK_SUBTRACT) || (param->wVKey == VK_LEFT) ||
+ (param->wVKey == VK_ADD) || (param->wVKey == VK_RIGHT))
+ {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) break;
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+ if (lvi.lParam) break;
+
+ if ((param->wVKey == VK_ADD) || (param->wVKey == VK_RIGHT))
+ {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE);
+ } else
+ // if ((param->wVKey == VK_SUBTRACT) || (param->wVKey == VK_LEFT))
+ {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE);
+ }
+ }
+ else if (param->wVKey == VK_F2)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+
+ break;
+ }
+ case LVN_ITEMACTIVATE:
+ {
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) break;
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (lvi.lParam)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ else
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem));
+ break;
+ }
+ case LVN_ITEMCHANGED:
+ {
+ LPNMLISTVIEW param = (LPNMLISTVIEW)lParam;
+ THotkeyItem *item = (THotkeyItem *)param->lParam;
+ if (!initialized || (param->uNewState>>12 == param->uOldState>>12))
+ break;
+
+ if (item && !item->rootHotkey) {
+ item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0;
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (!item) {
+ TCHAR buf[256];
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = param->iItem;
+ lvi.pszText = buf;
+ lvi.cchTextMax = SIZEOF(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (param->uNewState>>12 == 1) {
+ int count = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ THotkeyItem *item;
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+ item = (THotkeyItem *)lvi.lParam;
+ if (!item) continue;
+ if (!lstrcmp(item->ptszSection_tr, buf)) {
+ ListView_DeleteItem(lpnmhdr->hwndFrom, lvi.iItem);
+ --lvi.iItem;
+ --count;
+ } }
+ }
+ else if (param->uNewState>>12 == 2) {
+ int i, nItems = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ initialized = FALSE;
+ for (i = 0; i < hotkeys.getCount(); ++i) {
+ LVITEM lvi = {0};
+ THotkeyItem *item = hotkeys[i];
+
+ if (item->OptDeleted) continue;
+ if (lstrcmp(buf, item->ptszSection_tr)) continue;
+
+ lvi.mask = LVIF_PARAM|LVIF_INDENT;
+ lvi.iIndent = 1;
+ lvi.iItem = nItems++;
+ lvi.lParam = (LPARAM)item;
+ ListView_InsertItem(lpnmhdr->hwndFrom, &lvi);
+ sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems-1, item);
+ }
+ ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom);
+ initialized = TRUE;
+ }
+ }
+ break;
+ }
+ case NM_CUSTOMDRAW:
+ {
+ NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *) lParam;
+ switch (param->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW );
+ return TRUE;
+
+ case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+ {
+ THotkeyItem *item;
+ TCHAR buf[256];
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.iItem = param->nmcd.dwItemSpec;
+ lvi.pszText = buf;
+ lvi.cchTextMax = SIZEOF(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+ item = (THotkeyItem *)lvi.lParam;
+
+ if (!item) {
+ RECT rc;
+ HFONT hfnt;
+
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState&CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+ SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState&CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+
+ if (param->iSubItem == 0) {
+ rc.left += 3;
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+ ImageList_Draw(hIml,
+ ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7,
+ param->nmcd.hdc, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT);
+ rc.left += 18;
+ hfnt = ( HFONT )SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0));
+ DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
+ SelectObject(param->nmcd.hdc, hfnt);
+ }
+
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT );
+ return TRUE;
+ }
+
+ if (item->rootHotkey && (param->iSubItem == 0)) {
+ RECT rc;
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT );
+ return TRUE;
+ }
+ break;
+ } }
+ break;
+ }
+ break;
+ } }
+ break;
+ } /* case WM_NOTIFY */
+ } /* switch */
+
+ return FALSE;
+}
+
+static int sttOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hMirandaInst;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.position = -180000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS);
+ odp.ptszTitle = TranslateT("Hotkeys");
+ odp.ptszGroup = TranslateT("Customize");
+ odp.pfnDlgProc = sttOptionsDlgProc;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static int sttModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_OPT_INITIALISE, sttOptionsInit);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Hotkey manager
+
+static const char* oldSettings[] = { "ShowHide", "ReadMsg", "NetSearch", "ShowOptions" };
+static const char* newSettings[] = { "ShowHide", "ReadMessage", "SearchInWeb", "ShowOptions" };
+
+int LoadSkinHotkeys(void)
+{
+ WNDCLASSEX wcl = {0};
+
+ bModuleInitialized = TRUE;
+
+ wcl.cbSize = sizeof(wcl);
+ wcl.lpfnWndProc = sttHotkeyHostWndProc;
+ wcl.style = 0;
+ wcl.cbClsExtra = 0;
+ wcl.cbWndExtra = 0;
+ wcl.hInstance = hMirandaInst;
+ wcl.hIcon = NULL;
+ wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
+ wcl.lpszMenuName = NULL;
+ wcl.lpszClassName = _T("MirandaHotkeyHostWnd");
+ wcl.hIconSm = NULL;
+ RegisterClassEx(&wcl);
+
+ g_pid = GetCurrentProcessId();
+
+ g_hwndHotkeyHost = CreateWindow(_T("MirandaHotkeyHostWnd"), NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hMirandaInst, NULL);
+ SetWindowPos(g_hwndHotkeyHost, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_DEFERERASE|SWP_NOSENDCHANGING|SWP_HIDEWINDOW);
+
+ hhkKeyboard = SetWindowsHookEx(WH_KEYBOARD, sttKeyboardProc, NULL, GetCurrentThreadId());
+
+ hEvChanged = CreateHookableEvent(ME_HOTKEYS_CHANGED);
+
+ CreateServiceFunction(MS_HOTKEY_SUBCLASS, svcHotkeySubclass);
+ CreateServiceFunction(MS_HOTKEY_UNSUBCLASS, svcHotkeyUnsubclass);
+ CreateServiceFunction(MS_HOTKEY_REGISTER, svcHotkeyRegister);
+ CreateServiceFunction(MS_HOTKEY_UNREGISTER, svcHotkeyUnregister);
+ CreateServiceFunction(MS_HOTKEY_CHECK, svcHotkeyCheck);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, sttModulesLoaded);
+ {
+ WORD key;
+ int i;
+ for ( i = 0; i < SIZEOF( oldSettings ); i++ ) {
+ char szSetting[ 100 ];
+ mir_snprintf( szSetting, SIZEOF(szSetting), "HK%s", oldSettings[i] );
+ if (( key = DBGetContactSettingWord( NULL, "Clist", szSetting, 0 ))) {
+ DBDeleteContactSetting( NULL, "Clist", szSetting );
+ DBWriteContactSettingWord( NULL, DBMODULENAME, newSettings[i], key );
+ }
+
+ mir_snprintf( szSetting, SIZEOF(szSetting), "HKEn%s", oldSettings[i] );
+ if (( key = DBGetContactSettingByte( NULL, "Clist", szSetting, 0 ))) {
+ DBDeleteContactSetting( NULL, "Clist", szSetting );
+ DBWriteContactSettingByte( NULL, DBMODULENAME "Off", newSettings[i], (BYTE)(key == 0) );
+ } } }
+
+ return 0;
+}
+
+void UnloadSkinHotkeys(void)
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ DestroyHookableEvent(hEvChanged);
+ UnhookWindowsHookEx(hhkKeyboard);
+ sttUnregisterHotkeys();
+ DestroyWindow(g_hwndHotkeyHost);
+ for ( i = 0; i < hotkeys.getCount(); i++ )
+ sttFreeHotkey(hotkeys[i]);
+ hotkeys.destroy();
+}
diff --git a/src/modules/skin/skinicons.cpp b/src/modules/skin/skinicons.cpp
new file mode 100644
index 0000000000..9bfb0cf655
--- /dev/null
+++ b/src/modules/skin/skinicons.cpp
@@ -0,0 +1,489 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <io.h>
+
+struct StandardIconDescription
+{
+ int id;
+ const char* description;
+ int resource_id;
+ int pf2;
+ const char* section;
+};
+
+static const struct StandardIconDescription mainIcons[] =
+{
+ { SKINICON_OTHER_MIRANDA, LPGEN("Miranda IM"), -IDI_MIRANDA },
+ { SKINICON_EVENT_MESSAGE, LPGEN("Message"), -IDI_RECVMSG },
+ { SKINICON_EVENT_URL, LPGEN("URL"), -IDI_URL },
+ { SKINICON_EVENT_FILE, LPGEN("File"), -IDI_FILE },
+ { SKINICON_OTHER_USERONLINE, LPGEN("User Online"), -IDI_USERONLINE },
+ { SKINICON_OTHER_GROUPOPEN, LPGEN("Group (Open)"), -IDI_GROUPOPEN },
+ { SKINICON_OTHER_GROUPSHUT, LPGEN("Group (Closed)"), -IDI_GROUPSHUT },
+ { SKINICON_OTHER_CONNECTING, LPGEN("Connecting"), -IDI_LOAD },
+ { SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact"), -IDI_ADDCONTACT },
+ { SKINICON_OTHER_USERDETAILS, LPGEN("User Details"), -IDI_USERDETAILS },
+ { SKINICON_OTHER_HISTORY, LPGEN("History"), -IDI_HISTORY },
+ { SKINICON_OTHER_DOWNARROW, LPGEN("Down Arrow"), -IDI_DOWNARROW },
+ { SKINICON_OTHER_FINDUSER, LPGEN("Find User"), -IDI_FINDUSER },
+ { SKINICON_OTHER_OPTIONS, LPGEN("Options"), -IDI_OPTIONS },
+ { SKINICON_OTHER_SENDEMAIL, LPGEN("Send E-mail"), -IDI_SENDEMAIL },
+ { SKINICON_OTHER_DELETE, LPGEN("Delete"), -IDI_DELETE },
+ { SKINICON_OTHER_RENAME, LPGEN("Rename"), -IDI_RENAME },
+ { SKINICON_OTHER_SMS, LPGEN("SMS"), -IDI_SMS },
+ { SKINICON_OTHER_SEARCHALL, LPGEN("Search All"), -IDI_SEARCHALL },
+ { SKINICON_OTHER_TICK, LPGEN("Tick"), -IDI_TICK },
+ { SKINICON_OTHER_NOTICK, LPGEN("No Tick"), -IDI_NOTICK },
+ { SKINICON_OTHER_HELP, LPGEN("Help"), -IDI_HELP },
+ { SKINICON_OTHER_MIRANDAWEB, LPGEN("Miranda Website"), -IDI_MIRANDAWEBSITE },
+ { SKINICON_OTHER_TYPING, LPGEN("Typing"), -IDI_TYPING },
+ { SKINICON_OTHER_SMALLDOT, LPGEN("Small Dot"), -IDI_SMALLDOT },
+ { SKINICON_OTHER_FILLEDBLOB, LPGEN("Filled Blob"), -IDI_FILLEDBLOB },
+ { SKINICON_OTHER_EMPTYBLOB, LPGEN("Empty Blob"), -IDI_EMPTYBLOB },
+ { SKINICON_OTHER_UNICODE, LPGEN("Unicode plugin"), -IDI_UNICODE },
+ { SKINICON_OTHER_ANSI, LPGEN("ANSI plugin"), -IDI_ANSI },
+ { SKINICON_OTHER_LOADED, LPGEN("Running plugin"), -IDI_LOADED },
+ { SKINICON_OTHER_NOTLOADED, LPGEN("Unloaded plugin"), -IDI_NOTLOADED },
+ { SKINICON_OTHER_UNDO, LPGEN("Undo"), -IDI_UNDO },
+ { SKINICON_OTHER_WINDOW, LPGEN("Window"), -IDI_WINDOW },
+ { SKINICON_OTHER_WINDOWS, LPGEN("System"), -IDI_WINDOWS },
+ { SKINICON_OTHER_ACCMGR, LPGEN("Accounts"), -IDI_ACCMGR },
+ { SKINICON_OTHER_SHOWHIDE, LPGEN("ShowHide"), -IDI_SHOWHIDE },
+ { SKINICON_OTHER_EXIT, LPGEN("Exit"), -IDI_EXIT },
+ { SKINICON_OTHER_MAINMENU, LPGEN("Main Menu"), -IDI_MIRANDA },
+ { SKINICON_OTHER_STATUS, LPGEN("Status"), -IDI_ONLINE },
+ { SKINICON_CHAT_JOIN, LPGEN("Join chat"), -IDI_JOINCHAT },
+ { SKINICON_CHAT_LEAVE, LPGEN("Leave chat"), -IDI_LEAVECHAT },
+ { SKINICON_OTHER_GROUP, LPGEN("Move to Group"), -IDI_MOVETOGROUP },
+ { SKINICON_OTHER_ON, LPGEN("On"), -IDI_ON },
+ { SKINICON_OTHER_OFF, LPGEN("Off"), -IDI_OFF },
+ { SKINICON_OTHER_STATUS_LOCKED, LPGEN("Locked status"), -IDI_STATUS_LOCKED, 0, "Status Icons" },
+};
+
+HANDLE hMainIcons[SIZEOF(mainIcons)];
+
+static const struct StandardIconDescription statusIcons[] =
+{
+ { ID_STATUS_OFFLINE, LPGEN("Offline"), -IDI_OFFLINE, 0xFFFFFFFF },
+ { ID_STATUS_ONLINE, LPGEN("Online"), -IDI_ONLINE, PF2_ONLINE },
+ { ID_STATUS_AWAY, LPGEN("Away"), -IDI_AWAY, PF2_SHORTAWAY },
+ { ID_STATUS_NA, LPGEN("NA"), -IDI_NA, PF2_LONGAWAY },
+ { ID_STATUS_OCCUPIED, LPGEN("Occupied"), -IDI_OCCUPIED, PF2_LIGHTDND },
+ { ID_STATUS_DND, LPGEN("DND"), -IDI_DND, PF2_HEAVYDND },
+ { ID_STATUS_FREECHAT, LPGEN("Free for chat"), -IDI_FREE4CHAT, PF2_FREECHAT },
+ { ID_STATUS_INVISIBLE, LPGEN("Invisible"), -IDI_INVISIBLE, PF2_INVISIBLE },
+ { ID_STATUS_ONTHEPHONE, LPGEN("On the phone"), -IDI_ONTHEPHONE, PF2_ONTHEPHONE },
+ { ID_STATUS_OUTTOLUNCH, LPGEN("Out to lunch"), -IDI_OUTTOLUNCH, PF2_OUTTOLUNCH }
+};
+
+HANDLE hStatusIcons[SIZEOF(statusIcons)];
+
+const char* mainIconsFmt = "core_main_";
+const char* statusIconsFmt = "core_status_";
+const char* protoIconsFmt = LPGEN("%s Icons");
+
+#define PROTOCOLS_PREFIX "Status Icons/"
+#define GLOBAL_PROTO_NAME "*"
+
+
+
+
+// load small icon (shared) it's not need to be destroyed
+
+static HICON LoadSmallIconShared(HINSTANCE hInstance, LPCTSTR lpIconName)
+{
+ int cx = GetSystemMetrics(SM_CXSMICON);
+ return ( HICON )LoadImage( hInstance, lpIconName, IMAGE_ICON,cx, cx, LR_DEFAULTCOLOR | LR_SHARED );
+}
+
+// load small icon (not shared) it IS NEED to be destroyed
+static HICON LoadSmallIcon(HINSTANCE hInstance, LPCTSTR lpIconName)
+{
+ HICON hIcon = NULL; // icon handle
+ int index = -( int )lpIconName;
+ TCHAR filename[MAX_PATH] = {0};
+ GetModuleFileName( hInstance, filename, MAX_PATH );
+ ExtractIconEx( filename, index, NULL, &hIcon, 1 );
+ return hIcon;
+}
+
+// load small icon from hInstance
+HICON LoadIconEx(HINSTANCE hInstance, LPCTSTR lpIconName, BOOL bShared)
+{
+ HICON hResIcon = bShared ? LoadSmallIcon(hInstance,lpIconName) : LoadSmallIconShared(hInstance,lpIconName);
+ if ( !hResIcon ) { //Icon not found in hInstance lets try to load it from core
+ HINSTANCE hCoreInstance=hMirandaInst;
+ if ( hCoreInstance != hInstance )
+ hResIcon = bShared ? LoadSmallIcon(hCoreInstance,lpIconName) : LoadSmallIconShared(hCoreInstance,lpIconName);
+ }
+ return hResIcon;
+}
+
+int ImageList_AddIcon_NotShared(HIMAGELIST hIml, LPCTSTR szResource)
+{
+ HICON hTempIcon=LoadIconEx( hMirandaInst, szResource, 0);
+ int res = ImageList_AddIcon(hIml, hTempIcon);
+ Safe_DestroyIcon(hTempIcon);
+ return res;
+}
+
+int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId)
+{
+ HICON hIcon = LoadSkinIcon( iconId );
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IconLib_ReleaseIcon(hIcon,0);
+ return res;
+}
+
+int ImageList_AddIcon_ProtoIconLibLoaded(HIMAGELIST hIml, const char* szProto, int iconId)
+{
+ HICON hIcon = LoadSkinProtoIcon( szProto, iconId );
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IconLib_ReleaseIcon(hIcon,0);
+ return res;
+}
+
+int ImageList_ReplaceIcon_NotShared(HIMAGELIST hIml, int iIndex, HINSTANCE hInstance, LPCTSTR szResource)
+{
+ HICON hTempIcon = LoadIconEx(hInstance, szResource, 0);
+ int res = ImageList_ReplaceIcon(hIml, iIndex, hTempIcon);
+ Safe_DestroyIcon(hTempIcon);
+ return res;
+}
+
+int ImageList_ReplaceIcon_IconLibLoaded(HIMAGELIST hIml, int nIndex, HICON hIcon)
+{
+ int res = ImageList_ReplaceIcon(hIml,nIndex, hIcon);
+ IconLib_ReleaseIcon(hIcon,0);
+ return res;
+}
+
+void Window_SetIcon_IcoLib(HWND hWnd, int iconId)
+{
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )LoadSkinIcon( iconId, true ));
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )LoadSkinIcon( iconId ));
+}
+
+void Window_SetProtoIcon_IcoLib(HWND hWnd, const char* szProto, int iconId)
+{
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )LoadSkinProtoIcon( szProto, iconId, true ));
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )LoadSkinProtoIcon( szProto, iconId ));
+}
+
+void Window_FreeIcon_IcoLib(HWND hWnd)
+{
+ IconLib_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), NULL);
+ IconLib_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), NULL);
+}
+
+void Button_SetIcon_IcoLib(HWND hwndDlg, int itemId, int iconId, const char* tooltip)
+{
+ HWND hWnd = GetDlgItem( hwndDlg, itemId );
+ SendMessage( hWnd, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadSkinIcon( iconId ));
+ SendMessage( hWnd, BUTTONSETASFLATBTN, 0, 0 );
+ SendMessage( hWnd, BUTTONADDTOOLTIP, (WPARAM)tooltip, 0);
+}
+
+void Button_FreeIcon_IcoLib(HWND hwndDlg, int itemId)
+{
+ HICON hIcon = ( HICON )SendDlgItemMessage(hwndDlg, itemId, BM_SETIMAGE, IMAGE_ICON, 0 );
+ IconLib_ReleaseIcon(hIcon,0);
+}
+
+//
+// wParam = szProto
+// lParam = status
+//
+HICON LoadSkinProtoIcon( const char* szProto, int status, bool big )
+{
+ int i, statusIndx = -1;
+ char iconName[MAX_PATH];
+ HICON hIcon;
+ DWORD caps2 = ( szProto == NULL ) ? ( DWORD )-1 : CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_2,0);
+
+ if ( status >= ID_STATUS_CONNECTING && status < ID_STATUS_CONNECTING+MAX_CONNECT_RETRIES ) {
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, 7 );
+ return IcoLib_GetIcon( iconName, big );
+ }
+
+ for ( i = 0; i < SIZEOF(statusIcons); i++ ) {
+ if ( statusIcons[i].id == status ) {
+ statusIndx = i;
+ break;
+ } }
+
+ if ( statusIndx == -1 )
+ return NULL;
+
+ if ( !szProto ) {
+ // Only return a protocol specific icon if there is only one protocol
+ // Otherwise return the global icon. This affects the global status menu mainly.
+ if ( accounts.getCount() == 1 ) {
+ HICON hIcon;
+
+ // format: core_status_%proto%statusindex
+ mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx);
+
+ hIcon = IcoLib_GetIcon( iconName, big );
+ if ( hIcon )
+ return hIcon;
+ }
+
+ // format: core_status_%s%d
+ mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx);
+ return IcoLib_GetIcon( iconName, big );
+ }
+
+ // format: core_status_%s%d
+ mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx);
+ hIcon = IcoLib_GetIcon( iconName, big );
+ if ( hIcon == NULL && ( caps2 == 0 || ( caps2 & statusIcons[statusIndx].pf2 ))) {
+ PROTOACCOUNT* pa = Proto_GetAccount( szProto );
+ if ( pa ) {
+ TCHAR szPath[MAX_PATH], szFullPath[MAX_PATH], *str;
+ SKINICONDESC sid = { 0 };
+
+ //
+ // Queried protocol isn't in list, adding
+ //
+ TCHAR tszSection[MAX_PATH];
+ mir_sntprintf( tszSection, SIZEOF(tszSection), _T("%s%s"), _T(PROTOCOLS_PREFIX), pa->tszAccountName );
+ sid.ptszSection = tszSection;
+
+ sid.cbSize = sizeof(sid);
+ sid.flags = SIDF_ALL_TCHAR;
+
+ GetModuleFileName( hMirandaInst, szPath, MAX_PATH );
+ str = _tcsrchr( szPath, '\\' );
+ if ( str != NULL )
+ *str = 0;
+ mir_sntprintf( szFullPath, SIZEOF(szFullPath), _T("%s\\Icons\\proto_") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, pa->szProtoName );
+ if ( GetFileAttributes( szFullPath ) != INVALID_FILE_ATTRIBUTES )
+ sid.ptszDefaultFile = szFullPath;
+ else {
+ mir_sntprintf( szFullPath, SIZEOF(szFullPath), _T("%s\\Plugins\\") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, szProto );
+ if (( int )ExtractIconEx( szFullPath, statusIcons[i].resource_id, NULL, &hIcon, 1 ) > 0 ) {
+ DestroyIcon( hIcon );
+ sid.ptszDefaultFile = szFullPath;
+ hIcon = NULL;
+ }
+
+ if ( sid.pszDefaultFile == NULL ) {
+ if ( str != NULL )
+ *str = '\\';
+ sid.ptszDefaultFile = szPath;
+ } }
+
+ //
+ // Add global icons to list
+ //
+ {
+ int lowidx, highidx;
+ if ( caps2 == 0 )
+ lowidx = statusIndx, highidx = statusIndx+1;
+ else
+ lowidx = 0, highidx = SIZEOF(statusIcons);
+
+ for ( i = lowidx; i < highidx; i++ ) {
+ if ( caps2 == 0 || ( caps2 & statusIcons[i].pf2 )) {
+ // format: core_%s%d
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, i );
+ sid.pszName = iconName;
+ sid.ptszDescription = cli.pfnGetStatusModeDescription( statusIcons[i].id, 0 );
+ sid.iDefaultIndex = statusIcons[i].resource_id;
+ IcoLib_AddNewIcon( &sid );
+ } } } }
+
+ // format: core_status_%s%d
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx );
+ hIcon = IcoLib_GetIcon( iconName, big );
+ if ( hIcon )
+ return hIcon;
+ }
+
+ if ( hIcon == NULL ) {
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx );
+ hIcon = IcoLib_GetIcon( iconName, big );
+ }
+
+ return hIcon;
+}
+
+HANDLE GetSkinIconHandle( int idx )
+{
+ int i;
+ for ( i = 0; i < SIZEOF(mainIcons); i++ )
+ if ( idx == mainIcons[i].id )
+ return hMainIcons[i];
+
+ return NULL;
+}
+
+HICON LoadSkinIcon( int idx, bool big )
+{
+ //
+ // Query for global status icons
+ //
+ if ( idx < SKINICON_EVENT_MESSAGE ) {
+ if ( idx >= SIZEOF( statusIcons ))
+ return NULL;
+
+ return LoadSkinProtoIcon( NULL, statusIcons[ idx ].id, big );
+ }
+
+ return IcoLib_GetIconByHandle( GetSkinIconHandle( idx ), big );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Initializes the icon skin module
+
+static void convertOneProtocol( char* moduleName, char* iconName )
+{
+ char* pm = moduleName + strlen( moduleName );
+ char* pi = iconName + strlen( iconName );
+ DBVARIANT dbv;
+ int i;
+
+ for ( i = 0; i < SIZEOF(statusIcons); i++ ) {
+ _itoa( statusIcons[i].id, pm, 10 );
+
+ if ( !DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv ) ) {
+ _itoa( i, pi, 10 );
+
+ DBWriteContactSettingTString( NULL, "SkinIcons", iconName, dbv.ptszVal );
+ DBFreeVariant( &dbv );
+
+ DBDeleteContactSetting( NULL, "Icons", moduleName );
+} } }
+
+static INT_PTR sttLoadSkinIcon( WPARAM wParam, LPARAM lParam )
+{
+ switch (lParam)
+ {
+ case 0:
+ return (INT_PTR)LoadSkinIcon( wParam );
+
+ case 1:
+ return (INT_PTR)GetSkinIconHandle( wParam );
+
+ case 2:
+ return (INT_PTR)LoadSkinIcon( wParam, true );
+ }
+
+ return 0;
+}
+
+static INT_PTR sttLoadSkinProtoIcon( WPARAM wParam, LPARAM lParam )
+{
+ return (INT_PTR)LoadSkinProtoIcon( (char*)wParam, (int)lParam, false );
+}
+
+static INT_PTR sttLoadSkinProtoIconBig( WPARAM wParam, LPARAM lParam )
+{
+ return (INT_PTR)LoadSkinProtoIcon( (char*)wParam, (int)lParam, true );
+}
+
+int LoadSkinIcons(void)
+{
+ SKINICONDESC sid;
+ int i, j = 0;
+ char iconName[MAX_PATH], moduleName[MAX_PATH];
+ TCHAR modulePath[MAX_PATH];
+ DBVARIANT dbv;
+
+ //
+ // Perform "1st-time running import"
+
+ for ( i = 0; i < SIZEOF(mainIcons); i++ ) {
+ _itoa( mainIcons[i].id, moduleName, 10 );
+ if ( DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv ))
+ break;
+
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i );
+
+ DBWriteContactSettingTString( NULL, "SkinIcons", iconName, dbv.ptszVal );
+ DBFreeVariant( &dbv );
+
+ DBDeleteContactSetting( NULL, "Icons", moduleName );
+ }
+
+ for ( ;; ) {
+ // get the next protocol name
+ moduleName[0] = 'p';
+ moduleName[1] = 0;
+ _itoa( j++, moduleName+1, 100 );
+ if ( DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv ))
+ break;
+
+ DBDeleteContactSetting( NULL, "Icons", moduleName );
+
+ // make old skinicons' prefix
+ mir_snprintf( moduleName, SIZEOF(moduleName), TCHAR_STR_PARAM, dbv.ptszVal );
+ // make IcoLib's prefix
+ mir_snprintf( iconName, SIZEOF(iconName), "%s" TCHAR_STR_PARAM, statusIconsFmt, dbv.ptszVal );
+
+ convertOneProtocol( moduleName, iconName );
+ DBFreeVariant( &dbv );
+ }
+ moduleName[0] = 0;
+ strcpy(iconName, "core_status_" GLOBAL_PROTO_NAME);
+ convertOneProtocol( moduleName, iconName );
+
+ CreateServiceFunction( MS_SKIN_LOADICON, sttLoadSkinIcon );
+ CreateServiceFunction( MS_SKIN_LOADPROTOICON, sttLoadSkinProtoIcon );
+ CreateServiceFunction( MS_SKIN_LOADPROTOICONBIG, sttLoadSkinProtoIconBig );
+
+ ZeroMemory( &sid, sizeof(sid) );
+ sid.cbSize = sizeof(sid);
+ GetModuleFileName(NULL, modulePath, SIZEOF(modulePath));
+ sid.ptszDefaultFile = modulePath;
+ sid.flags = SIDF_PATH_TCHAR;
+ sid.pszName = iconName;
+
+ //
+ // Add main icons to list
+ //
+ for ( i = 0; i < SIZEOF(mainIcons); i++ ) {
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i );
+ sid.pszSection = mainIcons[i].section == NULL ? "Main Icons" : (char*)mainIcons[i].section;
+ sid.pszDescription = (char*)mainIcons[i].description;
+ sid.iDefaultIndex = mainIcons[i].resource_id;
+ hMainIcons[i] = IcoLib_AddNewIcon( &sid );
+ }
+ //
+ // Add global icons to list
+ //
+ sid.pszSection = PROTOCOLS_PREFIX "Global";
+ //
+ // Asterisk is used, to avoid conflict with proto-plugins
+ // 'coz users can't rename it to name with '*'
+ for ( i = 0; i < SIZEOF(statusIcons); i++ ) {
+ mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, i );
+ sid.pszName = iconName;
+ sid.pszDescription = (char*)statusIcons[i].description;
+ sid.iDefaultIndex = statusIcons[i].resource_id;
+ hStatusIcons[i] = IcoLib_AddNewIcon( &sid );
+ }
+ return 0;
+}
diff --git a/src/modules/skin/sounds.cpp b/src/modules/skin/sounds.cpp
new file mode 100644
index 0000000000..734c319ac9
--- /dev/null
+++ b/src/modules/skin/sounds.cpp
@@ -0,0 +1,469 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+struct SoundItem {
+ char* name;
+ TCHAR* section;
+ TCHAR* description;
+ char* tempFile;
+};
+
+static BOOL bModuleInitialized = FALSE;
+static struct SoundItem *soundList = NULL;
+static int soundCount;
+static HANDLE hPlayEvent = NULL;
+
+static INT_PTR ServiceSkinAddNewSound(WPARAM, LPARAM lParam)
+{
+ SKINSOUNDDESCEX *ssd = ( SKINSOUNDDESCEX* )lParam;
+ switch( ssd->cbSize ) {
+ case sizeof( SKINSOUNDDESCEX ):
+ case SKINSOUNDDESC_SIZE_V1:
+ case SKINSOUNDDESC_SIZE_V2:
+ break;
+
+ default:
+ return 1;
+ }
+
+ if ( ssd->pszName == NULL || ssd->pszDescription == NULL)
+ return 1;
+
+ DBVARIANT dbv;
+ DWORD dwFlags = ( ssd->cbSize == sizeof(SKINSOUNDDESCEX)) ? ssd->dwFlags : 0;
+
+ soundList=(struct SoundItem*)mir_realloc(soundList,sizeof(struct SoundItem)*(soundCount+1));
+ SoundItem* item = &soundList[soundCount++];
+ item->name = mir_strdup( ssd->pszName );
+ item->tempFile = NULL;
+ #if defined( _UNICODE )
+ TCHAR* ptszDefaultFile;
+ if ( dwFlags & SSDF_UNICODE ) {
+ item->description = mir_tstrdup( TranslateTS( ssd->ptszDescription ));
+ item->section = mir_tstrdup( TranslateTS( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->ptszSection : _T("Other")));
+ ptszDefaultFile = mir_tstrdup( ssd->ptszDefaultFile );
+ }
+ else {
+ item->description = LangPackPcharToTchar( ssd->pszDescription );
+ item->section = LangPackPcharToTchar( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->pszSection : "Other" );
+ ptszDefaultFile = mir_a2t( ssd->pszDefaultFile );
+ }
+
+ if ( ptszDefaultFile ) {
+ if ( DBGetContactSettingString(NULL, "SkinSounds", item->name, &dbv))
+ DBWriteContactSettingTString(NULL, "SkinSounds", item->name, ptszDefaultFile);
+ else
+ DBFreeVariant(&dbv);
+ mir_free( ptszDefaultFile );
+ }
+ #else
+ item->description = mir_tstrdup( TranslateTS( ssd->pszDescription ));
+ item->section = mir_tstrdup( TranslateTS( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->pszSection : "Other" ));
+ if ( ssd->pszDefaultFile ) {
+ if ( DBGetContactSettingString(NULL, "SkinSounds", item->name, &dbv))
+ DBWriteContactSettingString(NULL, "SkinSounds", item->name, ssd->pszDefaultFile);
+ else
+ DBFreeVariant(&dbv);
+ }
+ #endif
+ return 0;
+}
+
+static int SkinPlaySoundDefault(WPARAM wParam, LPARAM lParam)
+{
+ char * pszFile = (char *) lParam;
+ if ( pszFile && (DBGetContactSettingByte(NULL,"Skin","UseSound",0) || (int)wParam==1))
+ PlaySoundA(pszFile, NULL, SND_ASYNC | SND_FILENAME | SND_NOWAIT);
+
+ return 0;
+}
+
+static INT_PTR ServiceSkinPlaySound(WPARAM, LPARAM lParam)
+{
+ char* pszSoundName = ( char* )lParam;
+ int j;
+
+ for (j=0; j<soundCount; j++) {
+ if ( pszSoundName && strcmp( soundList[j].name, pszSoundName ) == 0) {
+ if (DBGetContactSettingByte(NULL, "SkinSoundsOff", pszSoundName, 0)==0) {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingString(NULL, "SkinSounds", pszSoundName, &dbv)==0) {
+ char szFull[MAX_PATH];
+
+ pathToAbsolute(dbv.pszVal, szFull, NULL);
+ NotifyEventHooks(hPlayEvent, 0, (LPARAM)szFull);
+ DBFreeVariant(&dbv);
+ }
+ }
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static HTREEITEM FindNamedTreeItemAtRoot(HWND hwndTree, const TCHAR* name)
+{
+ TVITEM tvi;
+ TCHAR str[128];
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = SIZEOF(str);
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while( tvi.hItem != NULL ) {
+ TreeView_GetItem( hwndTree, &tvi );
+ if ( !_tcsicmp( str, name ))
+ return tvi.hItem;
+
+ tvi.hItem = TreeView_GetNextSibling( hwndTree, tvi.hItem );
+ }
+ return NULL;
+}
+
+#define DM_REBUILD_STREE (WM_USER+1)
+#define DM_HIDEPANE (WM_USER+2)
+#define DM_SHOWPANE (WM_USER+3)
+#define DM_CHECKENABLED (WM_USER+4)
+INT_PTR CALLBACK DlgProcSoundOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HWND hwndTree = NULL;
+ switch (msg) {
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(hwndTree, TVSIL_STATE));
+ break;
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ hwndTree = GetDlgItem(hwndDlg, IDC_SOUNDTREE);
+ SetWindowLongPtr(hwndTree,GWL_STYLE,GetWindowLongPtr(hwndTree,GWL_STYLE)|TVS_NOHSCROLL|TVS_CHECKBOXES);
+ SendMessage(hwndDlg, DM_HIDEPANE, 0, 0);
+ SendMessage(hwndDlg, DM_REBUILD_STREE, 0, 0);
+ TreeView_SetItemState(hwndTree, 0, TVIS_SELECTED, TVIS_SELECTED);
+ CheckDlgButton(hwndDlg, IDC_ENABLESOUNDS, DBGetContactSettingByte(NULL, "Skin", "UseSound", 0));
+ SendMessage(hwndDlg, DM_CHECKENABLED, 0, 0);
+ return TRUE;
+
+ case DM_REBUILD_STREE:
+ TreeView_SelectItem(hwndTree, NULL);
+ ShowWindow(hwndTree, SW_HIDE);
+ TreeView_DeleteAllItems(hwndTree);
+ {
+ TVINSERTSTRUCT tvis;
+ int i;
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED;
+ for( i=0; i < soundCount; i++ ) {
+ tvis.item.stateMask = TVIS_EXPANDED;
+ tvis.item.state = TVIS_EXPANDED;
+ tvis.hParent = FindNamedTreeItemAtRoot( hwndTree, soundList[i].section );
+ if ( tvis.hParent == NULL ) {
+ tvis.item.lParam = -1;
+ tvis.item.pszText = soundList[i].section;
+ tvis.hParent = tvis.item.hItem = TreeView_InsertItem( hwndTree, &tvis );
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(0);
+ TreeView_SetItem( hwndTree, &tvis.item );
+ }
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(!DBGetContactSettingByte(NULL,"SkinSoundsOff",soundList[i].name,0)?2:1);
+ tvis.item.lParam = i;
+ tvis.item.pszText = soundList[i].description;
+ TreeView_InsertItem( hwndTree, &tvis );
+ } }
+ { TVITEM tvi;
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while ( tvi.hItem != NULL ) {
+ tvi.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
+ TreeView_GetItem(hwndTree, &tvi);
+ if ( tvi.lParam == -1 )
+ TreeView_SetItemState(hwndTree, tvi.hItem, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
+
+ tvi.hItem=TreeView_GetNextSibling(hwndTree,tvi.hItem);
+ } }
+
+ ShowWindow(hwndTree, SW_SHOW);
+ break;
+
+ case DM_HIDEPANE:
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SGROUP), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NAME), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NAMEVAL), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SLOC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOCATION), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHANGE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_GETMORE), SW_HIDE);
+ break;
+
+ case DM_SHOWPANE:
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SGROUP), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NAME), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NAMEVAL), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SLOC), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOCATION), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHANGE), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_GETMORE), SW_SHOW);
+ break;
+
+ case DM_CHECKENABLED:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SOUNDTREE), IsDlgButtonChecked(hwndDlg, IDC_ENABLESOUNDS));
+ if (!IsDlgButtonChecked(hwndDlg, IDC_ENABLESOUNDS))
+ SendMessage(hwndDlg, DM_HIDEPANE, 0, 0);
+ else if (TreeView_GetSelection(hwndTree)&&TreeView_GetParent(hwndTree, TreeView_GetSelection(hwndTree)))
+ SendMessage(hwndDlg, DM_SHOWPANE, 0, 0);
+ break;
+
+ case WM_COMMAND:
+ if ( LOWORD(wParam) == IDC_ENABLESOUNDS )
+ SendMessage(hwndDlg, DM_CHECKENABLED, 0, 0);
+
+ if ( LOWORD(wParam) == IDC_PREVIEW ) {
+ TVITEM tvi;
+ HTREEITEM hti;
+
+ ZeroMemory(&tvi,sizeof(tvi));
+ ZeroMemory(&hti,sizeof(hti));
+ hti=TreeView_GetSelection(hwndTree);
+ if (hti==NULL) break;
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM|TVIF_TEXT;
+ tvi.hItem = hti;
+ if (TreeView_GetItem(hwndTree, &tvi)==FALSE) break;
+ if (tvi.lParam==-1) break;
+ if (soundList[tvi.lParam].tempFile)
+ NotifyEventHooks(hPlayEvent, 1, (LPARAM)soundList[tvi.lParam].tempFile);
+ else {
+ DBVARIANT dbv;
+ if(!DBGetContactSettingString(NULL,"SkinSounds",soundList[tvi.lParam].name,&dbv)) {
+ char szPathFull[MAX_PATH];
+
+ pathToAbsolute(dbv.pszVal, szPathFull, NULL);
+ NotifyEventHooks(hPlayEvent, 1, (LPARAM)szPathFull);
+ DBFreeVariant(&dbv);
+ } }
+ break;
+ }
+ if ( LOWORD( wParam ) == IDC_CHANGE ) {
+ char str[MAX_PATH] = "", strFull[MAX_PATH], strdir[MAX_PATH]="", filter[MAX_PATH];
+ OPENFILENAMEA ofn;
+ TVITEM tvi;
+ HTREEITEM hti;
+
+ ZeroMemory(&tvi,sizeof(tvi));
+ ZeroMemory(&hti,sizeof(hti));
+ hti=TreeView_GetSelection(hwndTree);
+ if (hti==NULL) break;
+ tvi.mask=TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM|TVIF_TEXT;
+ tvi.hItem = hti;
+ if (TreeView_GetItem(hwndTree, &tvi)==FALSE) break;
+ if (tvi.lParam==-1) break;
+ if (soundList[tvi.lParam].tempFile)
+ mir_snprintf(strFull, SIZEOF(strFull), "%s", soundList[tvi.lParam].tempFile);
+ else {
+ if (DBGetContactSettingByte(NULL, "SkinSoundsOff", soundList[tvi.lParam].name, 0)==0) {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingString(NULL, "SkinSounds", soundList[tvi.lParam].name, &dbv)==0) {
+ pathToAbsolute(dbv.pszVal, strdir, NULL);
+ DBFreeVariant(&dbv);
+ } } }
+
+ mir_snprintf(strFull, SIZEOF(strFull), "%s", soundList[tvi.lParam].tempFile?soundList[tvi.lParam].tempFile:"");
+ pathToAbsolute(strFull, strdir, NULL);
+ ZeroMemory(&ofn, sizeof(ofn));
+ mir_snprintf(filter, SIZEOF(filter), "%s (*.wav; *.mp3; *.ogg; *.flac)%c*.WAV; *.MP3; *.OGG; *.FLAC%c%s (*)%c*%c", Translate("Sound Files"), 0, 0, Translate("All Files"), 0, 0);
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = GetParent(hwndDlg);
+ ofn.hInstance = NULL;
+ ofn.lpstrFilter = filter;
+ { char* slash = strrchr(strdir, '\\');
+ if (slash) {
+ *slash = 0;
+ ofn.lpstrInitialDir = strdir;
+ }
+ }
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_LONGNAMES|OFN_NOCHANGEDIR;
+ ofn.nMaxFile = SIZEOF(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = "wav";
+ if(!GetOpenFileNameA(&ofn)) break;
+ CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)str, (LPARAM)strFull);
+ soundList[tvi.lParam].tempFile = mir_strdup(strFull);
+ SetDlgItemTextA(hwndDlg, IDC_LOCATION, strFull);
+ }
+ if(LOWORD(wParam)==IDC_GETMORE) {
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)"http://addons.miranda-im.org/index.php?action=display&id=5");
+ break;
+ }
+ if(LOWORD(wParam)==IDC_LOCATION) {
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ int i;
+
+ DBWriteContactSettingByte(NULL, "Skin", "UseSound", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLESOUNDS));
+ for ( i=0; i < soundCount; i++ )
+ if ( soundList[i].tempFile )
+ DBWriteContactSettingString(NULL,"SkinSounds",soundList[i].name,soundList[i].tempFile);
+ {
+ TVITEM tvi,tvic;
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while ( tvi.hItem != NULL ) {
+ tvi.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
+ TreeView_GetItem(hwndTree, &tvi);
+ if ( tvi.lParam == -1 ) {
+ tvic.hItem = TreeView_GetChild(hwndTree, tvi.hItem);
+ while ( tvic.hItem != NULL ) {
+ tvic.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
+ TreeView_GetItem(hwndTree, &tvic);
+ if ((( tvic.state & TVIS_STATEIMAGEMASK ) >> 12 == 2 )) {
+ DBCONTACTGETSETTING cgs;
+ cgs.szModule = "SkinSoundsOff";
+ cgs.szSetting = soundList[tvic.lParam].name;
+ CallService(MS_DB_CONTACT_DELETESETTING,(WPARAM)(HANDLE)NULL,(LPARAM)&cgs);
+ }
+ else DBWriteContactSettingByte(NULL,"SkinSoundsOff",soundList[tvic.lParam].name,1);
+ tvic.hItem=TreeView_GetNextSibling(hwndTree,tvic.hItem);
+ } }
+
+ tvi.hItem=TreeView_GetNextSibling(hwndTree,tvi.hItem);
+ } }
+ return TRUE;
+ }
+ break;
+ case IDC_SOUNDTREE:
+ switch(((NMHDR*)lParam)->code) {
+ case TVN_SELCHANGEDA:
+ {
+ NMTREEVIEW *pnmtv = (NMTREEVIEW*)lParam;
+ TVITEM tvi = pnmtv->itemNew;
+
+ if (tvi.lParam==-1) {
+ SendMessage(hwndDlg, DM_HIDEPANE, 0, 0);
+ }
+ else {
+ TCHAR buf[256];
+ DBVARIANT dbv;
+
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s: %s"), soundList[tvi.lParam].section, soundList[tvi.lParam].description);
+ SetDlgItemText(hwndDlg, IDC_NAMEVAL, buf);
+ if (soundList[tvi.lParam].tempFile)
+ SetDlgItemTextA(hwndDlg, IDC_LOCATION, soundList[tvi.lParam].tempFile);
+ else if(!DBGetContactSettingString(NULL,"SkinSounds",soundList[tvi.lParam].name,&dbv)) {
+ SetDlgItemTextA(hwndDlg, IDC_LOCATION, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else SetDlgItemText(hwndDlg, IDC_LOCATION, TranslateT("<not specified>"));
+ SendMessage(hwndDlg, DM_SHOWPANE, 0, 0);
+ }
+ }
+ break;
+ case TVN_KEYDOWN:
+ {
+ NMTVKEYDOWN* ptkd = (NMTVKEYDOWN*)lParam;
+
+ if (ptkd&&ptkd->wVKey==VK_SPACE&&TreeView_GetSelection(ptkd->hdr.hwndFrom))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+ hti.pt.x=(short)LOWORD(GetMessagePos());
+ hti.pt.y=(short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom,&hti.pt);
+ if(TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom,&hti))
+ if (hti.flags&TVHT_ONITEM)
+ if(hti.flags&TVHT_ONITEMSTATEICON)
+ if (TreeView_GetParent(hwndTree, hti.hItem)!=NULL)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ } }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static UINT iconsExpertOnlyControls[]={IDC_IMPORT};
+
+static int SkinOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = -200000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SOUND);
+ odp.pszGroup = LPGEN("Customize");
+ odp.pszTitle = LPGEN("Sounds");
+ odp.pfnDlgProc = DlgProcSoundOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
+static int SkinSystemModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_OPT_INITIALISE,SkinOptionsInit);
+ return 0;
+}
+
+int LoadSkinSounds(void)
+{
+ bModuleInitialized = TRUE;
+
+ soundList=NULL;
+ soundCount=0;
+ CreateServiceFunction(MS_SKIN_ADDNEWSOUND,ServiceSkinAddNewSound);
+ CreateServiceFunction(MS_SKIN_PLAYSOUND,ServiceSkinPlaySound);
+ HookEvent(ME_SYSTEM_MODULESLOADED,SkinSystemModulesLoaded);
+ hPlayEvent=CreateHookableEvent(ME_SKIN_PLAYINGSOUND);
+ SetHookDefaultForHookableEvent(hPlayEvent, SkinPlaySoundDefault);
+ return 0;
+}
+
+void UnloadSkinSounds(void)
+{
+ int i;
+
+ if ( !bModuleInitialized ) return;
+
+ for(i=0;i<soundCount;i++) {
+ mir_free(soundList[i].name);
+ mir_free(soundList[i].section);
+ mir_free(soundList[i].description);
+ if (soundList[i].tempFile) mir_free(soundList[i].tempFile);
+ }
+ if(soundCount) mir_free(soundList);
+}
diff --git a/src/modules/srauth/auth.cpp b/src/modules/srauth/auth.cpp
new file mode 100644
index 0000000000..d62c4af4f4
--- /dev/null
+++ b/src/modules/srauth/auth.cpp
@@ -0,0 +1,129 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest"
+#define MS_AUTH_SHOWADDED "Auth/ShowAdded"
+
+INT_PTR CALLBACK DlgProcAuthReq(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK DlgProcAdded(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+INT_PTR ShowReqWindow(WPARAM, LPARAM lParam)
+{
+ CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_AUTHREQ), NULL, DlgProcAuthReq,
+ (LPARAM)((CLISTEVENT *)lParam)->hDbEvent);
+ return 0;
+}
+
+INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam)
+{
+ CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_ADDED), NULL, DlgProcAdded,
+ (LPARAM)((CLISTEVENT *)lParam)->hDbEvent);
+ return 0;
+}
+
+static int AuthEventAdded(WPARAM, LPARAM lParam)
+{
+ TCHAR szUid[128] = _T("");
+ TCHAR szTooltip[256];
+ const HANDLE hDbEvent = (HANDLE)lParam;
+
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET,(WPARAM)lParam,(LPARAM)&dbei);
+ if (dbei.flags & (DBEF_SENT | DBEF_READ) ||
+ (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED))
+ return 0;
+
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, lParam, 0);
+ dbei.pBlob = (PBYTE)alloca(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET, lParam, (LPARAM)&dbei);
+
+ HANDLE hContact = *(PHANDLE)(dbei.pBlob + sizeof(DWORD));
+
+ CLISTEVENT cli ={0};
+ cli.cbSize = sizeof(cli);
+ cli.hContact = hContact;
+ cli.ptszTooltip = szTooltip;
+ cli.flags = CLEF_TCHAR;
+ cli.lParam = lParam;
+ cli.hDbEvent = hDbEvent;
+
+ CONTACTINFO ci = {0};
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ ci.szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ ci.dwFlag = CNF_UNIQUEID | CNF_TCHAR;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci))
+ {
+ switch (ci.type)
+ {
+ case CNFT_ASCIIZ:
+ mir_sntprintf(szUid, SIZEOF(szUid), _T("%s"), ci.pszVal);
+ mir_free(ci.pszVal);
+ break;
+
+ case CNFT_DWORD:
+ mir_sntprintf(szUid, SIZEOF(szUid), _T("%u"), ci.dVal);
+ break;
+ }
+ }
+
+ if (dbei.eventType == EVENTTYPE_AUTHREQUEST)
+ {
+ SkinPlaySound("AuthRequest");
+ if (szUid[0])
+ mir_sntprintf(szTooltip, SIZEOF(szTooltip), TranslateT("%s requests authorization"), szUid);
+ else
+ mir_sntprintf(szTooltip, SIZEOF(szTooltip), TranslateT("%u requests authorization"), *((PDWORD)dbei.pBlob));
+
+ cli.hIcon = LoadSkinIcon(SKINICON_OTHER_MIRANDA);
+ cli.pszService = MS_AUTH_SHOWREQUEST;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cli);
+ }
+ else if (dbei.eventType == EVENTTYPE_ADDED)
+ {
+ SkinPlaySound("AddedEvent");
+ if (szUid[0])
+ mir_sntprintf(szTooltip, SIZEOF(szTooltip), TranslateT("%s added you to their contact list"), szUid);
+ else
+ mir_sntprintf(szTooltip, SIZEOF(szTooltip), TranslateT("%u added you to their contact list"), *((PDWORD)dbei.pBlob));
+
+ cli.hIcon = LoadSkinIcon(SKINICON_OTHER_MIRANDA);
+ cli.pszService = MS_AUTH_SHOWADDED;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cli);
+ }
+ return 0;
+}
+
+int LoadSendRecvAuthModule(void)
+{
+ CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow);
+ CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow);
+ HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded);
+
+ SkinAddNewSoundEx("AuthRequest", "Alerts", "Authorization request");
+ SkinAddNewSoundEx("AddedEvent", "Alerts", "Added event");
+
+ return 0;
+}
diff --git a/src/modules/srauth/authdialogs.cpp b/src/modules/srauth/authdialogs.cpp
new file mode 100644
index 0000000000..07651da4ce
--- /dev/null
+++ b/src/modules/srauth/authdialogs.cpp
@@ -0,0 +1,312 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+INT_PTR CALLBACK DlgProcAdded(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hDbEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User's Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact Permanently to List"));
+
+ hDbEvent = (HANDLE)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+
+ //blob is: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ)
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)hDbEvent,0);
+ dbei.pBlob = (PBYTE)alloca(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei);
+
+ DWORD uin = *(PDWORD)dbei.pBlob;
+ HANDLE hContact = *(HANDLE*)(dbei.pBlob + sizeof(DWORD));
+ char* nick = (char *)(dbei.pBlob + sizeof(DWORD) + sizeof(HANDLE));
+ char* first = nick + strlen(nick) + 1;
+ char* last = first + strlen(first) + 1;
+ char* email = last + strlen(last) + 1;
+
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+
+ PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule);
+
+ TCHAR* lastT = dbei.flags & DBEF_UTF ? Utf8DecodeT(last) : mir_a2t(last);
+ TCHAR* firstT = dbei.flags & DBEF_UTF ? Utf8DecodeT(first) : mir_a2t(first);
+ TCHAR* nickT = dbei.flags & DBEF_UTF ? Utf8DecodeT(nick) : mir_a2t(nick);
+ TCHAR* emailT = dbei.flags & DBEF_UTF ? Utf8DecodeT(email) : mir_a2t(email);
+
+ TCHAR name[128] = _T("");
+ int off = 0;
+ if (firstT[0] && lastT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s %s"), firstT, lastT);
+ else if (firstT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s"), firstT);
+ else if (lastT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s"), lastT);
+ if (nickT[0])
+ {
+ if (off)
+ mir_sntprintf(name + off, SIZEOF(name) - off, _T(" (%s)"), nickT);
+ else
+ mir_sntprintf(name, SIZEOF(name), _T("%s"), nickT);
+ }
+ if (!name[0])
+ _tcscpy(name, TranslateT("<Unknown>"));
+
+ TCHAR hdr[256];
+ if (uin && emailT[0])
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT, acc->tszAccountName);
+ else if (uin)
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName);
+ else
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT : TranslateT("(Unknown)"), acc->tszAccountName);
+
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, hdr);
+
+ mir_free(lastT);
+ mir_free(firstT);
+ mir_free(nickT);
+ mir_free(emailT);
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DETAILS), GWLP_USERDATA, (LONG_PTR)hContact);
+
+ if (hContact == INVALID_HANDLE_VALUE || !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_ADD:
+ {
+ ADDCONTACTSTRUCT acs = {0};
+ acs.handle = hDbEvent;
+ acs.handleType = HANDLE_EVENT;
+ acs.szProto = "";
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs);
+
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DETAILS), GWLP_USERDATA);
+ if ((hContact == INVALID_HANDLE_VALUE) || !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ ShowWindow(GetDlgItem(hwndDlg,IDC_ADD),FALSE);
+ break;
+ }
+ case IDC_DETAILS:
+ {
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DETAILS), GWLP_USERDATA);
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0);
+ break;
+ }
+
+ case IDOK:
+ {
+ ADDCONTACTSTRUCT acs = {0};
+ acs.handle = hDbEvent;
+ acs.handleType = HANDLE_EVENT;
+ acs.szProto = "";
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs);
+ }
+ //fall through
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_ADD);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, 0));
+ break;
+ }
+ return FALSE;
+}
+
+INT_PTR CALLBACK DlgProcAuthReq(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hDbEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact Permanently to List"));
+
+ {
+ DBEVENTINFO dbei = {0};
+ DWORD uin;
+ char *nick,*first,*last,*email,*reason;
+ HANDLE hContact;
+
+ hDbEvent = (HANDLE)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+
+ //blob is: uin(DWORD),hcontact(HANDLE),nick(ASCIIZ),first(ASCIIZ),last(ASCIIZ),email(ASCIIZ),reason(ASCIIZ)
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0);
+ dbei.pBlob = (PBYTE)alloca(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei);
+
+ uin = *(PDWORD)dbei.pBlob;
+ hContact = *(HANDLE*)(dbei.pBlob + sizeof(DWORD));
+ nick = (char *)(dbei.pBlob + sizeof(DWORD) + sizeof(HANDLE));
+ first = nick + strlen(nick) + 1;
+ last = first + strlen(first) + 1;
+ email = last + strlen(last) + 1;
+ reason = email + strlen(email) + 1;
+
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+
+ PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule);
+
+ TCHAR* lastT = dbei.flags & DBEF_UTF ? Utf8DecodeT(last) : mir_a2t(last);
+ TCHAR* firstT = dbei.flags & DBEF_UTF ? Utf8DecodeT(first) : mir_a2t(first);
+ TCHAR* nickT = dbei.flags & DBEF_UTF ? Utf8DecodeT(nick) : mir_a2t(nick);
+ TCHAR* emailT = dbei.flags & DBEF_UTF ? Utf8DecodeT(email) : mir_a2t(email);
+ TCHAR* reasonT = dbei.flags & DBEF_UTF ? Utf8DecodeT(reason) : mir_a2t(reason);
+
+ TCHAR name[128] =_T("");
+ int off = 0;
+ if (firstT[0] && lastT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s %s"), firstT, lastT);
+ else if (firstT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s"), firstT);
+ else if (lastT[0])
+ off = mir_sntprintf(name, SIZEOF(name), _T("%s"), lastT);
+ if (nickT[0])
+ {
+ if (off)
+ mir_sntprintf(name + off, SIZEOF(name) - off, _T(" (%s)"), nickT);
+ else
+ mir_sntprintf(name, SIZEOF(name), _T("%s"), nickT);
+ }
+ if (!name[0])
+ _tcscpy(name, TranslateT("<Unknown>"));
+
+ TCHAR hdr[256];
+ if (uin && emailT[0])
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s requested authorization\n%u (%s) on %s"), name, uin, emailT, acc->tszAccountName);
+ else if (uin)
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s requested authorization\n%u on %s"), name, uin, acc->tszAccountName);
+ else
+ mir_sntprintf(hdr, SIZEOF(hdr), TranslateT("%s requested authorization\n%s on %s"), name, emailT[0] ? emailT : TranslateT("(Unknown)"), acc->tszAccountName);
+
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, hdr);
+ SetDlgItemText(hwndDlg, IDC_REASON, reasonT);
+
+ mir_free(lastT);
+ mir_free(firstT);
+ mir_free(nickT);
+ mir_free(emailT);
+ mir_free(reasonT);
+
+ if (hContact == INVALID_HANDLE_VALUE || !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE);
+
+ SendDlgItemMessage(hwndDlg, IDC_DENYREASON, EM_LIMITTEXT, 255, 0);
+ if (CallProtoService(dbei.szModule, PS_GETCAPS,PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DENYREASON), FALSE);
+ SetDlgItemText(hwndDlg, IDC_DENYREASON, TranslateT("Feature is not supported by protocol"));
+ }
+ if (!DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADDCHECK), FALSE);
+ CheckDlgButton(hwndDlg, IDC_ADDCHECK, BST_UNCHECKED);
+ }
+ else
+ CheckDlgButton(hwndDlg, IDC_ADDCHECK, BST_CHECKED);
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DETAILS), GWLP_USERDATA, (LONG_PTR)hContact);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_DETAILS:
+ CallService(MS_USERINFO_SHOWDIALOG, GetWindowLongPtr((HWND)lParam, GWLP_USERDATA), 0);
+ break;
+
+ case IDC_DECIDELATER:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDOK:
+ {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei);
+ CallProtoService(dbei.szModule, PS_AUTHALLOW, (WPARAM)hDbEvent,0);
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_ADDCHECK))
+ {
+ ADDCONTACTSTRUCT acs = {0};
+ acs.handle = hDbEvent;
+ acs.handleType = HANDLE_EVENT;
+ acs.szProto = "";
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs);
+ }
+ }
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDCANCEL:
+ {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei);
+
+ if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_DENYREASON)))
+ {
+ TCHAR szReason[256];
+ GetDlgItemText(hwndDlg, IDC_DENYREASON, szReason, SIZEOF(szReason));
+ CallProtoService(dbei.szModule, PS_AUTHDENYT, (WPARAM)hDbEvent, (LPARAM)szReason);
+ }
+ else
+ CallProtoService(dbei.szModule, PS_AUTHDENYT, (WPARAM)hDbEvent, 0);
+ }
+ DestroyWindow(hwndDlg);
+ break;;
+ }
+ break;
+
+ case WM_DESTROY:
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_ADD);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, 0));
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/srawaymsg/awaymsg.cpp b/src/modules/srawaymsg/awaymsg.cpp
new file mode 100644
index 0000000000..39383e5367
--- /dev/null
+++ b/src/modules/srawaymsg/awaymsg.cpp
@@ -0,0 +1,194 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+int LoadAwayMessageSending(void);
+
+static HANDLE hAwayMsgMenuItem;
+static HANDLE hWindowList;
+
+struct AwayMsgDlgData {
+ HANDLE hContact;
+ HANDLE hSeq;
+ HANDLE hAwayMsgEvent;
+};
+#define HM_AWAYMSG (WM_USER+10)
+static INT_PTR CALLBACK ReadAwayMsgDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ AwayMsgDlgData *dat = (AwayMsgDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat = (AwayMsgDlgData*)mir_alloc(sizeof(AwayMsgDlgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ dat->hContact = (HANDLE)lParam;
+ dat->hAwayMsgEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_AWAYMSG);
+ dat->hSeq = (HANDLE)CallContactService(dat->hContact, PSS_GETAWAYMSG, 0, 0);
+ WindowList_Add(hWindowList, hwndDlg, dat->hContact);
+
+ {
+ TCHAR str[256], format[128];
+ TCHAR* contactName = cli.pfnGetContactDisplayName(dat->hContact, 0);
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)dat->hContact, 0);
+ WORD dwStatus = DBGetContactSettingWord(dat->hContact, szProto, "Status", ID_STATUS_OFFLINE);
+ TCHAR* status = cli.pfnGetStatusModeDescription(dwStatus, 0);
+
+ GetWindowText(hwndDlg, format, SIZEOF(format));
+ mir_sntprintf(str, SIZEOF(str), format, status, contactName);
+ SetWindowText(hwndDlg, str);
+
+ GetDlgItemText(hwndDlg, IDC_RETRIEVING, format, SIZEOF(format));
+ mir_sntprintf(str, SIZEOF(str), format, status);
+ SetDlgItemText(hwndDlg, IDC_RETRIEVING, str);
+
+ Window_SetProtoIcon_IcoLib(hwndDlg, szProto, dwStatus);
+ }
+ if (dat->hSeq == NULL)
+ {
+ ACKDATA ack = {0};
+ ack.cbSize = sizeof(ack);
+ ack.hContact = dat->hContact;
+ ack.type = ACKTYPE_AWAYMSG;
+ ack.result = ACKRESULT_SUCCESS;
+ SendMessage(hwndDlg, HM_AWAYMSG, 0, (LPARAM)&ack);
+ }
+ Utils_RestoreWindowPosition(hwndDlg, (HANDLE)lParam, "SRAway", "AwayMsgDlg");
+ return TRUE;
+
+ case HM_AWAYMSG:
+ {
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack->hContact != dat->hContact || ack->type != ACKTYPE_AWAYMSG) break;
+ if (ack->result != ACKRESULT_SUCCESS) break;
+ if (dat->hAwayMsgEvent && ack->hProcess == dat->hSeq) { UnhookEvent(dat->hAwayMsgEvent); dat->hAwayMsgEvent = NULL; }
+
+#ifdef _UNICODE
+ DBVARIANT dbv;
+ bool unicode = !DBGetContactSetting(dat->hContact, "CList", "StatusMsg", &dbv) &&
+ (dbv.type == DBVT_UTF8 || dbv.type == DBVT_WCHAR);
+ DBFreeVariant(&dbv);
+ if (unicode)
+ {
+ DBGetContactSettingWString(dat->hContact, "CList", "StatusMsg", &dbv);
+ SetDlgItemText(hwndDlg, IDC_MSG, dbv.pwszVal);
+ }
+ else
+#endif
+ SetDlgItemTextA(hwndDlg, IDC_MSG, (const char*)ack->lParam);
+
+ ShowWindow(GetDlgItem(hwndDlg,IDC_RETRIEVING), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_MSG), SW_SHOW);
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Close"));
+ break;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDCANCEL:
+ case IDOK:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ if (dat->hAwayMsgEvent) UnhookEvent(dat->hAwayMsgEvent);
+ Utils_SaveWindowPosition(hwndDlg,dat->hContact,"SRAway","AwayMsgDlg");
+ WindowList_Remove(hWindowList,hwndDlg);
+ Window_FreeIcon_IcoLib(hwndDlg);
+ mir_free(dat);
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR GetMessageCommand(WPARAM wParam, LPARAM)
+{
+ HWND hwnd;
+ if(hwnd=WindowList_Find(hWindowList,(HANDLE)wParam)) {
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ }
+ else CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_READAWAYMSG),NULL,ReadAwayMsgDlgProc,wParam);
+ return 0;
+}
+
+static int AwayMsgPreBuildMenu(WPARAM wParam, LPARAM)
+{
+ CLISTMENUITEM clmi;
+ TCHAR str[128];
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ ZeroMemory(&clmi,sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+ clmi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE | CMIF_HIDDEN | CMIF_TCHAR;
+
+ if ( szProto != NULL ) {
+ int chatRoom = szProto ? DBGetContactSettingByte((HANDLE)wParam, szProto, "ChatRoom", 0) : 0;
+ if ( !chatRoom ) {
+ int status = DBGetContactSettingWord((HANDLE)wParam,szProto,"Status",ID_STATUS_OFFLINE);
+ mir_sntprintf( str, SIZEOF(str), TranslateT("Re&ad %s Message"), cli.pfnGetStatusModeDescription( status, 0 ));
+ clmi.ptszName = str;
+ if ( CallProtoService( szProto, PS_GETCAPS, PFLAGNUM_1, 0 ) & PF1_MODEMSGRECV ) {
+ if ( CallProtoService( szProto, PS_GETCAPS, PFLAGNUM_3, 0 ) & Proto_Status2Flag( status )) {
+ clmi.flags = CMIM_FLAGS | CMIM_NAME | CMIF_NOTOFFLINE | CMIM_ICON | CMIF_TCHAR;
+ clmi.hIcon = LoadSkinProtoIcon(szProto, status);
+ } } } }
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )hAwayMsgMenuItem, ( LPARAM )&clmi );
+ IconLib_ReleaseIcon(clmi.hIcon,0);
+ return 0;
+}
+
+static int AwayMsgPreShutdown(WPARAM, LPARAM)
+{
+ if (hWindowList) WindowList_BroadcastAsync(hWindowList,WM_CLOSE,0,0);
+ return 0;
+}
+
+int LoadAwayMsgModule(void)
+{
+ CLISTMENUITEM mi = { 0 };
+
+ hWindowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST,0,0);
+ CreateServiceFunction(MS_AWAYMSG_SHOWAWAYMSG,GetMessageCommand);
+
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000005000;
+ mi.flags = CMIF_NOTOFFLINE;
+ mi.pszName = LPGEN("Re&ad Status Message");
+ mi.pszService = MS_AWAYMSG_SHOWAWAYMSG;
+ hAwayMsgMenuItem = ( HANDLE )CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU,AwayMsgPreBuildMenu);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,AwayMsgPreShutdown);
+ return LoadAwayMessageSending();
+}
diff --git a/src/modules/srawaymsg/sendmsg.cpp b/src/modules/srawaymsg/sendmsg.cpp
new file mode 100644
index 0000000000..09cfcc77ad
--- /dev/null
+++ b/src/modules/srawaymsg/sendmsg.cpp
@@ -0,0 +1,637 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+extern bool prochotkey;
+
+static DWORD protoModeMsgFlags;
+static HWND hwndStatusMsg;
+
+static const TCHAR *GetDefaultMessage(int status)
+{
+ switch(status) {
+ case ID_STATUS_AWAY: return TranslateT("I've been away since %time%.");
+ case ID_STATUS_NA: return TranslateT("Give it up, I'm not in!");
+ case ID_STATUS_OCCUPIED: return TranslateT("Not right now.");
+ case ID_STATUS_DND: return TranslateT("Give a guy some peace, would ya?");
+ case ID_STATUS_FREECHAT: return TranslateT("I'm a chatbot!");
+ case ID_STATUS_ONLINE: return TranslateT("Yep, I'm here.");
+ case ID_STATUS_OFFLINE: return TranslateT("Nope, not here.");
+ case ID_STATUS_INVISIBLE: return TranslateT("I'm hiding from the mafia.");
+ case ID_STATUS_ONTHEPHONE: return TranslateT("That'll be the phone.");
+ case ID_STATUS_OUTTOLUNCH: return TranslateT("Mmm...food.");
+ case ID_STATUS_IDLE: return TranslateT("idleeeeeeee");
+ }
+ return NULL;
+}
+
+static const char *StatusModeToDbSetting(int status, const char *suffix)
+{
+ const char *prefix;
+ static char str[64];
+
+ switch(status)
+ {
+ case ID_STATUS_AWAY: prefix = "Away"; break;
+ case ID_STATUS_NA: prefix = "Na"; break;
+ case ID_STATUS_DND: prefix = "Dnd"; break;
+ case ID_STATUS_OCCUPIED: prefix = "Occupied"; break;
+ case ID_STATUS_FREECHAT: prefix = "FreeChat"; break;
+ case ID_STATUS_ONLINE: prefix = "On"; break;
+ case ID_STATUS_OFFLINE: prefix = "Off"; break;
+ case ID_STATUS_INVISIBLE: prefix = "Inv"; break;
+ case ID_STATUS_ONTHEPHONE: prefix = "Otp"; break;
+ case ID_STATUS_OUTTOLUNCH: prefix = "Otl"; break;
+ case ID_STATUS_IDLE: prefix = "Idl"; break;
+ default: return NULL;
+ }
+ mir_snprintf(str, SIZEOF(str), "%s%s", prefix, suffix);
+ return str;
+}
+
+static TCHAR* GetAwayMessage(int statusMode, char *szProto)
+{
+ DBVARIANT dbv;
+
+ if (szProto && !(CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(statusMode)))
+ return NULL;
+
+ if (DBGetContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(statusMode, "Ignore"), 0))
+ return NULL;
+
+ if (DBGetContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(statusMode, "UsePrev"),0))
+ {
+ if (DBGetContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(statusMode, "Msg"), &dbv))
+ dbv.ptszVal = mir_tstrdup(GetDefaultMessage(statusMode));
+ }
+ else {
+ int i;
+ TCHAR substituteStr[128];
+ if (DBGetContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(statusMode,"Default"), &dbv))
+ dbv.ptszVal = mir_tstrdup(GetDefaultMessage(statusMode));
+
+ for (i=0; dbv.ptszVal[i]; i++)
+ {
+ if (dbv.ptszVal[i] != '%') continue;
+ if (!_tcsnicmp(dbv.ptszVal + i, _T("%time%"), 6))
+ {
+ MIRANDA_IDLE_INFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ CallService(MS_IDLE_GETIDLEINFO, 0, (LPARAM)&mii);
+
+ if (mii.idleType == 1)
+ {
+ int mm;
+ SYSTEMTIME t;
+ GetLocalTime(&t);
+ mm = t.wMinute + t.wHour * 60 - mii.idleTime;
+ if (mm < 0) mm += 60 * 24;
+ t.wMinute = mm % 60;
+ t.wHour = mm / 60;
+ GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &t, NULL, substituteStr, SIZEOF(substituteStr));
+ }
+ else GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, NULL, substituteStr, SIZEOF(substituteStr));
+ }
+ else if (!_tcsnicmp(dbv.ptszVal + i, _T("%date%"), 6))
+ GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, substituteStr, SIZEOF(substituteStr));
+ else continue;
+ if (lstrlen(substituteStr) > 6)
+ dbv.ptszVal = (TCHAR*)mir_realloc(dbv.ptszVal, (lstrlen(dbv.ptszVal) + 1 + lstrlen(substituteStr) - 6) * sizeof(TCHAR));
+ MoveMemory(dbv.ptszVal + i + lstrlen(substituteStr), dbv.ptszVal + i + 6, (lstrlen(dbv.ptszVal) - i - 5) * sizeof(TCHAR));
+ CopyMemory(dbv.ptszVal+i, substituteStr, lstrlen(substituteStr) * sizeof(TCHAR));
+ }
+ }
+ return dbv.ptszVal;
+}
+
+static WNDPROC OldMessageEditProc;
+
+static LRESULT CALLBACK MessageEditSubclassProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_CHAR:
+ if(wParam == '\n' && GetKeyState(VK_CONTROL) & 0x8000)
+ {
+ PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0);
+ return 0;
+ }
+ if (wParam == 1 && GetKeyState(VK_CONTROL) & 0x8000) //ctrl-a
+ {
+ SendMessage(hwnd, EM_SETSEL, 0, -1);
+ return 0;
+ }
+ if (wParam == 23 && GetKeyState(VK_CONTROL) & 0x8000) // ctrl-w
+ {
+ SendMessage(GetParent(hwnd), WM_CLOSE, 0, 0);
+ return 0;
+ }
+ if (wParam == 127 && GetKeyState(VK_CONTROL) & 0x8000) //ctrl-backspace
+ {
+ DWORD start, end;
+ TCHAR *text;
+ int textLen;
+ SendMessage(hwnd, EM_GETSEL, (WPARAM)&end, 0);
+ SendMessage(hwnd, WM_KEYDOWN, VK_LEFT, 0);
+ SendMessage(hwnd, EM_GETSEL, (WPARAM)&start, 0);
+ textLen = GetWindowTextLength(hwnd);
+ text = (TCHAR *)alloca(sizeof(TCHAR) * (textLen + 1));
+ GetWindowText(hwnd, text, textLen + 1);
+ memmove(text + start, text + end, sizeof(TCHAR) * (textLen + 1 - end));
+ SetWindowText(hwnd, text);
+ SendMessage(hwnd, EM_SETSEL, start, start);
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), EN_CHANGE), (LPARAM)hwnd);
+ return 0;
+ }
+ break;
+ }
+ return CallWindowProc(OldMessageEditProc, hwnd, msg, wParam, lParam);
+}
+
+void ChangeAllProtoMessages(char *szProto, int statusMode, TCHAR *msg)
+{
+ if (szProto == NULL)
+ {
+ for (int i=0; i < accounts.getCount(); i++)
+ {
+ PROTOACCOUNT* pa = accounts[i];
+ if (!Proto_IsAccountEnabled(pa)) continue;
+ if ((CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND) &&
+ !Proto_IsAccountLocked(pa))
+ CallProtoService(pa->szModuleName, PS_SETAWAYMSGT, statusMode, (LPARAM)msg);
+ }
+ }
+ else
+ CallProtoService(szProto, PS_SETAWAYMSGT, statusMode,(LPARAM)msg);
+}
+
+struct SetAwayMsgData
+{
+ int statusMode;
+ int countdown;
+ TCHAR okButtonFormat[64];
+ char *szProto;
+ HANDLE hPreshutdown;
+};
+struct SetAwasMsgNewData
+{
+ char *szProto;
+ int statusMode;
+};
+
+#define DM_SRAWAY_SHUTDOWN WM_USER+10
+
+static INT_PTR CALLBACK SetAwayMsgDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ SetAwayMsgData* dat = (SetAwayMsgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ {
+ SetAwasMsgNewData *newdat = (SetAwasMsgNewData*)lParam;
+ TranslateDialogDefault(hwndDlg);
+ dat = (SetAwayMsgData*)mir_alloc(sizeof(SetAwayMsgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->statusMode = newdat->statusMode;
+ dat->szProto = newdat->szProto;
+ mir_free(newdat);
+ SendDlgItemMessage(hwndDlg, IDC_MSG, EM_LIMITTEXT, 1024, 0);
+ OldMessageEditProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MSG), GWLP_WNDPROC, (LONG_PTR)MessageEditSubclassProc);
+ {
+ TCHAR str[256], format[128];
+ GetWindowText(hwndDlg, format, SIZEOF(format));
+ mir_sntprintf(str, SIZEOF(str), format, cli.pfnGetStatusModeDescription(dat->statusMode, 0));
+ SetWindowText(hwndDlg, str );
+ }
+ GetDlgItemText(hwndDlg, IDOK, dat->okButtonFormat, SIZEOF(dat->okButtonFormat));
+ {
+ TCHAR *msg = GetAwayMessage(dat->statusMode, dat->szProto);
+ SetDlgItemText(hwndDlg, IDC_MSG, msg);
+ mir_free(msg);
+ }
+ dat->countdown = 6;
+ SendMessage(hwndDlg, WM_TIMER, 0, 0);
+ Window_SetProtoIcon_IcoLib(hwndDlg, dat->szProto, dat->statusMode);
+ Utils_RestoreWindowPosition(hwndDlg,NULL,"SRAway","AwayMsgDlg");
+ SetTimer(hwndDlg, 1, 1000, 0);
+ dat->hPreshutdown = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, hwndDlg, DM_SRAWAY_SHUTDOWN);
+ }
+ return TRUE;
+
+ case WM_TIMER:
+ if (--dat->countdown >= 0)
+ {
+ TCHAR str[64];
+ mir_sntprintf(str, SIZEOF(str), dat->okButtonFormat, dat->countdown);
+ SetDlgItemText(hwndDlg, IDOK, str);
+ }
+ else
+ {
+ KillTimer(hwndDlg, 1);
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case WM_CLOSE:
+ {
+ TCHAR *msg = GetAwayMessage(dat->statusMode, dat->szProto);
+ ChangeAllProtoMessages(dat->szProto, dat->statusMode, msg);
+ mir_free(msg);
+ }
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ if (dat->countdown < 0)
+ {
+ TCHAR str[1024];
+ GetDlgItemText(hwndDlg, IDC_MSG, str, SIZEOF(str));
+ ChangeAllProtoMessages(dat->szProto, dat->statusMode, str);
+ DBWriteContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(dat->statusMode, "Msg"), str);
+ DestroyWindow(hwndDlg);
+ }
+ else
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+
+ case IDC_MSG:
+ if (dat->countdown >= 0)
+ {
+ KillTimer(hwndDlg, 1);
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("OK"));
+ dat->countdown = -1;
+ }
+ break;
+ }
+ break;
+
+ case DM_SRAWAY_SHUTDOWN:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ Utils_SaveWindowPosition(hwndDlg,NULL,"SRAway","AwayMsgDlg");
+ KillTimer(hwndDlg, 1);
+ UnhookEvent(dat->hPreshutdown);
+ Window_FreeIcon_IcoLib(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MSG), GWLP_WNDPROC, (LONG_PTR)OldMessageEditProc);
+ mir_free(dat);
+ hwndStatusMsg = NULL;
+ break;
+ }
+ return FALSE;
+}
+
+static int StatusModeChange(WPARAM wParam, LPARAM lParam)
+{
+ BOOL bScreenSaverRunning = FALSE;
+ int statusMode = (int)wParam;
+ char *szProto = (char*)lParam;
+
+ if (protoModeMsgFlags == 0) return 0;
+
+ // If its a global change check the complete PFLAGNUM_3 flags to see if a popup might be needed
+ if (!szProto)
+ {
+ if (!(protoModeMsgFlags & Proto_Status2Flag(statusMode)))
+ return 0;
+ }
+ else
+ {
+ // If its a single protocol check the PFLAGNUM_3 for the single protocol
+ if (!(CallProtoService(szProto, PS_GETCAPS,PFLAGNUM_1, 0) & PF1_MODEMSGSEND) ||
+ !(CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(statusMode)))
+ return 0;
+ }
+
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &bScreenSaverRunning, FALSE);
+ if (DBGetContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(statusMode, "Ignore"), 0))
+ {
+ ChangeAllProtoMessages(szProto, statusMode, NULL);
+ }
+ else if (bScreenSaverRunning || ((!GetAsyncKeyState(VK_CONTROL) || prochotkey) &&
+ DBGetContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(statusMode, "NoDlg"), 0)))
+ {
+ TCHAR *msg = GetAwayMessage(statusMode, szProto);
+ ChangeAllProtoMessages(szProto, statusMode, msg);
+ mir_free(msg);
+ }
+ else {
+ SetAwasMsgNewData *newdat = (SetAwasMsgNewData*)mir_alloc(sizeof(SetAwasMsgNewData));
+ newdat->szProto = szProto;
+ newdat->statusMode = statusMode;
+ if (hwndStatusMsg)
+ DestroyWindow(hwndStatusMsg);
+ hwndStatusMsg = CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_SETAWAYMSG),
+ NULL, SetAwayMsgDlgProc, (LPARAM)newdat);
+ }
+ return 0;
+}
+
+static const int statusModes[] =
+{
+ ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND,
+ ID_STATUS_FREECHAT,ID_STATUS_INVISIBLE,ID_STATUS_OUTTOLUNCH,ID_STATUS_ONTHEPHONE, ID_STATUS_IDLE
+};
+
+struct AwayMsgInfo
+{
+ int ignore;
+ int noDialog;
+ int usePrevious;
+ TCHAR msg[1024];
+};
+struct AwayMsgDlgData
+{
+ struct AwayMsgInfo info[ SIZEOF(statusModes) ];
+ int oldPage;
+};
+
+static INT_PTR CALLBACK DlgProcAwayMsgOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct AwayMsgDlgData *dat;
+
+ HWND hLst = GetDlgItem(hwndDlg, IDC_LST_STATUS);
+
+ dat=(struct AwayMsgDlgData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ dat = (AwayMsgDlgData*)mir_alloc(sizeof(AwayMsgDlgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->oldPage = -1;
+ for (int i = 0; i < SIZEOF(statusModes); i++)
+ {
+ int j;
+ if (!(protoModeMsgFlags & Proto_Status2Flag(statusModes[i])))
+ continue;
+
+ if (hLst)
+ {
+ j = SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_ADDSTRING, 0, (LPARAM)cli.pfnGetStatusModeDescription(statusModes[i], 0));
+ SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_SETITEMDATA, j, statusModes[i]);
+ }
+ else
+ {
+ j = SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_ADDSTRING, 0, (LPARAM)cli.pfnGetStatusModeDescription(statusModes[i], 0));
+ SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_SETITEMDATA, j, statusModes[i]);
+ }
+
+ dat->info[j].ignore = DBGetContactSettingByte(NULL, "SRAway",
+ StatusModeToDbSetting(statusModes[i], "Ignore"), 0);
+ dat->info[j].noDialog = DBGetContactSettingByte(NULL, "SRAway",
+ StatusModeToDbSetting(statusModes[i], "NoDlg"), 0);
+ dat->info[j].usePrevious = DBGetContactSettingByte(NULL, "SRAway",
+ StatusModeToDbSetting(statusModes[i], "UsePrev"), 0);
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(statusModes[i], "Default"), &dbv))
+ if (DBGetContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(statusModes[i], "Msg"), &dbv))
+ dbv.ptszVal = mir_tstrdup(GetDefaultMessage(statusModes[i]));
+ lstrcpy(dat->info[j].msg, dbv.ptszVal);
+ mir_free(dbv.ptszVal);
+ }
+ if (hLst)
+ SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_SETCURSEL, 0, 0);
+ else
+ SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_SETCURSEL, 0, 0);
+ SendMessage(hwndDlg, WM_COMMAND, hLst ? MAKEWPARAM(IDC_LST_STATUS, LBN_SELCHANGE) : MAKEWPARAM(IDC_STATUS, CBN_SELCHANGE), 0);
+ return TRUE;
+ }
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT mis = (LPMEASUREITEMSTRUCT)lParam;
+ if (mis->CtlID == IDC_LST_STATUS)
+ mis->itemHeight = 20;
+ break;
+ }
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->CtlID != IDC_LST_STATUS) break;
+ if (dis->itemID < 0) break;
+
+ TCHAR buf[128];
+ SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_GETTEXT, dis->itemID, (LPARAM)buf);
+
+ if (dis->itemState & (ODS_SELECTED|ODS_FOCUS))
+ {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+ RECT rc = dis->rcItem;
+ DrawIconEx(dis->hDC, 3, (rc.top + rc.bottom - 16) / 2, LoadSkinnedProtoIcon(NULL, dis->itemData), 16, 16, 0, NULL, DI_NORMAL);
+ rc.left += 25;
+ SetBkMode(dis->hDC, TRANSPARENT);
+ DrawText(dis->hDC, buf, -1, &rc, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
+ break;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_LST_STATUS:
+ case IDC_STATUS:
+ if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == LBN_SELCHANGE))
+ {
+ int i = hLst ?
+ SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_GETCURSEL, 0, 0) :
+ SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_GETCURSEL, 0, 0);
+ if (dat->oldPage != -1)
+ {
+ dat->info[dat->oldPage].ignore = IsDlgButtonChecked(hwndDlg, IDC_DONTREPLY);
+ dat->info[dat->oldPage].noDialog = IsDlgButtonChecked(hwndDlg, IDC_NODIALOG);
+ dat->info[dat->oldPage].usePrevious = IsDlgButtonChecked(hwndDlg, IDC_USEPREVIOUS);
+ GetDlgItemText(hwndDlg, IDC_MSG, dat->info[dat->oldPage].msg, SIZEOF(dat->info[dat->oldPage].msg));
+ }
+ CheckDlgButton(hwndDlg, IDC_DONTREPLY, i < 0 ? 0 : dat->info[i].ignore);
+ CheckDlgButton(hwndDlg, IDC_NODIALOG, i < 0 ? 0 : dat->info[i].noDialog);
+ CheckDlgButton(hwndDlg, IDC_USEPREVIOUS, i < 0 ? 0 : dat->info[i].usePrevious);
+ CheckDlgButton(hwndDlg, IDC_USESPECIFIC, i < 0 ? 0 : !dat->info[i].usePrevious);
+
+ SetDlgItemText(hwndDlg,IDC_MSG, i < 0 ? _T("") : dat->info[i].msg);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_NODIALOG), i < 0 ? 0 : !dat->info[i].ignore);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USEPREVIOUS), i < 0 ? 0 : !dat->info[i].ignore);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USESPECIFIC), i < 0 ? 0 : !dat->info[i].ignore);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MSG), i < 0 ? 0 : !(dat->info[i].ignore || dat->info[i].usePrevious));
+ dat->oldPage = i;
+ }
+ return 0;
+
+ case IDC_DONTREPLY:
+ case IDC_USEPREVIOUS:
+ case IDC_USESPECIFIC:
+ SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_STATUS,CBN_SELCHANGE),0);
+ break;
+
+ case IDC_MSG:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ switch(((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { int i, status;
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_STATUS,CBN_SELCHANGE), 0);
+ i = hLst ?
+ (SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_GETCOUNT, 0, 0) - 1) :
+ (SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_GETCOUNT, 0, 0) - 1);
+ for (; i>=0; i--)
+ {
+ status = hLst ?
+ SendDlgItemMessage(hwndDlg, IDC_LST_STATUS, LB_GETITEMDATA, i, 0):
+ SendDlgItemMessage(hwndDlg, IDC_STATUS, CB_GETITEMDATA, i, 0);
+ DBWriteContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(status,"Ignore"), (BYTE)dat->info[i].ignore);
+ DBWriteContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(status,"NoDlg"), (BYTE)dat->info[i].noDialog);
+ DBWriteContactSettingByte(NULL, "SRAway", StatusModeToDbSetting(status,"UsePrev"),(BYTE)dat->info[i].usePrevious);
+ DBWriteContactSettingTString(NULL, "SRAway", StatusModeToDbSetting(status,"Default"), dat->info[i].msg);
+ }
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ mir_free(dat);
+ break;
+ }
+ return FALSE;
+}
+
+static int AwayMsgOptInitialise(WPARAM wParam, LPARAM)
+{
+ if (protoModeMsgFlags == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = 870000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_AWAYMSG);
+ odp.pszTitle = LPGEN("Status Messages");
+ odp.pszGroup = LPGEN("Status");
+ odp.pfnDlgProc = DlgProcAwayMsgOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static int AwayMsgSendModernOptInit(WPARAM wParam, LPARAM)
+{
+ if (protoModeMsgFlags == 0)
+ return 0;
+
+ static const int iBoldControls[] =
+ {
+ IDC_TXT_TITLE1, IDC_TXT_TITLE2, IDC_TXT_TITLE3,
+ MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+ obj.cbSize = sizeof(obj);
+ obj.hInstance = hMirandaInst;
+ obj.dwFlags = MODEROPT_FLG_TCHAR | MODEROPT_FLG_NORESIZE;
+ obj.iSection = MODERNOPT_PAGE_STATUS;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+ obj.iBoldControls = (int*)iBoldControls;
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPT_STATUS);
+ obj.pfnDlgProc = DlgProcAwayMsgOpts;
+// obj.lpzClassicGroup = "Status";
+// obj.lpzClassicPage = "Messages";
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ return 0;
+}
+
+static int AwayMsgSendAccountsChanged(WPARAM, LPARAM)
+{
+ protoModeMsgFlags = 0;
+ for (int i=0; i < accounts.getCount(); i++)
+ {
+ if (!Proto_IsAccountEnabled(accounts[i])) continue;
+ protoModeMsgFlags |= CallProtoService(accounts[i]->szModuleName, PS_GETCAPS, PFLAGNUM_3, 0);
+ }
+
+ return 0;
+}
+
+static int AwayMsgSendModulesLoaded(WPARAM, LPARAM)
+{
+ AwayMsgSendAccountsChanged(0, 0);
+
+ HookEvent(ME_CLIST_STATUSMODECHANGE, StatusModeChange);
+ HookEvent(ME_MODERNOPT_INITIALIZE, AwayMsgSendModernOptInit);
+ HookEvent(ME_OPT_INITIALISE, AwayMsgOptInitialise);
+ return 0;
+}
+
+//remember to mir_free() the return value
+static INT_PTR sttGetAwayMessageT(WPARAM wParam, LPARAM lParam)
+{
+ return (INT_PTR)GetAwayMessage((int)wParam, (char*)lParam);
+}
+
+#ifdef UNICODE
+static INT_PTR sttGetAwayMessage(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR* msg = GetAwayMessage((int)wParam, (char*)lParam);
+ char* res = mir_t2a(msg);
+ mir_free(msg);
+ return (INT_PTR)res;
+}
+#endif
+
+int LoadAwayMessageSending(void)
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED,AwayMsgSendModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, AwayMsgSendAccountsChanged);
+
+#ifdef UNICODE
+ CreateServiceFunction(MS_AWAYMSG_GETSTATUSMSG, sttGetAwayMessage);
+ CreateServiceFunction(MS_AWAYMSG_GETSTATUSMSGW, sttGetAwayMessageT);
+#else
+ CreateServiceFunction(MS_AWAYMSG_GETSTATUSMSG, sttGetAwayMessageT);
+#endif
+ return 0;
+}
diff --git a/src/modules/sremail/email.cpp b/src/modules/sremail/email.cpp
new file mode 100644
index 0000000000..251963a406
--- /dev/null
+++ b/src/modules/sremail/email.cpp
@@ -0,0 +1,89 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static HANDLE hEMailMenuItem;
+
+void SendEmailThread(void *szUrl)
+{
+ ShellExecuteA(NULL,"open",( char* )szUrl,"","",SW_SHOW);
+ mir_free(szUrl);
+ return;
+}
+
+static INT_PTR SendEMailCommand(WPARAM wParam,LPARAM lParam)
+{
+ DBVARIANT dbv;
+ char *szUrl;
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ if(szProto==NULL || DBGetContactSettingString((HANDLE)wParam,szProto,"e-mail",&dbv)) {
+ if(DBGetContactSettingString((HANDLE)wParam,"UserInfo","Mye-mail0",&dbv)) {
+ MessageBox((HWND)lParam,TranslateT("User has not registered an e-mail address"),TranslateT("Send e-mail"),MB_OK);
+ return 1;
+ }
+ }
+ szUrl=(char*)mir_alloc(lstrlenA(dbv.pszVal)+8);
+ lstrcpyA(szUrl,"mailto:");
+ lstrcatA(szUrl,dbv.pszVal);
+ mir_free(dbv.pszVal);
+ forkthread(SendEmailThread,0,szUrl);
+ return 0;
+}
+
+static int EMailPreBuildMenu(WPARAM wParam, LPARAM)
+{
+ CLISTMENUITEM mi;
+ DBVARIANT dbv = { 0 };
+ char *szProto;
+
+ ZeroMemory(&mi,sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS;
+
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto == NULL || DBGetContactSettingString((HANDLE)wParam, szProto, "e-mail",& dbv))
+ if (DBGetContactSettingString((HANDLE)wParam, "UserInfo", "Mye-mail0", &dbv))
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hEMailMenuItem, (LPARAM)&mi);
+ if (dbv.pszVal) DBFreeVariant(&dbv);
+ return 0;
+}
+
+int LoadSendRecvEMailModule(void)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000010000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_SENDEMAIL );
+ mi.pszName = LPGEN("&E-mail");
+ mi.pszService = MS_EMAIL_SENDEMAIL;
+ hEMailMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ CreateServiceFunction(MS_EMAIL_SENDEMAIL, SendEMailCommand);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, EMailPreBuildMenu);
+ return 0;
+}
diff --git a/src/modules/srfile/file.cpp b/src/modules/srfile/file.cpp
new file mode 100644
index 0000000000..85eb99a7bc
--- /dev/null
+++ b/src/modules/srfile/file.cpp
@@ -0,0 +1,392 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "file.h"
+
+TCHAR* PFTS_StringToTchar( int flags, const PROTOCHAR* s );
+int PFTS_CompareWithTchar( PROTOFILETRANSFERSTATUS* ft, const PROTOCHAR* s, TCHAR* r );
+
+static HANDLE hSRFileMenuItem;
+
+static INT_PTR SendFileCommand(WPARAM wParam, LPARAM)
+{
+ struct FileSendData fsd;
+ fsd.hContact=(HANDLE)wParam;
+ fsd.ppFiles=NULL;
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILESEND),NULL,DlgProcSendFile,(LPARAM)&fsd);
+ return 0;
+}
+
+static INT_PTR SendSpecificFiles(WPARAM wParam,LPARAM lParam)
+{
+ FileSendData fsd;
+ fsd.hContact=(HANDLE)wParam;
+ #if defined( _UNICODE )
+ char** ppFiles = ( char** )lParam;
+ int count = 0;
+ while ( ppFiles[count] != NULL )
+ count++;
+
+ fsd.ppFiles = (const TCHAR**)alloca(( count+1 ) * sizeof( void* ));
+ for ( int i=0; i < count; i++ )
+ fsd.ppFiles[i] = ( const TCHAR* )mir_a2t( ppFiles[i] );
+ fsd.ppFiles[ count ] = NULL;
+ #else
+ fsd.ppFiles=(const char**)lParam;
+ #endif
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILESEND),NULL,DlgProcSendFile,(LPARAM)&fsd);
+ #if defined( _UNICODE )
+ for ( int j=0; j < count; j++ )
+ mir_free(( void* )fsd.ppFiles[j] );
+ #endif
+ return 0;
+}
+
+static INT_PTR SendSpecificFilesT(WPARAM wParam,LPARAM lParam)
+{
+ FileSendData fsd;
+ fsd.hContact=(HANDLE)wParam;
+ fsd.ppFiles=(const TCHAR**)lParam;
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILESEND),NULL,DlgProcSendFile,(LPARAM)&fsd);
+ return 0;
+}
+
+static INT_PTR GetReceivedFilesFolder(WPARAM wParam,LPARAM lParam)
+{
+ TCHAR buf[MAX_PATH];
+ GetContactReceivedFilesDir((HANDLE)wParam,buf,MAX_PATH,TRUE);
+ char* dir = mir_t2a(buf);
+ lstrcpynA((char*)lParam,dir,MAX_PATH);
+ mir_free(dir);
+ return 0;
+}
+
+static INT_PTR RecvFileCommand(WPARAM, LPARAM lParam)
+{
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILERECV),NULL,DlgProcRecvFile,lParam);
+ return 0;
+}
+
+void PushFileEvent( HANDLE hContact, HANDLE hdbe, LPARAM lParam )
+{
+ CLISTEVENT cle={0};
+ cle.cbSize = sizeof(cle);
+ cle.hContact = hContact;
+ cle.hDbEvent = hdbe;
+ cle.lParam = lParam;
+ if ( DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0) && !DBGetContactSettingByte(hContact,"CList","NotOnList",0)) {
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILERECV),NULL,DlgProcRecvFile,(LPARAM)&cle);
+ }
+ else {
+ SkinPlaySound("RecvFile");
+
+ TCHAR szTooltip[256];
+ mir_sntprintf(szTooltip,SIZEOF(szTooltip),TranslateT("File from %s"), cli.pfnGetContactDisplayName( hContact, 0 ));
+ cle.ptszTooltip = szTooltip;
+
+ cle.flags |= CLEF_TCHAR;
+ cle.hIcon = LoadSkinIcon( SKINICON_EVENT_FILE );
+ cle.pszService = "SRFile/RecvFile";
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&cle);
+} }
+
+static int FileEventAdded(WPARAM wParam,LPARAM lParam)
+{
+ DWORD dwSignature;
+
+ DBEVENTINFO dbei={0};
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = sizeof( DWORD );
+ dbei.pBlob = ( PBYTE )&dwSignature;
+ CallService( MS_DB_EVENT_GET, lParam, ( LPARAM )&dbei );
+ if ( dbei.flags&(DBEF_SENT|DBEF_READ) || dbei.eventType != EVENTTYPE_FILE || dwSignature == 0 )
+ return 0;
+
+ PushFileEvent(( HANDLE )wParam, ( HANDLE )lParam, 0 );
+ return 0;
+}
+
+int SRFile_GetRegValue(HKEY hKeyBase,const TCHAR *szSubKey,const TCHAR *szValue,TCHAR *szOutput,int cbOutput)
+{
+ HKEY hKey;
+ DWORD cbOut=cbOutput;
+
+ if ( RegOpenKeyEx( hKeyBase,szSubKey,0,KEY_QUERY_VALUE,&hKey ) != ERROR_SUCCESS)
+ return 0;
+
+ if ( RegQueryValueEx( hKey,szValue,NULL,NULL,(PBYTE)szOutput, &cbOut ) != ERROR_SUCCESS ) {
+ RegCloseKey(hKey);
+ return 0;
+ }
+
+ RegCloseKey(hKey);
+ return 1;
+}
+
+void GetSensiblyFormattedSize(__int64 size,TCHAR *szOut,int cchOut,int unitsOverride,int appendUnits,int *unitsUsed)
+{
+ if(!unitsOverride) {
+ if(size<1000) unitsOverride=UNITS_BYTES;
+ else if(size<100*1024) unitsOverride=UNITS_KBPOINT1;
+ else if(size<1024*1024) unitsOverride=UNITS_KBPOINT0;
+ else if(size<1024*1024*1024) unitsOverride=UNITS_MBPOINT2;
+ else unitsOverride=UNITS_GBPOINT3;
+ }
+ if(unitsUsed) *unitsUsed=unitsOverride;
+ switch(unitsOverride) {
+ case UNITS_BYTES: mir_sntprintf(szOut,cchOut,_T("%u%s%s"),(int)size,appendUnits?_T(" "):_T(""),appendUnits?TranslateT("bytes"):_T("")); break;
+ case UNITS_KBPOINT1: mir_sntprintf(szOut,cchOut,_T("%.1lf%s"),size/1024.0,appendUnits?_T(" KB"):_T("")); break;
+ case UNITS_KBPOINT0: mir_sntprintf(szOut,cchOut,_T("%u%s"),(int)(size/1024),appendUnits?_T(" KB"):_T("")); break;
+ case UNITS_GBPOINT3: mir_sntprintf(szOut,cchOut,_T("%.3f%s"),(size >> 20)/1024.0,appendUnits?_T(" GB"):_T("")); break;
+ default: mir_sntprintf(szOut,cchOut,_T("%.2lf%s"),size/1048576.0,appendUnits?_T(" MB"):_T("")); break;
+ }
+}
+
+// Tripple redirection sucks but is needed to nullify the array pointer
+void FreeFilesMatrix(TCHAR ***files)
+{
+ if (*files == NULL)
+ return;
+
+ // Free each filename in the pointer array
+ TCHAR **pFile = *files;
+ while (*pFile != NULL)
+ {
+ mir_free(*pFile);
+ *pFile = NULL;
+ pFile++;
+ }
+
+ // Free the array itself
+ mir_free(*files);
+ *files = NULL;
+}
+
+void FreeProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *fts)
+{
+ mir_free(fts->tszCurrentFile);
+ if(fts->ptszFiles) {
+ for( int i=0;i<fts->totalFiles;i++) mir_free(fts->ptszFiles[i]);
+ mir_free(fts->ptszFiles);
+ }
+ mir_free(fts->tszWorkingDir);
+}
+
+void CopyProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest, PROTOFILETRANSFERSTATUS *src)
+{
+ *dest=*src;
+ if ( src->tszCurrentFile ) dest->tszCurrentFile = PFTS_StringToTchar(src->flags, src->tszCurrentFile);
+ if ( src->ptszFiles ) {
+ dest->ptszFiles = (TCHAR**)mir_alloc(sizeof(TCHAR*)*src->totalFiles);
+ for( int i=0; i < src->totalFiles; i++ )
+ dest->ptszFiles[i] = PFTS_StringToTchar(src->flags, src->ptszFiles[i] );
+ }
+ if ( src->tszWorkingDir ) dest->tszWorkingDir = PFTS_StringToTchar(src->flags, src->tszWorkingDir );
+ dest->flags &= ~PFTS_UTF;
+ dest->flags |= PFTS_TCHAR;
+}
+
+void UpdateProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest, PROTOFILETRANSFERSTATUS *src)
+{
+ if (src->cbSize == sizeof(PROTOFILETRANSFERSTATUS_V1))
+ {
+ PROTOFILETRANSFERSTATUS_V1 *src1 = (PROTOFILETRANSFERSTATUS_V1*)src;
+ src = (PROTOFILETRANSFERSTATUS*)alloca(sizeof(PROTOFILETRANSFERSTATUS));
+
+ src->cbSize = sizeof(PROTOFILETRANSFERSTATUS);
+ src->hContact = src1->hContact;
+ src->flags = src1->sending ? PFTS_SENDING : 0;
+ src->pszFiles = src1->files;
+ src->totalFiles = src1->totalFiles;
+ src->currentFileNumber = src1->currentFileNumber;
+ src->totalBytes = src1->totalBytes;
+ src->totalProgress = src1->totalProgress;
+ src->szWorkingDir = src1->workingDir;
+ src->szCurrentFile = src1->currentFile;
+ src->currentFileSize = src1->currentFileSize;
+ src->currentFileProgress = src1->currentFileProgress;
+ src->currentFileTime = src1->currentFileTime;
+ }
+
+ dest->hContact = src->hContact;
+ dest->flags = src->flags;
+ if ( dest->totalFiles != src->totalFiles ) {
+ for( int i=0;i<dest->totalFiles;i++) mir_free(dest->ptszFiles[i]);
+ mir_free(dest->ptszFiles);
+ dest->ptszFiles = NULL;
+ dest->totalFiles = src->totalFiles;
+ }
+ if ( src->ptszFiles ) {
+ if ( !dest->ptszFiles )
+ dest->ptszFiles = ( TCHAR** )mir_calloc( sizeof(TCHAR*)*src->totalFiles);
+ for ( int i=0; i < src->totalFiles; i++ )
+ if ( !dest->ptszFiles[i] || !src->ptszFiles[i] || PFTS_CompareWithTchar( src, src->ptszFiles[i], dest->ptszFiles[i] )) {
+ mir_free( dest->ptszFiles[i] );
+ if ( src->ptszFiles[i] )
+ dest->ptszFiles[i] = PFTS_StringToTchar( src->flags, src->ptszFiles[i] );
+ else
+ dest->ptszFiles[i] = NULL;
+ }
+ }
+ else if (dest->ptszFiles) {
+ for( int i=0; i < dest->totalFiles; i++ )
+ mir_free(dest->ptszFiles[i]);
+ mir_free( dest->ptszFiles );
+ dest->ptszFiles = NULL;
+ }
+
+ dest->currentFileNumber = src->currentFileNumber;
+ dest->totalBytes = src->totalBytes;
+ dest->totalProgress = src->totalProgress;
+ if (src->tszWorkingDir && (!dest->tszWorkingDir || PFTS_CompareWithTchar( src, src->tszWorkingDir, dest->tszWorkingDir))) {
+ mir_free( dest->tszWorkingDir );
+ if ( src->tszWorkingDir )
+ dest->tszWorkingDir = PFTS_StringToTchar( src->flags, src->tszWorkingDir );
+ else
+ dest->tszWorkingDir = NULL;
+ }
+
+ if ( !dest->tszCurrentFile || !src->tszCurrentFile || PFTS_CompareWithTchar( src, src->tszCurrentFile, dest->tszCurrentFile )) {
+ mir_free( dest->tszCurrentFile );
+ if ( src->tszCurrentFile )
+ dest->tszCurrentFile = PFTS_StringToTchar( src->flags, src->tszCurrentFile );
+ else
+ dest->tszCurrentFile = NULL;
+ }
+ dest->currentFileSize = src->currentFileSize;
+ dest->currentFileProgress = src->currentFileProgress;
+ dest->currentFileTime = src->currentFileTime;
+ dest->flags &= ~PFTS_UTF;
+ dest->flags |= PFTS_TCHAR;
+}
+
+static void RemoveUnreadFileEvents(void)
+{
+ DBEVENTINFO dbei={0};
+ HANDLE hDbEvent,hContact;
+
+ dbei.cbSize=sizeof(dbei);
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while(hContact) {
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRSTUNREAD,(WPARAM)hContact,0);
+ while(hDbEvent) {
+ dbei.cbBlob=0;
+ CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei);
+ if(!(dbei.flags&(DBEF_SENT|DBEF_READ)) && dbei.eventType==EVENTTYPE_FILE)
+ CallService(MS_DB_EVENT_MARKREAD,(WPARAM)hContact,(LPARAM)hDbEvent);
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT,(WPARAM)hDbEvent,0);
+ }
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
+ }
+}
+
+static int SRFilePreBuildMenu(WPARAM wParam, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+
+ char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto != NULL) {
+ if ( CallProtoService(szProto, PS_GETCAPS,PFLAGNUM_1, 0 ) & PF1_FILESEND) {
+ if ( CallProtoService(szProto, PS_GETCAPS,PFLAGNUM_4, 0 ) & PF4_OFFLINEFILES )
+ mi.flags = CMIM_FLAGS;
+ else if ( DBGetContactSettingWord(( HANDLE )wParam, szProto, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE )
+ mi.flags = CMIM_FLAGS;
+ } }
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hSRFileMenuItem, (LPARAM)&mi);
+ return 0;
+}
+
+static int SRFileModulesLoaded(WPARAM, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000020000;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_EVENT_FILE );
+ mi.pszName = LPGEN("&File");
+ mi.pszService = MS_FILE_SENDFILE;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ hSRFileMenuItem = ( HANDLE )CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+
+ RemoveUnreadFileEvents();
+ return 0;
+}
+
+INT_PTR FtMgrShowCommand(WPARAM, LPARAM)
+{
+ FtMgr_Show(true, true);
+ return 0;
+}
+
+INT_PTR openContRecDir(WPARAM wparam, LPARAM)
+{
+ TCHAR szContRecDir[MAX_PATH];
+ HANDLE hContact = (HANDLE)wparam;
+ GetContactReceivedFilesDir(hContact, szContRecDir, SIZEOF(szContRecDir),TRUE);
+ ShellExecute(0, _T("open"), szContRecDir, 0, 0, SW_SHOW);
+ return 0;
+}
+
+INT_PTR openRecDir(WPARAM, LPARAM)
+{
+ TCHAR szContRecDir[MAX_PATH];
+ GetReceivedFilesDir(szContRecDir, SIZEOF(szContRecDir));
+ ShellExecute(0, _T("open"), szContRecDir, 0, 0, SW_SHOW);
+ return 0;
+}
+
+int LoadSendRecvFileModule(void)
+{
+ CreateServiceFunction("FtMgr/Show", FtMgrShowCommand);
+
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_EVENT_FILE );
+ mi.position = 1900000000;
+ mi.pszName = LPGEN("File &Transfers...");
+ mi.pszService = "FtMgr/Show"; //MS_PROTO_SHOWFTMGR;
+ CallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+
+ HookEvent(ME_SYSTEM_MODULESLOADED,SRFileModulesLoaded);
+ HookEvent(ME_DB_EVENT_ADDED,FileEventAdded);
+ HookEvent(ME_OPT_INITIALISE,FileOptInitialise);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, SRFilePreBuildMenu);
+
+ CreateServiceFunction(MS_FILE_SENDFILE,SendFileCommand);
+ CreateServiceFunction(MS_FILE_SENDSPECIFICFILES,SendSpecificFiles);
+ CreateServiceFunction(MS_FILE_SENDSPECIFICFILEST,SendSpecificFilesT);
+ CreateServiceFunction(MS_FILE_GETRECEIVEDFILESFOLDER,GetReceivedFilesFolder);
+ CreateServiceFunction("SRFile/RecvFile",RecvFileCommand);
+
+ CreateServiceFunction("SRFile/OpenContRecDir",openContRecDir);
+ CreateServiceFunction("SRFile/OpenRecDir",openRecDir);
+
+ SkinAddNewSoundEx("RecvFile", "File", "Incoming");
+ SkinAddNewSoundEx("FileDone", "File", "Complete");
+ SkinAddNewSoundEx("FileFailed", "File", "Error");
+ SkinAddNewSoundEx("FileDenied", "File", "Denied");
+ return 0;
+}
diff --git a/src/modules/srfile/file.h b/src/modules/srfile/file.h
new file mode 100644
index 0000000000..8ee9048eb9
--- /dev/null
+++ b/src/modules/srfile/file.h
@@ -0,0 +1,115 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define VIRUSSCAN_DISABLE 0
+#define VIRUSSCAN_AFTERDL 1
+#define VIRUSSCAN_DURINGDL 2
+
+#define FILERESUME_ASK 0
+//1,2,3,4: resume, overwrite, rename, skip: from proto library
+#define FILERESUMEF_ALL 0x80
+#define FILERESUME_RESUMEALL (FILERESUME_RESUME|FILERESUMEF_ALL)
+#define FILERESUME_OVERWRITEALL (FILERESUME_OVERWRITE|FILERESUMEF_ALL)
+#define FILERESUME_RENAMEALL (FILERESUME_RENAME|FILERESUMEF_ALL)
+#define FILERESUME_CANCEL 0xFFFFFFFF
+
+#define M_FILEEXISTSDLGREPLY (WM_USER+200)
+#define M_PRESHUTDOWN (WM_USER+201)
+
+struct FileSendData {
+ HANDLE hContact;
+ const TCHAR **ppFiles;
+};
+
+#define BYTESRECVEDHISTORYCOUNT 10 //the number of bytes recved is sampled once a second and the last 10 are used to get the transfer speed
+struct FileDlgData {
+ HWND hwndTransfer;
+ HANDLE fs;
+ HANDLE hContact;
+ HANDLE hDbEvent;
+ HANDLE hNotifyEvent;
+ TCHAR **files;
+ int send;
+ int closeIfFileChooseCancelled;
+ int resumeBehaviour;
+ int bytesRecvedHistory[BYTESRECVEDHISTORYCOUNT];
+ int bytesRecvedHistorySize;
+ int waitingForAcceptance;
+ PROTOFILETRANSFERSTATUS transferStatus;
+ int *fileVirusScanned;
+ HANDLE hPreshutdownEvent;
+ DWORD dwTicks;
+
+ TCHAR szSavePath[MAX_PATH];
+ TCHAR szMsg[450], szFilenames[1024];
+ HICON hIcon, hIconFolder;
+};
+
+//file.c
+#define UNITS_BYTES 1 // 0<=size<1000: "%d bytes"
+#define UNITS_KBPOINT1 2 // 1000<=size<100*1024: "%.1f KB"
+#define UNITS_KBPOINT0 3 // 100*1024<=size<1024*1024: "%d KB"
+#define UNITS_MBPOINT2 4 // 1024*1024<=size: "%.2f MB"
+#define UNITS_GBPOINT3 5 // 1024*1024*1024<=size: "%.3f GB"
+
+void GetSensiblyFormattedSize(__int64 size,TCHAR *szOut,int cchOut,int unitsOverride,int appendUnits,int *unitsUsed);
+void FreeFilesMatrix(TCHAR ***files); //loving that triple indirection
+void FreeProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *fts);
+void CopyProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest,PROTOFILETRANSFERSTATUS *src);
+void UpdateProtoFileTransferStatus(PROTOFILETRANSFERSTATUS *dest,PROTOFILETRANSFERSTATUS *src);
+int SRFile_GetRegValue(HKEY hKeyBase,const TCHAR *szSubKey,const TCHAR *szValue,TCHAR *szOutput,int cbOutput);
+//filesenddlg.c
+INT_PTR CALLBACK DlgProcSendFile(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+//filerecv.c
+INT_PTR CALLBACK DlgProcRecvFile(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+void RemoveInvalidFilenameChars(TCHAR *tszString);
+void RemoveInvalidPathChars(TCHAR *tszString);
+void GetContactReceivedFilesDir(HANDLE hContact,TCHAR *szDir,int cchDir,BOOL substVars);
+void GetReceivedFilesDir(TCHAR *szDir,int cchDir);
+int BrowseForFolder(HWND hwnd,TCHAR *szPath);
+//fileexistsdlg.c
+struct TDlgProcFileExistsParam
+{
+ HWND hwndParent;
+ PROTOFILETRANSFERSTATUS *fts;
+};
+INT_PTR CALLBACK DlgProcFileExists(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+//filexferdlg.c
+INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+//fileopts.c
+int FileOptInitialise(WPARAM wParam,LPARAM lParam);
+//ftmanager.c
+#define WM_FT_ADD (WM_USER+701)
+#define WM_FT_RESIZE (WM_USER+702)
+#define WM_FT_REMOVE (WM_USER+703)
+#define WM_FT_SELECTPAGE (WM_USER+704)
+#define WM_FT_CLEANUP (WM_USER+705)
+#define WM_FT_COMPLETED (WM_USER+706)
+
+HWND FtMgr_Show(bool bForceActivate, bool bFromMenu);
+void FtMgr_Destroy();
+HWND FtMgr_AddTransfer(struct FileDlgData *dat);
+
+void FreeFileDlgData( FileDlgData* dat );
+
+TCHAR *GetContactID(HANDLE hContact);
diff --git a/src/modules/srfile/fileexistsdlg.cpp b/src/modules/srfile/fileexistsdlg.cpp
new file mode 100644
index 0000000000..d97d6d8da2
--- /dev/null
+++ b/src/modules/srfile/fileexistsdlg.cpp
@@ -0,0 +1,354 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <shlobj.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "file.h"
+
+static void SetControlToUnixTime(HWND hwndDlg, UINT idCtrl, time_t unixTime)
+{
+ LARGE_INTEGER liFiletime;
+ FILETIME filetime;
+ SYSTEMTIME st;
+ char szTime[64],szDate[64],szOutput[128];
+
+ liFiletime.QuadPart=(BIGI(11644473600)+(__int64)unixTime)*10000000;
+ filetime.dwHighDateTime=liFiletime.HighPart;
+ filetime.dwLowDateTime=liFiletime.LowPart;
+ FileTimeToSystemTime(&filetime,&st);
+ GetTimeFormatA(LOCALE_USER_DEFAULT,0,&st,NULL,szTime,SIZEOF(szTime));
+ GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&st,NULL,szDate,SIZEOF(szDate));
+ mir_snprintf(szOutput, SIZEOF(szOutput), "%s %s",szDate,szTime);
+ SetDlgItemTextA(hwndDlg,idCtrl,szOutput);
+}
+
+#define C_CONTEXTMENU 0
+#define C_PROPERTIES 1
+// not defined in VC++ 6.0 SE
+#ifndef CMF_EXTENDEDVERBS
+#define CMF_EXTENDEDVERBS 0x00000100
+#endif
+static void DoAnnoyingShellCommand(HWND hwnd,const TCHAR *szFilename,int cmd,POINT *ptCursor)
+{
+ IShellFolder *pDesktopFolder;
+ if(SHGetDesktopFolder(&pDesktopFolder)==NOERROR) {
+ ITEMIDLIST *pCurrentIdl;
+ #if defined( _UNICODE )
+ WCHAR* wszFilename = ( LPWSTR )szFilename;
+ #else
+ WCHAR wszFilename[MAX_PATH];
+ MultiByteToWideChar(CP_ACP,0,szFilename,-1,wszFilename,SIZEOF(wszFilename));
+ #endif
+ if(pDesktopFolder->ParseDisplayName(NULL,NULL,wszFilename,NULL,&pCurrentIdl,NULL)==NOERROR) {
+ if(pCurrentIdl->mkid.cb) {
+ ITEMIDLIST *pidl,*pidlNext,*pidlFilename;
+ IShellFolder *pFileFolder;
+
+ for(pidl=pCurrentIdl;;) {
+ pidlNext=(ITEMIDLIST*)((PBYTE)pidl+pidl->mkid.cb);
+ if(pidlNext->mkid.cb==0) {
+ pidlFilename = (ITEMIDLIST*)CoTaskMemAlloc(pidl->mkid.cb+sizeof(pidl->mkid.cb));
+ CopyMemory(pidlFilename,pidl,pidl->mkid.cb+sizeof(pidl->mkid.cb));
+ pidl->mkid.cb=0;
+ break;
+ }
+ pidl=pidlNext;
+ }
+ if(pDesktopFolder->BindToObject(pCurrentIdl,NULL,IID_IShellFolder,(void**)&pFileFolder)==NOERROR) {
+ IContextMenu *pContextMenu;
+ if(pFileFolder->GetUIObjectOf(NULL,1,(LPCITEMIDLIST*)&pidlFilename,IID_IContextMenu,NULL,(void**)&pContextMenu)==NOERROR) {
+ switch(cmd) {
+ case C_PROPERTIES:
+ { CMINVOKECOMMANDINFO ici={0};
+ ici.cbSize=sizeof(ici);
+ ici.hwnd=hwnd;
+ ici.lpVerb="properties";
+ ici.nShow=SW_SHOW;
+ pContextMenu->InvokeCommand(&ici);
+ break;
+ }
+ case C_CONTEXTMENU:
+ { HMENU hMenu;
+ hMenu=CreatePopupMenu();
+ if(SUCCEEDED(pContextMenu->QueryContextMenu(hMenu,0,1000,65535,(GetKeyState(VK_SHIFT)&0x8000?CMF_EXTENDEDVERBS:0)|CMF_NORMAL))) {
+ int cmd;
+ cmd=TrackPopupMenu(hMenu,TPM_RETURNCMD,ptCursor->x,ptCursor->y,0,hwnd,NULL);
+ if(cmd) {
+ CMINVOKECOMMANDINFO ici={0};
+ ici.cbSize=sizeof(ici);
+ ici.hwnd=hwnd;
+ ici.lpVerb=MAKEINTRESOURCEA(cmd-1000);
+ ici.nShow=SW_SHOW;
+ pContextMenu->InvokeCommand(&ici);
+ }
+ }
+ DestroyMenu(hMenu);
+ break;
+ }
+ }
+ pContextMenu->Release();
+ }
+ pFileFolder->Release();
+ }
+ CoTaskMemFree(pidlFilename);
+ }
+ CoTaskMemFree(pCurrentIdl);
+ }
+ pDesktopFolder->Release();
+ }
+}
+
+static WNDPROC pfnIconWindowProc;
+static LRESULT CALLBACK IconCtrlSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PROTOFILETRANSFERSTATUS* pft = (PROTOFILETRANSFERSTATUS*)GetWindowLongPtr(GetParent(hwnd),GWLP_USERDATA);
+
+ switch(msg) {
+ case WM_LBUTTONDBLCLK:
+ ShellExecute(hwnd,NULL,pft->tszCurrentFile,NULL,NULL,SW_SHOW);
+ break;
+ case WM_RBUTTONUP:
+ { POINT pt;
+ pt.x=(short)LOWORD(lParam); pt.y=(short)HIWORD(lParam);
+ ClientToScreen( hwnd, &pt );
+ DoAnnoyingShellCommand( hwnd, pft->tszCurrentFile, C_CONTEXTMENU, &pt );
+ return 0;
+ }
+ }
+ return CallWindowProc(pfnIconWindowProc,hwnd,msg,wParam,lParam);
+}
+
+struct loadiconsstartinfo {
+ HWND hwndDlg;
+ TCHAR *szFilename;
+};
+void __cdecl LoadIconsAndTypesThread(void* param)
+{
+ loadiconsstartinfo *info = ( loadiconsstartinfo* )param;
+ SHFILEINFO fileInfo;
+
+ if ( SHGetFileInfo( info->szFilename, 0, &fileInfo, sizeof(fileInfo),SHGFI_TYPENAME|SHGFI_ICON|SHGFI_LARGEICON)) {
+ TCHAR *pszExtension,*pszFilename;
+ TCHAR szExtension[64];
+ TCHAR szIconFile[MAX_PATH];
+
+ pszFilename = _tcsrchr(info->szFilename,'\\');
+ if ( pszFilename == NULL )
+ pszFilename = info->szFilename;
+
+ pszExtension = _tcsrchr( pszFilename, '.' );
+ if ( pszExtension )
+ lstrcpyn( szExtension, pszExtension+1, SIZEOF( szExtension ));
+ else {
+ pszExtension = _T(".");
+ szExtension[0]='\0';
+ }
+ CharUpper(szExtension);
+ if ( fileInfo.szTypeName[0]=='\0' )
+ mir_sntprintf( fileInfo.szTypeName, SIZEOF(fileInfo.szTypeName), TranslateT("%s File"),szExtension);
+ SetDlgItemText(info->hwndDlg,IDC_EXISTINGTYPE,fileInfo.szTypeName);
+ SetDlgItemText(info->hwndDlg,IDC_NEWTYPE,fileInfo.szTypeName);
+ SendDlgItemMessage(info->hwndDlg,IDC_EXISTINGICON,STM_SETICON,(WPARAM)fileInfo.hIcon,0);
+ szIconFile[0]='\0';
+ if ( !lstrcmp( szExtension, _T("EXE"))) {
+ SRFile_GetRegValue(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons"),_T("2"),szIconFile,SIZEOF(szIconFile));
+ }
+ else {
+ TCHAR szTypeName[MAX_PATH];
+ if(SRFile_GetRegValue(HKEY_CLASSES_ROOT,pszExtension,NULL,szTypeName,SIZEOF(szTypeName))) {
+ lstrcat(szTypeName,_T("\\DefaultIcon"));
+ if(SRFile_GetRegValue(HKEY_CLASSES_ROOT,szTypeName,NULL,szIconFile,SIZEOF(szIconFile))) {
+ if ( _tcsstr( szIconFile, _T("%1")))
+ SRFile_GetRegValue(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons"),_T("0"),szIconFile,SIZEOF(szIconFile));
+ else szIconFile[0]='\0';
+ } } }
+
+ if ( szIconFile[0]) {
+ int iconIndex;
+ HICON hIcon;
+ TCHAR *pszComma = _tcsrchr(szIconFile,',');
+ if ( pszComma == NULL )
+ iconIndex=0;
+ else {
+ iconIndex = _ttoi(pszComma+1); *pszComma='\0';
+ }
+ hIcon = ExtractIcon( hMirandaInst, szIconFile, iconIndex );
+ if ( hIcon )
+ fileInfo.hIcon = hIcon;
+ }
+ SendDlgItemMessage(info->hwndDlg,IDC_NEWICON,STM_SETICON,(WPARAM)fileInfo.hIcon,0);
+ }
+ mir_free(info->szFilename);
+ mir_free(info);
+}
+
+INT_PTR CALLBACK DlgProcFileExists(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PROTOFILETRANSFERSTATUS *fts;
+
+ fts=(PROTOFILETRANSFERSTATUS*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch(msg) {
+ case WM_INITDIALOG:
+ {
+ TCHAR szSize[64];
+ struct _stati64 statbuf;
+ HWND hwndFocus;
+ struct TDlgProcFileExistsParam *dat = (struct TDlgProcFileExistsParam *)lParam;
+
+ SetPropA(hwndDlg,"Miranda.Preshutdown",HookEventMessage(ME_SYSTEM_PRESHUTDOWN,hwndDlg,M_PRESHUTDOWN));
+ SetPropA(hwndDlg,"Miranda.ParentWnd",dat->hwndParent);
+
+ TranslateDialogDefault(hwndDlg);
+ fts=(PROTOFILETRANSFERSTATUS*)mir_alloc(sizeof(PROTOFILETRANSFERSTATUS));
+ CopyProtoFileTransferStatus(fts,dat->fts);
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)fts);
+ SetDlgItemText(hwndDlg,IDC_FILENAME,fts->tszCurrentFile);
+ SetControlToUnixTime(hwndDlg,IDC_NEWDATE,fts->currentFileTime);
+ GetSensiblyFormattedSize(fts->currentFileSize,szSize,SIZEOF(szSize),0,1,NULL);
+ SetDlgItemText(hwndDlg,IDC_NEWSIZE,szSize);
+
+ pfnIconWindowProc=(WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_EXISTINGICON),GWLP_WNDPROC,(LONG_PTR)IconCtrlSubclassProc);
+
+ hwndFocus=GetDlgItem(hwndDlg,IDC_RESUME);
+ if ( _tstati64(fts->tszCurrentFile,&statbuf)==0) {
+ SetControlToUnixTime(hwndDlg,IDC_EXISTINGDATE,statbuf.st_mtime);
+ GetSensiblyFormattedSize(statbuf.st_size,szSize,SIZEOF(szSize),0,1,NULL);
+ SetDlgItemText(hwndDlg,IDC_EXISTINGSIZE,szSize);
+ if(statbuf.st_size>(int)fts->currentFileSize) {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_RESUME),FALSE);
+ hwndFocus=GetDlgItem(hwndDlg,IDC_OVERWRITE);
+ } }
+
+ loadiconsstartinfo *lisi = ( loadiconsstartinfo* )mir_alloc(sizeof(loadiconsstartinfo));
+ lisi->hwndDlg=hwndDlg;
+ lisi->szFilename = mir_tstrdup(fts->tszCurrentFile);
+ //can be a little slow, so why not?
+ forkthread(LoadIconsAndTypesThread,0,lisi);
+ SetFocus(hwndFocus);
+ SetWindowLongPtr(hwndFocus,GWL_STYLE,GetWindowLongPtr(hwndFocus,GWL_STYLE)|BS_DEFPUSHBUTTON);
+ return FALSE;
+ }
+ case WM_COMMAND:
+ {
+ PROTOFILERESUME pfr={0};
+ switch(LOWORD(wParam)) {
+ case IDC_OPENFILE:
+ ShellExecute( hwndDlg, NULL, fts->tszCurrentFile, NULL, NULL, SW_SHOW );
+ return FALSE;
+
+ case IDC_OPENFOLDER:
+ {
+ TCHAR szFile[MAX_PATH];
+ lstrcpyn( szFile, fts->tszCurrentFile, SIZEOF(szFile));
+ TCHAR* pszLastBackslash = _tcsrchr( szFile, '\\' );
+ if ( pszLastBackslash )
+ *pszLastBackslash = '\0';
+ ShellExecute(hwndDlg,NULL,szFile,NULL,NULL,SW_SHOW);
+ return FALSE;
+ }
+ case IDC_PROPERTIES:
+ DoAnnoyingShellCommand(hwndDlg,fts->tszCurrentFile,C_PROPERTIES,NULL);
+ return FALSE;
+ case IDC_RESUME:
+ pfr.action=FILERESUME_RESUME;
+ break;
+ case IDC_RESUMEALL:
+ pfr.action=FILERESUME_RESUMEALL;
+ break;
+ case IDC_OVERWRITE:
+ pfr.action=FILERESUME_OVERWRITE;
+ break;
+ case IDC_OVERWRITEALL:
+ pfr.action=FILERESUME_OVERWRITEALL;
+ break;
+
+ case IDC_AUTORENAME:
+ pfr.action = FILERESUME_RENAMEALL;
+ break;
+
+ case IDC_SAVEAS:
+ {
+ OPENFILENAME ofn={0};
+ TCHAR filter[512],*pfilter;
+ TCHAR str[MAX_PATH];
+
+ lstrcpyn( str, fts->tszCurrentFile, SIZEOF(str));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+ _tcscpy( filter, TranslateT("All Files"));
+ _tcscat( filter, _T(" (*)"));
+ pfilter = filter + _tcslen(filter) + 1;
+ _tcscpy( pfilter, _T("*"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ *pfilter='\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = SIZEOF(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ if(!GetSaveFileName(&ofn))
+ return FALSE;
+
+ pfr.szFilename = mir_tstrdup(str);
+ pfr.action = FILERESUME_RENAME;
+ break;
+ }
+ case IDC_SKIP:
+ pfr.action=FILERESUME_SKIP;
+ break;
+ case IDCANCEL:
+ pfr.action=FILERESUME_CANCEL;
+ break;
+ default:
+ return FALSE;
+ }
+ { PROTOFILERESUME *pfrCopy;
+ pfrCopy=(PROTOFILERESUME*)mir_alloc(sizeof(pfr));
+ CopyMemory(pfrCopy,&pfr,sizeof(pfr));
+ PostMessage((HWND)GetPropA(hwndDlg,"Miranda.ParentWnd"),M_FILEEXISTSDLGREPLY,(WPARAM)mir_tstrdup(fts->tszCurrentFile),(LPARAM)pfrCopy);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDCANCEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDCANCEL));
+ break;
+
+ case M_PRESHUTDOWN:
+ PostMessage(hwndDlg,WM_CLOSE,0,0);
+ break;
+
+ case WM_DESTROY:
+ UnhookEvent(GetPropA(hwndDlg,"Miranda.Preshutdown")); // GetProp() will return NULL if it couldnt find anything
+ RemovePropA(hwndDlg,"Miranda.Preshutdown");
+ RemovePropA(hwndDlg,"Miranda.ParentWnd");
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg,IDC_EXISTINGICON,STM_GETICON,0,0));
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg,IDC_NEWICON,STM_GETICON,0,0));
+ FreeProtoFileTransferStatus(fts);
+ mir_free(fts);
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/srfile/fileopts.cpp b/src/modules/srfile/fileopts.cpp
new file mode 100644
index 0000000000..3107f109a3
--- /dev/null
+++ b/src/modules/srfile/fileopts.cpp
@@ -0,0 +1,247 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "file.h"
+
+#define VSCAN_MCAFEE 1
+#define VSCAN_DRSOLOMON 2
+#define VSCAN_NORTON 3
+#define VSCAN_CA 4
+
+struct virusscannerinfo {
+ const TCHAR *szProductName;
+ const TCHAR *szExeRegPath;
+ const TCHAR *szExeRegValue;
+ const TCHAR *szCommandLine;
+};
+
+static const struct virusscannerinfo virusScanners[]={
+ {_T("Network Associates/McAfee VirusScan"),_T("SOFTWARE\\McAfee\\VirusScan"),_T("Scan32EXE"),_T("\"%s\" %%f /nosplash /comp /autoscan /autoexit /noboot")},
+ {_T("Dr Solomon's VirusScan (Network Associates)"),_T("SOFTWARE\\Network Associates\\TVD\\VirusScan\\AVConsol\\General"),_T("szScannerExe"),_T("\"%s\" %%f /uinone /noboot /comp /prompt /autoexit")},
+ {_T("Norton AntiVirus"),_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Navw32.exe"),NULL,_T("\"%s\" %%f /b- /m- /s+ /noresults")},
+ {_T("Computer Associates/Inoculate IT"),_T("Software\\Antivirus"),_T("ImageFilename"),_T("\"%s\" %%f /display=progress /exit")},
+ {_T("Computer Associates eTrust"),_T("SOFTWARE\\ComputerAssociates\\Anti-Virus\\Resident"),_T("VetPath"),_T("\"%s\" %%f /display=progress /exit")},
+ {_T("Kaspersky Anti-Virus"),_T("SOFTWARE\\KasperskyLab\\Components\\101"),_T("EXEName"),_T("\"%s\" /S /Q %%f")},
+ {_T("Kaspersky Anti-Virus"),_T("SOFTWARE\\KasperskyLab\\SetupFolders"),_T("KAV8"),_T("\"%savp.exe\" SCAN %%f")},
+ {_T("Kaspersky Anti-Virus"),_T("SOFTWARE\\KasperskyLab\\SetupFolders"),_T("KAV9"),_T("\"%savp.exe\" SCAN %%f")},
+ {_T("AntiVir PersonalEdition Classic"),_T("SOFTWARE\\Avira\\AntiVir PersonalEdition Classic"),_T("Path"),_T("\"%savscan.exe\" /GUIMODE=2 /PATH=\"%%f\"")},
+ {_T("ESET NOD32 Antivirus"),_T("SOFTWARE\\ESET\\ESET Security\\CurrentVersion\\Info"),_T("InstallDir"),_T("\"%secls.exe\" /log-all /aind /no-boots /adware /sfx /unsafe /unwanted /heur /adv-heur /action=clean \"%%f\"")},
+};
+
+#define M_UPDATEENABLING (WM_USER+100)
+#define M_SCANCMDLINESELCHANGE (WM_USER+101)
+
+#ifndef SHACF_FILESYS_DIRS
+ #define SHACF_FILESYS_DIRS 0x00000020
+#endif
+
+static INT_PTR CALLBACK DlgProcFileOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ if (shAutoComplete)
+ shAutoComplete(GetDlgItem(hwndDlg, IDC_FILEDIR), SHACF_FILESYS_DIRS);
+
+ {
+ TCHAR str[MAX_PATH];
+ GetContactReceivedFilesDir(NULL,str,SIZEOF(str),FALSE);
+ SetDlgItemText(hwndDlg,IDC_FILEDIR,str);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_AUTOACCEPT, DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOMIN, DBGetContactSettingByte(NULL,"SRFile","AutoMin",0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOCLOSE, DBGetContactSettingByte(NULL,"SRFile","AutoClose",0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTOCLEAR, DBGetContactSettingByte(NULL,"SRFile","AutoClear",1) ? BST_CHECKED : BST_UNCHECKED);
+ switch(DBGetContactSettingByte(NULL,"SRFile","UseScanner",VIRUSSCAN_DISABLE)) {
+ case VIRUSSCAN_AFTERDL: CheckDlgButton(hwndDlg, IDC_SCANAFTERDL, BST_CHECKED); break;
+ case VIRUSSCAN_DURINGDL: CheckDlgButton(hwndDlg, IDC_SCANDURINGDL, BST_CHECKED); break;
+ default: CheckDlgButton(hwndDlg, IDC_NOSCANNER, BST_CHECKED); break;
+ }
+ CheckDlgButton(hwndDlg, IDC_WARNBEFOREOPENING, DBGetContactSettingByte(NULL,"SRFile","WarnBeforeOpening",1) ? BST_CHECKED : BST_UNCHECKED);
+
+ { TCHAR szScanExe[MAX_PATH];
+ int i,iItem;
+ for( i=0; i < SIZEOF(virusScanners); i++ ) {
+ if(SRFile_GetRegValue(HKEY_LOCAL_MACHINE,virusScanners[i].szExeRegPath,virusScanners[i].szExeRegValue,szScanExe,SIZEOF(szScanExe))) {
+ iItem=SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_ADDSTRING,0,(LPARAM)virusScanners[i].szProductName);
+ SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_SETITEMDATA,iItem,i);
+ }
+ }
+ if ( SendDlgItemMessageA(hwndDlg,IDC_SCANCMDLINE,CB_GETCOUNT,0,0) == 0 )
+ {
+ iItem = SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_ADDSTRING,0,(LPARAM)_T("") );
+ SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_SETITEMDATA,iItem, (LPARAM)-1);
+ }
+ }
+
+ DBVARIANT dbv;
+ if(DBGetContactSettingTString(NULL,"SRFile","ScanCmdLine",&dbv)==0) {
+ SetDlgItemText(hwndDlg,IDC_SCANCMDLINE,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else {
+ if(SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_GETCOUNT,0,0)) {
+ SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_SETCURSEL,0,0);
+ PostMessage(hwndDlg,M_SCANCMDLINESELCHANGE,0,0);
+ }
+ }
+ switch(DBGetContactSettingByte(NULL,"SRFile","IfExists",FILERESUME_ASK)) {
+ case FILERESUME_RESUMEALL: CheckDlgButton(hwndDlg, IDC_RESUME, BST_CHECKED); break;
+ case FILERESUME_OVERWRITEALL: CheckDlgButton(hwndDlg, IDC_OVERWRITE, BST_CHECKED); break;
+ case FILERESUME_RENAMEALL: CheckDlgButton(hwndDlg, IDC_RENAME, BST_CHECKED); break;
+ default: CheckDlgButton(hwndDlg, IDC_ASK, BST_CHECKED); break;
+ }
+ SendMessage(hwndDlg,M_UPDATEENABLING,0,0);
+ return TRUE;
+ }
+ case M_UPDATEENABLING:
+ { int on=!IsDlgButtonChecked(hwndDlg,IDC_NOSCANNER);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ST_CMDLINE),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_SCANCMDLINE),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_SCANCMDLINEBROWSE),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ST_CMDLINEHELP),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_AUTOMIN),IsDlgButtonChecked(hwndDlg,IDC_AUTOACCEPT));
+ break;
+ }
+ case M_SCANCMDLINESELCHANGE:
+ { TCHAR str[512];
+ TCHAR szScanExe[MAX_PATH];
+ int iScanner=SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_SCANCMDLINE,CB_GETCURSEL,0,0),0);
+ if(iScanner >= SIZEOF(virusScanners) || iScanner<0) break;
+ str[0]='\0';
+ if(SRFile_GetRegValue(HKEY_LOCAL_MACHINE,virusScanners[iScanner].szExeRegPath,virusScanners[iScanner].szExeRegValue,szScanExe,SIZEOF(szScanExe)))
+ mir_sntprintf(str, SIZEOF(str), virusScanners[iScanner].szCommandLine,szScanExe);
+ SetDlgItemText(hwndDlg,IDC_SCANCMDLINE,str);
+ break;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_FILEDIR:
+ if((HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus())) return 0;
+ break;
+ case IDC_FILEDIRBROWSE:
+ { TCHAR str[MAX_PATH];
+ GetDlgItemText(hwndDlg,IDC_FILEDIR,str,SIZEOF(str));
+ if(BrowseForFolder(hwndDlg,str))
+ SetDlgItemText(hwndDlg,IDC_FILEDIR,str);
+ break;
+ }
+ case IDC_AUTOACCEPT:
+ case IDC_NOSCANNER:
+ case IDC_SCANAFTERDL:
+ case IDC_SCANDURINGDL:
+ SendMessage(hwndDlg,M_UPDATEENABLING,0,0);
+ break;
+ case IDC_SCANCMDLINE:
+ if(HIWORD(wParam)==CBN_SELCHANGE) PostMessage(hwndDlg,M_SCANCMDLINESELCHANGE,0,0);
+ else if(HIWORD(wParam)!=CBN_EDITCHANGE) return 0;
+ break;
+ case IDC_SCANCMDLINEBROWSE:
+ { TCHAR str[MAX_PATH+2];
+ OPENFILENAME ofn = {0};
+ TCHAR filter[512], *pfilter;
+
+ GetDlgItemText(hwndDlg, IDC_SCANCMDLINE, str, SIZEOF(str));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ _tcscpy(filter,TranslateT("Executable Files"));
+ _tcscat(filter, _T(" (*.exe)"));
+ pfilter = filter + _tcslen(filter) + 1;
+ _tcscpy(pfilter, _T("*.exe"));
+ pfilter = pfilter + _tcslen(pfilter)+1;
+ _tcscpy(pfilter, TranslateT("All Files"));
+ _tcscat(pfilter, _T(" (*)"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ _tcscpy(pfilter, _T("*"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ *pfilter = 0;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = SIZEOF(str)-2;
+ if(str[0]=='"') {
+ TCHAR *pszQuote = _tcschr(str + 1, '"');
+ if (pszQuote) *pszQuote = 0;
+ MoveMemory(str, str + 1, _tcslen(str) * sizeof(TCHAR));
+ }
+ else {
+ TCHAR *pszSpace = _tcschr(str, ' ');
+ if (pszSpace) *pszSpace = 0;
+ }
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (!GetOpenFileName(&ofn)) break;
+ if (_tcschr(str, ' ') != NULL) {
+ MoveMemory(str+1, str, SIZEOF(str) - 2 * sizeof(TCHAR));
+ str[0] = '"';
+ _tcscat(str, _T("\""));
+ }
+ SetDlgItemText(hwndDlg, IDC_SCANCMDLINE, str);
+ break;
+ }
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { TCHAR str[512];
+ GetDlgItemText(hwndDlg, IDC_FILEDIR, str, SIZEOF(str));
+ RemoveInvalidPathChars(str);
+ DBWriteContactSettingTString(NULL,"SRFile","RecvFilesDirAdv",str);
+ DBWriteContactSettingByte(NULL,"SRFile","AutoAccept",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_AUTOACCEPT));
+ DBWriteContactSettingByte(NULL,"SRFile","AutoMin",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_AUTOMIN));
+ DBWriteContactSettingByte(NULL,"SRFile","AutoClose",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_AUTOCLOSE));
+ DBWriteContactSettingByte(NULL,"SRFile","AutoClear",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_AUTOCLEAR));
+ DBWriteContactSettingByte(NULL,"SRFile","UseScanner",(BYTE)(IsDlgButtonChecked(hwndDlg,IDC_SCANAFTERDL)?VIRUSSCAN_AFTERDL:(IsDlgButtonChecked(hwndDlg,IDC_SCANDURINGDL)?VIRUSSCAN_DURINGDL:VIRUSSCAN_DISABLE)));
+ GetDlgItemText(hwndDlg, IDC_SCANCMDLINE, str, SIZEOF(str));
+ DBWriteContactSettingTString(NULL,"SRFile","ScanCmdLine",str);
+ DBWriteContactSettingByte(NULL,"SRFile","WarnBeforeOpening",(BYTE)IsDlgButtonChecked(hwndDlg,IDC_WARNBEFOREOPENING));
+ DBWriteContactSettingByte(NULL,"SRFile","IfExists",(BYTE)(IsDlgButtonChecked(hwndDlg,IDC_ASK)?FILERESUME_ASK:(IsDlgButtonChecked(hwndDlg,IDC_RESUME)?FILERESUME_RESUMEALL:(IsDlgButtonChecked(hwndDlg,IDC_OVERWRITE)?FILERESUME_OVERWRITEALL:FILERESUME_RENAMEALL))));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+int FileOptInitialise(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp={0};
+ odp.cbSize = sizeof(odp);
+ odp.position = 900000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_FILETRANSFER);
+ odp.pszTitle = LPGEN("File Transfers");
+ odp.pszGroup = LPGEN("Events");
+ odp.pfnDlgProc = DlgProcFileOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.nIDBottomSimpleControl = IDC_VIRUSSCANNERGROUP;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
diff --git a/src/modules/srfile/filerecvdlg.cpp b/src/modules/srfile/filerecvdlg.cpp
new file mode 100644
index 0000000000..451a27e8bc
--- /dev/null
+++ b/src/modules/srfile/filerecvdlg.cpp
@@ -0,0 +1,446 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "file.h"
+
+#define MAX_MRU_DIRS 5
+
+static BOOL CALLBACK ClipSiblingsChildEnumProc(HWND hwnd, LPARAM)
+{
+ SetWindowLongPtr(hwnd,GWL_STYLE,GetWindowLongPtr(hwnd,GWL_STYLE)|WS_CLIPSIBLINGS);
+ return TRUE;
+}
+
+static void GetLowestExistingDirName(const TCHAR *szTestDir,TCHAR *szExistingDir,int cchExistingDir)
+{
+ DWORD dwAttributes;
+ TCHAR *pszLastBackslash;
+
+ lstrcpyn(szExistingDir,szTestDir,cchExistingDir);
+ while((dwAttributes=GetFileAttributes(szExistingDir))!=INVALID_FILE_ATTRIBUTES && !(dwAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
+ pszLastBackslash=_tcsrchr(szExistingDir,'\\');
+ if(pszLastBackslash==NULL) {*szExistingDir='\0'; break;}
+ *pszLastBackslash='\0';
+ }
+ if(szExistingDir[0]=='\0') GetCurrentDirectory(cchExistingDir,szExistingDir);
+}
+
+static const TCHAR InvalidFilenameChars[] = _T("\\/:*?\"<>|");
+void RemoveInvalidFilenameChars(TCHAR *tszString)
+{
+ size_t i;
+ if (tszString) {
+ for(i=_tcscspn(tszString,InvalidFilenameChars); tszString[i]; i+=_tcscspn(tszString+i+1,InvalidFilenameChars)+1)
+ if(tszString[i] >= 0)
+ tszString[i] = _T('_');
+ }
+}
+
+static const TCHAR InvalidPathChars[] = _T("*?\"<>|"); // "\/:" are excluded as they are allowed in file path
+void RemoveInvalidPathChars(TCHAR *tszString)
+{
+ size_t i;
+ if (tszString) {
+ for(i=_tcscspn(tszString,InvalidPathChars); tszString[i]; i+=_tcscspn(tszString+i+1,InvalidPathChars)+1)
+ if(tszString[i] >= 0)
+ tszString[i] = _T('_');
+ }
+}
+
+static INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
+{
+ TCHAR szDir[MAX_PATH];
+ switch(uMsg) {
+ case BFFM_INITIALIZED:
+ SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData);
+ break;
+ case BFFM_SELCHANGED:
+ if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir))
+ SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)szDir);
+ break;
+ }
+ return 0;
+}
+
+int BrowseForFolder(HWND hwnd,TCHAR *szPath)
+{
+ BROWSEINFO bi={0};
+ LPITEMIDLIST pidlResult;
+
+ bi.hwndOwner=hwnd;
+ bi.pszDisplayName=szPath;
+ bi.lpszTitle=TranslateT("Select Folder");
+ bi.ulFlags=BIF_NEWDIALOGSTYLE|BIF_EDITBOX|BIF_RETURNONLYFSDIRS; // Use this combo instead of BIF_USENEWUI
+ bi.lpfn=BrowseCallbackProc;
+ bi.lParam=(LPARAM)szPath;
+
+ pidlResult=SHBrowseForFolder(&bi);
+ if(pidlResult) {
+ SHGetPathFromIDList(pidlResult,szPath);
+ lstrcat(szPath,_T("\\"));
+ CoTaskMemFree(pidlResult);
+ }
+ return pidlResult != NULL;
+}
+
+static REPLACEVARSARRAY sttVarsToReplace[] =
+{
+ { ( TCHAR* )"///", ( TCHAR* )"//" },
+ { ( TCHAR* )"//", ( TCHAR* )"/" },
+ { ( TCHAR* )"()", ( TCHAR* )"" },
+ { NULL, NULL }
+};
+
+static void patchDir( TCHAR* str, size_t strSize )
+{
+ REPLACEVARSDATA dat = { 0 };
+ dat.cbSize = sizeof( dat );
+ dat.dwFlags = RVF_TCHAR;
+ dat.variables = sttVarsToReplace;
+
+ TCHAR* result = ( TCHAR* )CallService( MS_UTILS_REPLACEVARS, (WPARAM)str, (LPARAM)&dat );
+ if ( result ) {
+ _tcsncpy( str, result, strSize );
+ mir_free( result );
+ }
+
+ size_t len = lstrlen( str );
+ if ( len+1 < strSize && str[len-1] != '\\' )
+ lstrcpy( str+len, _T("\\") );
+}
+
+void GetContactReceivedFilesDir(HANDLE hContact, TCHAR *szDir, int cchDir, BOOL patchVars)
+{
+ DBVARIANT dbv;
+ TCHAR szTemp[MAX_PATH];
+ szTemp[0] = 0;
+
+ if ( !DBGetContactSettingTString( NULL, "SRFile", "RecvFilesDirAdv", &dbv)) {
+ if ( lstrlen( dbv.ptszVal ) > 0 )
+ lstrcpyn( szTemp, dbv.ptszVal, SIZEOF( szTemp ));
+ DBFreeVariant( &dbv );
+ }
+
+ if ( !szTemp[0] )
+#ifdef _UNICODE
+ mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%%mydocuments%%\\%s\\%%userid%%"), TranslateT("My Received Files"));
+#else
+ mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%%mydocuments%%\\%s\\%%userid%%"), "My Received Files");
+#endif
+
+ if ( hContact ) {
+ REPLACEVARSDATA dat = { 0 };
+ REPLACEVARSARRAY rvaVarsToReplace[4];
+ rvaVarsToReplace[0].lptzKey = _T("nick");
+ rvaVarsToReplace[0].lptzValue = mir_tstrdup((TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+ rvaVarsToReplace[1].lptzKey = _T("userid");
+ rvaVarsToReplace[1].lptzValue = GetContactID(hContact);
+ rvaVarsToReplace[2].lptzKey = _T("proto");
+ rvaVarsToReplace[2].lptzValue = mir_a2t((char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact,0));
+ rvaVarsToReplace[3].lptzKey = NULL;
+ rvaVarsToReplace[3].lptzValue = NULL;
+ for (int i=0; i < (SIZEOF(rvaVarsToReplace)-1);i++)
+ RemoveInvalidFilenameChars(rvaVarsToReplace[i].lptzValue);
+
+ dat.cbSize = sizeof( dat );
+ dat.dwFlags = RVF_TCHAR;
+ dat.variables = rvaVarsToReplace;
+ dat.hContact = hContact;
+ TCHAR* result = ( TCHAR* )CallService( MS_UTILS_REPLACEVARS, (WPARAM)szTemp, (LPARAM)&dat );
+ if ( result ) {
+ _tcsncpy( szTemp, result, SIZEOF(szTemp));
+ mir_free( result );
+ for (int i=0; i < (SIZEOF(rvaVarsToReplace)-1);i++)
+ mir_free(rvaVarsToReplace[i].lptzValue);
+ } }
+
+ if (patchVars)
+ patchDir( szTemp, SIZEOF(szTemp));
+ RemoveInvalidPathChars(szTemp);
+ lstrcpyn( szDir, szTemp, cchDir );
+}
+
+void GetReceivedFilesDir(TCHAR *szDir, int cchDir)
+{
+ DBVARIANT dbv;
+ TCHAR szTemp[MAX_PATH];
+ szTemp[0] = 0;
+
+ if ( !DBGetContactSettingTString( NULL, "SRFile", "RecvFilesDirAdv", &dbv )) {
+ if ( lstrlen( dbv.ptszVal ) > 0 )
+ lstrcpyn( szTemp, dbv.ptszVal, SIZEOF( szTemp ));
+ DBFreeVariant(&dbv);
+ }
+
+ if ( !szTemp[0] )
+#ifdef _UNICODE
+ mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%%mydocuments%%\\%s"), TranslateT("My Received Files"));
+#else
+ mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%%mydocuments%%\\%s"), "My Received Files");
+#endif
+
+ patchDir( szTemp, SIZEOF(szTemp));
+ RemoveInvalidPathChars(szTemp);
+ lstrcpyn( szDir, szTemp, cchDir );
+}
+
+INT_PTR CALLBACK DlgProcRecvFile(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct FileDlgData *dat;
+
+ dat=(struct FileDlgData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TCHAR *contactName;
+ TCHAR szPath[450];
+ CLISTEVENT* cle = (CLISTEVENT*)lParam;
+
+ TranslateDialogDefault(hwndDlg);
+
+ dat=(struct FileDlgData*)mir_calloc(sizeof(struct FileDlgData));
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)dat);
+ dat->hContact = cle->hContact;
+ dat->hDbEvent = cle->hDbEvent;
+ dat->hPreshutdownEvent = HookEventMessage(ME_SYSTEM_PRESHUTDOWN,hwndDlg,M_PRESHUTDOWN);
+ dat->dwTicks = GetTickCount();
+
+ EnumChildWindows(hwndDlg,ClipSiblingsChildEnumProc,0);
+
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_EVENT_FILE);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact Permanently to List"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User's Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_HISTORY, SKINICON_OTHER_HISTORY, LPGEN("View User's History"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_USERMENU, SKINICON_OTHER_DOWNARROW, LPGEN("User Menu"));
+
+ contactName = cli.pfnGetContactDisplayName( dat->hContact, 0 );
+ SetDlgItemText(hwndDlg,IDC_FROM,contactName);
+ GetContactReceivedFilesDir(dat->hContact,szPath,SIZEOF(szPath),TRUE);
+ SetDlgItemText(hwndDlg,IDC_FILEDIR,szPath);
+ {
+ int i;
+ char idstr[32];
+ DBVARIANT dbv;
+
+ if (shAutoComplete)
+ shAutoComplete(GetWindow(GetDlgItem(hwndDlg,IDC_FILEDIR),GW_CHILD),1);
+
+ for(i=0;i<MAX_MRU_DIRS;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), "MruDir%d",i);
+ if(DBGetContactSettingTString(NULL,"SRFile",idstr,&dbv)) break;
+ SendDlgItemMessage(hwndDlg,IDC_FILEDIR,CB_ADDSTRING,0,(LPARAM)dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ CallService(MS_DB_EVENT_MARKREAD,(WPARAM)dat->hContact,(LPARAM)dat->hDbEvent);
+ {
+ DBEVENTINFO dbei={0};
+ TCHAR datetimestr[64];
+ char buf[540];
+
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)dat->hDbEvent,0);
+ dbei.pBlob=(PBYTE)mir_alloc(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET,(WPARAM)dat->hDbEvent,(LPARAM)&dbei);
+ dat->fs = cle->lParam ? (HANDLE)cle->lParam : (HANDLE)*(PDWORD)dbei.pBlob;
+ lstrcpynA(buf, (char*)dbei.pBlob+4, min(dbei.cbBlob+1,SIZEOF(buf)));
+ TCHAR* ptszFileName = DbGetEventStringT( &dbei, buf );
+ SetDlgItemText(hwndDlg,IDC_FILENAMES,ptszFileName);
+ mir_free(ptszFileName);
+ lstrcpynA(buf, (char*)dbei.pBlob+4+strlen((char*)dbei.pBlob+4)+1, min((int)(dbei.cbBlob-4-strlen((char*)dbei.pBlob+4)),SIZEOF(buf)));
+ TCHAR* ptszDescription = DbGetEventStringT( &dbei, buf );
+ SetDlgItemText(hwndDlg,IDC_MSG,ptszDescription);
+ mir_free(ptszDescription);
+ mir_free(dbei.pBlob);
+
+ tmi.printTimeStamp(NULL, dbei.timestamp, _T("t d"), datetimestr, SIZEOF(datetimestr), 0);
+ SetDlgItemText(hwndDlg, IDC_DATE, datetimestr);
+ }
+ {
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)dat->hContact, 0);
+ if (szProto) {
+ CONTACTINFO ci;
+ int hasName = 0;
+ char buf[128];
+ ZeroMemory(&ci,sizeof(ci));
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = dat->hContact;
+ ci.szProto = szProto;
+ ci.dwFlag = CNF_UNIQUEID;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) {
+ switch(ci.type) {
+ case CNFT_ASCIIZ:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf), "%s", ci.pszVal);
+ mir_free(ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf),"%u",ci.dVal);
+ break;
+ } }
+ if (hasName)
+ SetDlgItemTextA(hwndDlg, IDC_NAME, buf );
+ else
+ SetDlgItemText(hwndDlg, IDC_NAME, contactName);
+ } }
+
+ if(DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0)) {
+ RECT rcBtn1,rcBtn2,rcDateCtrl;
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_ADD),&rcBtn1);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_USERMENU),&rcBtn2);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_DATE),&rcDateCtrl);
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_DATE),0,0,0,rcDateCtrl.right-rcDateCtrl.left-(rcBtn2.left-rcBtn1.left),rcDateCtrl.bottom-rcDateCtrl.top,SWP_NOZORDER|SWP_NOMOVE);
+ }
+ else if(DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0)) {
+ //don't check auto-min here to fix BUG#647620
+ PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDOK));
+ }
+ if(!DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD),SW_HIDE);
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+
+ case WM_DRAWITEM:
+ { LPDRAWITEMSTRUCT dis=(LPDRAWITEMSTRUCT)lParam;
+ if(dis->hwndItem==GetDlgItem(hwndDlg, IDC_PROTOCOL)) {
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if (szProto) {
+ HICON hIcon;
+
+ hIcon=(HICON)CallProtoService(szProto,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ if (hIcon) {
+ DrawIconEx(dis->hDC,dis->rcItem.left,dis->rcItem.top,hIcon,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL);
+ DestroyIcon(hIcon);
+ } } } }
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ case WM_COMMAND:
+ if ( CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU), (LPARAM)dat->hContact ))
+ break;
+
+ switch ( LOWORD( wParam )) {
+ case IDC_FILEDIRBROWSE:
+ {
+ TCHAR szDirName[MAX_PATH],szExistingDirName[MAX_PATH];
+
+ GetDlgItemText(hwndDlg,IDC_FILEDIR,szDirName,SIZEOF(szDirName));
+ GetLowestExistingDirName(szDirName,szExistingDirName,SIZEOF(szExistingDirName));
+ if(BrowseForFolder(hwndDlg,szExistingDirName))
+ SetDlgItemText(hwndDlg,IDC_FILEDIR,szExistingDirName);
+ }
+ break;
+
+ case IDOK:
+ { //most recently used directories
+ TCHAR szRecvDir[MAX_PATH],szDefaultRecvDir[MAX_PATH];
+ GetDlgItemText(hwndDlg,IDC_FILEDIR,szRecvDir,SIZEOF(szRecvDir));
+ RemoveInvalidPathChars(szRecvDir);
+ GetContactReceivedFilesDir(NULL,szDefaultRecvDir,SIZEOF(szDefaultRecvDir),TRUE);
+ if(_tcsnicmp(szRecvDir,szDefaultRecvDir,lstrlen(szDefaultRecvDir))) {
+ char idstr[32];
+ int i;
+ DBVARIANT dbv;
+ for(i=MAX_MRU_DIRS-2;i>=0;i--) {
+ mir_snprintf(idstr, SIZEOF(idstr), "MruDir%d",i);
+ if(DBGetContactSettingTString(NULL,"SRFile",idstr,&dbv)) continue;
+ mir_snprintf(idstr, SIZEOF(idstr), "MruDir%d",i+1);
+ DBWriteContactSettingTString(NULL,"SRFile",idstr,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ DBWriteContactSettingTString(NULL,"SRFile",idstr,szRecvDir);
+ }
+ }
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FILENAMES),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_MSG),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FILEDIR),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FILEDIRBROWSE),FALSE);
+
+ GetDlgItemText(hwndDlg,IDC_FILEDIR,dat->szSavePath,SIZEOF(dat->szSavePath));
+ GetDlgItemText(hwndDlg,IDC_FILE,dat->szFilenames,SIZEOF(dat->szFilenames));
+ GetDlgItemText(hwndDlg,IDC_MSG,dat->szMsg,SIZEOF(dat->szMsg));
+ dat->hwndTransfer = FtMgr_AddTransfer(dat);
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, 0);
+ //check for auto-minimize here to fix BUG#647620
+ if(DBGetContactSettingByte(NULL,"SRFile","AutoAccept",0) && DBGetContactSettingByte(NULL,"SRFile","AutoMin",0)) {
+ ShowWindow(hwndDlg,SW_HIDE);
+ ShowWindow(hwndDlg,SW_SHOWMINNOACTIVE);
+ }
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDCANCEL:
+ if (dat->fs) CallContactService(dat->hContact,PSS_FILEDENYT,(WPARAM)dat->fs,(LPARAM)TranslateT("Cancelled"));
+ dat->fs=NULL; /* the protocol will free the handle */
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDC_ADD:
+ { ADDCONTACTSTRUCT acs={0};
+
+ acs.handle=dat->hContact;
+ acs.handleType=HANDLE_CONTACT;
+ acs.szProto="";
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)hwndDlg,(LPARAM)&acs);
+ if(!DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0))
+ ShowWindow(GetDlgItem(hwndDlg,IDC_ADD), SW_HIDE);
+ }
+ break;
+
+ case IDC_USERMENU:
+ { RECT rc;
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)dat->hContact,0);
+ GetWindowRect((HWND)lParam,&rc);
+ TrackPopupMenu(hMenu,0,rc.left,rc.bottom,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ }
+ break;
+
+ case IDC_DETAILS:
+ CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)dat->hContact,0);
+ break;
+
+ case IDC_HISTORY:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY,(WPARAM)dat->hContact,0);
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_ADD);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_HISTORY);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_USERMENU);
+
+ if ( dat ) FreeFileDlgData( dat );
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/srfile/filesenddlg.cpp b/src/modules/srfile/filesenddlg.cpp
new file mode 100644
index 0000000000..94c24a2479
--- /dev/null
+++ b/src/modules/srfile/filesenddlg.cpp
@@ -0,0 +1,363 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "file.h"
+
+static void SetFileListAndSizeControls(HWND hwndDlg,struct FileDlgData *dat)
+{
+ int fileCount=0,dirCount=0,totalSize=0,i;
+ struct _stat statbuf;
+ TCHAR str[64];
+
+ for ( i=0; dat->files[i]; i++ ) {
+ if ( _tstat( dat->files[i], &statbuf ) == 0 ) {
+ if ( statbuf.st_mode & _S_IFDIR)
+ dirCount++;
+ else
+ fileCount++;
+ totalSize += statbuf.st_size;
+ } }
+
+ GetSensiblyFormattedSize(totalSize,str,SIZEOF(str),0,1,NULL);
+ SetDlgItemText(hwndDlg,IDC_TOTALSIZE,str);
+ if(i>1) {
+ TCHAR szFormat[32];
+ if(fileCount && dirCount) {
+ mir_sntprintf(szFormat,SIZEOF(szFormat),_T("%s, %s"),TranslateTS(fileCount==1?_T("%d file"):_T("%d files")),TranslateTS(dirCount==1?_T("%d directory"):_T("%d directories")));
+ mir_sntprintf(str,SIZEOF(str),szFormat,fileCount,dirCount);
+ }
+ else if(fileCount) {
+ lstrcpy(szFormat,TranslateT("%d files"));
+ mir_sntprintf(str,SIZEOF(str),szFormat,fileCount);
+ }
+ else {
+ lstrcpy(szFormat,TranslateT("%d directories"));
+ mir_sntprintf(str,SIZEOF(str),szFormat,dirCount);
+ }
+ SetDlgItemText(hwndDlg,IDC_FILE,str);
+ }
+ else SetDlgItemText(hwndDlg,IDC_FILE,dat->files[0]);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), fileCount || dirCount);
+}
+
+static void FilenameToFileList(HWND hwndDlg, struct FileDlgData* dat, const TCHAR* buf)
+{
+ DWORD dwFileAttributes;
+
+ // Make sure that the file matrix is empty (the user may select files several times)
+ FreeFilesMatrix(&dat->files);
+
+ // Get the file attributes of selection
+ dwFileAttributes = GetFileAttributes( buf );
+ if (dwFileAttributes == INVALID_FILE_ATTRIBUTES)
+ return;
+
+ // Check if the selection is a directory or a file
+ if ( GetFileAttributes( buf ) & FILE_ATTRIBUTE_DIRECTORY ) {
+ const TCHAR* pBuf;
+ int nNumberOfFiles = 0;
+ int nTemp;
+ int fileOffset;
+
+ // :NOTE: The first string in the buffer is the directory, followed by a
+ // NULL separated list of all files
+
+ // fileOffset is the offset to the first file.
+ fileOffset = lstrlen(buf) + 1;
+
+ // Count number of files
+ pBuf = buf + fileOffset;
+ while ( *pBuf ) {
+ pBuf += lstrlen(pBuf) + 1;
+ nNumberOfFiles++;
+ }
+
+ // Allocate memory for a pointer array
+ if (( dat->files = ( TCHAR* *)mir_alloc((nNumberOfFiles + 1) * sizeof(TCHAR*))) == NULL )
+ return;
+
+ // Fill the array
+ pBuf = buf + fileOffset;
+ nTemp = 0;
+ while(*pBuf)
+ {
+ // Allocate space for path+filename
+ int cbFileNameLen = lstrlen( pBuf );
+ dat->files[nTemp] = ( TCHAR* )mir_alloc( sizeof(TCHAR)*(fileOffset + cbFileNameLen + 1));
+
+ // Add path to filename and copy into array
+ #if defined( _UNICODE )
+ CopyMemory(dat->files[nTemp], buf, (fileOffset-1)*sizeof( TCHAR ));
+ dat->files[nTemp][fileOffset-1] = '\\';
+ _tcscpy(dat->files[nTemp] + fileOffset - (buf[fileOffset-2]=='\\'?1:0), pBuf);
+ #else
+ CopyMemory(dat->files[nTemp], buf, fileOffset-1 );
+ dat->files[nTemp][fileOffset-1] = '\\';
+ strcpy(dat->files[nTemp] + fileOffset - (buf[fileOffset-2]=='\\'?1:0), pBuf);
+ #endif
+ // Move pointers to next file...
+ pBuf += cbFileNameLen + 1;
+ nTemp++;
+ }
+ // Terminate array
+ dat->files[nNumberOfFiles] = NULL;
+ }
+ // ...the selection is a single file
+ else
+ {
+ if (( dat->files = ( TCHAR **)mir_alloc(2 * sizeof( TCHAR*))) == NULL ) // Leaks when aborted
+ return;
+
+ dat->files[0] = mir_tstrdup(buf);
+ dat->files[1] = NULL;
+ }
+
+ // Update dialog text with new file selection
+ SetFileListAndSizeControls(hwndDlg, dat);
+}
+
+#define M_FILECHOOSEDONE (WM_USER+100)
+void __cdecl ChooseFilesThread(void* param)
+{
+ HWND hwndDlg = ( HWND )param;
+ TCHAR filter[128], *pfilter;
+ TCHAR* buf = ( TCHAR* )mir_alloc( sizeof(TCHAR)*32767 );
+ if ( buf == NULL )
+ PostMessage( hwndDlg, M_FILECHOOSEDONE, 0, ( LPARAM )( TCHAR* )NULL );
+ else {
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ lstrcpy( filter, TranslateT( "All Files" ));
+ lstrcat( filter, _T(" (*)" ));
+ pfilter = filter + lstrlen( filter )+1;
+ lstrcpy( pfilter, _T( "*" ));
+ pfilter = filter + lstrlen( filter )+1;
+ pfilter[ 0 ] = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = buf; *buf = 0;
+ ofn.nMaxFile = 32767;
+ ofn.Flags = OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if ( GetOpenFileName( &ofn ))
+ PostMessage( hwndDlg, M_FILECHOOSEDONE, 0, ( LPARAM )buf );
+ else {
+ mir_free( buf );
+ PostMessage( hwndDlg, M_FILECHOOSEDONE, 0, ( LPARAM )( TCHAR* )NULL );
+} } }
+
+static BOOL CALLBACK ClipSiblingsChildEnumProc(HWND hwnd,LPARAM)
+{
+ SetWindowLongPtr(hwnd,GWL_STYLE,GetWindowLongPtr(hwnd,GWL_STYLE)|WS_CLIPSIBLINGS);
+ return TRUE;
+}
+
+static WNDPROC OldSendEditProc;
+static LRESULT CALLBACK SendEditSubclassProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg) {
+ case WM_CHAR:
+ if(wParam=='\n' && GetKeyState(VK_CONTROL)&0x8000) {
+ PostMessage(GetParent(hwnd),WM_COMMAND,IDOK,0);
+ return 0;
+ }
+ break;
+ case WM_SYSCHAR:
+ if((wParam=='s' || wParam=='S') && GetKeyState(VK_MENU)&0x8000) {
+ PostMessage(GetParent(hwnd),WM_COMMAND,IDOK,0);
+ return 0;
+ }
+ break;
+ }
+ return CallWindowProc(OldSendEditProc,hwnd,msg,wParam,lParam);
+}
+
+INT_PTR CALLBACK DlgProcSendFile(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct FileDlgData *dat;
+
+ dat=(struct FileDlgData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ struct FileSendData *fsd=(struct FileSendData*)lParam;
+
+ dat=(struct FileDlgData*)mir_calloc(sizeof(struct FileDlgData));
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)dat);
+ dat->hContact=fsd->hContact;
+ dat->send=1;
+ dat->hPreshutdownEvent=HookEventMessage(ME_SYSTEM_PRESHUTDOWN,hwndDlg,M_PRESHUTDOWN);
+ dat->fs=NULL;
+ dat->dwTicks=GetTickCount();
+
+ TranslateDialogDefault(hwndDlg);
+ EnumChildWindows(hwndDlg,ClipSiblingsChildEnumProc,0);
+ OldSendEditProc=(WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MSG),GWLP_WNDPROC,(LONG_PTR)SendEditSubclassProc);
+
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_EVENT_FILE);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User's Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_HISTORY, SKINICON_OTHER_HISTORY, LPGEN("View User's History"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_USERMENU, SKINICON_OTHER_DOWNARROW, LPGEN("User Menu"));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
+
+ if(fsd->ppFiles!=NULL && fsd->ppFiles[0]!=NULL) {
+ int totalCount,i;
+ for(totalCount=0;fsd->ppFiles[totalCount];totalCount++);
+ dat->files = ( TCHAR** )mir_alloc( sizeof(TCHAR*)*(totalCount+1)); // Leaks
+ for(i=0;i<totalCount;i++)
+ dat->files[i] = mir_tstrdup( fsd->ppFiles[i] );
+ dat->files[totalCount]=NULL;
+ SetFileListAndSizeControls(hwndDlg,dat);
+ }
+ {
+ char *szProto;
+ TCHAR* contactName = cli.pfnGetContactDisplayName( dat->hContact, 0 );
+ SetDlgItemText(hwndDlg,IDC_TO,contactName);
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if (szProto) {
+ CONTACTINFO ci;
+ int hasName = 0;
+ char buf[128];
+ ZeroMemory(&ci,sizeof(ci));
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = dat->hContact;
+ ci.szProto = szProto;
+ ci.dwFlag = CNF_UNIQUEID;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) {
+ switch(ci.type) {
+ case CNFT_ASCIIZ:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf), "%s", ci.pszVal);
+ mir_free(ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf),"%u",ci.dVal);
+ break;
+ } }
+
+ if ( hasName )
+ SetDlgItemTextA(hwndDlg,IDC_NAME,buf);
+ else
+ SetDlgItemText(hwndDlg,IDC_NAME,contactName);
+ } }
+
+ if ( fsd->ppFiles == NULL ) {
+ EnableWindow(hwndDlg, FALSE);
+ dat->closeIfFileChooseCancelled=1;
+ PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_CHOOSE,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_CHOOSE));
+ }
+ return TRUE;
+ }
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis=(LPDRAWITEMSTRUCT)lParam;
+ if(dis->hwndItem==GetDlgItem(hwndDlg, IDC_PROTOCOL)) {
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if (szProto) {
+ HICON hIcon = (HICON)CallProtoService(szProto,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ if (hIcon) {
+ DrawIconEx(dis->hDC,dis->rcItem.left,dis->rcItem.top,hIcon,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL);
+ DestroyIcon(hIcon);
+ } } } }
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ case M_FILECHOOSEDONE:
+ if( lParam != 0 ) {
+ FilenameToFileList( hwndDlg, dat, ( TCHAR* )lParam );
+ mir_free(( TCHAR* )lParam );
+ dat->closeIfFileChooseCancelled = 0;
+ }
+ else if(dat->closeIfFileChooseCancelled) DestroyWindow(hwndDlg);
+ EnableWindow(hwndDlg,TRUE);
+ break;
+
+ case WM_COMMAND:
+ if(CallService(MS_CLIST_MENUPROCESSCOMMAND,MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),(LPARAM)dat->hContact))
+ break;
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHOOSE:
+ EnableWindow(hwndDlg,FALSE);
+ //GetOpenFileName() creates its own message queue which prevents any incoming events being processed
+ forkthread(ChooseFilesThread,0,hwndDlg);
+ break;
+ case IDOK:
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FILENAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_MSG),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_CHOOSE),FALSE);
+
+ GetDlgItemText(hwndDlg,IDC_FILEDIR,dat->szSavePath,SIZEOF(dat->szSavePath));
+ GetDlgItemText(hwndDlg,IDC_FILE,dat->szFilenames,SIZEOF(dat->szFilenames));
+ GetDlgItemText(hwndDlg,IDC_MSG,dat->szMsg,SIZEOF(dat->szMsg));
+ dat->hwndTransfer = FtMgr_AddTransfer(dat);
+ SetWindowLongPtr( hwndDlg, GWLP_USERDATA, 0);
+ DestroyWindow(hwndDlg);
+ return TRUE;
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+
+ case IDC_USERMENU:
+ { RECT rc;
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)dat->hContact,0);
+ GetWindowRect((HWND)lParam,&rc);
+ TrackPopupMenu(hMenu,0,rc.left,rc.bottom,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ break;
+ }
+ case IDC_DETAILS:
+ CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)dat->hContact,0);
+ return TRUE;
+ case IDC_HISTORY:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY,(WPARAM)dat->hContact,0);
+ return TRUE;
+ }
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_HISTORY);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_USERMENU);
+
+ if ( dat )
+ FreeFileDlgData( dat );
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MSG),GWLP_WNDPROC,(LONG_PTR)OldSendEditProc);
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/src/modules/srfile/filexferdlg.cpp b/src/modules/srfile/filexferdlg.cpp
new file mode 100644
index 0000000000..0a6246dea0
--- /dev/null
+++ b/src/modules/srfile/filexferdlg.cpp
@@ -0,0 +1,799 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <io.h>
+#include "file.h"
+
+#define HM_RECVEVENT (WM_USER+10)
+
+static int CheckVirusScanned(HWND hwnd,struct FileDlgData *dat,int i)
+{
+ if(dat->send) return 1;
+ if(dat->fileVirusScanned == NULL) return 0;
+ if(dat->fileVirusScanned[i]) return 1;
+ if(DBGetContactSettingByte(NULL,"SRFile","WarnBeforeOpening",1)==0) return 1;
+ return IDYES==MessageBox(hwnd,TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"),TranslateT("File Received"),MB_YESNO|MB_DEFBUTTON2);
+}
+
+#define M_VIRUSSCANDONE (WM_USER+100)
+struct virusscanthreadstartinfo {
+ TCHAR *szFile;
+ int returnCode;
+ HWND hwndReply;
+};
+
+TCHAR* PFTS_StringToTchar( int flags, const PROTOCHAR* s )
+{
+#ifdef _UNICODE
+ if ( flags & PFTS_UTF )
+ return Utf8DecodeUcs2(( char* )s );
+ else if ( flags & PFTS_UNICODE )
+ return mir_tstrdup( s );
+ else
+ return mir_a2t(( char* )s );
+#else
+ if ( flags & PFTS_UTF ) {
+ char *szAnsi = mir_strdup(( char* )s );
+ return Utf8Decode(szAnsi, NULL);
+ }
+ else
+ return mir_strdup( s );
+#endif
+}
+
+int PFTS_CompareWithTchar( PROTOFILETRANSFERSTATUS* ft, const PROTOCHAR* s, TCHAR* r )
+{
+#ifdef _UNICODE
+ if ( ft->flags & PFTS_UTF ) {
+ TCHAR* ts = Utf8DecodeUcs2(( char* )s );
+ int res = _tcscmp( ts, r );
+ mir_free( ts );
+ return res;
+ }
+ else if ( ft->flags & PFTS_UNICODE )
+ return _tcscmp( s, r );
+ else {
+ TCHAR* ts = mir_a2t(( char* )s );
+ int res = _tcscmp( ts, r );
+ mir_free( ts );
+ return res;
+ }
+#else
+ if ( ft->flags & PFTS_UTF ) {
+ char *ts = NEWSTR_ALLOCA(( char* )s );
+ return _tcscmp( Utf8Decode(( char* )ts, NULL), r );
+ }
+ else
+ return _tcscmp( s, r );
+#endif
+}
+
+static void SetOpenFileButtonStyle(HWND hwndButton,int enabled)
+{
+ EnableWindow(hwndButton,enabled);
+}
+
+void FillSendData( FileDlgData* dat, DBEVENTINFO& dbei )
+{
+ dbei.cbSize = sizeof(dbei);
+ dbei.szModule = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ dbei.eventType = EVENTTYPE_FILE;
+ dbei.flags = DBEF_SENT;
+ dbei.timestamp = time(NULL);
+ #if defined( _UNICODE )
+ char *szFileNames = Utf8EncodeT(dat->szFilenames), *szMsg = Utf8EncodeT(dat->szMsg);
+ dbei.flags |= DBEF_UTF;
+ #else
+ char *szFileNames = dat->szFilenames, *szMsg = dat->szMsg;
+ #endif
+
+ dbei.cbBlob = sizeof(DWORD) + lstrlenA(szFileNames)+lstrlenA(szMsg)+2;
+ dbei.pBlob=(PBYTE)mir_alloc(dbei.cbBlob);
+ *(PDWORD)dbei.pBlob=0;
+ lstrcpyA((char*)dbei.pBlob+sizeof(DWORD),szFileNames);
+ lstrcpyA((char*)dbei.pBlob+sizeof(DWORD)+lstrlenA(szFileNames)+1,szMsg);
+
+ #if defined( _UNICODE )
+ mir_free( szFileNames ), mir_free( szMsg );
+ #endif
+}
+
+static void __cdecl RunVirusScannerThread(struct virusscanthreadstartinfo *info)
+{
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si={0};
+ DBVARIANT dbv;
+ TCHAR szCmdLine[768];
+
+ if (!DBGetContactSettingTString(NULL,"SRFile", "ScanCmdLine", &dbv))
+ {
+ if(dbv.ptszVal[0])
+ {
+ TCHAR *pszReplace;
+ si.cb=sizeof(si);
+ pszReplace = _tcsstr(dbv.ptszVal, _T("%f"));
+ if (pszReplace)
+ {
+ if ( info->szFile[_tcslen(info->szFile) - 1] == '\\')
+ info->szFile[_tcslen(info->szFile) - 1] = '\0';
+ *pszReplace = 0;
+ mir_sntprintf(szCmdLine, SIZEOF(szCmdLine), _T("%s\"%s\"%s"), dbv.ptszVal, info->szFile, pszReplace+2);
+ }
+ else lstrcpyn(szCmdLine, dbv.ptszVal, SIZEOF(szCmdLine));
+ if(CreateProcess(NULL,szCmdLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) {
+ if(WaitForSingleObject(pi.hProcess,3600*1000)==WAIT_OBJECT_0)
+ PostMessage(info->hwndReply,M_VIRUSSCANDONE,info->returnCode,0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+ mir_free(info->szFile);
+ mir_free(info);
+}
+
+static void SetFilenameControls(HWND hwndDlg, struct FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts)
+{
+ TCHAR msg[MAX_PATH];
+ TCHAR *fnbuf = NULL, *fn = NULL;
+ SHFILEINFO shfi = {0};
+
+ if ( fts->tszCurrentFile ) {
+ fnbuf = mir_tstrdup( fts->tszCurrentFile );
+ if (( fn = _tcsrchr( fnbuf, '\\' )) == NULL )
+ fn = fnbuf;
+ else fn++;
+ }
+
+ if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = NULL;
+
+ if (fn && (fts->totalFiles > 1)) {
+ mir_sntprintf(msg, SIZEOF(msg), _T("%s: %s (%d %s %d)"),
+ cli.pfnGetContactDisplayName( fts->hContact, 0 ),
+ fn, fts->currentFileNumber+1, TranslateT("of"), fts->totalFiles);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else if (fn) {
+ mir_sntprintf(msg, SIZEOF(msg), _T("%s: %s"), cli.pfnGetContactDisplayName( fts->hContact, 0 ), fn);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else {
+ lstrcpyn(msg, cli.pfnGetContactDisplayName( fts->hContact, 0 ), SIZEOF(msg));
+ HICON hIcon = LoadSkinIcon(SKINICON_OTHER_DOWNARROW);
+ dat->hIcon = CopyIcon(hIcon);
+ IconLib_ReleaseIcon(hIcon, NULL);
+ }
+
+ mir_free( fnbuf );
+
+ SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon);
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg);
+}
+
+enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN };
+static void SetFtStatus(HWND hwndDlg, TCHAR *text, int mode)
+{
+ SetDlgItemText(hwndDlg,IDC_STATUS,TranslateTS(text));
+ SetDlgItemText(hwndDlg,IDC_TRANSFERCOMPLETED,TranslateTS(text));
+
+ ShowWindow(GetDlgItem(hwndDlg,IDC_STATUS), (mode == FTS_TEXT)?SW_SHOW:SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS)?SW_SHOW:SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN)?SW_SHOW:SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_FILEICON), (mode == FTS_OPEN)?SW_SHOW:SW_HIDE);
+}
+
+static void HideProgressControls(HWND hwndDlg)
+{
+ RECT rc;
+ char buf[64];
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc);
+ MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2);
+ SetWindowPos(hwndDlg, NULL, 0, 0, 100, rc.bottom+3, SWP_NOMOVE|SWP_NOZORDER);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE);
+
+ _strtime(buf);
+ SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf);
+
+ PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg);
+}
+
+static int FileTransferDlgResizer(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch(urc->wId) {
+ case IDC_CONTACTNAME:
+ case IDC_STATUS:
+ case IDC_ALLFILESPROGRESS:
+ case IDC_TRANSFERCOMPLETED:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ case IDC_FRAME:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM;
+ case IDC_ALLPRECENTS:
+ case IDCANCEL:
+ case IDC_OPENFILE:
+ case IDC_OPENFOLDER:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP;
+
+ case IDC_ALLTRANSFERRED:
+ urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3;
+ return RD_ANCHORX_CUSTOM|RD_ANCHORY_CUSTOM;
+
+ case IDC_ALLSPEED:
+ urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx;
+ urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3;
+ return RD_ANCHORX_CUSTOM|RD_ANCHORY_CUSTOM;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ FileDlgData *dat = (FileDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat = (FileDlgData*)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->hNotifyEvent=HookEventMessage(ME_PROTO_ACK,hwndDlg,HM_RECVEVENT);
+ dat->transferStatus.currentFileNumber = -1;
+ if(dat->send) {
+ dat->fs=(HANDLE)CallContactService(dat->hContact,PSS_FILET,(WPARAM)dat->szMsg,(LPARAM)dat->files);
+ SetFtStatus(hwndDlg, LPGENT("Request sent, waiting for acceptance..."), FTS_TEXT);
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg,IDC_OPENFILE),1);
+ dat->waitingForAcceptance=1;
+ // hide "open" button since it may cause potential access violations...
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE);
+ }
+ else { //recv
+ CreateDirectoryTreeT(dat->szSavePath);
+ dat->fs=(HANDLE)CallContactService(dat->hContact,PSS_FILEALLOWT,(WPARAM)dat->fs,(LPARAM)dat->szSavePath);
+ dat->transferStatus.tszWorkingDir = mir_tstrdup(dat->szSavePath);
+ if(DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0)) dat->resumeBehaviour=FILERESUME_ASK;
+ else dat->resumeBehaviour=DBGetContactSettingByte(NULL,"SRFile","IfExists",FILERESUME_ASK);
+ SetFtStatus(hwndDlg, LPGENT("Waiting for connection..."), FTS_TEXT);
+ }
+ {
+ /* check we actually got an fs handle back from the protocol */
+ if (!dat->fs) {
+ SetFtStatus(hwndDlg, LPGENT("Unable to initiate transfer."), FTS_TEXT);
+ dat->waitingForAcceptance=0;
+ }
+ }
+ { LOGFONT lf;
+ HFONT hFont;
+ hFont=(HFONT)SendDlgItemMessage(hwndDlg,IDC_CONTACTNAME,WM_GETFONT,0,0);
+ GetObject(hFont,sizeof(lf),&lf);
+ lf.lfWeight=FW_BOLD;
+ hFont=CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg,IDC_CONTACTNAME,WM_SETFONT,(WPARAM)hFont,0);
+ }
+
+ { SHFILEINFO shfi = {0};
+ SHGetFileInfo(_T(""), FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SMALLICON);
+ dat->hIconFolder = shfi.hIcon;
+ }
+
+ dat->hIcon = NULL;
+
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadSkinnedProtoIcon((char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)dat->hContact, 0), ID_STATUS_ONLINE));
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, 0, 0);
+
+ Button_SetIcon_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open..."));
+ SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, 0, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, 0, 0);
+
+ Button_SetIcon_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel"));
+
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, cli.pfnGetContactDisplayName( dat->hContact, 0 ));
+
+ if(!dat->waitingForAcceptance) SetTimer(hwndDlg,1,1000,NULL);
+ return TRUE;
+ case WM_TIMER:
+ MoveMemory(dat->bytesRecvedHistory+1,dat->bytesRecvedHistory,sizeof(dat->bytesRecvedHistory)-sizeof(dat->bytesRecvedHistory[0]));
+ dat->bytesRecvedHistory[0]=dat->transferStatus.totalProgress;
+ if ( dat->bytesRecvedHistorySize < SIZEOF(dat->bytesRecvedHistory))
+ dat->bytesRecvedHistorySize++;
+
+ { TCHAR szSpeed[32], szTime[32], szDisplay[96];
+ SYSTEMTIME st;
+ ULARGE_INTEGER li;
+ FILETIME ft;
+
+ GetSensiblyFormattedSize((dat->bytesRecvedHistory[0]-dat->bytesRecvedHistory[dat->bytesRecvedHistorySize-1])/dat->bytesRecvedHistorySize,szSpeed,SIZEOF(szSpeed),0,1,NULL);
+ if(dat->bytesRecvedHistory[0]==dat->bytesRecvedHistory[dat->bytesRecvedHistorySize-1])
+ lstrcpy(szTime,_T("??:??:??"));
+ else {
+ li.QuadPart=BIGI(10000000)*(dat->transferStatus.currentFileSize-dat->transferStatus.currentFileProgress)*dat->bytesRecvedHistorySize/(dat->bytesRecvedHistory[0]-dat->bytesRecvedHistory[dat->bytesRecvedHistorySize-1]);
+ ft.dwHighDateTime=li.HighPart; ft.dwLowDateTime=li.LowPart;
+ FileTimeToSystemTime(&ft,&st);
+ GetTimeFormat(LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT|TIME_NOTIMEMARKER,&st,NULL,szTime,SIZEOF(szTime));
+ }
+ if(dat->bytesRecvedHistory[0]!=dat->bytesRecvedHistory[dat->bytesRecvedHistorySize-1]) {
+ li.QuadPart=BIGI(10000000)*(dat->transferStatus.totalBytes-dat->transferStatus.totalProgress)*dat->bytesRecvedHistorySize/(dat->bytesRecvedHistory[0]-dat->bytesRecvedHistory[dat->bytesRecvedHistorySize-1]);
+ ft.dwHighDateTime=li.HighPart; ft.dwLowDateTime=li.LowPart;
+ FileTimeToSystemTime(&ft,&st);
+ GetTimeFormat(LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT|TIME_NOTIMEMARKER,&st,NULL,szTime,SIZEOF(szTime));
+ }
+
+ mir_sntprintf(szDisplay,SIZEOF(szDisplay),_T("%s/%s (%s %s)"),szSpeed,TranslateT("sec"),szTime,TranslateT("remaining"));
+ SetDlgItemText(hwndDlg,IDC_ALLSPEED,szDisplay);
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+
+ case WM_DRAWITEM:
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ case WM_FT_CLEANUP:
+ if (!dat->fs)
+ {
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+
+ case WM_COMMAND:
+ if ( CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU), (LPARAM)dat->hContact ))
+ break;
+
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDC_CONTACT:
+ { RECT rc;
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)dat->hContact,0);
+ GetWindowRect((HWND)lParam,&rc);
+ TrackPopupMenu(hMenu,0,rc.left,rc.bottom,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ break;
+ }
+
+ case IDC_TRANSFERCOMPLETED:
+ if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0))
+ {
+ ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW);
+ break;
+ }
+
+ case IDC_OPENFOLDER:
+ if ( dat )
+ {
+ TCHAR* path = dat->transferStatus.tszWorkingDir;
+ if (!path || !path[0])
+ {
+ path = NEWTSTR_ALLOCA(dat->transferStatus.tszCurrentFile);
+ TCHAR* p = _tcsrchr(path, '\\'); if (p) *p = 0;
+ }
+
+ if (path) ShellExecute(NULL, _T("open"), path, NULL, NULL, SW_SHOW);
+ }
+ break;
+
+ case IDC_OPENFILE:
+ {
+ TCHAR **files;
+ HMENU hMenu;
+ RECT rc;
+ int ret;
+
+ if (dat->send)
+ if (dat->files == NULL)
+ files = dat->transferStatus.ptszFiles;
+ else
+ files = dat->files;
+ else
+ files=dat->files;
+
+ hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
+
+ if (files && *files)
+ {
+ int i, limit;
+ TCHAR *pszFilename,*pszNewFileName;
+
+ if (dat->send)
+ limit = dat->transferStatus.totalFiles;
+ else
+ limit = dat->transferStatus.currentFileNumber;
+
+ // Loop over all transfered files and add them to the menu
+ for (i = 0; i < limit; i++) {
+ pszFilename = _tcsrchr(files[i], '\\');
+ if (pszFilename == NULL)
+ pszFilename = files[i];
+ else
+ pszFilename++;
+ {
+ if (pszFilename) {
+ size_t cbFileNameLen = _tcslen(pszFilename);
+
+ pszNewFileName = (TCHAR*)mir_alloc( cbFileNameLen*2*sizeof( TCHAR ));
+ TCHAR *p = pszNewFileName;
+ for (size_t pszlen=0; pszlen < cbFileNameLen; pszlen++) {
+ *p++ = pszFilename[pszlen];
+ if (pszFilename[pszlen]=='&')
+ *p++ = '&';
+ }
+ *p = '\0';
+ AppendMenu(hMenu, MF_STRING, i+10, pszNewFileName);
+ mir_free(pszNewFileName);
+ }
+ }
+ }
+ }
+
+ GetWindowRect((HWND)lParam, &rc);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED);
+ ret = TrackPopupMenu(hMenu, TPM_RETURNCMD|TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, NULL);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED);
+ DestroyMenu(hMenu);
+
+ if (ret == 1)
+ {
+ TCHAR* path = dat->transferStatus.tszWorkingDir;
+ if (!path || !path[0])
+ {
+ path = NEWTSTR_ALLOCA(dat->transferStatus.tszCurrentFile);
+ TCHAR* p = _tcsrchr(path, '\\'); if (p) *p = 0;
+ }
+
+ if (path) ShellExecute(NULL, _T("open"), path, NULL, NULL, SW_SHOW);
+ }
+ else if (ret && CheckVirusScanned(hwndDlg, dat, ret))
+ ShellExecute(NULL, NULL, files[ret-10], NULL, NULL, SW_SHOW);
+
+ break;
+ }
+ }
+ break;
+ case M_FILEEXISTSDLGREPLY:
+ { PROTOFILERESUME *pfr=(PROTOFILERESUME*)lParam;
+ TCHAR *szOriginalFilename=(TCHAR*)wParam;
+ char *szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+
+ EnableWindow(hwndDlg,TRUE);
+ switch(pfr->action) {
+ case FILERESUME_CANCEL:
+ if (dat->fs) CallContactService(dat->hContact,PSS_FILECANCEL,(WPARAM)dat->fs,0);
+ dat->fs=NULL;
+ mir_free(szOriginalFilename);
+ if(pfr->szFilename) mir_free((char*)pfr->szFilename);
+ mir_free(pfr);
+ return 0;
+ case FILERESUME_RESUMEALL:
+ case FILERESUME_OVERWRITEALL:
+ dat->resumeBehaviour=pfr->action;
+ pfr->action&=~FILERESUMEF_ALL;
+ break;
+ case FILERESUME_RENAMEALL:
+ pfr->action=FILERESUME_RENAME;
+ { TCHAR *pszExtension,*pszFilename;
+ int i;
+ if((pszFilename = _tcsrchr(szOriginalFilename,'\\'))==NULL) pszFilename=szOriginalFilename;
+ if((pszExtension = _tcsrchr(pszFilename+1,'.'))==NULL) pszExtension=pszFilename+lstrlen(pszFilename);
+ if(pfr->szFilename) mir_free((TCHAR*)pfr->szFilename);
+ pfr->szFilename = (TCHAR*)mir_alloc(sizeof(TCHAR)*((pszExtension-szOriginalFilename)+21+lstrlen(pszExtension)));
+ for(i=1;;i++) {
+ _stprintf((TCHAR*)pfr->szFilename,_T("%.*s (%u)%s"),pszExtension-szOriginalFilename,szOriginalFilename,i,pszExtension);
+ if(_taccess(pfr->szFilename,0)!=0)
+ break;
+ }
+ }
+ break;
+ }
+ mir_free(szOriginalFilename);
+ CallProtoService(szProto,PS_FILERESUMET,(WPARAM)dat->fs,(LPARAM)pfr);
+ if(pfr->szFilename) mir_free((char*)pfr->szFilename);
+ mir_free(pfr);
+ break;
+ }
+ case HM_RECVEVENT:
+ { ACKDATA *ack=(ACKDATA*)lParam;
+ if (ack->hProcess!=dat->fs) break; /* icq abuses this sometimes */
+ if(ack->hContact!=dat->hContact) break;
+ if(ack->type!=ACKTYPE_FILE) break;
+
+ if(dat->waitingForAcceptance) {
+ SetTimer(hwndDlg,1,1000,NULL);
+ dat->waitingForAcceptance=0;
+ }
+ switch(ack->result) {
+ case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENT("Decision sent"), FTS_TEXT); break;
+ case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENT("Connecting..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENT("Connecting to proxy..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENT("Connected"), FTS_TEXT); break;
+ case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENT("Waiting for connection..."), FTS_TEXT); break;
+ case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENT("Initialising..."), FTS_TEXT); break;
+ case ACKRESULT_NEXTFILE:
+ SetFtStatus(hwndDlg, LPGENT("Moving to next file..."), FTS_TEXT);
+ SetDlgItemTextA(hwndDlg,IDC_FILENAME,"");
+ if(dat->transferStatus.currentFileNumber==1 && dat->transferStatus.totalFiles>1 && !dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg,IDC_OPENFILE),1);
+ if(dat->transferStatus.currentFileNumber!=-1 && dat->files && !dat->send && DBGetContactSettingByte(NULL,"SRFile","UseScanner",VIRUSSCAN_DISABLE)==VIRUSSCAN_DURINGDL) {
+ if(GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber])&FILE_ATTRIBUTE_DIRECTORY)
+ PostMessage(hwndDlg,M_VIRUSSCANDONE,dat->transferStatus.currentFileNumber,0);
+ else {
+ virusscanthreadstartinfo *vstsi;
+ vstsi=(struct virusscanthreadstartinfo*)mir_alloc(sizeof(struct virusscanthreadstartinfo));
+ vstsi->hwndReply = hwndDlg;
+ vstsi->szFile = mir_tstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ vstsi->returnCode = dat->transferStatus.currentFileNumber;
+ forkthread((void (*)(void*))RunVirusScannerThread,0,vstsi);
+ }
+ }
+ break;
+ case ACKRESULT_FILERESUME:
+ {
+ UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS*)ack->lParam);
+ PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus;
+
+ SetFilenameControls( hwndDlg, dat, fts );
+ int res = _taccess( fts->tszCurrentFile, 0 );
+ if ( res )
+ break;
+
+ SetFtStatus(hwndDlg, LPGENT("File already exists"), FTS_TEXT);
+ if(dat->resumeBehaviour==FILERESUME_ASK) {
+ TDlgProcFileExistsParam param = { hwndDlg, fts };
+ ShowWindow(hwndDlg,SW_SHOWNORMAL);
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_FILEEXISTS),hwndDlg,DlgProcFileExists,(LPARAM)&param);
+ EnableWindow(hwndDlg,FALSE);
+ }
+ else {
+ PROTOFILERESUME *pfr;
+ pfr=(PROTOFILERESUME*)mir_alloc(sizeof(PROTOFILERESUME));
+ pfr->action = dat->resumeBehaviour;
+ pfr->szFilename = NULL;
+ PostMessage(hwndDlg,M_FILEEXISTSDLGREPLY,(WPARAM)mir_tstrdup(fts->tszCurrentFile),(LPARAM)pfr);
+ }
+ SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,1);
+ return TRUE;
+ }
+ case ACKRESULT_DATA:
+ {
+ PROTOFILETRANSFERSTATUS *fts=(PROTOFILETRANSFERSTATUS*)ack->lParam;
+ TCHAR str[64], str2[64], szSizeDone[32], szSizeTotal[32];//,*contactName;
+ int units;
+
+ if ( dat->fileVirusScanned==NULL )
+ dat->fileVirusScanned=(int*)mir_calloc(sizeof(int) * fts->totalFiles);
+
+ // This needs to be here - otherwise we get holes in the files array
+ if (!dat->send)
+ {
+ if (dat->files == NULL)
+ dat->files = (TCHAR**)mir_calloc((fts->totalFiles + 1) * sizeof(TCHAR*));
+ if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == NULL)
+ {
+ if (fts->cbSize == sizeof(PROTOFILETRANSFERSTATUS_V1))
+ {
+ PROTOFILETRANSFERSTATUS_V1 *fts1 = (PROTOFILETRANSFERSTATUS_V1*)fts;
+ dat->files[fts->currentFileNumber] = PFTS_StringToTchar(0, (PROTOCHAR*)fts1->currentFile);
+ }
+ else
+ dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->tszCurrentFile);
+ }
+ }
+
+ /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */
+ if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650)) break; // the last update was less than a second ago!
+ dat->dwTicks = GetTickCount();
+
+ // Update local transfer status with data from protocol
+ UpdateProtoFileTransferStatus(&dat->transferStatus, fts);
+ fts = &dat->transferStatus;
+
+ bool firstTime = false;
+ if ((GetWindowLong(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0)
+ {
+ SetFtStatus(hwndDlg, ( fts->flags & PFTS_SENDING ) ? LPGENT("Sending...") : LPGENT("Receiving..."), FTS_PROGRESS);
+ SetFilenameControls(hwndDlg, dat, fts);
+ firstTime = true;
+ }
+
+ const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0);
+ const unsigned long nextPos = fts->totalBytes ? (BIGI(100) * fts->totalProgress / fts->totalBytes) : 0;
+ if (lastPos != nextPos || firstTime)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0);
+ mir_sntprintf(str, SIZEOF(str), _T("%u%%"), nextPos);
+ SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str);
+ }
+
+ GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, SIZEOF(szSizeTotal), 0, 1, &units);
+ GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, SIZEOF(szSizeDone), units, 0, NULL);
+ mir_sntprintf(str, SIZEOF(str), _T("%s/%s"), szSizeDone, szSizeTotal);
+ str2[0] = 0;
+ GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, SIZEOF(str2));
+ if (_tcscmp(str, str2)) SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str);
+ break;
+ }
+
+ case ACKRESULT_SUCCESS:
+ case ACKRESULT_FAILED:
+ case ACKRESULT_DENIED:
+ {
+
+ HideProgressControls(hwndDlg);
+ KillTimer(hwndDlg,1);
+ if (!dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg,IDC_OPENFILE),1);
+ SetDlgItemText(hwndDlg,IDCANCEL,TranslateT("Close"));
+ if (dat->hNotifyEvent)
+ UnhookEvent(dat->hNotifyEvent);
+ dat->hNotifyEvent=NULL;
+
+ if (ack->result == ACKRESULT_DENIED)
+ {
+ dat->fs=NULL; /* protocol will free structure */
+ SkinPlaySound("FileDenied");
+ SetFtStatus(hwndDlg, LPGENT("File transfer denied"), FTS_TEXT);
+ } else if (ack->result == ACKRESULT_FAILED)
+ {
+ dat->fs=NULL; /* protocol will free structure */
+ SkinPlaySound("FileFailed");
+ SetFtStatus(hwndDlg, LPGENT("File transfer failed"), FTS_TEXT);
+ } else {
+ SkinPlaySound("FileDone");
+ if (dat->send)
+ {
+ dat->fs=NULL; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENT("Transfer completed."), FTS_TEXT);
+
+ DBEVENTINFO dbei={0};
+ FillSendData( dat, dbei );
+ CallService(MS_DB_EVENT_ADD,(WPARAM)dat->hContact,(LPARAM)&dbei);
+ if (dbei.pBlob)
+ mir_free(dbei.pBlob);
+ dat->files=NULL; //protocol library frees this
+
+ } else {
+ SetFtStatus(hwndDlg,
+ (dat->transferStatus.totalFiles == 1) ?
+ LPGENT("Transfer completed, open file.") :
+ LPGENT("Transfer completed, open folder."),
+ FTS_OPEN);
+
+ int useScanner=DBGetContactSettingByte(NULL,"SRFile","UseScanner",VIRUSSCAN_DISABLE);
+ if (useScanner!=VIRUSSCAN_DISABLE) {
+ struct virusscanthreadstartinfo *vstsi;
+ vstsi=(struct virusscanthreadstartinfo*)mir_alloc(sizeof(struct virusscanthreadstartinfo));
+ vstsi->hwndReply=hwndDlg;
+ if(useScanner==VIRUSSCAN_DURINGDL) {
+ vstsi->returnCode=dat->transferStatus.currentFileNumber;
+ if ( GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber])&FILE_ATTRIBUTE_DIRECTORY) {
+ PostMessage(hwndDlg,M_VIRUSSCANDONE,vstsi->returnCode,0);
+ mir_free(vstsi);
+ vstsi=NULL;
+ }
+ else vstsi->szFile = mir_tstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ }
+ else {
+ vstsi->szFile = mir_tstrdup(dat->transferStatus.tszWorkingDir);
+ vstsi->returnCode = -1;
+ }
+ SetFtStatus(hwndDlg, LPGENT("Scanning for viruses..."), FTS_TEXT);
+ if(vstsi) forkthread((void (*)(void*))RunVirusScannerThread,0,vstsi);
+ } else {
+ dat->fs=NULL; /* protocol will free structure */
+ }
+ dat->transferStatus.currentFileNumber=dat->transferStatus.totalFiles;
+ } // else dat->send
+
+ } // else ack->result
+
+ PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg);
+ break;
+ }
+ break;
+ } // switch ack->result
+ } break; // case HM_RECVEVENT
+ case M_VIRUSSCANDONE:
+ { int done=1,i;
+ if((int)wParam==-1) {
+ for(i=0;i<dat->transferStatus.totalFiles;i++) dat->fileVirusScanned[i]=1;
+ }
+ else {
+ dat->fileVirusScanned[wParam]=1;
+ for(i=0;i<dat->transferStatus.totalFiles;i++) if(!dat->fileVirusScanned[i]) {done=0; break;}
+ }
+ if (done)
+ {
+ dat->fs=NULL; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENT("Transfer and virus scan complete"), FTS_TEXT);
+ }
+ break;
+ }
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd={0};
+ urd.cbSize=sizeof(urd);
+ urd.hwndDlg=hwndDlg;
+ urd.hInstance=hMirandaInst;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_FILETRANSFERINFO);
+ urd.pfnResizer=FileTransferDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd);
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE|RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE|RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE|RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE|RDW_NOERASE);
+ break;
+ }
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1);
+
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg,IDC_CONTACTNAME,WM_GETFONT,0,0);
+ DeleteObject(hFont);
+
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT);
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE);
+ Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL);
+
+ FreeFileDlgData(dat);
+ break;
+ }
+ return FALSE;
+}
+
+void FreeFileDlgData( FileDlgData* dat )
+{
+ if(dat->fs)
+ CallContactService(dat->hContact,PSS_FILECANCEL,(WPARAM)dat->fs,0);
+ dat->fs = NULL;
+
+ if (dat->hPreshutdownEvent) UnhookEvent(dat->hPreshutdownEvent);
+ if (dat->hNotifyEvent) UnhookEvent(dat->hNotifyEvent);
+ dat->hNotifyEvent = NULL;
+
+ FreeProtoFileTransferStatus(&dat->transferStatus);
+ FreeFilesMatrix(&dat->files);
+
+ mir_free(dat->fileVirusScanned);
+ Safe_DestroyIcon(dat->hIcon);
+ Safe_DestroyIcon(dat->hIconFolder);
+ mir_free(dat);
+}
diff --git a/src/modules/srfile/ftmanager.cpp b/src/modules/srfile/ftmanager.cpp
new file mode 100644
index 0000000000..a219d2188a
--- /dev/null
+++ b/src/modules/srfile/ftmanager.cpp
@@ -0,0 +1,592 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "file.h"
+
+static HWND hwndFtMgr = NULL;
+
+struct TFtMgrData
+{
+ HWND hwndIncoming;
+ HWND hwndOutgoing;
+
+ HANDLE hhkPreshutdown;
+ TBPFLAG errorState;
+};
+
+
+#define M_CALCPROGRESS (WM_USER + 200)
+struct TFtProgressData
+{
+ unsigned int init, run, scan;
+ unsigned __int64 totalBytes, totalProgress;
+};
+
+struct TLayoutWindowInfo
+{
+ HWND hwnd;
+ RECT rc;
+};
+
+struct TLayoutWindowList
+{
+ struct TLayoutWindowInfo **items;
+ int realCount, limit, increment;
+ FSortFunc sortFunc;
+};
+
+struct TFtPageData
+{
+ struct TLayoutWindowList *wnds;
+ int runningCount;
+ int height, dataHeight, scrollPos;
+};
+
+static void LayoutTransfers(HWND hwnd, struct TFtPageData *dat)
+{
+ int top = 0;
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+
+ dat->scrollPos = GetScrollPos(hwnd, SB_VERT);
+ dat->height = rc.bottom - rc.top;
+
+ if (dat->wnds->realCount)
+ {
+ int i;
+ HDWP hdwp;
+
+ hdwp = BeginDeferWindowPos(dat->wnds->realCount);
+ top -= dat->scrollPos;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ {
+ int height = dat->wnds->items[i]->rc.bottom - dat->wnds->items[i]->rc.top;
+ hdwp = DeferWindowPos(hdwp, dat->wnds->items[i]->hwnd, NULL, 0, top, rc.right, height, SWP_NOZORDER);
+ top += height;
+ }
+ top += dat->scrollPos;
+ EndDeferWindowPos(hdwp);
+ }
+
+ dat->dataHeight = top;
+
+ {
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_DISABLENOSCROLL|SIF_PAGE|SIF_RANGE;
+ si.nPage = dat->height;
+ si.nMin = 0;
+ si.nMax = dat->dataHeight;
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ }
+}
+
+static INT_PTR CALLBACK FtMgrPageDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TFtPageData *dat = (struct TFtPageData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ // Force scrollbar visibility
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_DISABLENOSCROLL;
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+
+ dat = (struct TFtPageData *)mir_alloc(sizeof(struct TFtPageData));
+ dat->wnds = (struct TLayoutWindowList *)List_Create(0, 1);
+ dat->scrollPos = 0;
+ dat->runningCount = 0;
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat);
+ break;
+ }
+
+ case WM_FT_ADD:
+ {
+ struct TLayoutWindowInfo *wnd = (struct TLayoutWindowInfo *)mir_alloc(sizeof(struct TLayoutWindowInfo));
+ wnd->hwnd = (HWND)lParam;
+ GetWindowRect(wnd->hwnd, &wnd->rc);
+ List_Insert((SortedList *)dat->wnds, wnd, dat->wnds->realCount);
+ LayoutTransfers(hwnd, dat);
+ dat->runningCount++;
+ PostMessage(GetParent(hwnd), WM_TIMER, 1, NULL);
+ break;
+ }
+
+ case WM_FT_RESIZE:
+ {
+ int i;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ if (dat->wnds->items[i]->hwnd == (HWND)lParam)
+ {
+ GetWindowRect(dat->wnds->items[i]->hwnd, &dat->wnds->items[i]->rc);
+ break;
+ }
+ LayoutTransfers(hwnd, dat);
+ break;
+ }
+
+ case WM_FT_REMOVE:
+ {
+ int i;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ if (dat->wnds->items[i]->hwnd == (HWND)lParam)
+ {
+ mir_free(dat->wnds->items[i]);
+ List_Remove((SortedList *)dat->wnds, i);
+ break;
+ }
+ LayoutTransfers(hwnd, dat);
+ break;
+ }
+
+ case WM_FT_COMPLETED:
+ { //wParam: { ACKRESULT_SUCCESS | ACKRESULT_FAILED | ACKRESULT_DENIED }
+ dat->runningCount--;
+ int i = 0;
+ while (i < dat->wnds->realCount)
+ {
+ // no error when canceling (WM_FT_REMOVE is send first, check if hwnd is still registered)
+ if (dat->wnds->items[i]->hwnd == (HWND)lParam)
+ {
+ SendMessage(GetParent(hwnd), WM_TIMER, 1, (LPARAM)wParam);
+ break;
+ }
+ ++i;
+ }
+ if (i == dat->wnds->realCount)
+ PostMessage(GetParent(hwnd), WM_TIMER, 1, NULL);
+
+ if(dat->runningCount == 0 && (int)wParam == ACKRESULT_SUCCESS && DBGetContactSettingByte(NULL,"SRFile","AutoClose",0))
+ ShowWindow(hwndFtMgr, SW_HIDE);
+ break;
+ }
+
+ case WM_FT_CLEANUP:
+ {
+ int i;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ SendMessage(dat->wnds->items[i]->hwnd, WM_FT_CLEANUP, wParam, lParam);
+ break;
+ }
+
+ case WM_SIZE:
+ {
+ LayoutTransfers(hwnd, dat);
+ break;
+ }
+
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if (zDelta)
+ {
+ int i, nScrollLines = 0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0);
+ for (i = 0; i < (nScrollLines + 1) / 2; i++ )
+ SendMessage(hwnd, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0);
+ }
+
+ SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+
+ case WM_VSCROLL:
+ {
+ int pos = dat->scrollPos;
+ switch (LOWORD(wParam))
+ {
+ case SB_LINEDOWN:
+ pos += 15;
+ break;
+ case SB_LINEUP:
+ pos -= 15;
+ break;
+ case SB_PAGEDOWN:
+ pos += dat->height - 10;
+ break;
+ case SB_PAGEUP:
+ pos -= dat->height - 10;
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD(wParam);
+ break;
+ }
+
+ if (pos > dat->dataHeight - dat->height) pos = dat->dataHeight - dat->height;
+ if (pos < 0) pos = 0;
+
+ if (dat->scrollPos != pos)
+ {
+ ScrollWindow(hwnd, 0, dat->scrollPos - pos, NULL, NULL);
+ SetScrollPos(hwnd, SB_VERT, pos, TRUE);
+ dat->scrollPos = pos;
+ }
+ break;
+ }
+
+ case M_PRESHUTDOWN:
+ {
+ int i;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ PostMessage(dat->wnds->items[i]->hwnd, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), 0);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ int i;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ mir_free(dat->wnds->items[i]);
+ List_Destroy((SortedList *)dat->wnds);
+ mir_free(dat->wnds);
+ mir_free(dat);
+ break;
+ }
+
+ case M_CALCPROGRESS:
+ {
+ int i;
+ TFtProgressData * prg = (TFtProgressData *)wParam;
+ for (i = 0; i < dat->wnds->realCount; ++i)
+ {
+ struct FileDlgData *trdat = (struct FileDlgData *)GetWindowLongPtr(dat->wnds->items[i]->hwnd, GWLP_USERDATA);
+ if (trdat->transferStatus.totalBytes && trdat->fs && !trdat->send && (trdat->transferStatus.totalBytes == trdat->transferStatus.totalProgress))
+ {
+ prg->scan++;
+ } else if (trdat->transferStatus.totalBytes && trdat->fs)
+ { // in progress
+ prg->run++;
+ prg->totalBytes += trdat->transferStatus.totalBytes;
+ prg->totalProgress += trdat->transferStatus.totalProgress;
+ } else if (trdat->fs)
+ { // starting
+ prg->init++;
+ }
+
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static INT_PTR CALLBACK FtMgrDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TFtMgrData *dat = (struct TFtMgrData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCITEM tci = {0};
+ HWND hwndTab = GetDlgItem(hwnd, IDC_TABS);
+
+ TranslateDialogDefault(hwnd);
+ Window_SetIcon_IcoLib(hwnd, SKINICON_EVENT_FILE);
+
+ dat = (struct TFtMgrData *)mir_calloc(sizeof(struct TFtMgrData));
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat);
+
+ dat->hhkPreshutdown = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, hwnd, M_PRESHUTDOWN);
+
+ dat->hwndIncoming = CreateDialog(hMirandaInst, MAKEINTRESOURCE(IDD_FTPAGE), hwnd, FtMgrPageDlgProc);
+ dat->hwndOutgoing = CreateDialog(hMirandaInst, MAKEINTRESOURCE(IDD_FTPAGE), hwnd, FtMgrPageDlgProc);
+ ShowWindow(dat->hwndIncoming, SW_SHOW);
+
+ tci.mask = TCIF_PARAM|TCIF_TEXT;
+ tci.pszText = TranslateT("Incoming");
+ tci.lParam = (LPARAM)dat->hwndIncoming;
+ TabCtrl_InsertItem(hwndTab, 0, &tci);
+ tci.pszText = TranslateT("Outgoing");
+ tci.lParam = (LPARAM)dat->hwndOutgoing;
+ TabCtrl_InsertItem(hwndTab, 1, &tci);
+
+ // Utils_RestoreWindowPosition(hwnd, NULL, "SRFile", "FtMgrDlg_");
+ SAVEWINDOWPOS swp;
+ swp.hwnd=hwnd; swp.hContact=NULL; swp.szModule="SRFile"; swp.szNamePrefix="FtMgrDlg_";
+ CallService(MS_UTILS_RESTOREWINDOWPOSITION, RWPF_NOACTIVATE, (LPARAM)&swp);
+
+ // Fall through to setup initial placement
+ }
+ case WM_SIZE:
+ {
+ RECT rc, rcButton;
+ HDWP hdwp;
+ HWND hwndTab = GetDlgItem(hwnd, IDC_TABS);
+
+ GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rcButton);
+ OffsetRect(&rcButton, -rcButton.left, -rcButton.top);
+
+ GetClientRect(hwnd, &rc);
+ InflateRect(&rc, -6, -6);
+
+ hdwp = BeginDeferWindowPos(3);
+
+ hdwp = DeferWindowPos(hdwp, GetDlgItem(hwnd, IDC_CLEAR), NULL, rc.left, rc.bottom-rcButton.bottom, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
+ hdwp = DeferWindowPos(hdwp, GetDlgItem(hwnd, IDCANCEL), NULL, rc.right-rcButton.right, rc.bottom-rcButton.bottom, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
+
+ rc.bottom -= rcButton.bottom + 5;
+
+ hdwp = DeferWindowPos(hdwp, hwndTab, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER);
+
+ EndDeferWindowPos(hdwp);
+
+ GetWindowRect(hwndTab, &rc);
+ MapWindowPoints(NULL, hwnd, (LPPOINT)&rc, 2);
+ TabCtrl_AdjustRect(hwndTab, FALSE, &rc);
+ InflateRect(&rc, -5, -5);
+
+ hdwp = BeginDeferWindowPos(2);
+
+ hdwp = DeferWindowPos(hdwp, dat->hwndIncoming, HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
+ hdwp = DeferWindowPos(hdwp, dat->hwndOutgoing, HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
+
+ EndDeferWindowPos(hdwp);
+
+ break;
+ }
+
+ case WM_MOUSEWHEEL:
+ {
+ if (IsWindowVisible(dat->hwndIncoming)) SendMessage(dat->hwndIncoming, msg, wParam, lParam);
+ if (IsWindowVisible(dat->hwndOutgoing)) SendMessage(dat->hwndOutgoing, msg, wParam, lParam);
+ break;
+ }
+
+ case WM_FT_SELECTPAGE:
+ {
+ TCITEM tci = {0};
+ HWND hwndTab = GetDlgItem(hwnd, IDC_TABS);
+
+ if (TabCtrl_GetCurSel(hwndTab) == (int)wParam) break;
+
+ tci.mask = TCIF_PARAM;
+
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci);
+ ShowWindow((HWND)tci.lParam, SW_HIDE);
+
+ TabCtrl_SetCurSel(hwndTab, wParam);
+
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci);
+ ShowWindow((HWND)tci.lParam, SW_SHOW);
+
+ break;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
+ lpmmi->ptMinTrackSize.x = 300;
+ lpmmi->ptMinTrackSize.y = 400;
+ return 0;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ PostMessage(hwnd, WM_CLOSE , 0, 0);
+ break;
+
+ case IDC_CLEAR:
+ PostMessage(dat->hwndIncoming, WM_FT_CLEANUP, 0, 0);
+ PostMessage(dat->hwndOutgoing, WM_FT_CLEANUP, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case IDC_TABS:
+ {
+ HWND hwndTab = GetDlgItem(hwnd, IDC_TABS);
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case TCN_SELCHANGING:
+ {
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci);
+ ShowWindow((HWND)tci.lParam, SW_HIDE);
+ break;
+ }
+
+ case TCN_SELCHANGE:
+ {
+ TCITEM tci = {0};
+ tci.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci);
+ ShowWindow((HWND)tci.lParam, SW_SHOW);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case M_PRESHUTDOWN:
+ SendMessage(dat->hwndIncoming, M_PRESHUTDOWN, 0, 0);
+ SendMessage(dat->hwndOutgoing, M_PRESHUTDOWN, 0, 0);
+ DestroyWindow(hwnd);
+ break;
+
+ case WM_CLOSE:
+ ShowWindow(hwnd, SW_HIDE);
+ if (DBGetContactSettingByte(NULL, "SRFile", "AutoClear", 1)) {
+ PostMessage(dat->hwndIncoming, WM_FT_CLEANUP, 0, 0);
+ PostMessage(dat->hwndOutgoing, WM_FT_CLEANUP, 0, 0);
+ }
+ return TRUE; /* Disable default IDCANCEL notification */
+
+ case WM_DESTROY:
+ UnhookEvent(dat->hhkPreshutdown);
+ Window_FreeIcon_IcoLib(hwnd);
+ DestroyWindow(dat->hwndIncoming);
+ DestroyWindow(dat->hwndOutgoing);
+ mir_free(dat);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ Utils_SaveWindowPosition(hwnd, NULL, "SRFile", "FtMgrDlg_");
+ break;
+
+ case WM_ACTIVATE:
+ {
+ dat->errorState = TBPF_NOPROGRESS;
+ wParam = 1;
+ } break;
+ case WM_SHOWWINDOW:
+ {
+ if (!wParam) // hiding
+ {
+ KillTimer(hwnd, 1);
+ break;
+ }
+ lParam = 0;
+ }
+ case WM_TIMER:
+ {
+ if (pTaskbarInterface)
+ {
+ SetTimer(hwnd, 1, 400, NULL);
+ if ((lParam == ACKRESULT_FAILED) || (lParam == ACKRESULT_DENIED))
+ dat->errorState = TBPF_ERROR;
+
+ TFtProgressData prg = {0};
+ SendMessage(dat->hwndIncoming, M_CALCPROGRESS, (WPARAM)&prg, 0);
+ SendMessage(dat->hwndOutgoing, M_CALCPROGRESS, (WPARAM)&prg, 0);
+ if (dat->errorState)
+ {
+ pTaskbarInterface->SetProgressState(hwnd, dat->errorState);
+ if (!prg.run)
+ pTaskbarInterface->SetProgressValue(hwnd, 1, 1);
+ } else if (prg.run)
+ {
+ pTaskbarInterface->SetProgressState(hwnd, TBPF_NORMAL);
+ } else if (prg.init || prg.scan)
+ {
+ pTaskbarInterface->SetProgressState(hwnd, TBPF_INDETERMINATE);
+ } else {
+ pTaskbarInterface->SetProgressState(hwnd, TBPF_NOPROGRESS);
+ KillTimer(hwnd, 1);
+ }
+
+ if (prg.run)
+ {
+ pTaskbarInterface->SetProgressValue(hwnd, prg.totalProgress, prg.totalBytes);
+ }
+
+ }
+ } break;
+
+ }
+
+ return FALSE;
+}
+
+HWND FtMgr_Show(bool bForceActivate, bool bFromMenu)
+{
+ bool bAutoMin = DBGetContactSettingByte(NULL,"SRFile","AutoMin",0) != 0; /* lqbe */
+
+ bool bJustCreated = (hwndFtMgr == NULL);
+ if (bJustCreated)
+ {
+ hwndFtMgr = CreateDialog(hMirandaInst, MAKEINTRESOURCE(IDD_FTMGR), NULL, FtMgrDlgProc);
+ }
+ if (bFromMenu) /* lqbe */
+ {
+ ShowWindow(hwndFtMgr, SW_RESTORE);
+ ShowWindow(hwndFtMgr, SW_SHOW);
+ SetForegroundWindow(hwndFtMgr);
+ return hwndFtMgr;
+ }
+ else if (bAutoMin && bJustCreated) /* lqbe */
+ {
+ ShowWindow(hwndFtMgr, SW_HIDE);
+ ShowWindow(hwndFtMgr, SW_MINIMIZE);
+ return hwndFtMgr;
+ }
+ else if (bForceActivate) /* lqbe */
+ {
+ ShowWindow(hwndFtMgr, SW_RESTORE);
+ ShowWindow(hwndFtMgr, SW_SHOWNOACTIVATE);
+ SetForegroundWindow(hwndFtMgr);
+ return hwndFtMgr;
+ }
+ if (!bJustCreated && IsWindowVisible(hwndFtMgr))
+ return hwndFtMgr;
+
+ ShowWindow(hwndFtMgr, bAutoMin ? SW_SHOWMINNOACTIVE : SW_SHOWNOACTIVATE);
+ return hwndFtMgr;
+ }
+
+void FtMgr_Destroy()
+{
+ if (hwndFtMgr)
+ DestroyWindow(hwndFtMgr);
+}
+
+void FtMgr_ShowPage(int page)
+{
+ if (hwndFtMgr)
+ SendMessage(hwndFtMgr, WM_FT_SELECTPAGE, page, 0);
+}
+
+HWND FtMgr_AddTransfer(FileDlgData *fdd)
+{
+ bool bForceActivate = fdd->send || !DBGetContactSettingByte(NULL, "SRFile", "AutoAccept", 0);
+ TFtMgrData *dat = (TFtMgrData*)GetWindowLongPtr(FtMgr_Show(bForceActivate, false), GWLP_USERDATA);
+ if (dat == NULL) return NULL;
+ HWND hwndBox = fdd->send ? dat->hwndOutgoing : dat->hwndIncoming;
+ HWND hwndFt = CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_FILETRANSFERINFO), hwndBox, DlgProcFileTransfer, (LPARAM)fdd);
+ ShowWindow(hwndFt, SW_SHOWNA);
+ SendMessage(hwndBox, WM_FT_ADD, 0, (LPARAM)hwndFt);
+ FtMgr_ShowPage(fdd->send ? 1 : 0);
+ return hwndFt;
+} \ No newline at end of file
diff --git a/src/modules/srurl/url.cpp b/src/modules/srurl/url.cpp
new file mode 100644
index 0000000000..a6dfb9141f
--- /dev/null
+++ b/src/modules/srurl/url.cpp
@@ -0,0 +1,182 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <m_url.h>
+#include "url.h"
+
+HANDLE hUrlWindowList = NULL;
+static HANDLE hEventContactSettingChange = NULL;
+static HANDLE hContactDeleted = NULL;
+static HANDLE hSRUrlMenuItem = NULL;
+
+INT_PTR CALLBACK DlgProcUrlSend(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK DlgProcUrlRecv(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static INT_PTR ReadUrlCommand(WPARAM, LPARAM lParam)
+{
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_URLRECV),NULL,DlgProcUrlRecv,lParam);
+ return 0;
+}
+
+static int UrlEventAdded(WPARAM wParam,LPARAM lParam)
+{
+ CLISTEVENT cle;
+ DBEVENTINFO dbei;
+ TCHAR szTooltip[256];
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=0;
+ CallService(MS_DB_EVENT_GET,lParam,(LPARAM)&dbei);
+ if(dbei.flags&(DBEF_SENT|DBEF_READ) || dbei.eventType!=EVENTTYPE_URL) return 0;
+
+ SkinPlaySound("RecvUrl");
+ ZeroMemory(&cle,sizeof(cle));
+ cle.cbSize=sizeof(cle);
+ cle.flags = CLEF_TCHAR;
+ cle.hContact=(HANDLE)wParam;
+ cle.hDbEvent=(HANDLE)lParam;
+ cle.hIcon = LoadSkinIcon( SKINICON_EVENT_URL );
+ cle.pszService="SRUrl/ReadUrl";
+ mir_sntprintf(szTooltip,SIZEOF(szTooltip),TranslateT("URL from %s"), cli.pfnGetContactDisplayName(( HANDLE )wParam, 0 ));
+ cle.ptszTooltip=szTooltip;
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&cle);
+ return 0;
+}
+
+static INT_PTR SendUrlCommand(WPARAM wParam, LPARAM)
+{
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_URLSEND),NULL,DlgProcUrlSend,wParam);
+ return 0;
+}
+
+static void RestoreUnreadUrlAlerts(void)
+{
+ CLISTEVENT cle={0};
+ DBEVENTINFO dbei={0};
+ TCHAR toolTip[256];
+ HANDLE hDbEvent,hContact;
+
+ dbei.cbSize=sizeof(dbei);
+ cle.cbSize=sizeof(cle);
+ cle.hIcon = LoadSkinIcon( SKINICON_EVENT_URL );
+ cle.pszService="SRUrl/ReadUrl";
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while(hContact) {
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRSTUNREAD,(WPARAM)hContact,0);
+ while(hDbEvent) {
+ dbei.cbBlob=0;
+ CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei);
+ if(!(dbei.flags&(DBEF_SENT|DBEF_READ)) && dbei.eventType==EVENTTYPE_URL) {
+ cle.hContact=hContact;
+ cle.hDbEvent=hDbEvent;
+ cle.flags = CLEF_TCHAR;
+ mir_sntprintf(toolTip,SIZEOF(toolTip),TranslateT("URL from %s"), cli.pfnGetContactDisplayName( hContact, 0 ));
+ cle.ptszTooltip=toolTip;
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&cle);
+ }
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT,(WPARAM)hDbEvent,0);
+ }
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
+ }
+}
+
+static int ContactSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam;
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ if(lstrcmpA(cws->szModule,"CList") && (szProto==NULL || lstrcmpA(cws->szModule,szProto))) return 0;
+ WindowList_Broadcast(hUrlWindowList,DM_UPDATETITLE,0,0);
+ return 0;
+}
+
+static int SRUrlPreBuildMenu(WPARAM wParam, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+
+ char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if ( szProto != NULL )
+ if ( CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_URLSEND )
+ mi.flags = CMIM_FLAGS;
+
+ CallService( MS_CLIST_MODIFYMENUITEM, (WPARAM)hSRUrlMenuItem, (LPARAM)&mi );
+ return 0;
+}
+
+static int SRUrlModulesLoaded(WPARAM, LPARAM)
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000040000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_EVENT_URL );
+ mi.pszName = LPGEN("Web Page Address (&URL)");
+ mi.pszService = MS_URL_SENDURL;
+ hSRUrlMenuItem = (HANDLE)CallService( MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi );
+
+ RestoreUnreadUrlAlerts();
+ return 0;
+}
+
+static int SRUrlShutdown(WPARAM, LPARAM)
+{
+ if ( hEventContactSettingChange )
+ UnhookEvent( hEventContactSettingChange );
+
+ if ( hContactDeleted )
+ UnhookEvent( hContactDeleted );
+
+ if ( hUrlWindowList )
+ WindowList_BroadcastAsync( hUrlWindowList, WM_CLOSE, 0, 0 );
+
+ return 0;
+}
+
+int UrlContactDeleted(WPARAM wParam, LPARAM)
+{
+ HWND h = WindowList_Find(hUrlWindowList,(HANDLE)wParam);
+ if ( h )
+ SendMessage(h,WM_CLOSE,0,0);
+
+ return 0;
+}
+
+int LoadSendRecvUrlModule(void)
+{
+ hUrlWindowList=(HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST,0,0);
+ HookEvent(ME_SYSTEM_MODULESLOADED,SRUrlModulesLoaded);
+ HookEvent(ME_DB_EVENT_ADDED,UrlEventAdded);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU,SRUrlPreBuildMenu);
+ hEventContactSettingChange = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChanged);
+ hContactDeleted = HookEvent(ME_DB_CONTACT_DELETED, UrlContactDeleted);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,SRUrlShutdown);
+ CreateServiceFunction(MS_URL_SENDURL,SendUrlCommand);
+ CreateServiceFunction("SRUrl/ReadUrl",ReadUrlCommand);
+ SkinAddNewSoundEx("RecvUrl","URL","Incoming");
+ return 0;
+}
diff --git a/src/modules/srurl/url.h b/src/modules/srurl/url.h
new file mode 100644
index 0000000000..98c5566f53
--- /dev/null
+++ b/src/modules/srurl/url.h
@@ -0,0 +1,41 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define TIMEOUT_URLSEND 9000
+#define HM_EVENTSENT (WM_USER+10)
+#define DM_UPDATETITLE (WM_USER+11)
+
+#define DDEMESSAGETIMEOUT 1000
+
+
+struct UrlRcvData {
+ HANDLE hContact;
+ HANDLE hDbEvent;
+};
+
+struct UrlSendData {
+ HANDLE hContact;
+ HANDLE hSendId;
+ HANDLE hAckEvent;
+ char *sendBuffer;
+};
diff --git a/src/modules/srurl/urldialogs.cpp b/src/modules/srurl/urldialogs.cpp
new file mode 100644
index 0000000000..294e3be4da
--- /dev/null
+++ b/src/modules/srurl/urldialogs.cpp
@@ -0,0 +1,664 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "url.h"
+
+INT_PTR CALLBACK DlgProcUrlSend(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+extern HANDLE hUrlWindowList;
+
+static void sttUpdateTitle( HWND hwndDlg, HANDLE hContact )
+{
+ TCHAR newtitle[256],oldtitle[256];
+ TCHAR *szStatus, *contactName, *pszNewTitleStart = TranslateT("Send URL to");
+ char *szProto;
+
+ if ( hContact ) {
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if ( szProto ) {
+ CONTACTINFO ci;
+ int hasName = 0;
+ char buf[128];
+ ZeroMemory(&ci,sizeof(ci));
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ ci.szProto = szProto;
+ ci.dwFlag = CNF_UNIQUEID;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) {
+ switch(ci.type) {
+ case CNFT_ASCIIZ:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf), "%s", ci.pszVal);
+ mir_free(ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ hasName = 1;
+ mir_snprintf(buf, SIZEOF(buf),"%u",ci.dVal);
+ break;
+ } }
+
+ contactName = cli.pfnGetContactDisplayName( hContact, 0 );
+ if ( hasName )
+ SetDlgItemTextA( hwndDlg, IDC_NAME, buf );
+ else
+ SetDlgItemText( hwndDlg, IDC_NAME, contactName );
+
+ szStatus = cli.pfnGetStatusModeDescription( szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord( hContact, szProto, "Status", ID_STATUS_OFFLINE ), 0 );
+ mir_sntprintf( newtitle, SIZEOF(newtitle), _T("%s %s (%s)"), pszNewTitleStart, contactName, szStatus);
+ }
+ }
+ else lstrcpyn( newtitle, pszNewTitleStart, SIZEOF(newtitle));
+
+ GetWindowText( hwndDlg, oldtitle, SIZEOF(oldtitle));
+
+ if ( lstrcmp( newtitle, oldtitle )) //swt() flickers even if the title hasn't actually changed
+ SetWindowText( hwndDlg, newtitle );
+}
+
+INT_PTR CALLBACK DlgProcUrlRecv(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct UrlRcvData *dat = (struct UrlRcvData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_EVENT_URL);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact Permanently to List"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User's Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_HISTORY, SKINICON_OTHER_HISTORY, LPGEN("View User's History"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_USERMENU, SKINICON_OTHER_DOWNARROW, LPGEN("User Menu"));
+
+ dat=(struct UrlRcvData*)mir_alloc(sizeof(struct UrlRcvData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ dat->hContact = ((CLISTEVENT*)lParam)->hContact;
+ dat->hDbEvent = ((CLISTEVENT*)lParam)->hDbEvent;
+
+ WindowList_Add(hUrlWindowList, hwndDlg, dat->hContact);
+ {
+ DBEVENTINFO dbei;
+ TCHAR* contactName;
+ TCHAR msg[128];
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.cbBlob=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)dat->hDbEvent,0);
+ dbei.pBlob=(PBYTE)mir_alloc(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET,(WPARAM)dat->hDbEvent,(LPARAM)&dbei);
+ SetDlgItemTextA(hwndDlg,IDC_URL,(char*)dbei.pBlob);
+ SetDlgItemTextA(hwndDlg,IDC_MSG,(char*)dbei.pBlob+lstrlenA((char*)dbei.pBlob)+1);
+ mir_free(dbei.pBlob);
+
+ CallService(MS_DB_EVENT_MARKREAD,(WPARAM)dat->hContact,(LPARAM)dat->hDbEvent);
+
+ contactName = cli.pfnGetContactDisplayName( dat->hContact, 0 );
+ mir_sntprintf(msg,SIZEOF(msg),TranslateT("URL from %s"),contactName);
+ SetWindowText(hwndDlg,msg);
+ SetDlgItemText(hwndDlg,IDC_FROM,contactName);
+ SendDlgItemMessage(hwndDlg,IDOK,BUTTONSETARROW,1,0);
+ { TCHAR str[128];
+ tmi.printTimeStamp(NULL, dbei.timestamp, _T("t d"), str, SIZEOF(str), 0);
+ SetDlgItemText(hwndDlg, IDC_DATE, str);
+ } }
+
+ // From message dlg
+ if (!DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE);
+
+ SendMessage(hwndDlg,DM_UPDATETITLE,0,0);
+ // From message dlg end
+
+ Utils_RestoreWindowPositionNoSize(hwndDlg,NULL,"SRUrl","recv");
+ return TRUE;
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis=(LPDRAWITEMSTRUCT)lParam;
+ if(dis->hwndItem==GetDlgItem(hwndDlg, IDC_PROTOCOL)) {
+ char *szProto;
+
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if (szProto) {
+ HICON hIcon;
+
+ hIcon = ( HICON )CallProtoService(szProto,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ if (hIcon) {
+ DrawIconEx(dis->hDC,dis->rcItem.left,dis->rcItem.top,hIcon,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL);
+ DestroyIcon( hIcon );
+ } } } }
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ case DM_UPDATETITLE:
+ sttUpdateTitle( hwndDlg, dat->hContact );
+ break;
+
+ case WM_COMMAND:
+ if (dat)
+ if(CallService(MS_CLIST_MENUPROCESSCOMMAND,MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),(LPARAM)dat->hContact))
+ break;
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ { HMENU hMenu,hSubMenu;
+ RECT rc;
+ char url[256];
+
+ hMenu=LoadMenu(hMirandaInst,MAKEINTRESOURCE(IDR_CONTEXT));
+ hSubMenu=GetSubMenu(hMenu,6);
+ CallService(MS_LANGPACK_TRANSLATEMENU,(WPARAM)hSubMenu,0);
+ GetWindowRect((HWND)lParam, &rc);
+ GetDlgItemTextA(hwndDlg, IDC_URL, url, SIZEOF(url));
+ switch(TrackPopupMenu(hSubMenu,TPM_RETURNCMD,rc.left,rc.bottom,0,hwndDlg,NULL)) {
+ case IDM_OPENNEW:
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)url);
+ break;
+ case IDM_OPENEXISTING:
+ CallService(MS_UTILS_OPENURL,0,(LPARAM)url);
+ break;
+ case IDM_COPYLINK:
+ { HGLOBAL hData;
+ if(!OpenClipboard(hwndDlg)) break;
+ EmptyClipboard();
+ hData=GlobalAlloc(GMEM_MOVEABLE,lstrlenA(url)+1);
+ lstrcpyA((char*)GlobalLock(hData),url);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_TEXT,hData);
+ CloseClipboard();
+ break;
+ }
+ }
+ DestroyMenu(hMenu);
+ }
+ return TRUE;
+
+ case IDC_USERMENU:
+ {
+ RECT rc;
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)dat->hContact,0);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_USERMENU),&rc);
+ TrackPopupMenu(hMenu,0,rc.left,rc.bottom,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ }
+ break;
+
+ case IDC_HISTORY:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY,(WPARAM)dat->hContact,0);
+ break;
+
+ case IDC_DETAILS:
+ CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)dat->hContact,0);
+ break;
+
+ case IDC_ADD:
+ {
+ ADDCONTACTSTRUCT acs={0};
+
+ acs.handle=dat->hContact;
+ acs.handleType=HANDLE_CONTACT;
+ acs.szProto=0;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)hwndDlg,(LPARAM)&acs);
+ }
+ if (!DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0)) {
+ ShowWindow(GetDlgItem(hwndDlg,IDC_ADD),FALSE);
+ }
+ break;
+
+ case IDC_REPLY:
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)dat->hContact,0);
+ //fall through
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_ADD);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_HISTORY);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_USERMENU);
+
+ WindowList_Remove(hUrlWindowList, hwndDlg);
+ mir_free(dat);
+ Utils_SaveWindowPosition(hwndDlg,NULL,"SRUrl","recv");
+ break;
+ }
+ return FALSE;
+}
+
+static int ddeAcked,ddeData;
+static ATOM hSzDdeData;
+static HWND hwndDde;
+static HGLOBAL hGlobalDdeData;
+static LRESULT DdeMessage(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ UINT_PTR hSzItem;
+
+ switch(msg) {
+ case WM_DDE_ACK:
+ ddeAcked=1;
+ hwndDde=(HWND)wParam;
+ break;
+
+ case WM_DDE_DATA:
+ UnpackDDElParam(msg,lParam,(PUINT_PTR)&hGlobalDdeData,(PUINT_PTR)&hSzItem);
+ ddeData = 1;
+ if( hGlobalDdeData ) {
+ DDEDATA* data = ( DDEDATA* )GlobalLock( hGlobalDdeData );
+ if ( data->fAckReq ) {
+ DDEACK ack = {0};
+ PostMessage(( HWND )wParam, WM_DDE_ACK, ( WPARAM )hwndDlg, PackDDElParam( WM_DDE_ACK, *(PUINT)&ack, hSzItem ));
+ }
+ else GlobalDeleteAtom(( ATOM )hSzItem );
+ GlobalUnlock( hGlobalDdeData );
+ }
+ else GlobalDeleteAtom(( ATOM )hSzItem );
+ break;
+ }
+ return 0;
+}
+
+static HGLOBAL DoDdeRequest(const char *szItemName,HWND hwndDlg)
+{
+ ATOM hSzItemName;
+ DWORD timeoutTick,thisTick;
+ MSG msg;
+
+ hSzItemName=GlobalAddAtomA(szItemName);
+ if(!PostMessage(hwndDde,WM_DDE_REQUEST,(WPARAM)hwndDlg,MAKELPARAM(CF_TEXT,hSzItemName))) {
+ GlobalDeleteAtom(hSzItemName);
+ return NULL;
+ }
+ timeoutTick=GetTickCount()+5000;
+ ddeData=0; ddeAcked=0;
+ do {
+ if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ if(ddeData || ddeAcked) break;
+ thisTick=GetTickCount();
+ if(thisTick>timeoutTick) break;
+ }
+ while(MsgWaitForMultipleObjects(0,NULL,FALSE,timeoutTick-thisTick,QS_ALLINPUT)==WAIT_OBJECT_0);
+
+ if(!ddeData) {
+ GlobalDeleteAtom(hSzItemName);
+ return NULL;
+ }
+
+ return hGlobalDdeData;
+}
+
+static void FreeDdeRequestData(HGLOBAL hData)
+{
+ DDEDATA *data;
+ data=(DDEDATA*)GlobalLock(hData);
+ if(data->fRelease) {
+ GlobalUnlock(hData);
+ GlobalFree(hData);
+ }
+ else GlobalUnlock(hData);
+}
+
+static void AddBrowserPageToCombo(char *url,HWND hwndCombo)
+{
+ char *title,*frame,*end;
+
+ if(url[0]!='"') return;
+ url++;
+ title=strchr(url,'"');
+ if(title==NULL) return;
+ *title='\0'; title++;
+ if(*title) {
+ title+=2;
+ frame=strchr(title,'"');
+ if(frame==NULL) return;
+ *frame='\0'; frame++;
+ if(*frame) {
+ frame+=2;
+ end=strchr(frame,'"');
+ if(end==NULL) return;
+ *end='\0';
+ }
+ else frame=NULL;
+ }
+ else title=frame=NULL;
+ if(frame==NULL || *frame==0) {
+ char *szItemData;
+ int i;
+ char szExistingUrl[1024];
+
+ for(i=SendMessage(hwndCombo,CB_GETCOUNT,0,0)-1;i>=0;i--) {
+ if(SendMessage(hwndCombo,CB_GETLBTEXTLEN,i,0) >= SIZEOF(szExistingUrl))
+ continue;
+ SendMessageA(hwndCombo,CB_GETLBTEXT,i,(LPARAM)szExistingUrl);
+ if(!lstrcmpA(szExistingUrl,url)) return;
+ }
+ i=SendMessageA(hwndCombo,CB_ADDSTRING,0,(LPARAM)url);
+ szItemData=mir_strdup(title);
+ SendMessage(hwndCombo,CB_SETITEMDATA,i,(LPARAM)szItemData);
+ }
+}
+
+//see Q160957 and http://developer.netscape.com/docs/manuals/communicator/DDE/index.htm
+static void GetOpenBrowserUrlsForBrowser(const char *szBrowser,HWND hwndDlg,HWND hwndCombo)
+{
+ ATOM hSzBrowser,hSzTopic;
+ int windowCount,i;
+ DWORD *windowId;
+ DWORD dwResult;
+ HGLOBAL hData;
+ DDEDATA *data;
+ int dataLength;
+
+ hSzBrowser=GlobalAddAtomA(szBrowser);
+
+ hSzTopic=GlobalAddAtomA("WWW_ListWindows");
+ ddeAcked=0;
+ if(!SendMessageTimeout(HWND_BROADCAST,WM_DDE_INITIATE,(WPARAM)hwndDlg,MAKELPARAM(hSzBrowser,hSzTopic),SMTO_ABORTIFHUNG|SMTO_NORMAL,DDEMESSAGETIMEOUT,(PDWORD_PTR)&dwResult)
+ || !ddeAcked) {
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ return;
+ }
+ hData=DoDdeRequest("WWW_ListWindows",hwndDlg);
+ if(hData==NULL) {
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ return;
+ }
+ dataLength=GlobalSize(hData)-offsetof(DDEDATA,Value);
+ data=(DDEDATA*)GlobalLock(hData);
+ windowCount=dataLength/sizeof(DWORD);
+ windowId=(PDWORD)mir_alloc(sizeof(DWORD)*windowCount);
+ memcpy(windowId,data->Value,windowCount*sizeof(DWORD));
+ GlobalUnlock(hData);
+ FreeDdeRequestData(hData);
+ PostMessage(hwndDde,WM_DDE_TERMINATE,(WPARAM)hwndDlg,0);
+ GlobalDeleteAtom(hSzTopic);
+
+ hSzTopic=GlobalAddAtomA("WWW_GetWindowInfo");
+ ddeAcked=0;
+ if(!SendMessageTimeout(HWND_BROADCAST,WM_DDE_INITIATE,(WPARAM)hwndDlg,MAKELPARAM(hSzBrowser,hSzTopic),SMTO_ABORTIFHUNG|SMTO_NORMAL,DDEMESSAGETIMEOUT,(PDWORD_PTR)&dwResult)
+ || !ddeAcked) {
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ mir_free(windowId);
+ return;
+ }
+ for(i=0;i<windowCount;i++) {
+ if(windowId[i]==0) break;
+ { char str[16];
+ mir_snprintf(str, SIZEOF(str), "%d",windowId[i]);
+ hData=DoDdeRequest(str,hwndDlg);
+ }
+ if(hData!=NULL) {
+ dataLength=GlobalSize(hData)-offsetof(DDEDATA,Value);
+ data=(DDEDATA*)GlobalLock(hData);
+ AddBrowserPageToCombo((char*)data->Value,hwndCombo);
+ GlobalUnlock(hData);
+ FreeDdeRequestData(hData);
+ }
+ }
+ PostMessage(hwndDde,WM_DDE_TERMINATE,(WPARAM)hwndDlg,0);
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ mir_free(windowId);
+}
+
+static void GetOpenBrowserUrls(HWND hwndDlg,HWND hwndCombo)
+{
+ GetOpenBrowserUrlsForBrowser("opera",hwndDlg,hwndCombo);
+ GetOpenBrowserUrlsForBrowser("netscape",hwndDlg,hwndCombo);
+ GetOpenBrowserUrlsForBrowser("iexplore",hwndDlg,hwndCombo);
+}
+
+static WNDPROC OldSendEditProc;
+static LRESULT CALLBACK SendEditSubclassProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg) {
+ case WM_CHAR:
+ if(wParam=='\n' && GetKeyState(VK_CONTROL)&0x8000) {
+ PostMessage(GetParent(hwnd),WM_COMMAND,IDOK,0);
+ return 0;
+ }
+ break;
+ case WM_SYSCHAR:
+ if((wParam=='s' || wParam=='S') && GetKeyState(VK_MENU)&0x8000) {
+ PostMessage(GetParent(hwnd),WM_COMMAND,IDOK,0);
+ return 0;
+ }
+ break;
+ }
+ return CallWindowProc(OldSendEditProc,hwnd,msg,wParam,lParam);
+}
+
+INT_PTR CALLBACK DlgProcUrlSend(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct UrlSendData* dat = (struct UrlSendData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_EVENT_URL);
+ Button_SetIcon_IcoLib(hwndDlg, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact Permanently to List"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View User's Details"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_HISTORY, SKINICON_OTHER_HISTORY, LPGEN("View User's History"));
+ Button_SetIcon_IcoLib(hwndDlg, IDC_USERMENU, SKINICON_OTHER_DOWNARROW, LPGEN("User Menu"));
+
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_LIMITTEXT, 450, 0);
+ dat=(struct UrlSendData*)mir_alloc(sizeof(struct UrlSendData));
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)dat);
+ dat->hContact=(HANDLE)lParam;
+ dat->hAckEvent=NULL;
+ dat->hSendId=NULL;
+ dat->sendBuffer=NULL;
+
+ WindowList_Add(hUrlWindowList, hwndDlg, dat->hContact);
+ {
+ TCHAR *str = cli.pfnGetContactDisplayName( dat->hContact, 0 );
+ SetDlgItemText(hwndDlg,IDC_NAME,str);
+ }
+
+ GetOpenBrowserUrls(hwndDlg,GetDlgItem(hwndDlg,IDC_URLS));
+ SendDlgItemMessage(hwndDlg, IDC_URLS, CB_SETCURSEL, 0, 0);
+ if (SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETCOUNT,0,0))SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_URLS,CBN_SELCHANGE),0);
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), (SendDlgItemMessage(hwndDlg, IDC_URLS, CB_GETCURSEL, 0, 0) == CB_ERR)?FALSE:TRUE);
+ Utils_RestoreWindowPositionNoSize(hwndDlg,NULL,"SRUrl","send");
+ OldSendEditProc=(WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MESSAGE),GWLP_WNDPROC,(LONG_PTR)SendEditSubclassProc);
+ OldSendEditProc=(WNDPROC)SetWindowLongPtr(GetWindow(GetDlgItem(hwndDlg,IDC_URLS),GW_CHILD),GWLP_WNDPROC,(LONG_PTR)SendEditSubclassProc);
+
+ // From message dlg
+ if (!DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE);
+
+ SendMessage(hwndDlg,DM_UPDATETITLE,0,0);
+ // From message dlg end
+ return TRUE;
+
+ case WM_DDE_DATA:
+ case WM_DDE_ACK:
+ return DdeMessage(hwndDlg,msg,wParam,lParam);
+
+ case WM_TIMER:
+ if ( wParam == 0 ) {
+ //ICQ sendurl timed out
+ KillTimer(hwndDlg,0);
+ MessageBox(hwndDlg,TranslateT("Send timed out"),_T(""),MB_OK);
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_URLS),TRUE);
+ SendDlgItemMessage(hwndDlg,IDC_MESSAGE,EM_SETREADONLY,FALSE,0);
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = ( LPDRAWITEMSTRUCT )lParam;
+ if ( dis->hwndItem == GetDlgItem(hwndDlg, IDC_PROTOCOL)) {
+ char *szProto = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if (szProto) {
+ HICON hIcon = (HICON)CallProtoService(szProto,PS_LOADICON,PLI_PROTOCOL|PLIF_SMALL,0);
+ if ( hIcon ) {
+ DrawIconEx(dis->hDC,dis->rcItem.left,dis->rcItem.top,hIcon,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL);
+ DestroyIcon(hIcon);
+ } } } }
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ case DM_UPDATETITLE:
+ sttUpdateTitle( hwndDlg, dat->hContact );
+ break;
+
+ case WM_COMMAND:
+ if(CallService(MS_CLIST_MENUPROCESSCOMMAND,MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),(LPARAM)dat->hContact))
+ break;
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ char *body,*url;
+ int bodySize,urlSize;
+
+ urlSize=GetWindowTextLength(GetDlgItem(hwndDlg,IDC_URLS))+1;
+ url=(char*)mir_alloc(urlSize);
+ GetDlgItemTextA(hwndDlg,IDC_URLS,url,urlSize);
+ if (url[0] == 0) {
+ mir_free(url);
+ break;
+ }
+ bodySize=GetWindowTextLength(GetDlgItem(hwndDlg,IDC_MESSAGE))+1;
+ body=(char*)mir_alloc(bodySize);
+ GetDlgItemTextA(hwndDlg,IDC_MESSAGE,body,bodySize);
+
+ dat->sendBuffer=(char*)mir_realloc(dat->sendBuffer,lstrlenA(url)+lstrlenA(body)+2);
+ lstrcpyA(dat->sendBuffer,url);
+ lstrcpyA(dat->sendBuffer+lstrlenA(url)+1,body);
+ dat->hAckEvent=HookEventMessage(ME_PROTO_ACK,hwndDlg,HM_EVENTSENT);
+ dat->hSendId=(HANDLE)CallContactService(dat->hContact,PSS_URL,0,(LPARAM)dat->sendBuffer);
+ mir_free(url);
+ mir_free(body);
+
+ //create a timeout timer
+ SetTimer(hwndDlg,0,TIMEOUT_URLSEND,NULL);
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_URLS),FALSE);
+ SendDlgItemMessage(hwndDlg,IDC_MESSAGE,EM_SETREADONLY,TRUE,0);
+
+ return TRUE;
+ }
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ case IDC_URLS:
+ if(HIWORD(wParam)==CBN_SELCHANGE) {
+ int i, urlSize;
+ char *title;
+ i=SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETCURSEL,0,0);
+ title=(char*)SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETITEMDATA,(WPARAM)i,0);
+ SetDlgItemTextA(hwndDlg,IDC_MESSAGE,title);
+ urlSize=SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETLBTEXTLEN,(WPARAM)i,0);
+ EnableWindow(GetDlgItem(hwndDlg,IDOK), (urlSize>0));
+ }
+ else if(HIWORD(wParam)==CBN_EDITCHANGE) {
+ int urlSize = GetWindowTextLength(GetDlgItem(hwndDlg,IDC_URLS));
+ EnableWindow(GetDlgItem(hwndDlg,IDOK), (urlSize>0));
+ }
+ break;
+ case IDC_USERMENU:
+ { RECT rc;
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)dat->hContact,0);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_USERMENU),&rc);
+ TrackPopupMenu(hMenu,0,rc.left,rc.bottom,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ }
+ break;
+ case IDC_HISTORY:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY,(WPARAM)dat->hContact,0);
+ break;
+ case IDC_DETAILS:
+ CallService(MS_USERINFO_SHOWDIALOG,(WPARAM)dat->hContact,0);
+ break;
+ case IDC_ADD:
+ { ADDCONTACTSTRUCT acs={0};
+
+ acs.handle=dat->hContact;
+ acs.handleType=HANDLE_CONTACT;
+ acs.szProto=0;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)hwndDlg,(LPARAM)&acs);
+ }
+ if (!DBGetContactSettingByte(dat->hContact,"CList","NotOnList",0)) {
+ ShowWindow(GetDlgItem(hwndDlg,IDC_ADD),FALSE);
+ }
+ break;
+ }
+ break;
+ case HM_EVENTSENT:
+ { ACKDATA *ack=(ACKDATA*)lParam;
+ DBEVENTINFO dbei;
+ if(ack->hProcess!=dat->hSendId) break;
+ if(ack->hContact!=dat->hContact) break;
+ if(ack->type!=ACKTYPE_URL || ack->result!=ACKRESULT_SUCCESS) break;
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.eventType=EVENTTYPE_URL;
+ dbei.flags=DBEF_SENT;
+ dbei.szModule=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ dbei.timestamp=time(NULL);
+ dbei.cbBlob=(DWORD)(strlen(dat->sendBuffer)+strlen(dat->sendBuffer+strlen(dat->sendBuffer)+1)+2);
+ dbei.pBlob=(PBYTE)dat->sendBuffer;
+ CallService(MS_DB_EVENT_ADD,(WPARAM)dat->hContact,(LPARAM)&dbei);
+ KillTimer(hwndDlg,0);
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_ADD);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_DETAILS);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_HISTORY);
+ Button_FreeIcon_IcoLib(hwndDlg,IDC_USERMENU);
+
+ WindowList_Remove(hUrlWindowList, hwndDlg);
+ SetWindowLongPtr(GetWindow(GetDlgItem(hwndDlg,IDC_URLS),GW_CHILD),GWLP_WNDPROC,(LONG_PTR)OldSendEditProc);
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_MESSAGE),GWLP_WNDPROC,(LONG_PTR)OldSendEditProc);
+ if(dat->hAckEvent) UnhookEvent(dat->hAckEvent);
+ if(dat->sendBuffer!=NULL) mir_free(dat->sendBuffer);
+ mir_free(dat);
+ Utils_SaveWindowPosition(hwndDlg,NULL,"SRUrl","send");
+ { int i;
+ for(i=SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETCOUNT,0,0)-1;i>=0;i--)
+ mir_free((char*)SendDlgItemMessage(hwndDlg,IDC_URLS,CB_GETITEMDATA,i,0));
+ }
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/src/modules/updatenotify/updatenotify.cpp b/src/modules/updatenotify/updatenotify.cpp
new file mode 100644
index 0000000000..ef955ba3c5
--- /dev/null
+++ b/src/modules/updatenotify/updatenotify.cpp
@@ -0,0 +1,680 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+#define UN_MOD "UpdateNotify"
+#define UN_ENABLE "UpdateNotifyEnable"
+#define UN_ENABLE_DEF 1
+#define UN_LASTCHECK "UpdateNotifyLastCheck"
+#define UN_SERVERPERIOD "UpdateNotifyPingDelayPeriod"
+#define UN_CURRENTVERSION "UpdateNotifyCurrentVersion"
+#define UN_CURRENTVERSIONFND "UpdateNotifyCurrentVersionFound"
+#define UN_NOTIFYTYPE "UpdateNotifyType"
+#define UN_NOTIFYTYPE_STABLE 1
+#define UN_NOTIFYTYPE_BETA 2
+#define UN_NOTIFYTYPE_ALPHA 3
+#define UN_NOTIFYTYPE_DEF UN_NOTIFYTYPE_STABLE
+#define UN_CUSTOMXMLURL "UpdateNotifyCustomXMLURL"
+#define UN_URLXML "http://update.miranda-im.org/update.xml"
+#define UN_MINCHECKTIME 60*60 /* Check no more than once an hour */
+#define UN_DEFAULTCHECKTIME 60*48*60 /* Default to check once every 48 hours */
+#define UN_FIRSTCHECK 15 /* First check 15 seconds after startup */
+#define UN_REPEATNOTIFYDLY 24*60*60 /* 24 hours before showing release notification again */
+
+typedef struct {
+ int isNew;
+ int isManual;
+ char version[64];
+ char versionReal[16];
+ char notesUrl[256];
+ char downloadUrl[256];
+ DWORD reqTime;
+} UpdateNotifyData;
+
+typedef struct {
+ DWORD dwVersion;
+ char *szVersionPublic;
+ char *szVersion;
+ char *szDownload;
+ char *szNotes;
+} UpdateNotifyReleaseData;
+
+static BOOL bModuleInitialized = FALSE;
+static HANDLE hNetlibUser = 0, hHookModules, hHookPreShutdown;
+static UINT_PTR updateTimerId;
+static HANDLE dwUpdateThreadID = 0;
+static HWND hwndUpdateDlg = 0, hwndManualUpdateDlg = 0;
+static XML_API xun;
+
+static int UpdateNotifyOptInit(WPARAM wParam, LPARAM lParam);
+static INT_PTR UpdateNotifyMenuCommand(WPARAM wParam, LPARAM lParam);
+static VOID CALLBACK UpdateNotifyTimerCheck(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+static int UpdateNotifyMakeRequest(UpdateNotifyData *und);
+static void UpdateNotifyPerform(void *m);
+static INT_PTR CALLBACK UpdateNotifyProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK UpdateNotifyOptsProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static int UpdateNotifyModulesLoaded(WPARAM, LPARAM) {
+ NETLIBUSER nlu;
+
+ ZeroMemory(&nlu, sizeof(nlu));
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = NUF_OUTGOING|NUF_HTTPCONNS;
+ nlu.szSettingsModule = UN_MOD;
+ nlu.szDescriptiveName = Translate("Update notification");
+ hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+ return 0;
+}
+
+static int UpdateNotifyPreShutdown(WPARAM, LPARAM) {
+ if (IsWindow(hwndUpdateDlg)) {
+ SendMessage(hwndUpdateDlg, WM_COMMAND, MAKELONG(IDOK, 0), 0);
+ }
+ if (IsWindow(hwndManualUpdateDlg)) {
+ SendMessage(hwndManualUpdateDlg, WM_COMMAND, MAKELONG(IDOK, 0), 0);
+ }
+ return 0;
+}
+
+int LoadUpdateNotifyModule(void) {
+ CLISTMENUITEM mi = { 0 };
+
+ bModuleInitialized = TRUE;
+
+ // Upgrade Routine
+ if (DBGetContactSettingByte(NULL, UN_MOD, "UpdateNotifyNotifyAlpha", 0)) {
+ DBDeleteContactSetting(NULL, UN_MOD, "UpdateNotifyNotifyAlpha");
+ DBWriteContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_ALPHA);
+ }
+ // Ene Upgrade Routine
+
+ CreateServiceFunction("UpdateNotify/UpdateCommand", UpdateNotifyMenuCommand);
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_EMPTYBLOB);
+ mi.pszPopupName = LPGEN("&Help");
+ mi.position = 2000030000;
+ mi.pszName = LPGEN("Check for Update");
+ mi.pszService = "UpdateNotify/UpdateCommand";
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ hHookModules = HookEvent(ME_SYSTEM_MODULESLOADED, UpdateNotifyModulesLoaded);
+ hHookPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, UpdateNotifyPreShutdown);
+ HookEvent(ME_OPT_INITIALISE, UpdateNotifyOptInit);
+ updateTimerId = SetTimer(NULL, 0, 1000*UN_FIRSTCHECK, UpdateNotifyTimerCheck);
+ mir_getXI(&xun);
+ return 0;
+}
+
+void UnloadUpdateNotifyModule()
+{
+ if (!bModuleInitialized) return;
+ UnhookEvent(hHookModules);
+ UnhookEvent(hHookPreShutdown);
+}
+
+static int UpdateNotifyOptInit(WPARAM wParam, LPARAM) {
+ OPTIONSDIALOGPAGE odp;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_UPDATENOTIFY);
+ odp.pszGroup = LPGEN("Events");
+ odp.pszTitle = LPGEN("Update Notify");
+ odp.pfnDlgProc = UpdateNotifyOptsProc;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+static INT_PTR UpdateNotifyMenuCommand(WPARAM, LPARAM) {
+ UpdateNotifyData und;
+
+ if (IsWindow(hwndManualUpdateDlg)) {
+ SetForegroundWindow(hwndManualUpdateDlg);
+ return 0;
+ }
+ ZeroMemory(&und, sizeof(und));
+ UpdateNotifyMakeRequest(&und);
+ if (und.isNew) {
+ DBWriteContactSettingString(NULL, UN_MOD, UN_CURRENTVERSION, und.versionReal);
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_CURRENTVERSIONFND, und.reqTime);
+ }
+ und.isManual = 1;
+ DialogBoxParam(hMirandaInst, MAKEINTRESOURCE(IDD_UPDATE_NOTIFY), 0, UpdateNotifyProc,(LPARAM)&und);
+ hwndManualUpdateDlg = 0;
+ return 0;
+}
+
+static VOID CALLBACK UpdateNotifyTimerCheck(HWND, UINT, UINT_PTR, DWORD)
+{
+ KillTimer(NULL, updateTimerId);
+ if (!DBGetContactSettingByte(NULL, UN_MOD, UN_ENABLE, UN_ENABLE_DEF))
+ return;
+ if (dwUpdateThreadID!=0) {
+ Netlib_Logf(hNetlibUser, "Update notification already running, ignoring attempt");
+ return;
+ }
+ {
+ DWORD lastCheck = 0;
+
+ if (!hNetlibUser)
+ return;
+ lastCheck = DBGetContactSettingDword(NULL, UN_MOD, UN_LASTCHECK, 0);
+ if (!lastCheck) { // never checked for update before
+ Netlib_Logf(hNetlibUser, "Running update notify check for the first time.");
+ dwUpdateThreadID = mir_forkthread(UpdateNotifyPerform, 0);
+ }
+ else {
+ DWORD dwNow = time(NULL), dwTimeDiff;
+ DWORD dwServerPing = DBGetContactSettingDword(NULL, UN_MOD, UN_SERVERPERIOD, UN_DEFAULTCHECKTIME);
+
+ if (lastCheck>dwNow) {
+ // time for last check is after the current date so reset lastcheck and quit
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_LASTCHECK, dwNow);
+ return;
+ }
+ dwTimeDiff = dwNow - lastCheck;
+ if (dwServerPing<UN_MINCHECKTIME)
+ dwServerPing = UN_MINCHECKTIME;
+ if (dwTimeDiff>dwServerPing)
+ dwUpdateThreadID = mir_forkthread(UpdateNotifyPerform, 0);
+ }
+ updateTimerId = SetTimer(NULL, 0, 1000*UN_MINCHECKTIME, UpdateNotifyTimerCheck);
+ }
+}
+
+static DWORD UpdateNotifyMakeVersion(char *str) {
+ DWORD a1,a2,a3,a4;
+ if (!str)
+ return 0;
+ sscanf(str, "%u.%u.%u.%u", &a1, &a2, &a3, &a4);
+ return PLUGIN_MAKE_VERSION(a1, a2, a3, a4);
+}
+
+static int UpdateNotifyIsNewer(DWORD dwCurrent, DWORD dwTest) {
+ if (dwTest>dwCurrent)
+ return 1;
+ return 0;
+}
+
+static int UpdateNotifyReleaseDataValid(UpdateNotifyReleaseData *d) {
+ if (d&&d->szVersionPublic&&d->szVersion&&d->szDownload&&d->szNotes)
+ return 1;
+ return 0;
+}
+
+static void UpdateNotifyFreeReleaseData(UpdateNotifyReleaseData *d) {
+ if (!d)
+ return;
+ if (d->szVersionPublic) mir_free(d->szVersionPublic);
+ if (d->szVersion) mir_free(d->szVersion);
+ if (d->szDownload) mir_free(d->szDownload);
+ if (d->szNotes) mir_free(d->szNotes);
+}
+
+static void UpdateNotifyReleaseLogUpdate(UpdateNotifyReleaseData *d) {
+ if (!UpdateNotifyReleaseDataValid(d))
+ return;
+ #ifdef _WIN64
+ Netlib_Logf(hNetlibUser, "Update server version: %s [%s] [64-bit]", d->szVersionPublic, d->szVersion);
+ #elif defined(_UNICODE)
+ Netlib_Logf(hNetlibUser, "Update server version: %s [%s] [Unicode]", d->szVersionPublic, d->szVersion);
+ #else
+ Netlib_Logf(hNetlibUser, "Update server version: %s [%s] [ANSI]", d->szVersionPublic, d->szVersion);
+ #endif
+
+}
+
+static void UpdateNotifyReleaseCopyData(UpdateNotifyReleaseData *d, UpdateNotifyData *und) {
+ if (!UpdateNotifyReleaseDataValid(d)||!und)
+ return;
+ mir_snprintf(und->version, sizeof(und->version), "%s", d->szVersionPublic);
+ mir_snprintf(und->versionReal, sizeof(und->versionReal), "%s", d->szVersion);
+ mir_snprintf(und->notesUrl, sizeof(und->notesUrl), "%s", d->szNotes);
+ mir_snprintf(und->downloadUrl, sizeof(und->downloadUrl), "%s", d->szDownload);
+}
+
+static int UpdateNotifyMakeRequest(UpdateNotifyData *und) {
+ NETLIBHTTPREQUEST req;
+ NETLIBHTTPREQUEST *resp;
+ NETLIBHTTPHEADER headers[1];
+ DWORD dwVersion;
+ char szVersion[32], szUrl[256], szVersionText[128], szUserAgent[64];
+ int isUnicode, isAlphaCheck, isBetaCheck;
+ DBVARIANT dbv;
+
+ if (!und)
+ return 0;
+ und->version[0] = 0;
+ und->versionReal[0] = 0;
+ und->notesUrl[0] = 0;
+ und->downloadUrl[0] = 0;
+ und->reqTime = time(NULL);
+
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_LASTCHECK, und->reqTime);
+ CallService(MS_SYSTEM_GETVERSIONTEXT, sizeof(szVersionText), (LPARAM)szVersionText);
+ isUnicode = strstr(szVersionText, "Unicode") != NULL ? 1 : 0;
+ isBetaCheck = DBGetContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_DEF)==UN_NOTIFYTYPE_BETA?1:0;
+ isAlphaCheck = DBGetContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_DEF)==UN_NOTIFYTYPE_ALPHA?1:0;
+ dwVersion = CallService(MS_SYSTEM_GETVERSION, 0, 0);
+ mir_snprintf(szVersion, sizeof(szVersion), "%d.%d.%d.%d",
+ HIBYTE(HIWORD(dwVersion)), LOBYTE(HIWORD(dwVersion)),
+ HIBYTE(LOWORD(dwVersion)), LOBYTE(LOWORD(dwVersion)));
+ if (!DBGetContactSettingString(NULL, UN_MOD, UN_CUSTOMXMLURL, &dbv)) {
+ mir_snprintf(szUrl, sizeof(szUrl), "%s", dbv.pszVal?dbv.pszVal:UN_URLXML);
+ DBFreeVariant(&dbv);
+ }
+ else mir_snprintf(szUrl, sizeof(szUrl), "%s", UN_URLXML);
+ ZeroMemory(&req, sizeof(req));
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_GET;
+ req.szUrl = szUrl;
+ req.flags = 0;
+ headers[0].szName = "User-Agent";
+ headers[0].szValue = szUserAgent;
+ #ifdef _WIN64
+ mir_snprintf(szUserAgent, sizeof(szUserAgent), "Miranda/%s (x64)", szVersion);
+ #elif defined(_UNICODE)
+ mir_snprintf(szUserAgent, sizeof(szUserAgent), "Miranda/%s (Unicode)", szVersion);
+ #else
+ mir_snprintf(szUserAgent, sizeof(szUserAgent), "Miranda/%s (ANSI)", szVersion);
+ #endif
+ req.headersCount = 1;
+ req.headers = headers;
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUser, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0) {
+ //int i;
+ int resUpdate = 0;
+ TCHAR *tXml;
+ char *tmp;
+ HXML nodeDoc, n;
+
+ tXml = mir_a2t(resp->pData);
+ nodeDoc = xun.parseString(tXml, 0, _T("miranda"));
+ if (nodeDoc) {
+ int rdStableValid = 0, rdBetaValid = 0, rdAlphaValid = 0;
+ // stable release
+ UpdateNotifyReleaseData rdStable;
+ ZeroMemory(&rdStable, sizeof(rdStable));
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/versionpublic"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szVersionPublic = mir_t2a(xun.getText(n));
+ }
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/versionreal"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szVersion = mir_t2a(xun.getText(n));
+ if (rdStable.szVersion)
+ rdStable.dwVersion = UpdateNotifyMakeVersion(rdStable.szVersion);
+ }
+ #ifdef _WIN64
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/downloadx64exe"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szDownload = mir_t2a(xun.getText(n));
+ }
+ #elif defined(_UNICODE)
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/downloadunicodeexe"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szDownload = mir_t2a(xun.getText(n));
+ }
+ #else
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/downloadansiexe"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szDownload = mir_t2a(xun.getText(n));
+ }
+ #endif
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasestable/notesurl"), 0)) != NULL && xun.getText(n)) {
+ rdStable.szNotes = mir_t2a(xun.getText(n));
+ }
+ rdStableValid = UpdateNotifyReleaseDataValid(&rdStable);
+
+ // beta release
+ UpdateNotifyReleaseData rdBeta;
+ ZeroMemory(&rdBeta, sizeof(rdBeta));
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/versionpublic"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szVersionPublic = mir_t2a(xun.getText(n));
+ }
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/versionreal"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szVersion = mir_t2a(xun.getText(n));
+ if (rdBeta.szVersion)
+ rdBeta.dwVersion = UpdateNotifyMakeVersion(rdBeta.szVersion);
+ }
+ #ifdef _WIN64
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/downloadx64zip"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szDownload = mir_t2a(xun.getText(n));
+ }
+ #elif defined(_UNICODE)
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/downloadunicodeexe"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szDownload = mir_t2a(xun.getText(n));
+ }
+ #else
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/downloadansiexe"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szDownload = mir_t2a(xun.getText(n));
+ }
+ #endif
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasebeta/notesurl"), 0)) != NULL && xun.getText(n)) {
+ rdBeta.szNotes = mir_t2a(xun.getText(n));
+ }
+ rdBetaValid = UpdateNotifyReleaseDataValid(&rdBeta);
+
+ // alpha release
+ UpdateNotifyReleaseData rdAlpha;
+ ZeroMemory(&rdAlpha, sizeof(rdAlpha));
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/versionpublic"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szVersionPublic = mir_t2a(xun.getText(n));
+ }
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/versionreal"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szVersion = mir_t2a(xun.getText(n));
+ if (rdAlpha.szVersion)
+ rdAlpha.dwVersion = UpdateNotifyMakeVersion(rdAlpha.szVersion);
+ }
+ #ifdef _WIN64
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/downloadx64zip"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szDownload = mir_t2a(xun.getText(n));
+ }
+ #elif defined(_UNICODE)
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/downloadunicodezip"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szDownload = mir_t2a(xun.getText(n));
+ }
+ #else
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/downloadansizip"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szDownload = mir_t2a(xun.getText(n));
+ }
+ #endif
+ if ((n = xun.getChildByPath(nodeDoc, _T("releases/releasealpha/notesurl"), 0)) != NULL && xun.getText(n)) {
+ rdAlpha.szNotes = mir_t2a(xun.getText(n));
+ }
+ rdAlphaValid = UpdateNotifyReleaseDataValid(&rdAlpha);
+
+ if (isBetaCheck) {
+ if (!rdBetaValid&&rdStableValid) {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, rdStable.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ else if (rdBetaValid&&rdStableValid&&UpdateNotifyIsNewer(rdBeta.dwVersion, rdStable.dwVersion)) {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, UpdateNotifyMakeVersion(rdStable.szVersion)))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ else if (rdBetaValid) {
+ UpdateNotifyReleaseLogUpdate(&rdBeta);
+ if (UpdateNotifyIsNewer(dwVersion, rdBeta.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdBeta, und);
+ }
+ }
+ else if (isAlphaCheck) {
+ if (!rdAlphaValid&&rdStableValid) {
+ if (UpdateNotifyIsNewer(rdStable.dwVersion, rdAlpha.dwVersion)) {
+ UpdateNotifyReleaseLogUpdate(&rdAlpha);
+ if (UpdateNotifyIsNewer(dwVersion, rdAlpha.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdAlpha, und);
+ }
+ else {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, rdStable.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ }
+ else if (rdAlphaValid&&rdStableValid&&UpdateNotifyIsNewer(rdAlpha.dwVersion, rdStable.dwVersion)) {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, rdStable.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ else if (!rdAlphaValid&&rdBetaValid&&rdStableValid) {
+ if (UpdateNotifyIsNewer(rdStable.dwVersion, rdBeta.dwVersion)) {
+ UpdateNotifyReleaseLogUpdate(&rdBeta);
+ if (UpdateNotifyIsNewer(dwVersion, rdBeta.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdBeta, und);
+ }
+ else {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, rdStable.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ }
+ else if (rdAlphaValid) {
+ UpdateNotifyReleaseLogUpdate(&rdAlpha);
+ if (UpdateNotifyIsNewer(dwVersion, rdAlpha.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdAlpha, und);
+ }
+ }
+ else {
+ if (rdStableValid) {
+ UpdateNotifyReleaseLogUpdate(&rdStable);
+ if (UpdateNotifyIsNewer(dwVersion, rdStable.dwVersion))
+ resUpdate = 1;
+ UpdateNotifyReleaseCopyData(&rdStable, und);
+ }
+ }
+
+ UpdateNotifyFreeReleaseData(&rdStable);
+ UpdateNotifyFreeReleaseData(&rdBeta);
+ UpdateNotifyFreeReleaseData(&rdAlpha);
+ // settings
+ if ((n = xun.getChildByPath(nodeDoc, _T("settings/ping"), 0)) != NULL && xun.getText(n)) {
+ tmp = mir_t2a(xun.getText(n));
+ if (tmp) {
+ int pingval = atoi(tmp);
+ if ((pingval*60*60)>UN_MINCHECKTIME) {
+ Netlib_Logf(hNetlibUser, "Next update check in %d hours", pingval);
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_SERVERPERIOD, pingval*60*60);
+ }
+ mir_free(tmp);
+ }
+ }
+ if ((n = xun.getChildByPath(nodeDoc, _T("settings/updateurl"), 0)) != NULL && xun.getText(n)) {
+ tmp = mir_t2a(xun.getText(n));
+ if (tmp) {
+ Netlib_Logf(hNetlibUser, "Update URL has changed (%s)", tmp);
+ DBWriteContactSettingString(NULL, UN_MOD, UN_CUSTOMXMLURL, tmp);
+ mir_free(tmp);
+ }
+ }
+ if (resUpdate&&und->version&&und->versionReal&&und->notesUrl&&und->downloadUrl) {
+ Netlib_Logf(hNetlibUser, "A new version of Miranda IM is available: %s", und->version);
+ und->isNew = 1;
+ }
+ xun.destroyNode(nodeDoc);
+ }
+ mir_free(tXml);
+ }
+ else Netlib_Logf(hNetlibUser, "Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else Netlib_Logf(hNetlibUser, "No response from HTTP request");
+ return und->isNew;
+}
+
+static void UpdateNotifyPerform(void *)
+{
+ UpdateNotifyData und;
+ DBVARIANT dbv;
+
+ ZeroMemory(&und, sizeof(und));
+ UpdateNotifyMakeRequest(&und);
+ if (und.isNew) {
+ int notify = 1;
+
+ if (!DBGetContactSettingString(NULL, UN_MOD, UN_CURRENTVERSION, &dbv)) {
+ if (!strcmp(dbv.pszVal, und.versionReal)) { // already notified of this version
+
+ DWORD dwNotifyLast = DBGetContactSettingDword(NULL, UN_MOD, UN_CURRENTVERSIONFND, 0);
+
+ if (dwNotifyLast>und.reqTime) { // fix last check date if time has changed
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_CURRENTVERSIONFND, und.reqTime);
+ notify = 0;
+ }
+ else if (und.reqTime-dwNotifyLast<UN_REPEATNOTIFYDLY) {
+ notify = 0;
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+ if (notify) {
+ DBWriteContactSettingString(NULL, UN_MOD, UN_CURRENTVERSION, und.versionReal);
+ DBWriteContactSettingDword(NULL, UN_MOD, UN_CURRENTVERSIONFND, und.reqTime);
+ DialogBoxParam(hMirandaInst, MAKEINTRESOURCE(IDD_UPDATE_NOTIFY), 0, UpdateNotifyProc,(LPARAM)&und);
+ hwndUpdateDlg = 0;
+ }
+ }
+ dwUpdateThreadID = 0;
+}
+
+static INT_PTR CALLBACK UpdateNotifyProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_OTHER_MIRANDA);
+ {
+ UpdateNotifyData *und = (UpdateNotifyData*)lParam;
+ char szVersion[128], szVersionTmp[128], *p;
+ TCHAR szTmp[128];
+
+ if (und->isManual)
+ hwndManualUpdateDlg = hwndDlg;
+ else hwndUpdateDlg = hwndDlg;
+ if (und->isNew) {
+ TCHAR* ptszVer = mir_a2t( und->version );
+ mir_sntprintf(szTmp, SIZEOF(szTmp), TranslateT("Miranda IM %s Now Available"), ptszVer);
+ mir_free(ptszVer);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_UPDATE), SW_HIDE);
+ }
+ else {
+ mir_sntprintf(szTmp, SIZEOF(szTmp), TranslateT("No Update Available"));
+ SetDlgItemText(hwndDlg, IDC_MESSAGE, TranslateT("You are running the latest version of Miranda IM. No update is available at this time."));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_VERSION), SW_HIDE);
+ }
+ SetWindowText(hwndDlg, szTmp);
+ CallService(MS_SYSTEM_GETVERSIONTEXT, sizeof(szVersion), (LPARAM)szVersion);
+ p = strstr(szVersion, "x64 Unicode");
+ if (p)
+ *p = '\0';
+ p = strstr(szVersion, " Unicode");
+ if (p)
+ *p = '\0';
+ SetDlgItemTextA(hwndDlg, IDC_CURRENTVERSION, szVersion);
+ mir_snprintf(szVersionTmp, SIZEOF(szVersionTmp), "%s", und->version?und->version:szVersion);
+ SetDlgItemTextA(hwndDlg, und->isNew?IDC_VERSION:IDC_UPDATE, szVersionTmp);
+ if (und->isNew) {
+ HFONT hFont;
+ LOGFONT lf;
+
+ hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_VERSION, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_VERSION, WM_SETFONT, (WPARAM)hFont, 0);
+ hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_NEWVERSIONLABEL, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_NEWVERSIONLABEL, WM_SETFONT, (WPARAM)hFont, 0);
+ }
+ SetFocus(GetDlgItem(hwndDlg, IDOK));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_VERSION:
+ {
+ UpdateNotifyData *und = (UpdateNotifyData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (und&&und->notesUrl)
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)und->notesUrl);
+ break;
+ }
+ case IDC_DOWNLOAD:
+ {
+ UpdateNotifyData *und = (UpdateNotifyData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (und&&und->downloadUrl) {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)und->downloadUrl);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+ }
+ case IDOK:
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib( hwndDlg );
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK UpdateNotifyOptsProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_ENABLEUPDATES, DBGetContactSettingByte(NULL, UN_MOD, UN_ENABLE, UN_ENABLE_DEF) ? BST_CHECKED : BST_UNCHECKED);
+ switch (DBGetContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_STABLE)) {
+ case UN_NOTIFYTYPE_BETA: CheckDlgButton(hwndDlg, IDC_ENABLEBETA, BST_CHECKED); break;
+ case UN_NOTIFYTYPE_ALPHA: CheckDlgButton(hwndDlg, IDC_ENABLEALPHA, BST_CHECKED); break;
+ default: CheckDlgButton(hwndDlg, IDC_ENABLESTABLE, BST_CHECKED); break;
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_ENABLEUPDATES:
+ case IDC_ENABLEALPHA:
+ case IDC_ENABLEBETA:
+ case IDC_ENABLESTABLE:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ NMHDR *hdr = (NMHDR *)lParam;
+ if (hdr&&hdr->code==PSN_APPLY) {
+ DBWriteContactSettingByte(NULL, UN_MOD, UN_ENABLE, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_ENABLEUPDATES)));
+ if (IsDlgButtonChecked(hwndDlg, IDC_ENABLESTABLE))
+ DBWriteContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_STABLE);
+ if (IsDlgButtonChecked(hwndDlg, IDC_ENABLEBETA))
+ DBWriteContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_BETA);
+ if (IsDlgButtonChecked(hwndDlg, IDC_ENABLEALPHA))
+ DBWriteContactSettingByte(NULL, UN_MOD, UN_NOTIFYTYPE, UN_NOTIFYTYPE_ALPHA);
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
diff --git a/src/modules/userinfo/contactinfo.cpp b/src/modules/userinfo/contactinfo.cpp
new file mode 100644
index 0000000000..dc66067bbf
--- /dev/null
+++ b/src/modules/userinfo/contactinfo.cpp
@@ -0,0 +1,515 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static HFONT hEmailFont=NULL;
+static HCURSOR hHandCursor=NULL;
+
+static INT_PTR CALLBACK EditUserEmailDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)lParam);
+ if(*(char*)lParam) SetWindowText(hwndDlg,TranslateT("Edit E-Mail Address"));
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemTextA(hwndDlg,IDC_EMAIL,(char*)lParam);
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),*(char*)lParam);
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ GetDlgItemTextA(hwndDlg,IDC_EMAIL,(char*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA),256);
+ //fall through
+ case IDCANCEL:
+ EndDialog(hwndDlg,wParam);
+ case IDC_EMAIL:
+ if(HIWORD(wParam)==EN_CHANGE)
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),GetWindowTextLength(GetDlgItem(hwndDlg,IDC_EMAIL)));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK EditUserPhoneDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static int noRecursion=0;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ { char *szText=(char*)lParam;
+ int i,item,countryCount;
+ struct CountryListEntry *countries;
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)lParam);
+ if(szText[0]) SetWindowText(hwndDlg,TranslateT("Edit Phone Number"));
+ TranslateDialogDefault(hwndDlg);
+ if(lstrlenA(szText)>4 && !lstrcmpA(szText+lstrlenA(szText)-4," SMS")) {
+ CheckDlgButton(hwndDlg,IDC_SMS,BST_CHECKED);
+ szText[lstrlenA(szText)-4]='\0';
+ }
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),szText[0]);
+ SendDlgItemMessage(hwndDlg,IDC_AREA,EM_LIMITTEXT,31,0);
+ SendDlgItemMessage(hwndDlg,IDC_NUMBER,EM_LIMITTEXT,63,0);
+ CallService(MS_UTILS_GETCOUNTRYLIST,(WPARAM)&countryCount,(LPARAM)&countries);
+ for(i=0;i<countryCount;i++) {
+ if(countries[i].id==0 || countries[i].id==0xFFFF) continue;
+ item=SendDlgItemMessageA(hwndDlg,IDC_COUNTRY,CB_ADDSTRING,0,(LPARAM)Translate(countries[i].szName));
+ SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_SETITEMDATA,item,countries[i].id);
+ }
+ SetDlgItemTextA(hwndDlg,IDC_PHONE,szText);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ { char *szText=(char*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ int isValid=1;
+ GetDlgItemTextA(hwndDlg,IDC_PHONE,szText,252);
+ if(lstrlenA(szText)<7 || szText[0]!='+') isValid=0;
+ if(isValid) isValid=(lstrlenA(szText+1)==(int)strspn(szText+1,"0123456789 ()-"));
+ if(!isValid) {
+ MessageBox(hwndDlg,TranslateT("The phone number should start with a + and consist of numbers, spaces, brackets and hyphens only."),TranslateT("Invalid Phone Number"),MB_OK);
+ break;
+ }
+ if(IsDlgButtonChecked(hwndDlg,IDC_SMS)) lstrcatA(szText," SMS");
+ }
+ //fall through
+ case IDCANCEL:
+ EndDialog(hwndDlg,wParam);
+ case IDC_COUNTRY:
+ if(HIWORD(wParam)!=CBN_SELCHANGE) break;
+ case IDC_AREA:
+ case IDC_NUMBER:
+ if(LOWORD(wParam)!=IDC_COUNTRY && HIWORD(wParam)!=EN_CHANGE) break;
+ if(noRecursion) break;
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),TRUE);
+ { char szPhone[96],szArea[32],szNumber[64];
+ GetDlgItemTextA(hwndDlg,IDC_AREA,szArea,SIZEOF(szArea));
+ GetDlgItemTextA(hwndDlg,IDC_NUMBER,szNumber,SIZEOF(szNumber));
+ mir_snprintf(szPhone,SIZEOF(szPhone),"+%u (%s) %s",SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_GETCURSEL,0,0),0),szArea,szNumber);
+ noRecursion=1;
+ SetDlgItemTextA(hwndDlg,IDC_PHONE,szPhone);
+ noRecursion=0;
+ }
+ break;
+ case IDC_PHONE:
+ if(HIWORD(wParam)!=EN_UPDATE) break;
+ if(noRecursion) break;
+ noRecursion=1;
+ {
+ char szText[256],*pText=NULL,*pArea,*pNumber;
+ int isValid=1;
+ GetDlgItemTextA(hwndDlg,IDC_PHONE,szText,SIZEOF(szText));
+ if (szText[0] != '+')
+ isValid=0;
+
+ if ( isValid ) {
+ int i, country = strtol( szText+1, &pText, 10 );
+ if ( pText - szText > 4 )
+ isValid = 0;
+ else {
+ for ( i = SendDlgItemMessage( hwndDlg, IDC_COUNTRY, CB_GETCOUNT, 0, 0 )-1; i >= 0; i-- )
+ if ( country == SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_GETITEMDATA,i,0)) {
+ SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_SETCURSEL,i,0);
+ break;
+ }
+ if ( i < 0 )
+ isValid = 0;
+ }
+ }
+ if ( isValid ) {
+ pArea = pText+strcspn(pText,"0123456789");
+ pText = pArea+strspn(pArea,"0123456789");
+ if(*pText) {
+ *pText='\0';
+ pNumber = pText+1+strcspn(pText+1,"0123456789");
+ SetDlgItemTextA(hwndDlg,IDC_NUMBER,pNumber);
+ }
+ SetDlgItemTextA(hwndDlg,IDC_AREA,pArea);
+ }
+ if ( !isValid ) {
+ SendDlgItemMessage(hwndDlg,IDC_COUNTRY,CB_SETCURSEL,-1,0);
+ SetDlgItemTextA(hwndDlg,IDC_AREA,"");
+ SetDlgItemTextA(hwndDlg,IDC_NUMBER,"");
+ }
+ }
+ noRecursion=0;
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),GetWindowTextLength(GetDlgItem(hwndDlg,IDC_PHONE)));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static int IsOverEmail(HWND hwndDlg,TCHAR* szEmail,int cchEmail)
+{
+ RECT rc;
+ HWND hwndEmails;
+ TCHAR szText[256];
+ HDC hdc;
+ SIZE textSize;
+ LVHITTESTINFO hti;
+
+ hwndEmails=GetDlgItem(hwndDlg,IDC_EMAILS);
+ GetCursorPos(&hti.pt);
+ ScreenToClient(hwndEmails,&hti.pt);
+ GetClientRect(hwndEmails,&rc);
+ if(!PtInRect(&rc,hti.pt)) return 0;
+ if(ListView_SubItemHitTest(hwndEmails,&hti)==-1) return 0;
+ if(hti.iSubItem!=1) return 0;
+ if(!(hti.flags&LVHT_ONITEMLABEL)) return 0;
+ ListView_GetSubItemRect(hwndEmails,hti.iItem,1,LVIR_LABEL,&rc);
+ szText[0] = 0;
+ ListView_GetItemText(hwndEmails,hti.iItem,1,szText,SIZEOF(szText));
+ hdc=GetDC(hwndEmails);
+ SelectObject(hdc,hEmailFont);
+ GetTextExtentPoint32(hdc,szText,lstrlen(szText),&textSize);
+ ReleaseDC(hwndEmails,hdc);
+ if(hti.pt.x<rc.left+textSize.cx) {
+ if(szEmail && cchEmail) lstrcpyn(szEmail,szText,cchEmail);
+ return 1;
+ }
+ return 0;
+}
+
+#define M_REMAKELISTS (WM_USER+1)
+INT_PTR CALLBACK ContactDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)lParam);
+ if(hEmailFont) DeleteObject(hEmailFont);
+ { LOGFONT lf;
+ hEmailFont=(HFONT)SendDlgItemMessage(hwndDlg,IDC_EMAILS,WM_GETFONT,0,0);
+ GetObject(hEmailFont,sizeof(lf),&lf);
+ lf.lfUnderline=1;
+ hEmailFont=CreateFontIndirect(&lf);
+ }
+ if(hHandCursor==NULL) {
+ if(IsWinVer2000Plus()) hHandCursor=LoadCursor(NULL,IDC_HAND);
+ else hHandCursor=LoadCursor(hMirandaInst,MAKEINTRESOURCE(IDC_HYPERLINKHAND));
+ }
+ TranslateDialogDefault(hwndDlg);
+ { LVCOLUMN lvc;
+ RECT rc;
+ GetClientRect(GetDlgItem(hwndDlg,IDC_EMAILS),&rc);
+ rc.right-=GetSystemMetrics(SM_CXVSCROLL);
+ lvc.mask=LVCF_WIDTH;
+ ListView_SetExtendedListViewStyleEx(GetDlgItem(hwndDlg,IDC_EMAILS), LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+ ListView_SetExtendedListViewStyleEx(GetDlgItem(hwndDlg,IDC_PHONES), LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+ lvc.cx=rc.right/4;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_EMAILS),0,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PHONES),0,&lvc);
+ lvc.cx=rc.right-rc.right/4-40;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_EMAILS),1,&lvc);
+ lvc.cx=rc.right-rc.right/4-90;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PHONES),1,&lvc);
+ lvc.cx=50;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PHONES),2,&lvc);
+ lvc.cx=20;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_EMAILS),2,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_EMAILS),3,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PHONES),3,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PHONES),4,&lvc);
+ }
+ break;
+ case M_REMAKELISTS:
+ { char *szProto;
+ LVITEM lvi;
+ int i;
+ char idstr[33];
+ TCHAR idstr2[33];
+ DBVARIANT dbv;
+ HANDLE hContact=(HANDLE)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+
+ if (hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ //e-mails
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_EMAILS));
+ lvi.mask=LVIF_TEXT|LVIF_PARAM;
+ lvi.lParam=(LPARAM)(-1);
+ lvi.iSubItem=0;
+ lvi.iItem=0;
+ for(i=-1;;i++) {
+ if(i==-1) {
+ if(DBGetContactSettingTString(hContact,szProto,"e-mail",&dbv))
+ continue;
+ lvi.pszText=TranslateT("Primary");
+ }
+ else {
+ mir_snprintf(idstr, SIZEOF(idstr), "e-mail%d", i );
+ if(DBGetContactSettingTString(hContact,szProto,idstr,&dbv))
+ break;
+ lvi.pszText=idstr2;
+ mir_sntprintf(idstr2, SIZEOF(idstr2), _T("%d"),i+2);
+ }
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_EMAILS),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_EMAILS),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ lvi.iSubItem=0;
+ for(i=0;;i++) {
+ lvi.lParam=i;
+ mir_snprintf(idstr, SIZEOF(idstr), "Mye-mail%d",i);
+ if(DBGetContactSettingTString(hContact,"UserInfo",idstr,&dbv))
+ break;
+ lvi.pszText=idstr2;
+ mir_sntprintf(idstr2, SIZEOF(idstr2), TranslateT("Custom %d"),i+1);
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_EMAILS),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_EMAILS),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ lvi.mask=LVIF_PARAM;
+ lvi.lParam=(LPARAM)(-2);
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_EMAILS),&lvi);
+ //phones
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_PHONES));
+ lvi.mask=LVIF_TEXT|LVIF_PARAM;
+ lvi.lParam=(LPARAM)(-1);
+ lvi.iSubItem=0;
+ lvi.iItem=0;
+ if(!DBGetContactSettingTString(hContact,szProto,"Phone",&dbv)) {
+ lvi.pszText=TranslateT("Primary");
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ if(!DBGetContactSettingTString(hContact,szProto,"Fax",&dbv)) {
+ lvi.pszText=TranslateT("Fax");
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ if(!DBGetContactSettingTString(hContact,szProto,"Cellular",&dbv)) {
+ lvi.pszText=TranslateT("Mobile");
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ if(lstrlenA(dbv.pszVal)>4 && !lstrcmpA(dbv.pszVal+lstrlenA(dbv.pszVal)-4," SMS")) {
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,2,_T("y"));
+ dbv.ptszVal[lstrlen(dbv.ptszVal)-4]='\0';
+ }
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ if(!DBGetContactSettingTString(hContact,szProto,"CompanyPhone",&dbv)) {
+ lvi.pszText=TranslateT("Work Phone");
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ if(!DBGetContactSettingTString(hContact,szProto,"CompanyFax",&dbv)) {
+ lvi.pszText=TranslateT("Work Fax");
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ lvi.iSubItem=0;
+ for(i=0;;i++) {
+ lvi.lParam=i;
+ mir_snprintf(idstr, SIZEOF(idstr), "MyPhone%d",i);
+ if(DBGetContactSettingTString(hContact,"UserInfo",idstr,&dbv))
+ break;
+ lvi.pszText=idstr2;
+ mir_sntprintf(idstr2, SIZEOF(idstr2), TranslateT("Custom %d"),i+1);
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ if(lstrlen(dbv.ptszVal)>4 && !lstrcmp(dbv.ptszVal+lstrlen(dbv.ptszVal)-4,_T(" SMS"))) {
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,2,_T("y"));
+ dbv.ptszVal[lstrlen(dbv.ptszVal)-4]='\0';
+ }
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PHONES),lvi.iItem,1,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ lvi.mask=LVIF_PARAM;
+ lvi.lParam=(LPARAM)(-2);
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PHONES),&lvi);
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_INFOCHANGED:
+ SendMessage(hwndDlg,M_REMAKELISTS,0,0);
+ break;
+ }
+ break;
+ case IDC_EMAILS:
+ case IDC_PHONES:
+ switch (((LPNMHDR)lParam)->code) {
+ case NM_CUSTOMDRAW:
+ { NMLVCUSTOMDRAW *nm=(NMLVCUSTOMDRAW*)lParam;
+ switch(nm->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,CDRF_NOTIFYSUBITEMDRAW);
+ return TRUE;
+ case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+ {
+ RECT rc;
+ ListView_GetSubItemRect(nm->nmcd.hdr.hwndFrom,nm->nmcd.dwItemSpec,nm->iSubItem,LVIR_LABEL,&rc);
+ if(nm->iSubItem==1 && nm->nmcd.hdr.idFrom==IDC_EMAILS) {
+ HFONT hoFont;
+ TCHAR szText[256] = {0};
+ ListView_GetItemText(nm->nmcd.hdr.hwndFrom,nm->nmcd.dwItemSpec,nm->iSubItem,szText,SIZEOF(szText));
+ hoFont=(HFONT)SelectObject(nm->nmcd.hdc,hEmailFont);
+ SetTextColor(nm->nmcd.hdc,RGB(0,0,255));
+ DrawText(nm->nmcd.hdc,szText,-1,&rc,DT_END_ELLIPSIS|DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_TOP);
+ SelectObject(nm->nmcd.hdc,hoFont);
+ SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+
+ HICON hIcon = NULL;
+ if(nm->nmcd.lItemlParam==(LPARAM)(-2) && nm->iSubItem-3==(nm->nmcd.hdr.idFrom==IDC_PHONES))
+ hIcon = LoadSkinIcon( SKINICON_OTHER_ADDCONTACT );
+ else if(nm->iSubItem>1 && nm->nmcd.lItemlParam!=(LPARAM)(-1) && nm->nmcd.lItemlParam!=(LPARAM)(-2)) {
+ static int iconResources[3]={SKINICON_OTHER_RENAME,SKINICON_OTHER_DELETE};
+ if(nm->iSubItem==2 && nm->nmcd.hdr.idFrom==IDC_PHONES) {
+ TCHAR szText[2];
+ ListView_GetItemText(nm->nmcd.hdr.hwndFrom,nm->nmcd.dwItemSpec,nm->iSubItem,szText,SIZEOF(szText));
+ if(szText[0]) hIcon = LoadSkinIcon( SKINICON_OTHER_SMS );
+ }
+ else hIcon = LoadSkinIcon( iconResources[nm->iSubItem-3+(nm->nmcd.hdr.idFrom==IDC_EMAILS)] );
+ }
+ else break;
+ DrawIconEx(nm->nmcd.hdc,(rc.left+rc.right-GetSystemMetrics(SM_CXSMICON))/2,(rc.top+rc.bottom-GetSystemMetrics(SM_CYSMICON))/2,hIcon,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0,NULL,DI_NORMAL);
+ IconLib_ReleaseIcon(hIcon, 0);
+ SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ case NM_CLICK:
+ { NMLISTVIEW *nm=(NMLISTVIEW*)lParam;
+ LVITEM lvi;
+ TCHAR szEmail[256];
+ HANDLE hContact=(HANDLE)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ char *szIdTemplate=nm->hdr.idFrom==IDC_PHONES?"MyPhone%d":"Mye-mail%d";
+ LVHITTESTINFO hti;
+
+ if(IsOverEmail(hwndDlg,szEmail,SIZEOF(szEmail))) {
+ TCHAR szExec[264];
+ mir_sntprintf(szExec, SIZEOF(szExec), _T("mailto:%s"), szEmail);
+ ShellExecute(hwndDlg,_T("open"),szExec,NULL,NULL,SW_SHOW);
+ break;
+ }
+ if(nm->iSubItem<2) break;
+ hti.pt.x=(short)LOWORD(GetMessagePos());
+ hti.pt.y=(short)HIWORD(GetMessagePos());
+ ScreenToClient(nm->hdr.hwndFrom,&hti.pt);
+ if(ListView_SubItemHitTest(nm->hdr.hwndFrom,&hti)==-1) break;
+ lvi.mask=LVIF_PARAM;
+ lvi.iItem=hti.iItem;
+ lvi.iSubItem=0;
+ ListView_GetItem(nm->hdr.hwndFrom,&lvi);
+ if(lvi.lParam==(LPARAM)(-1)) break;
+ if(lvi.lParam==(LPARAM)(-2)) {
+ if(hti.iSubItem-3==(nm->hdr.idFrom==IDC_PHONES)) {
+ //add
+ char szNewData[256]="",idstr[33];
+ int i;
+ DBVARIANT dbv;
+ if(IDOK!=DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(nm->hdr.idFrom==IDC_PHONES?IDD_ADDPHONE:IDD_ADDEMAIL),hwndDlg,nm->hdr.idFrom==IDC_PHONES?EditUserPhoneDlgProc:EditUserEmailDlgProc,(LPARAM)szNewData))
+ break;
+ for(i=0;;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), szIdTemplate,i);
+ if(DBGetContactSettingString(hContact,"UserInfo",idstr,&dbv)) break;
+ DBFreeVariant(&dbv);
+ }
+ DBWriteContactSettingString(hContact,"UserInfo",idstr,szNewData);
+ SendMessage(hwndDlg,M_REMAKELISTS,0,0);
+ }
+ }
+ else {
+ if(hti.iSubItem-3==(nm->hdr.idFrom==IDC_PHONES)) {
+ //delete
+ int i;
+ char idstr[33];
+ DBVARIANT dbv;
+ for(i=lvi.lParam;;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), szIdTemplate,i+1);
+ if(DBGetContactSettingString(hContact,"UserInfo",idstr,&dbv)) break;
+ mir_snprintf(idstr, SIZEOF(idstr), szIdTemplate,i);
+ DBWriteContactSettingString(hContact,"UserInfo",idstr,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ mir_snprintf(idstr, SIZEOF(idstr), szIdTemplate,i);
+ DBDeleteContactSetting(hContact,"UserInfo",idstr);
+ SendMessage(hwndDlg,M_REMAKELISTS,0,0);
+ }
+ else if(hti.iSubItem-2==(nm->hdr.idFrom==IDC_PHONES)) {
+ //edit
+ char szText[256],idstr[33];
+ DBVARIANT dbv;
+ mir_snprintf(idstr, SIZEOF(idstr), szIdTemplate,lvi.lParam);
+ if(DBGetContactSettingString(hContact,"UserInfo",idstr,&dbv)) break;
+ lstrcpynA(szText,dbv.pszVal,SIZEOF(szText));
+ DBFreeVariant(&dbv);
+ if(IDOK!=DialogBoxParam(hMirandaInst,MAKEINTRESOURCE(nm->hdr.idFrom==IDC_PHONES?IDD_ADDPHONE:IDD_ADDEMAIL),hwndDlg,nm->hdr.idFrom==IDC_PHONES?EditUserPhoneDlgProc:EditUserEmailDlgProc,(LPARAM)szText))
+ break;
+ DBWriteContactSettingString(hContact,"UserInfo",idstr,szText);
+ SendMessage(hwndDlg,M_REMAKELISTS,0,0);
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_SETCURSOR:
+ if(LOWORD(lParam)!=HTCLIENT) break;
+ if(GetForegroundWindow()==GetParent(hwndDlg)) {
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwndDlg,&pt);
+// SetFocus(ChildWindowFromPoint(hwndDlg,pt)); //ugly hack because listviews ignore their first click
+ }
+ if(IsOverEmail(hwndDlg,NULL,0)) {
+ SetCursor(hHandCursor);
+ SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,TRUE);
+ return TRUE;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/src/modules/userinfo/stdinfo.cpp b/src/modules/userinfo/stdinfo.cpp
new file mode 100644
index 0000000000..89f6ab0f45
--- /dev/null
+++ b/src/modules/userinfo/stdinfo.cpp
@@ -0,0 +1,613 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+INT_PTR CALLBACK ContactDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define SVS_NORMAL 0
+#define SVS_GENDER 1
+#define SVS_ZEROISUNSPEC 2
+#define SVS_IP 3
+#define SVS_COUNTRY 4
+#define SVS_MONTH 5
+#define SVS_SIGNED 6
+#define SVS_TIMEZONE 7
+
+static int Proto_GetContactInfoSetting(HANDLE hContact,const char *szProto,const char *szModule,const char *szSetting,DBVARIANT *dbv, const int nType)
+{
+ DBCONTACTGETSETTING cgs={szModule,szSetting,dbv};
+ dbv->type=(BYTE)nType;
+
+ return CallProtoService(szProto,PS_GETINFOSETTING,(WPARAM)hContact,(LPARAM)&cgs);
+}
+
+static void Proto_FreeInfoVariant(DBVARIANT *dbv)
+{
+ switch ( dbv->type ) {
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ case DBVT_WCHAR:
+ {
+ mir_free(dbv->pszVal);
+ dbv->pszVal=0;
+ break;
+ }
+ case DBVT_BLOB:
+ {
+ mir_free(dbv->pbVal);
+ dbv->pbVal=0;
+ break;
+ }
+ }
+ dbv->type=0;
+}
+
+static void SetValue(HWND hwndDlg,int idCtrl,HANDLE hContact,char *szModule,char *szSetting,int special)
+{
+ DBVARIANT dbv = { 0 };
+ char str[80],*pstr = NULL;
+ TCHAR* ptstr = NULL;
+ int unspecified=0;
+ char* szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ bool proto_service = szProto && (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_INFOSETTINGSVC);
+
+ dbv.type=DBVT_DELETED;
+ if(szModule==NULL) unspecified=1;
+ else if (proto_service) unspecified=Proto_GetContactInfoSetting(hContact,szProto,szModule,szSetting,&dbv,0);
+ else unspecified=DBGetContactSettingW(hContact,szModule,szSetting,&dbv);
+ if(!unspecified) {
+ switch(dbv.type) {
+ case DBVT_BYTE:
+ if(special==SVS_GENDER) {
+ if(dbv.cVal=='M') ptstr=TranslateT("Male");
+ else if(dbv.cVal=='F') ptstr=TranslateT("Female");
+ else unspecified=1;
+ }
+ else if(special==SVS_MONTH) {
+ if(dbv.bVal>0 && dbv.bVal<=12) {
+ pstr=str;
+ GetLocaleInfoA(LOCALE_USER_DEFAULT,LOCALE_SABBREVMONTHNAME1-1+dbv.bVal,str,SIZEOF(str));
+ }
+ else unspecified=1;
+ }
+ else if(special==SVS_TIMEZONE) {
+ if(dbv.cVal==-100) unspecified=1;
+ else {
+ pstr=str;
+ mir_snprintf(str, SIZEOF(str), dbv.cVal?"UTC%+d:%02d":"UTC",-dbv.cVal/2,(dbv.cVal&1)*30);
+ }
+ }
+ else {
+ unspecified=(special==SVS_ZEROISUNSPEC && dbv.bVal==0);
+ pstr=_itoa(special==SVS_SIGNED?dbv.cVal:dbv.bVal,str,10);
+ }
+ break;
+ case DBVT_WORD:
+ if(special==SVS_COUNTRY) {
+ WORD wSave = dbv.wVal;
+ if (wSave == ( WORD )-1) {
+ char szSettingName[100];
+ mir_snprintf( szSettingName, SIZEOF(szSettingName), "%sName", szSetting );
+ if ( !DBGetContactSettingTString(hContact,szModule,szSettingName,&dbv)) {
+ ptstr = dbv.ptszVal;
+ unspecified = false;
+ break;
+ }
+ }
+
+ pstr = Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,wSave,0));
+ unspecified=pstr==NULL;
+ }
+ else {
+ unspecified=(special==SVS_ZEROISUNSPEC && dbv.wVal==0);
+ pstr=_itoa(special==SVS_SIGNED?dbv.sVal:dbv.wVal,str,10);
+ }
+ break;
+ case DBVT_DWORD:
+ unspecified=(special==SVS_ZEROISUNSPEC && dbv.dVal==0);
+ if(special==SVS_IP) {
+ struct in_addr ia;
+ ia.S_un.S_addr=htonl(dbv.dVal);
+ pstr=inet_ntoa(ia);
+ if(dbv.dVal==0) unspecified=1;
+ }
+ else pstr=_itoa(special==SVS_SIGNED?dbv.lVal:dbv.dVal,str,10);
+ break;
+ case DBVT_ASCIIZ:
+ unspecified=(special==SVS_ZEROISUNSPEC && dbv.pszVal[0]=='\0');
+ pstr=dbv.pszVal;
+ break;
+ case DBVT_UTF8:
+ unspecified=(special==SVS_ZEROISUNSPEC && dbv.pszVal[0]=='\0');
+ #if defined( _UNICODE )
+ if ( !unspecified )
+ { WCHAR* wszStr;
+ Utf8Decode( dbv.pszVal, &wszStr );
+ SetDlgItemTextW( hwndDlg, idCtrl, TranslateTS(wszStr));
+ mir_free( wszStr );
+ goto LBL_Exit;
+ }
+ #endif
+ pstr=dbv.pszVal;
+ Utf8Decode( dbv.pszVal, NULL );
+ break;
+ default: pstr=str; lstrcpyA(str,"???"); break;
+ } }
+
+ if (unspecified)
+ SetDlgItemText(hwndDlg, idCtrl, TranslateT("<not specified>"));
+ else if ( ptstr != NULL )
+ SetDlgItemText(hwndDlg, idCtrl, ptstr);
+ else
+ SetDlgItemTextA(hwndDlg, idCtrl, pstr);
+
+#if defined( _UNICODE )
+LBL_Exit:
+#endif
+ EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified);
+ if (proto_service)
+ Proto_FreeInfoVariant(&dbv);
+ else
+ DBFreeVariant(&dbv);
+}
+
+static INT_PTR CALLBACK SummaryDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_INFOCHANGED )
+ { char *szProto;
+ HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ if (hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ SetValue(hwndDlg,IDC_NICK,hContact,szProto,"Nick",0);
+ SetValue(hwndDlg,IDC_FIRSTNAME,hContact,szProto,"FirstName",0);
+ SetValue(hwndDlg,IDC_LASTNAME,hContact,szProto,"LastName",0);
+ SetValue(hwndDlg,IDC_EMAIL,hContact,szProto,"e-mail",0);
+ SetValue(hwndDlg,IDC_AGE,hContact,szProto,"Age",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_GENDER,hContact,szProto,"Gender",SVS_GENDER);
+ SetValue(hwndDlg,IDC_DOBDAY,hContact,szProto,"BirthDay",0);
+ SetValue(hwndDlg,IDC_DOBMONTH,hContact,szProto,"BirthMonth",SVS_MONTH);
+ SetValue(hwndDlg,IDC_DOBYEAR,hContact,szProto,"BirthYear",0);
+ SetValue(hwndDlg,IDC_MARITAL,hContact,szProto,"MaritalStatus",0);
+ } }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ case IDC_EMAIL:
+ if(IsWindowEnabled(GetDlgItem(hwndDlg,IDC_EMAIL))) {
+ TCHAR szExec[264], szEmail[256];
+ GetDlgItemText(hwndDlg, IDC_EMAIL, szEmail, SIZEOF(szEmail));
+ mir_sntprintf(szExec, SIZEOF(szExec), _T("mailto:%s"), szEmail);
+ ShellExecute(hwndDlg, _T("open"), szExec, NULL, NULL, SW_SHOW);
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK LocationDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+ TranslateDialogDefault(hwndDlg);
+ SetTimer(hwndDlg,1,1000,NULL);
+
+ tmi.prepareList((HANDLE)lParam, GetDlgItem(hwndDlg, IDC_TIMEZONESELECT), TZF_PLF_CB);
+ SendMessage(hwndDlg,WM_TIMER,0,0);
+ break;
+
+ case WM_TIMER:
+ {
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ if (hContact != NULL)
+ {
+ TCHAR szTime[80];
+
+ if (tmi.printDateTimeByContact(hContact, _T("s"), szTime, SIZEOF(szTime), TZF_KNOWNONLY))
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_LOCALTIME),FALSE);
+ SetDlgItemText(hwndDlg, IDC_LOCALTIME, TranslateT("<not specified>"));
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_LOCALTIME), TRUE);
+ SetDlgItemText(hwndDlg, IDC_LOCALTIME, szTime);
+ }
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_INFOCHANGED )
+ { char *szProto;
+ HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ if (hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ SetValue(hwndDlg,IDC_STREET,hContact,szProto,"Street",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_CITY,hContact,szProto,"City",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_STATE,hContact,szProto,"State",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_ZIP,hContact,szProto,"ZIP",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_COUNTRY,hContact,szProto,"Country",SVS_COUNTRY);
+ SetValue(hwndDlg,IDC_LANGUAGE1,hContact,szProto,"Language1",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_LANGUAGE2,hContact,szProto,"Language2",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_LANGUAGE3,hContact,szProto,"Language3",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_TIMEZONE,hContact,szProto,"Timezone",SVS_TIMEZONE);
+ }
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ case IDC_TIMEZONESELECT:
+ if(HIWORD(wParam) == CBN_SELCHANGE) {
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ SendMessage(GetParent(hwndDlg),PSM_CHANGED, 0,0);
+ tmi.storeListResults(hContact, GetDlgItem(hwndDlg, IDC_TIMEZONESELECT), TZF_PLF_CB);
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK WorkDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_INFOCHANGED)
+ { char *szProto;
+ HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ if (hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ SetValue(hwndDlg,IDC_COMPANY,hContact,szProto,"Company",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_DEPARTMENT,hContact,szProto,"CompanyDepartment",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_POSITION,hContact,szProto,"CompanyPosition",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_STREET,hContact,szProto,"CompanyStreet",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_CITY,hContact,szProto,"CompanyCity",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_STATE,hContact,szProto,"CompanyState",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_ZIP,hContact,szProto,"CompanyZIP",SVS_ZEROISUNSPEC);
+ SetValue(hwndDlg,IDC_COUNTRY,hContact,szProto,"CompanyCountry",SVS_COUNTRY);
+ SetValue(hwndDlg,IDC_WEBPAGE,hContact,szProto,"CompanyHomepage",SVS_ZEROISUNSPEC);
+ } }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ case IDC_WEBPAGE:
+ if(IsWindowEnabled(GetDlgItem(hwndDlg,IDC_WEBPAGE))) {
+ char szPage[256];
+ GetDlgItemTextA(hwndDlg,IDC_WEBPAGE,szPage,SIZEOF(szPage));
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)szPage);
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+// Resizes all columns in a listview (report style)
+// to make all text visible
+void ResizeColumns(HWND hwndLV)
+{
+ int nCol = 0; LVCOLUMN lvCol;
+ lvCol.mask = LVCF_WIDTH;
+ while(ListView_GetColumn(hwndLV, nCol++, &lvCol))
+ ListView_SetColumnWidth(hwndLV, nCol-1, LVSCW_AUTOSIZE);
+}
+
+static INT_PTR CALLBACK BackgroundDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { LVCOLUMN lvc;
+ RECT rc;
+ GetClientRect(GetDlgItem(hwndDlg,IDC_PAST),&rc);
+ rc.right-=GetSystemMetrics(SM_CXVSCROLL);
+ lvc.mask=LVCF_WIDTH;
+ lvc.cx=rc.right/3;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PAST),0,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_INTERESTS),0,&lvc);
+ lvc.cx=rc.right-rc.right/3;
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_PAST),1,&lvc);
+ ListView_InsertColumn(GetDlgItem(hwndDlg,IDC_INTERESTS),1,&lvc);
+ }
+ ListView_SetExtendedListViewStyleEx(GetDlgItem(hwndDlg,IDC_PAST),LVS_EX_LABELTIP,LVS_EX_LABELTIP);
+ ListView_SetExtendedListViewStyleEx(GetDlgItem(hwndDlg,IDC_INTERESTS),LVS_EX_LABELTIP,LVS_EX_LABELTIP);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_INFOCHANGED)
+ { LVITEM lvi;
+ int i;
+ char idstr[33];
+ DBVARIANT dbv,dbvText;
+ HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+
+ if (hContact != NULL) {
+ char *szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ bool proto_service = (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_INFOSETTINGSVC) == PF4_INFOSETTINGSVC;
+ SetValue(hwndDlg,IDC_WEBPAGE,hContact,szProto,"Homepage",SVS_ZEROISUNSPEC);
+
+ //past
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_PAST));
+ lvi.mask=LVIF_TEXT;
+ lvi.iSubItem=0;
+ lvi.iItem=0;
+ for(i=0;;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), "Past%d",i);
+ if((proto_service && Proto_GetContactInfoSetting(hContact,szProto,szProto,idstr,&dbv,DBVT_TCHAR)) ||
+ (!proto_service && DBGetContactSettingTString(hContact,szProto,idstr,&dbv)))
+ break;
+ mir_snprintf(idstr, SIZEOF(idstr), "Past%dText",i);
+ if(DBGetContactSettingTString(hContact,szProto,idstr,&dbvText))
+ {if(proto_service) Proto_FreeInfoVariant(&dbv); else DBFreeVariant(&dbv); break;}
+ lvi.pszText=dbv.ptszVal;
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PAST),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PAST),lvi.iItem,1,dbvText.ptszVal);
+ DBFreeVariant(&dbvText);
+ if(proto_service)
+ Proto_FreeInfoVariant(&dbv);
+ else
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+
+ for(i=0;;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), "Affiliation%d", i);
+ if((proto_service && Proto_GetContactInfoSetting(hContact,szProto,szProto,idstr,&dbv,DBVT_TCHAR)) ||
+ (!proto_service && DBGetContactSettingTString(hContact,szProto,idstr,&dbv)))
+ break;
+ mir_snprintf(idstr, SIZEOF(idstr), "Affiliation%dText",i);
+ if(DBGetContactSettingTString(hContact,szProto,idstr,&dbvText))
+ {if(proto_service) Proto_FreeInfoVariant(&dbv); else DBFreeVariant(&dbv); break;}
+ lvi.pszText=dbv.ptszVal;
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_PAST),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_PAST),lvi.iItem,1,dbvText.ptszVal);
+ DBFreeVariant(&dbvText);
+ if(proto_service)
+ Proto_FreeInfoVariant(&dbv);
+ else
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+
+ ResizeColumns(GetDlgItem(hwndDlg,IDC_PAST));
+
+ //interests
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_INTERESTS));
+ lvi.mask=LVIF_TEXT;
+ lvi.iSubItem=0;
+ lvi.iItem=0;
+ for(i=0;;i++) {
+ mir_snprintf(idstr, SIZEOF(idstr), "Interest%dCat", i);
+ if((proto_service && Proto_GetContactInfoSetting(hContact,szProto,szProto,idstr,&dbv,DBVT_TCHAR)) ||
+ (!proto_service && DBGetContactSettingTString(hContact,szProto,idstr,&dbv)))
+ break;
+ mir_snprintf(idstr, SIZEOF(idstr), "Interest%dText", i);
+ if(DBGetContactSettingTString(hContact,szProto,idstr,&dbvText))
+ {if(proto_service) Proto_FreeInfoVariant(&dbv); else DBFreeVariant(&dbv); break;}
+ lvi.pszText=dbv.ptszVal;
+ ListView_InsertItem(GetDlgItem(hwndDlg,IDC_INTERESTS),&lvi);
+ ListView_SetItemText(GetDlgItem(hwndDlg,IDC_INTERESTS),lvi.iItem,1,dbvText.ptszVal);
+ DBFreeVariant(&dbvText);
+ if(proto_service)
+ Proto_FreeInfoVariant(&dbv);
+ else
+ DBFreeVariant(&dbv);
+ lvi.iItem++;
+ }
+ ResizeColumns(GetDlgItem(hwndDlg,IDC_INTERESTS));
+ } }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ case IDC_WEBPAGE:
+ if(IsWindowEnabled(GetDlgItem(hwndDlg,IDC_WEBPAGE))) {
+ char szPage[256];
+ GetDlgItemTextA(hwndDlg,IDC_WEBPAGE,szPage,SIZEOF(szPage));
+ CallService(MS_UTILS_OPENURL,1,(LPARAM)szPage);
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK NotesDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { DBVARIANT dbv;
+ HFONT hFont;
+ LOGFONT lf;
+ HDC hDC = GetDC(hwndDlg);
+ lf.lfHeight = -MulDiv(10, GetDeviceCaps(hDC, LOGPIXELSY), 72);
+ ReleaseDC(hwndDlg, hDC);
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ lf.lfWeight = FW_NORMAL;
+ lf.lfItalic = 0;
+ lf.lfUnderline = 0;
+ lf.lfStrikeOut = 0;
+ lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = DEFAULT_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ lstrcpy(lf.lfFaceName, _T("Courier New"));
+ lf.lfCharSet = DEFAULT_CHARSET;
+// hFont = (HFONT) GetStockObject(ANSI_FIXED_FONT);
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_ABOUT, WM_SETFONT, (WPARAM) hFont, MAKELPARAM(TRUE, 0));
+
+ if(!DBGetContactSettingString((HANDLE)lParam,"UserInfo","MyNotes",&dbv)) {
+ SetDlgItemTextA(hwndDlg,IDC_MYNOTES,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ SendDlgItemMessage(hwndDlg,IDC_MYNOTES,EM_LIMITTEXT,2048,0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_INFOCHANGED:
+ { char *szProto;
+ HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ if (hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (szProto==NULL) break;
+ SetValue(hwndDlg,IDC_ABOUT,hContact,szProto,"About",0);
+ }
+ break;
+ }
+ case PSN_APPLY:
+ { HANDLE hContact=(HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ if(GetWindowTextLength(GetDlgItem(hwndDlg,IDC_MYNOTES))) {
+ char text[2048];
+ GetDlgItemTextA(hwndDlg,IDC_MYNOTES,text,SIZEOF(text));
+ DBWriteContactSettingString(hContact,"UserInfo","MyNotes",text);
+ }
+ else DBDeleteContactSetting(hContact,"UserInfo","MyNotes");
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ if(wParam==MAKEWPARAM(IDC_MYNOTES,EN_CHANGE))
+ SendMessage(GetParent(hwndDlg),PSM_CHANGED,0,0);
+ else if(LOWORD(wParam)==IDCANCEL)
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ case WM_DESTROY:
+ {
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_ABOUT, WM_GETFONT, 0, 0);
+ DeleteObject(hFont);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+int DetailsInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ if ((HANDLE)lParam == NULL)
+ return 0;
+
+ if ( CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0) == 0 )
+ return 0;
+
+ odp.cbSize = sizeof(odp);
+ odp.hIcon = NULL;
+ odp.hInstance = hMirandaInst;
+ odp.flags = 0;
+
+ odp.pfnDlgProc = SummaryDlgProc;
+ odp.position = -2100000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_SUMMARY);
+ odp.pszTitle = LPGEN("Summary");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp);
+
+ odp.pfnDlgProc = ContactDlgProc;
+ odp.position = -1800000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_CONTACT);
+ odp.pszTitle = LPGEN("Contact");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp );
+
+ odp.pfnDlgProc = LocationDlgProc;
+ odp.position = -1500000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_LOCATION);
+ odp.pszTitle = LPGEN("Location");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp);
+
+ odp.pfnDlgProc = WorkDlgProc;
+ odp.position = -1200000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_WORK);
+ odp.pszTitle = LPGEN("Work");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp);
+
+ odp.pfnDlgProc = BackgroundDlgProc;
+ odp.position = -900000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_BACKGROUND);
+ odp.pszTitle = LPGEN("Background info");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp );
+
+ odp.pfnDlgProc = NotesDlgProc;
+ odp.position = 0;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_NOTES);
+ odp.pszTitle = LPGEN("Notes");
+ CallService(MS_USERINFO_ADDPAGE, wParam, ( LPARAM )&odp);
+ return 0;
+}
diff --git a/src/modules/userinfo/userinfo.cpp b/src/modules/userinfo/userinfo.cpp
new file mode 100644
index 0000000000..a17b2d4c71
--- /dev/null
+++ b/src/modules/userinfo/userinfo.cpp
@@ -0,0 +1,636 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#define UPDATEANIMFRAMES 20
+
+int DetailsInit(WPARAM wParam,LPARAM lParam);
+static INT_PTR CALLBACK DlgProcDetails(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static HANDLE hWindowList=NULL;
+static HANDLE hDetailsInitEvent;
+
+struct DetailsPageInit {
+ int pageCount;
+ OPTIONSDIALOGPAGE *odp;
+};
+
+struct DetailsPageData {
+ DLGTEMPLATE *pTemplate;
+ HINSTANCE hInst;
+ DLGPROC dlgProc;
+ LPARAM dlgParam;
+ HWND hwnd;
+ HTREEITEM hItem;
+ int changed;
+ TCHAR *ptszTitle, *ptszTab;
+};
+
+struct DetailsData {
+ HANDLE hContact;
+ HANDLE hProtoAckEvent;
+ HINSTANCE hInstIcmp;
+ HFONT hBoldFont;
+ int pageCount;
+ int currentPage;
+ struct DetailsPageData *opd;
+ RECT rcDisplay, rcDisplayTab;
+ int updateAnimFrame;
+ TCHAR szUpdating[64];
+ int *infosUpdated;
+};
+
+static int PageSortProc(OPTIONSDIALOGPAGE *item1,OPTIONSDIALOGPAGE *item2)
+{
+ int res;
+ if (!lstrcmp(item1->ptszTitle, TranslateT("Summary"))) return -1;
+ if (!lstrcmp(item2->ptszTitle, TranslateT("Summary"))) return 1;
+ if (res = lstrcmp(item1->ptszTitle, item2->ptszTitle)) return res;
+ if (item1->ptszTab && !item2->ptszTab) return -1;
+ if (!item1->ptszTab && item2->ptszTab) return 1;
+ if (!item1->ptszTab && !item2->ptszTab) return 0;
+ if (item1->ptszTab && !lstrcmp(item1->ptszTab, TranslateT("General"))) return -1;
+ if (item2->ptszTab && !lstrcmp(item2->ptszTab, TranslateT("General"))) return 1;
+ return lstrcmp(item1->ptszTab, item2->ptszTab);
+}
+
+static INT_PTR ShowDetailsDialogCommand(WPARAM wParam,LPARAM)
+{
+ HWND hwnd;
+ PROPSHEETHEADER psh;
+ struct DetailsPageInit opi;
+ int i;
+
+ if(hwnd=WindowList_Find(hWindowList,(HANDLE)wParam)) {
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ return 0;
+ }
+
+ opi.pageCount=0;
+ opi.odp=NULL;
+ NotifyEventHooks(hDetailsInitEvent,(WPARAM)&opi,wParam);
+ if(opi.pageCount==0) return 0;
+ qsort(opi.odp,opi.pageCount,sizeof(OPTIONSDIALOGPAGE),(int (*)(const void*,const void*))PageSortProc);
+
+ ZeroMemory(&psh,sizeof(psh));
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_PROPSHEETPAGE|PSH_NOAPPLYNOW;
+ psh.hwndParent = NULL;
+ psh.nPages = opi.pageCount;
+ psh.pStartPage = 0;
+ psh.pszCaption = (TCHAR*)wParam; //more abuses of structure: this is hContact
+ psh.ppsp = (PROPSHEETPAGE*)opi.odp; //blatent misuse of the structure, but what the hell
+
+ CreateDialogParam(hMirandaInst,MAKEINTRESOURCE(IDD_DETAILS), NULL, DlgProcDetails, (LPARAM)&psh);
+ for(i=0;i<opi.pageCount;i++) {
+ //cleanup moved to WM_DESTROY
+ //mir_free((char*)opi.odp[i].pszTitle);
+ //mir_free((char*)opi.odp[i].pszTab);
+ if(opi.odp[i].pszGroup!=NULL) mir_free(opi.odp[i].pszGroup);
+ if((DWORD_PTR)opi.odp[i].pszTemplate&0xFFFF0000) mir_free((char*)opi.odp[i].pszTemplate);
+ }
+ mir_free(opi.odp);
+ return 0;
+}
+
+static INT_PTR AddDetailsPage(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE *odp=(OPTIONSDIALOGPAGE*)lParam, *dst;
+ struct DetailsPageInit *opi=(struct DetailsPageInit*)wParam;
+
+ if(odp==NULL||opi==NULL) return 1;
+ if(odp->cbSize!=sizeof(OPTIONSDIALOGPAGE)
+ && odp->cbSize != OPTIONPAGE_OLD_SIZE2
+ && odp->cbSize != OPTIONPAGE_OLD_SIZE3)
+ return 1;
+
+ opi->odp=(OPTIONSDIALOGPAGE*)mir_realloc(opi->odp,sizeof(OPTIONSDIALOGPAGE)*(opi->pageCount+1));
+ dst = opi->odp + opi->pageCount;
+ dst->cbSize = sizeof(OPTIONSDIALOGPAGE);
+ dst->hInstance = odp->hInstance;
+ dst->pfnDlgProc = odp->pfnDlgProc;
+ dst->position = odp->position;
+ if((DWORD_PTR)odp->pszTemplate&0xFFFF0000) dst->pszTemplate = mir_strdup(odp->pszTemplate);
+ else dst->pszTemplate = odp->pszTemplate;
+
+ #if defined(_UNICODE)
+ if ( odp->flags & ODPF_UNICODE )
+ {
+ dst->ptszTitle = (odp->ptszTitle==0) ? NULL : mir_wstrdup(odp->ptszTitle);
+ dst->ptszTab = (!(odp->flags & ODPF_USERINFOTAB) || !odp->ptszTab) ? NULL : mir_wstrdup(odp->ptszTab);
+ }
+ else
+ #endif
+ {
+ if ( odp->flags & ODPF_DONTTRANSLATE )
+ dst->ptszTitle = (odp->pszTitle==0) ? NULL : mir_a2t(odp->pszTitle);
+ else
+ dst->ptszTitle = (odp->pszTitle==0) ? NULL : LangPackPcharToTchar(odp->pszTitle);
+ dst->ptszTab = (!(odp->flags & ODPF_USERINFOTAB) || !odp->pszTab) ? NULL : LangPackPcharToTchar(odp->pszTab);
+ }
+
+ dst->pszGroup = NULL;
+ dst->groupPosition = odp->groupPosition;
+ dst->hGroupIcon = odp->hGroupIcon;
+ dst->hIcon = odp->hIcon;
+ if ( odp->cbSize == sizeof(OPTIONSDIALOGPAGE))
+ dst->dwInitParam = odp->dwInitParam;
+ opi->pageCount++;
+ return 0;
+}
+
+static void ThemeDialogBackground(HWND hwnd)
+{
+ if (enableThemeDialogTexture)
+ enableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
+}
+
+static void CreateDetailsTabs( HWND hwndDlg, struct DetailsData* dat, struct DetailsPageData* ppg )
+{
+ HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABS);
+ int i, sel=0, pages=0;
+ TCITEM tie;
+ tie.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
+ tie.iImage = -1;
+ TabCtrl_DeleteAllItems(hwndTab);
+ for ( i=0; i < dat->pageCount; i++ ) {
+ if (!dat->opd[i].ptszTab || lstrcmp(dat->opd[i].ptszTitle, ppg->ptszTitle)) continue;
+
+ tie.pszText = TranslateTS(dat->opd[i].ptszTab);
+ tie.lParam = i;
+ TabCtrl_InsertItem(hwndTab, pages, &tie);
+ if (!lstrcmp(dat->opd[i].ptszTab,ppg->ptszTab))
+ sel = pages;
+ pages++;
+ }
+ TabCtrl_SetCurSel(hwndTab,sel);
+
+ LONG style = GetWindowLong(hwndTab, GWL_STYLE);
+ SetWindowLong(hwndTab, GWL_STYLE, pages > 1 ? style | WS_TABSTOP : style & ~WS_TABSTOP);
+}
+
+static void CreateDetailsPageWindow( HWND hwndDlg, struct DetailsData* dat, struct DetailsPageData* ppg )
+{
+ RECT *rc = ppg->ptszTab ? &dat->rcDisplayTab : &dat->rcDisplay;
+ ppg->hwnd=CreateDialogIndirectParam(ppg->hInst,ppg->pTemplate,hwndDlg,ppg->dlgProc,(LPARAM)dat->hContact);
+ ThemeDialogBackground(ppg->hwnd);
+ SetWindowPos(ppg->hwnd, HWND_TOP, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, 0);
+ SetWindowPos(ppg->hwnd, HWND_TOP, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, 0);
+ {
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_PARAMCHANGED;
+ pshn.hdr.hwndFrom = ppg->hwnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)ppg->dlgParam;
+ SendMessage(ppg->hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+
+ pshn.hdr.code=PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom=ppg->hwnd;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)dat->hContact;
+ SendMessage(ppg->hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+}
+
+static int UserInfoContactDelete(WPARAM wParam,LPARAM)
+{
+ HWND hwnd;
+ hwnd=WindowList_Find(hWindowList,(HANDLE)wParam);
+ if(hwnd!=NULL) DestroyWindow(hwnd);
+ return 0;
+}
+
+#define HM_PROTOACK (WM_USER+10)
+#define M_CHECKONLINE (WM_USER+11)
+static INT_PTR CALLBACK DlgProcDetails(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct DetailsData *dat =(struct DetailsData*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, SKINICON_OTHER_USERDETAILS);
+ {
+ PROPSHEETHEADER *psh = (PROPSHEETHEADER*)lParam;
+ dat = (DetailsData*)mir_calloc(sizeof(DetailsData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->hContact = (HANDLE)psh->pszCaption;
+ dat->hProtoAckEvent = HookEventMessage(ME_PROTO_ACK,hwndDlg,HM_PROTOACK);
+ WindowList_Add(hWindowList,hwndDlg,dat->hContact);
+ {
+ TCHAR *name, oldTitle[256], newTitle[256];
+ if (dat->hContact == NULL)
+ name = TranslateT("Owner");
+ else
+ name = cli.pfnGetContactDisplayName( dat->hContact, 0 );
+
+ GetWindowText( hwndDlg, oldTitle, SIZEOF( oldTitle ));
+ mir_sntprintf( newTitle, SIZEOF(newTitle), oldTitle, name );
+ SetWindowText( hwndDlg, newTitle );
+
+ GetDlgItemText( hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF( oldTitle ));
+ mir_sntprintf( newTitle, SIZEOF(newTitle), oldTitle, name );
+ SetDlgItemText( hwndDlg, IDC_HEADERBAR, newTitle );
+ }
+ { LOGFONT lf;
+ HFONT hNormalFont=(HFONT)SendDlgItemMessage(hwndDlg,IDC_NAME,WM_GETFONT,0,0);
+ GetObject(hNormalFont,sizeof(lf),&lf);
+ lf.lfWeight=FW_BOLD;
+ dat->hBoldFont=CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg,IDC_NAME,WM_SETFONT,(WPARAM)dat->hBoldFont,0);
+ }
+ { OPTIONSDIALOGPAGE *odp;
+ int i;
+ TVINSERTSTRUCT tvis;
+ DBVARIANT dbv;
+
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_PAGETREE);
+
+ dat->currentPage = 0;
+ if ( DBGetContactSettingTString( NULL, "UserInfo", "LastTab", &dbv ))
+ dbv.type = DBVT_DELETED;
+ dat->pageCount = psh->nPages;
+ dat->opd = (DetailsPageData*)mir_calloc(sizeof(DetailsPageData) * dat->pageCount);
+ odp = (OPTIONSDIALOGPAGE*)psh->ppsp;
+
+ for ( i=0; i < dat->pageCount; i++ ) {
+ dat->opd[i].pTemplate = (LPDLGTEMPLATE)LockResource(LoadResource(odp[i].hInstance,
+ FindResourceA(odp[i].hInstance, odp[i].pszTemplate, MAKEINTRESOURCEA(5))));
+ dat->opd[i].dlgProc = odp[i].pfnDlgProc;
+ dat->opd[i].dlgParam = odp[i].dwInitParam;
+ dat->opd[i].hInst = odp[i].hInstance;
+
+ dat->opd[i].ptszTitle = odp[i].ptszTitle;
+ dat->opd[i].ptszTab = odp[i].ptszTab;
+
+ if (i && dat->opd[i].ptszTab && !lstrcmp(dat->opd[i-1].ptszTitle, dat->opd[i].ptszTitle)) {
+ dat->opd[i].hItem = dat->opd[i-1].hItem;
+ continue;
+ }
+
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
+ tvis.item.lParam = (LPARAM) i;
+ if (odp[i].flags & ODPF_DONTTRANSLATE)
+ tvis.item.pszText = mir_tstrdup(odp[i].ptszTitle);
+ else
+ tvis.item.pszText = TranslateTS(odp[i].ptszTitle);
+ if ( dbv.type != DBVT_DELETED && !lstrcmp( tvis.item.pszText, dbv.ptszVal ))
+ dat->currentPage = i;
+ dat->opd[i].hItem = TreeView_InsertItem(hwndTree, &tvis);
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ {
+ HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABS);
+
+ TCITEM tci;
+ tci.mask = TCIF_TEXT | TCIF_IMAGE;
+ tci.iImage = -1;
+ tci.pszText = _T("X");
+ TabCtrl_InsertItem(hwndTab,0,&tci);
+
+ GetWindowRect(hwndTab, &dat->rcDisplayTab);
+ TabCtrl_AdjustRect(hwndTab, FALSE, &dat->rcDisplayTab);
+ { POINT pt={0,0};
+ ClientToScreen(hwndDlg, &pt);
+ OffsetRect(&dat->rcDisplayTab, -pt.x, -pt.y);
+ }
+
+ TabCtrl_DeleteAllItems(hwndTab);
+
+ GetWindowRect(hwndTab, &dat->rcDisplay);
+ TabCtrl_AdjustRect(hwndTab, FALSE, &dat->rcDisplay);
+ { POINT pt={0,0};
+ ClientToScreen(hwndDlg, &pt);
+ OffsetRect(&dat->rcDisplay, -pt.x, -pt.y);
+ } }
+
+ TreeView_SelectItem(GetDlgItem(hwndDlg, IDC_PAGETREE), dat->opd[dat->currentPage].hItem);
+
+ dat->updateAnimFrame = 0;
+ GetDlgItemText(hwndDlg,IDC_UPDATING,dat->szUpdating,SIZEOF(dat->szUpdating));
+ SendMessage(hwndDlg,M_CHECKONLINE,0,0);
+ if (!CallContactService(dat->hContact,PSS_GETINFO,SGIF_ONOPEN,0)) {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_UPDATE),FALSE);
+ SetTimer(hwndDlg,1,100,NULL);
+ } else
+ ShowWindow(GetDlgItem(hwndDlg,IDC_UPDATING),SW_HIDE);
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_PAGETREE));
+
+ return TRUE;
+ }
+ case WM_TIMER:
+ {
+ TCHAR str[128];
+ mir_sntprintf(str,SIZEOF(str), _T("%.*s%s%.*s"),dat->updateAnimFrame%10,_T("........."),dat->szUpdating,dat->updateAnimFrame%10,_T("........."));
+ SetDlgItemText(hwndDlg,IDC_UPDATING,str);
+ if(++dat->updateAnimFrame==UPDATEANIMFRAMES) dat->updateAnimFrame=0;
+ break;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch (GetDlgCtrlID((HWND)lParam)) {
+ case IDC_WHITERECT:
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ case IDC_UPDATING:
+ {
+ COLORREF textCol,bgCol,newCol;
+ int ratio;
+ textCol=GetSysColor(COLOR_BTNTEXT);
+ bgCol=GetSysColor(COLOR_3DFACE);
+ ratio=abs(UPDATEANIMFRAMES/2-dat->updateAnimFrame)*510/UPDATEANIMFRAMES;
+ newCol=RGB(GetRValue(bgCol)+(GetRValue(textCol)-GetRValue(bgCol))*ratio/256,
+ GetGValue(bgCol)+(GetGValue(textCol)-GetGValue(bgCol))*ratio/256,
+ GetBValue(bgCol)+(GetBValue(textCol)-GetBValue(bgCol))*ratio/256);
+ SetTextColor((HDC)wParam,newCol);
+ SetBkColor((HDC)wParam,GetSysColor(COLOR_3DFACE));
+ return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
+ }
+ default:
+ SetBkMode((HDC)wParam,TRANSPARENT);
+ return (INT_PTR)GetStockObject(NULL_BRUSH);
+ }
+ break;
+
+ case PSM_CHANGED:
+ dat->opd[dat->currentPage].changed=1;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ {
+ int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.code=PSN_INFOCHANGED;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)dat->hContact;
+ for(i=0;i<dat->pageCount;i++) {
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ if(dat->opd[i].hwnd!=NULL)
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ break;
+ }
+ case M_CHECKONLINE:
+ {
+ char *szProto;
+ if (dat->hContact != NULL) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)dat->hContact,0);
+ if(szProto==NULL) {EnableWindow(GetDlgItem(hwndDlg,IDC_UPDATE),FALSE); break;}
+ if(CallProtoService(szProto,PS_GETSTATUS,0,0)<ID_STATUS_ONLINE) EnableWindow(GetDlgItem(hwndDlg,IDC_UPDATE),FALSE);
+ else EnableWindow(GetDlgItem(hwndDlg,IDC_UPDATE),!IsWindowVisible(GetDlgItem(hwndDlg,IDC_UPDATING)));
+ }
+ break;
+ }
+ case HM_PROTOACK:
+ {
+ ACKDATA *ack=(ACKDATA*)lParam;
+ int i;
+
+ if(ack->hContact==NULL && ack->type==ACKTYPE_STATUS) {
+ SendMessage(hwndDlg,M_CHECKONLINE,0,0);
+ break;
+ }
+ if(ack->hContact!=dat->hContact) break;
+ if(ack->type!=ACKTYPE_GETINFO) break;
+ SendMessage(hwndDlg,PSM_FORCECHANGED,0,0);
+ /* if they're not gonna send any more ACK's don't let that mean we should crash */
+ if (!ack->hProcess && !ack->lParam) {
+ ShowWindow(GetDlgItem(hwndDlg,IDC_UPDATING),SW_HIDE);
+ KillTimer(hwndDlg,1);
+ SendMessage(hwndDlg,M_CHECKONLINE,0,0);
+ break;
+ } //if
+ if(dat->infosUpdated==NULL) dat->infosUpdated=(int*)mir_calloc(sizeof(int)*(INT_PTR)ack->hProcess);
+ if(ack->result==ACKRESULT_SUCCESS || ack->result==ACKRESULT_FAILED) dat->infosUpdated[ack->lParam]=1;
+ for(i=0;i<(int)ack->hProcess;i++)
+ if(dat->infosUpdated[i]==0) break;
+ if(i==(int)ack->hProcess) {
+ ShowWindow(GetDlgItem(hwndDlg,IDC_UPDATING),SW_HIDE);
+ KillTimer(hwndDlg,1);
+ SendMessage(hwndDlg,M_CHECKONLINE,0,0);
+ } }
+ break;
+
+ case WM_NOTIFY:
+ switch(wParam) {
+ case IDC_TABS:
+ case IDC_PAGETREE:
+ switch(((LPNMHDR)lParam)->code)
+ {
+ case TCN_SELCHANGING:
+ case TVN_SELCHANGING:
+ if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL)
+ {
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)dat->hContact;
+ if (SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn))
+ {
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ break;
+
+ case TCN_SELCHANGE:
+ if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL)
+ {
+ HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABS);
+ ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE);
+
+ TCITEM tie;
+ TVITEM tvi;
+
+ tie.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tie);
+ dat->currentPage = tie.lParam;
+
+ tvi.hItem = TreeView_GetNextItem(GetDlgItem(hwndDlg,IDC_PAGETREE), NULL, TVGN_CARET);
+ tvi.mask = TVIF_PARAM;
+ tvi.lParam = dat->currentPage;
+ TreeView_SetItem(GetDlgItem(hwndDlg,IDC_PAGETREE), &tvi);
+
+ if (dat->currentPage != -1)
+ {
+ if (dat->opd[dat->currentPage].hwnd == NULL)
+ CreateDetailsPageWindow(hwndDlg, dat, &dat->opd[dat->currentPage]);
+ ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOWNA);
+ }
+ }
+ break;
+
+ case TVN_SELCHANGED:
+ if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL)
+ ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE);
+
+ {
+ LPNMTREEVIEW pnmtv = (LPNMTREEVIEW) lParam;
+ TVITEM tvi = pnmtv->itemNew;
+ dat->currentPage = tvi.lParam;
+
+ if(dat->currentPage != -1)
+ {
+ CreateDetailsTabs(hwndDlg, dat, &dat->opd[dat->currentPage]);
+ if (dat->opd[dat->currentPage].hwnd == NULL)
+ CreateDetailsPageWindow(hwndDlg, dat, &dat->opd[dat->currentPage]);
+ ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOWNA);
+
+ }
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDCANCEL:
+ {
+ int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)dat->hContact;
+ pshn.hdr.code=PSN_RESET;
+ for(i=0;i<dat->pageCount;i++) {
+ if(dat->opd[i].hwnd==NULL || !dat->opd[i].changed) continue;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn);
+ }
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDOK:
+ {
+ int i;
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom=0;
+ pshn.lParam=(LPARAM)dat->hContact;
+ if(dat->currentPage!=-1) {
+ pshn.hdr.code=PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom=dat->opd[dat->currentPage].hwnd;
+ if(SendMessage(dat->opd[dat->currentPage].hwnd,WM_NOTIFY,0,(LPARAM)&pshn))
+ break;
+ }
+
+ pshn.hdr.code=PSN_APPLY;
+ for(i=0;i<dat->pageCount;i++) {
+ if(dat->opd[i].hwnd==NULL || !dat->opd[i].changed) continue;
+ pshn.hdr.hwndFrom=dat->opd[i].hwnd;
+ if(SendMessage(dat->opd[i].hwnd,WM_NOTIFY,0,(LPARAM)&pshn)==PSNRET_INVALID_NOCHANGEPAGE) {
+ TreeView_Select(GetDlgItem(hwndDlg,IDC_PAGETREE), dat->opd[i].hItem, TVGN_CARET);
+ if(dat->currentPage!=-1) ShowWindow(dat->opd[dat->currentPage].hwnd,SW_HIDE);
+ dat->currentPage=i;
+ ShowWindow(dat->opd[dat->currentPage].hwnd,SW_SHOW);
+ return 0;
+ }
+ }
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDC_UPDATE:
+ if(dat->infosUpdated!=NULL) {mir_free(dat->infosUpdated); dat->infosUpdated=NULL;}
+ if(dat->hContact != NULL) {
+ if (!CallContactService(dat->hContact,PSS_GETINFO,0,0)) {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_UPDATE),FALSE);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_UPDATING),SW_SHOW);
+ SetTimer(hwndDlg,1,100,NULL);
+ }
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDOK));
+ break;
+
+ case WM_DESTROY:
+ {
+ TCHAR name[128];
+ TVITEM tvi;
+ tvi.mask = TVIF_TEXT;
+ tvi.hItem = dat->opd[dat->currentPage].hItem;
+ tvi.pszText=name;
+ tvi.cchTextMax=SIZEOF(name);
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_PAGETREE), &tvi);
+ DBWriteContactSettingTString(NULL,"UserInfo","LastTab", name);
+ }
+ Window_FreeIcon_IcoLib(hwndDlg);
+ SendDlgItemMessage(hwndDlg,IDC_NAME,WM_SETFONT,SendDlgItemMessage(hwndDlg,IDC_WHITERECT,WM_GETFONT,0,0),0);
+ DeleteObject(dat->hBoldFont);
+ WindowList_Remove(hWindowList,hwndDlg);
+ UnhookEvent(dat->hProtoAckEvent);
+ { int i;
+ for(i=0;i<dat->pageCount;i++)
+ {
+ if(dat->opd[i].hwnd!=NULL) DestroyWindow(dat->opd[i].hwnd);
+ mir_free(dat->opd[i].ptszTitle);
+ mir_free(dat->opd[i].ptszTab);
+ }
+ }
+ mir_free(dat->infosUpdated);
+ mir_free(dat->opd);
+ mir_free(dat);
+ break;
+ }
+ return FALSE;
+}
+
+int ShutdownUserInfo(WPARAM, LPARAM)
+{
+ WindowList_BroadcastAsync(hWindowList,WM_DESTROY,0,0);
+ return 0;
+}
+
+int LoadUserInfoModule(void)
+{
+ CLISTMENUITEM mi = { 0 };
+
+ CreateServiceFunction(MS_USERINFO_SHOWDIALOG,ShowDetailsDialogCommand);
+ hDetailsInitEvent=CreateHookableEvent(ME_USERINFO_INITIALISE);
+ HookEvent(ME_USERINFO_INITIALISE,DetailsInit);
+ HookEvent(ME_DB_CONTACT_DELETED,UserInfoContactDelete);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN,ShutdownUserInfo);
+ CreateServiceFunction(MS_USERINFO_ADDPAGE,AddDetailsPage);
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.position = 1000050000;
+ mi.icolibItem = GetSkinIconHandle( SKINICON_OTHER_USERDETAILS );
+ mi.pszName = LPGEN("User &Details");
+ mi.pszService = MS_USERINFO_SHOWDIALOG;
+ CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi);
+
+ mi.position = 500050000;
+ mi.pszName = LPGEN("View/Change My &Details...");
+ CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+
+ hWindowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST,0,0);
+ return 0;
+}
diff --git a/src/modules/useronline/useronline.cpp b/src/modules/useronline/useronline.cpp
new file mode 100644
index 0000000000..23371de267
--- /dev/null
+++ b/src/modules/useronline/useronline.cpp
@@ -0,0 +1,117 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static int uniqueEventId=0;
+
+static int UserOnlineSettingChanged(WPARAM wParam,LPARAM lParam)
+{
+ DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam;
+ int newStatus,oldStatus;
+
+ if((HANDLE)wParam==NULL || strcmp(cws->szSetting,"Status")) return 0;
+ newStatus=cws->value.wVal;
+ oldStatus=DBGetContactSettingWord((HANDLE)wParam,"UserOnline","OldStatus",ID_STATUS_OFFLINE);
+ DBWriteContactSettingWord((HANDLE)wParam,"UserOnline","OldStatus",(WORD)newStatus);
+ if(CallService(MS_IGNORE_ISIGNORED,wParam,IGNOREEVENT_USERONLINE)) return 0;
+ if(DBGetContactSettingByte((HANDLE)wParam,"CList","Hidden",0)) return 0;
+ if(newStatus==ID_STATUS_OFFLINE&&oldStatus!=ID_STATUS_OFFLINE) {
+ // Remove the event from the queue if it exists since they are now offline
+ int lastEvent = (int)DBGetContactSettingDword((HANDLE)wParam,"UserOnline","LastEvent",0);
+
+ if (lastEvent) {
+ CallService(MS_CLIST_REMOVEEVENT,wParam,(LPARAM)lastEvent);
+ DBWriteContactSettingDword((HANDLE)wParam,"UserOnline", "LastEvent", 0);
+ }
+ }
+ if((newStatus==ID_STATUS_ONLINE || newStatus==ID_STATUS_FREECHAT) &&
+ oldStatus!=ID_STATUS_ONLINE && oldStatus!=ID_STATUS_FREECHAT) {
+ {
+ DWORD ticked = db_dword_get(NULL, "UserOnline", cws->szModule, GetTickCount());
+ // only play the sound (or show event) if this event happens at least 10 secs after the proto went from offline
+ if ( GetTickCount() - ticked > (1000*10) ) {
+ CLISTEVENT cle;
+ TCHAR tooltip[256];
+
+ ZeroMemory(&cle,sizeof(cle));
+ cle.cbSize=sizeof(cle);
+ cle.flags=CLEF_ONLYAFEW | CLEF_TCHAR;
+ cle.hContact=(HANDLE)wParam;
+ cle.hDbEvent=(HANDLE)(uniqueEventId++);
+ cle.hIcon = LoadSkinIcon( SKINICON_OTHER_USERONLINE, false );
+ cle.pszService="UserOnline/Description";
+ mir_sntprintf(tooltip,SIZEOF(tooltip),TranslateT("%s is Online"), cli.pfnGetContactDisplayName(( HANDLE )wParam, 0 ));
+ cle.ptszTooltip=tooltip;
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&cle);
+ IconLib_ReleaseIcon( cle.hIcon, 0 );
+ DBWriteContactSettingDword(cle.hContact,"UserOnline", "LastEvent", (DWORD)cle.hDbEvent);
+ SkinPlaySound("UserOnline");
+ }
+ }
+ }
+ return 0;
+}
+
+static int UserOnlineAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA * ack = (ACKDATA*) lParam;
+ if ( ack != 0 && ack->szModule && ack->type == ACKTYPE_STATUS && ack->result == ACKRESULT_SUCCESS && ack->hProcess == (HANDLE)ID_STATUS_OFFLINE) {
+ // if going from offline to any other mode, remember when it happened.
+ db_dword_set(NULL, "UserOnline", ack->szModule, GetTickCount());
+ }
+ return 0;
+}
+
+static int UserOnlineModulesLoaded(WPARAM, LPARAM)
+{
+ // reset the counter
+ for ( int j = 0; j < accounts.getCount(); j++ )
+ if ( Proto_IsAccountEnabled( accounts[j] )) db_dword_set( NULL, "UserOnline", accounts[j]->szModuleName, GetTickCount());
+
+ return 0;
+}
+
+static int UserOnlineAccountsChanged( WPARAM eventCode, LPARAM lParam )
+{
+ PROTOACCOUNT* pa = (PROTOACCOUNT*)lParam;
+
+ switch( eventCode ) {
+ case PRAC_ADDED:
+ case PRAC_CHECKED:
+ // reset the counter
+ if ( Proto_IsAccountEnabled( pa ))
+ db_dword_set( NULL, "UserOnline", pa->szModuleName, GetTickCount());
+ break;
+ }
+ return 0;
+}
+
+int LoadUserOnlineModule(void)
+{
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED,UserOnlineSettingChanged);
+ HookEvent(ME_PROTO_ACK, UserOnlineAck);
+ HookEvent(ME_SYSTEM_MODULESLOADED, UserOnlineModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, UserOnlineAccountsChanged);
+ SkinAddNewSoundEx("UserOnline","Alerts","Online");
+ return 0;
+}
diff --git a/src/modules/utils/bmpfilter.cpp b/src/modules/utils/bmpfilter.cpp
new file mode 100644
index 0000000000..6f92eba126
--- /dev/null
+++ b/src/modules/utils/bmpfilter.cpp
@@ -0,0 +1,242 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <olectl.h>
+
+#include "m_png.h"
+#include "m_imgsrvc.h"
+
+static INT_PTR sttBitmapLoader( const TCHAR* ptszFileName )
+{
+ IPicture *pic;
+ HBITMAP hBmp,hBmpCopy;
+ HBITMAP hOldBitmap, hOldBitmap2;
+ BITMAP bmpInfo;
+ HDC hdc,hdcMem1,hdcMem2;
+ short picType;
+
+ TCHAR szFilename[MAX_PATH];
+ if ( !pathToAbsoluteT(ptszFileName, szFilename, NULL ))
+ mir_sntprintf(szFilename, SIZEOF(szFilename), _T("%s"), ptszFileName);
+
+ int filenameLen = lstrlen(szFilename);
+ if ( filenameLen > 4 ) {
+ TCHAR* pszExt = szFilename + filenameLen - 4;
+
+ if ( ServiceExists( MS_IMG_LOAD ))
+ return CallService( MS_IMG_LOAD, (WPARAM)szFilename, IMGL_TCHAR );
+
+ if ( !lstrcmpi( pszExt, _T(".bmp")) || !lstrcmpi( pszExt, _T(".rle"))) {
+ //LoadImage can do this much faster
+ return (INT_PTR)LoadImage( hMirandaInst, szFilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
+ }
+
+ if ( !lstrcmpi( pszExt, _T(".png"))) {
+ HANDLE hFile, hMap = NULL;
+ BYTE* ppMap = NULL;
+ INT_PTR cbFileSize = 0;
+ BITMAPINFOHEADER* pDib;
+ BYTE* pDibBits;
+
+ if ( !ServiceExists( MS_PNG2DIB )) {
+ MessageBox( NULL, TranslateT( "You need an image services plugin to process PNG images." ), TranslateT( "Error" ), MB_OK );
+ return 0;
+ }
+
+ if (( hFile = CreateFile( szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE )
+ if (( hMap = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL )) != NULL )
+ if (( ppMap = ( BYTE* )MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 )) != NULL )
+ cbFileSize = GetFileSize( hFile, NULL );
+
+ if ( cbFileSize != 0 ) {
+ PNG2DIB param;
+ param.pSource = ppMap;
+ param.cbSourceSize = cbFileSize;
+ param.pResult = &pDib;
+ if ( CallService( MS_PNG2DIB, 0, ( LPARAM )&param )) {
+ pDibBits = ( BYTE* )( pDib+1 );
+ HDC sDC = GetDC( NULL );
+ HBITMAP hBitmap = CreateDIBitmap( sDC, pDib, CBM_INIT, pDibBits, ( BITMAPINFO* )pDib, DIB_PAL_COLORS );
+ SelectObject( sDC, hBitmap );
+ ReleaseDC( NULL, sDC );
+ GlobalFree( pDib );
+ cbFileSize = (INT_PTR)hBitmap;
+ }
+ else cbFileSize = 0;
+ }
+
+ if ( ppMap != NULL ) UnmapViewOfFile( ppMap );
+ if ( hMap != NULL ) CloseHandle( hMap );
+ if ( hFile != NULL ) CloseHandle( hFile );
+
+ return (INT_PTR)cbFileSize;
+ } }
+
+ if (S_OK != OleLoadPicturePath( LPOLESTR(( const wchar_t* )StrConvU(szFilename)), NULL, 0, 0, IID_IPicture, (PVOID*)&pic ))
+ return 0;
+
+ pic->get_Type(&picType);
+ if (picType!=PICTYPE_BITMAP) {
+ pic->Release();
+ return 0;
+ }
+ OLE_HANDLE hOleBmp;
+ pic->get_Handle(&hOleBmp);
+ hBmp = (HBITMAP)hOleBmp;
+ GetObject(hBmp,sizeof(bmpInfo),&bmpInfo);
+
+ //need to copy bitmap so we can free the IPicture
+ hdc=GetDC(NULL);
+ hdcMem1=CreateCompatibleDC(hdc);
+ hdcMem2=CreateCompatibleDC(hdc);
+ hOldBitmap=( HBITMAP )SelectObject(hdcMem1,hBmp);
+ hBmpCopy=CreateCompatibleBitmap(hdcMem1,bmpInfo.bmWidth,bmpInfo.bmHeight);
+ hOldBitmap2=( HBITMAP )SelectObject(hdcMem2,hBmpCopy);
+ BitBlt(hdcMem2,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,hdcMem1,0,0,SRCCOPY);
+ SelectObject(hdcMem1,hOldBitmap);
+ SelectObject(hdcMem2,hOldBitmap2);
+ DeleteDC(hdcMem2);
+ DeleteDC(hdcMem1);
+ ReleaseDC(NULL,hdc);
+
+ DeleteObject(hBmp);
+ pic->Release();
+ return (INT_PTR)hBmpCopy;
+}
+
+static INT_PTR BmpFilterLoadBitmap(WPARAM, LPARAM lParam)
+{
+ return sttBitmapLoader( StrConvT(( const char* )lParam ));
+}
+
+#if defined( _UNICODE )
+static INT_PTR BmpFilterLoadBitmapW(WPARAM, LPARAM lParam)
+{
+ return sttBitmapLoader(( const wchar_t* )lParam );
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR BmpFilterGetStrings(WPARAM wParam,LPARAM lParam)
+{
+ int bytesLeft=wParam;
+ char *filter=(char*)lParam,*pfilter;
+
+ lstrcpynA(filter,Translate("All Bitmaps"),bytesLeft); bytesLeft-=lstrlenA(filter);
+ strncat(filter," (*.bmp;*.jpg;*.gif;*.png)",bytesLeft);
+ pfilter=filter+lstrlenA(filter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpynA(pfilter,Translate("Windows Bitmaps"),bytesLeft); bytesLeft-=lstrlenA(pfilter);
+ strncat(pfilter," (*.bmp;*.rle)",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*.BMP;*.RLE",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpynA(pfilter,Translate("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlenA(pfilter);
+ strncat(pfilter," (*.jpg;*.jpeg)",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*.JPG;*.JPEG",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpynA(pfilter,Translate("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlenA(pfilter);
+ strncat(pfilter," (*.gif)",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*.GIF",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpynA(pfilter,Translate("PNG Bitmaps"),bytesLeft); bytesLeft-=lstrlenA(pfilter);
+ strncat(pfilter," (*.png)",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*.PNG",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpynA(pfilter,Translate("All Files"),bytesLeft); bytesLeft-=lstrlenA(pfilter);
+ strncat(pfilter," (*)",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpynA(pfilter,"*",bytesLeft);
+ pfilter+=lstrlenA(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ if (bytesLeft) *pfilter='\0';
+ return 0;
+}
+
+#if defined( _UNICODE )
+static INT_PTR BmpFilterGetStringsW(WPARAM wParam,LPARAM lParam)
+{
+ int bytesLeft=wParam;
+ TCHAR *filter=(TCHAR*)lParam,*pfilter;
+
+ lstrcpyn(filter,TranslateT("All Bitmaps"),bytesLeft); bytesLeft-=lstrlen(filter);
+ _tcsncat(filter, _T(" (*.bmp;*.jpg;*.gif;*.png)"), bytesLeft );
+ pfilter=filter+lstrlen(filter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpyn(pfilter,TranslateT("Windows Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
+ _tcsncat(pfilter,_T(" (*.bmp;*.rle)"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*.BMP;*.RLE"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpyn(pfilter,TranslateT("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
+ _tcsncat(pfilter,_T(" (*.jpg;*.jpeg)"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*.JPG;*.JPEG"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpyn(pfilter,TranslateT("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
+ _tcsncat(pfilter,_T(" (*.gif)"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*.GIF"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpyn(pfilter,TranslateT("PNG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
+ _tcsncat(pfilter,_T(" (*.png)"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*.PNG"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ lstrcpyn(pfilter,TranslateT("All Files"),bytesLeft); bytesLeft-=lstrlen(pfilter);
+ _tcsncat(pfilter,_T(" (*)"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+ lstrcpyn(pfilter,_T("*"),bytesLeft);
+ pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
+
+ if (bytesLeft) *pfilter='\0';
+ return 0;
+}
+#endif
+
+int InitBitmapFilter(void)
+{
+ CreateServiceFunction(MS_UTILS_LOADBITMAP,BmpFilterLoadBitmap);
+ CreateServiceFunction(MS_UTILS_GETBITMAPFILTERSTRINGS,BmpFilterGetStrings);
+ #if defined( _UNICODE )
+ CreateServiceFunction(MS_UTILS_GETBITMAPFILTERSTRINGSW,BmpFilterGetStringsW);
+ CreateServiceFunction(MS_UTILS_LOADBITMAPW,BmpFilterLoadBitmapW);
+ #endif
+ return 0;
+}
diff --git a/src/modules/utils/colourpicker.cpp b/src/modules/utils/colourpicker.cpp
new file mode 100644
index 0000000000..ec1a87153c
--- /dev/null
+++ b/src/modules/utils/colourpicker.cpp
@@ -0,0 +1,107 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_CREATE:
+ SetWindowLongPtr(hwnd,0,0);
+ SetWindowLongPtr(hwnd,sizeof(COLORREF),0);
+ break;
+ case CPM_SETDEFAULTCOLOUR:
+ SetWindowLongPtr(hwnd,sizeof(COLORREF),lParam);
+ break;
+ case CPM_GETDEFAULTCOLOUR:
+ return GetWindowLongPtr(hwnd,sizeof(COLORREF));
+ case CPM_SETCOLOUR:
+ SetWindowLongPtr(hwnd,0,lParam);
+ InvalidateRect(hwnd,NULL,FALSE);
+ break;
+ case CPM_GETCOLOUR:
+ return GetWindowLongPtr(hwnd,0);
+ case WM_LBUTTONUP:
+ {
+ CHOOSECOLOR cc={0};
+ COLORREF custColours[16]={0};
+ custColours[0]=GetWindowLongPtr(hwnd,sizeof(COLORREF));
+ cc.lStructSize=sizeof(CHOOSECOLOR);
+ cc.hwndOwner=hwnd;
+ cc.hInstance=(HWND)hMirandaInst;
+ cc.rgbResult=GetWindowLongPtr(hwnd,0);
+ cc.lpCustColors=custColours;
+ cc.Flags=CC_ANYCOLOR|CC_FULLOPEN|CC_RGBINIT;
+ if(ChooseColor(&cc)) {
+ SetWindowLongPtr(hwnd,0,cc.rgbResult);
+ SendMessage(GetParent(hwnd),WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(hwnd),CPN_COLOURCHANGED),(LPARAM)hwnd);
+ InvalidateRect(hwnd,NULL,FALSE);
+ }
+ break;
+ }
+ case WM_ENABLE:
+ InvalidateRect(hwnd,NULL,FALSE);
+ break;
+ case WM_NCPAINT:
+ case WM_PAINT:
+ { PAINTSTRUCT ps;
+ HDC hdc1;
+ RECT rc;
+ HBRUSH hBrush;
+
+ hdc1=BeginPaint(hwnd,&ps);
+ GetClientRect(hwnd,&rc);
+ DrawEdge(hdc1,&rc,EDGE_ETCHED,BF_RECT);
+ InflateRect(&rc,-2,-2);
+ if(IsWindowEnabled(hwnd))
+ hBrush=CreateSolidBrush(GetWindowLongPtr(hwnd,0));
+ else
+ hBrush=CreateHatchBrush(HS_BDIAGONAL,GetSysColor(COLOR_GRAYTEXT));
+ SetBkColor(hdc1,GetSysColor(COLOR_BTNFACE));
+ FillRect(hdc1,&rc,hBrush);
+ DeleteObject(hBrush);
+ EndPaint(hwnd,&ps);
+ break;
+ }
+ case WM_DESTROY:
+ break;
+ }
+ return DefWindowProc(hwnd,message,wParam,lParam);
+}
+
+int InitColourPicker(void)
+{
+ WNDCLASS wcl;
+
+ wcl.lpfnWndProc=ColourPickerWndProc;
+ wcl.cbClsExtra=0;
+ wcl.cbWndExtra=sizeof(COLORREF)*2;
+ wcl.hInstance=hMirandaInst;
+ wcl.hCursor=NULL;
+ wcl.lpszClassName=WNDCLASS_COLOURPICKER;
+ wcl.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
+ wcl.hIcon=NULL;
+ wcl.lpszMenuName=NULL;
+ wcl.style=CS_HREDRAW|CS_VREDRAW|CS_GLOBALCLASS;
+ RegisterClass(&wcl);
+ return 0;
+}
diff --git a/src/modules/utils/hyperlink.cpp b/src/modules/utils/hyperlink.cpp
new file mode 100644
index 0000000000..62d307a958
--- /dev/null
+++ b/src/modules/utils/hyperlink.cpp
@@ -0,0 +1,274 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+struct HyperlinkWndData {
+ HFONT hEnableFont,hDisableFont;
+ RECT rcText;
+ COLORREF enableColor, disableColor, focusColor;
+ BYTE flags; /* see HLKF_* */
+};
+
+/* flags */
+#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */
+#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */
+
+/* internal messages */
+#define HLK_MEASURETEXT (WM_USER+1)
+#define HLK_INVALIDATE (WM_USER+2)
+
+static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ struct HyperlinkWndData *dat=(struct HyperlinkWndData*)GetWindowLongPtr(hwnd,0);
+ switch(msg) {
+ case WM_NCCREATE:
+ dat=(struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData));
+ if(dat==NULL) return FALSE; /* fail creation */
+ SetWindowLongPtr(hwnd,0,(LONG_PTR)dat); /* always succeeds */
+ /* fall thru */
+ case WM_SYSCOLORCHANGE:
+ if(!(dat->flags&HLKF_HASENABLECOLOR)) {
+ if(GetSysColorBrush(COLOR_HOTLIGHT)==NULL) dat->enableColor=RGB(0,0,255);
+ else dat->enableColor=GetSysColor(COLOR_HOTLIGHT);
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ }
+ if(!(dat->flags&HLKF_HASDISABLECOLOR))
+ dat->disableColor=GetSysColor(COLOR_GRAYTEXT);
+ break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
+ break;
+ case WM_MOUSEACTIVATE:
+ SetFocus(hwnd);
+ return MA_ACTIVATE;
+ case WM_GETDLGCODE:
+ {
+ if (lParam)
+ {
+ MSG *msg = (MSG *) lParam;
+ if (msg->message == WM_KEYDOWN)
+ {
+ if (msg->wParam == VK_TAB)
+ return 0;
+ if (msg->wParam == VK_ESCAPE)
+ return 0;
+ } else
+ if (msg->message == WM_CHAR)
+ {
+ if (msg->wParam == '\t')
+ return 0;
+ if (msg->wParam == 27)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+ }
+
+ case WM_KEYDOWN:
+ {
+ switch (wParam)
+ {
+ case VK_SPACE:
+ case VK_RETURN:
+ SendMessage(GetParent(hwnd),WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(hwnd),STN_CLICKED),(LPARAM)hwnd);
+ break;
+ }
+ return 0;
+ }
+
+ case WM_LBUTTONDOWN:
+ { POINT pt;
+ POINTSTOPOINT(pt,MAKEPOINTS(lParam));
+ if(!PtInRect(&dat->rcText,pt)) break;
+ SendMessage(GetParent(hwnd),WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(hwnd),STN_CLICKED),(LPARAM)hwnd);
+ return 0;
+ }
+ case WM_SETFONT:
+ { LOGFONT lf;
+ HFONT hFont;
+ if((HFONT)wParam==NULL) { /* use default system color */
+ dat->hEnableFont=dat->hDisableFont=NULL;
+ return 0;
+ }
+ if(GetObject((HFONT)wParam,sizeof(lf),&lf)) {
+ lf.lfUnderline=1;
+ hFont=CreateFontIndirect(&lf);
+ if(hFont!=NULL) {
+ dat->hEnableFont=hFont;
+ dat->hDisableFont=(HFONT)wParam;
+ if(LOWORD(lParam)) SendMessage(hwnd,HLK_INVALIDATE,0,0);
+ SendMessage(hwnd,HLK_MEASURETEXT,0,0);
+ }
+ }
+ return 0;
+ }
+ case WM_ERASEBKGND:
+ return TRUE;
+ case WM_ENABLE:
+ case HLK_INVALIDATE:
+ { RECT rcWnd;
+ POINT pt;
+ HWND hwndParent;
+ if(!GetWindowRect(hwnd,&rcWnd)) break;
+ pt.x=rcWnd.left;
+ pt.y=rcWnd.top;
+ hwndParent=GetParent(hwnd);
+ if(hwndParent==NULL) hwndParent=hwnd;
+ if(!ScreenToClient(hwndParent,&pt)) break;
+ rcWnd.right=pt.x+(rcWnd.right-rcWnd.left);
+ rcWnd.bottom=pt.y+(rcWnd.bottom-rcWnd.top);
+ rcWnd.left=pt.x;
+ rcWnd.top=pt.y;
+ InvalidateRect(hwndParent,&rcWnd,TRUE);
+ return 0;
+ }
+ case WM_GETFONT:
+ return (LRESULT)dat->hDisableFont;
+ case WM_CREATE:
+ case HLK_MEASURETEXT:
+ { TCHAR szText[256];
+ if(!GetWindowText(hwnd,szText,SIZEOF(szText))) return 0;
+ lParam=(LPARAM)szText;
+ /* fall thru */
+ case WM_SETTEXT:
+ { HFONT hPrevFont = NULL;
+ SIZE textSize;
+ RECT rc;
+ HDC hdc;
+ LONG style;
+ BOOL fMeasured=FALSE;
+ hdc=GetDC(hwnd);
+ if(hdc==NULL) return 0; /* text change failed */
+ if(dat->hEnableFont!=NULL) hPrevFont=(HFONT)SelectObject(hdc,dat->hEnableFont);
+ if(dat->hEnableFont==NULL || hPrevFont!=NULL) /* select failed? */
+ if(GetTextExtentPoint32(hdc,(TCHAR*)lParam,lstrlen((TCHAR*)lParam),&textSize))
+ if(GetClientRect(hwnd,&rc)) {
+ dat->rcText.top=0;
+ dat->rcText.bottom=dat->rcText.top+textSize.cy;
+ style=GetWindowLongPtr(hwnd,GWL_STYLE);
+ if(style&SS_CENTER) dat->rcText.left=(rc.right-textSize.cx)/2;
+ else if(style&SS_RIGHT) dat->rcText.left=rc.right-textSize.cx;
+ else dat->rcText.left=0;
+ dat->rcText.right=dat->rcText.left+textSize.cx;
+ fMeasured=TRUE;
+ }
+ if(dat->hEnableFont!=NULL && hPrevFont!=NULL) SelectObject(hdc,hPrevFont);
+ ReleaseDC(hwnd,hdc);
+ if(!fMeasured) return 0; /* text change failed */
+ SendMessage(hwnd,HLK_INVALIDATE,0,0);
+ break;
+ }}
+ case WM_SETCURSOR:
+ { POINT pt;
+ HCURSOR hCursor;
+ if(!GetCursorPos(&pt)) return FALSE;
+ if(!ScreenToClient(hwnd,&pt)) return FALSE;
+ if(PtInRect(&dat->rcText,pt)) {
+ hCursor=(HCURSOR)GetClassLongPtr(hwnd,GCLP_HCURSOR);
+ if(hCursor==NULL) hCursor=LoadCursor(NULL,IDC_HAND); /* Win2000+ */
+ }
+ else hCursor=LoadCursor(NULL,IDC_ARROW);
+ SetCursor(hCursor);
+ return TRUE;
+ }
+ case HLK_SETENABLECOLOUR:
+ { COLORREF prevColor=dat->enableColor;
+ dat->enableColor=(COLORREF)wParam;
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ dat->flags|=HLKF_HASENABLECOLOR;
+ return (LRESULT)prevColor;
+ }
+ case HLK_SETDISABLECOLOUR:
+ { COLORREF prevColor=dat->disableColor;
+ dat->disableColor=(COLORREF)wParam;
+ dat->flags|=HLKF_HASDISABLECOLOR;
+ return (LRESULT)prevColor;
+ }
+ case WM_NCPAINT:
+ return 0;
+ case WM_PAINT:
+ { HFONT hPrevFont;
+ RECT rc;
+ TCHAR szText[256];
+ UINT alignFlag;
+ COLORREF textColor;
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc=BeginPaint(hwnd,&ps);
+ if(hdc!=NULL) {
+ if(IsWindowEnabled(hwnd)) {
+ hPrevFont=(HFONT)SelectObject(hdc,dat->hEnableFont);
+ textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor;
+ } else {
+ hPrevFont=(HFONT)SelectObject(hdc,dat->hDisableFont);
+ textColor=dat->disableColor;
+ }
+ if(GetClientRect(hwnd,&rc) && GetWindowText(hwnd,szText,SIZEOF(szText))) {
+ if (drawThemeParentBackground && IsWinVerXPPlus())
+ {
+ BOOL fSmoothing;
+ UINT fSmoothingType;
+ SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0);
+ SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0);
+ if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE)
+ drawThemeParentBackground(hwnd, hdc, &rc);
+ }
+ SetBkMode(hdc,TRANSPARENT);
+ SetTextColor(hdc,textColor);
+ alignFlag=(GetWindowLongPtr(hwnd,GWL_STYLE)&(SS_CENTER|SS_RIGHT|SS_LEFT));
+ DrawText(hdc,szText,-1,&rc,alignFlag|DT_NOPREFIX|DT_SINGLELINE|DT_TOP);
+ }
+ if(hPrevFont!=NULL) SelectObject(hdc,hPrevFont);
+ EndPaint(hwnd,&ps);
+ }
+ return 0;
+ }
+ case WM_NCDESTROY:
+ if(dat->hEnableFont!=NULL) DeleteObject(dat->hEnableFont);
+ mir_free(dat);
+ break;
+ }
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+}
+
+int InitHyperlink(void)
+{
+ WNDCLASS wcl;
+
+ wcl.lpfnWndProc=HyperlinkWndProc;
+ wcl.cbClsExtra=0;
+ wcl.cbWndExtra=sizeof(struct HyperlinkWndData*);
+ wcl.hInstance=hMirandaInst;
+ if(IsWinVer2000Plus()) wcl.hCursor=NULL;
+ else wcl.hCursor=LoadCursor(wcl.hInstance,MAKEINTRESOURCE(IDC_HYPERLINKHAND));
+ wcl.lpszClassName=WNDCLASS_HYPERLINK;
+ wcl.hbrBackground=NULL;
+ wcl.hIcon=NULL;
+ wcl.lpszMenuName=NULL;
+ wcl.style=CS_HREDRAW|CS_VREDRAW|CS_GLOBALCLASS|CS_PARENTDC;
+ RegisterClass(&wcl); /* automatically unregistered on exit */
+ return 0;
+}
diff --git a/src/modules/utils/imgconv.cpp b/src/modules/utils/imgconv.cpp
new file mode 100644
index 0000000000..e25953e471
--- /dev/null
+++ b/src/modules/utils/imgconv.cpp
@@ -0,0 +1,152 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2010 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+typedef DWORD ARGB;
+
+void InitBitmapInfo(BITMAPINFO &bmi, const SIZE &size)
+{
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biBitCount = 32;
+
+ bmi.bmiHeader.biWidth = size.cx;
+ bmi.bmiHeader.biHeight = size.cy;
+}
+
+void ConvertToPARGB32(HDC hdc, ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)
+{
+ BITMAPINFO bmi;
+ InitBitmapInfo(bmi, sizImage);
+
+ void *pvBits = malloc(sizImage.cx * 4 * sizImage.cy);
+ if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)
+ {
+ ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;
+ ARGB *pargbMask = (ARGB *)pvBits;
+
+ for (ULONG y = bmi.bmiHeader.biHeight + 1; --y; )
+ {
+ for (ULONG x = bmi.bmiHeader.biWidth + 1; --x; )
+ {
+ if (*pargbMask++)
+ {
+ // transparent pixel
+ *pargb++ = 0;
+ }
+ else
+ {
+ // opaque pixel
+ *pargb++ |= 0xFF000000;
+ }
+ }
+
+ pargb += cxDelta;
+ }
+ }
+ free(pvBits);
+}
+
+bool HasAlpha( ARGB *pargb, SIZE& sizImage, int cxRow)
+{
+ ULONG cxDelta = cxRow - sizImage.cx;
+ for (ULONG y = sizImage.cy; y--; )
+ {
+ for (ULONG x = sizImage.cx; x--; )
+ {
+ if (*pargb++ & 0xFF000000)
+ return true;
+ }
+ pargb += cxDelta;
+ }
+
+ return false;
+}
+
+void ConvertBufferToPARGB32(HANDLE hPaintBuffer, HDC hdc, HICON hIcon, SIZE& sizIcon)
+{
+ RGBQUAD *prgbQuad;
+ int cxRow;
+ HRESULT hr = getBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);
+ if (SUCCEEDED(hr))
+ {
+ ARGB *pargb = (ARGB *)prgbQuad;
+ if (!HasAlpha(pargb, sizIcon, cxRow))
+ {
+ ICONINFO info;
+ if (GetIconInfo(hIcon, &info))
+ {
+ if (info.hbmMask)
+ ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);
+
+ DeleteObject(info.hbmColor);
+ DeleteObject(info.hbmMask);
+ }
+ }
+ }
+}
+
+HBITMAP ConvertIconToBitmap(HICON hicon, HIMAGELIST hIml, int iconId)
+{
+ SIZE sizIcon;
+ sizIcon.cx = GetSystemMetrics(SM_CXSMICON);
+ sizIcon.cy = GetSystemMetrics(SM_CYSMICON);
+
+ RECT rcIcon = { 0, 0, sizIcon.cx, sizIcon.cy };
+
+ HDC hdc = CreateCompatibleDC(NULL);
+
+ BITMAPINFO bmi;
+ InitBitmapInfo(bmi, sizIcon);
+
+ HBITMAP hbmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
+ HBITMAP hbmpOld = (HBITMAP)SelectObject(hdc, hbmp);
+
+ BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ BP_PAINTPARAMS paintParams = {0};
+ paintParams.cbSize = sizeof(paintParams);
+ paintParams.dwFlags = BPPF_ERASE;
+ paintParams.pBlendFunction = &bfAlpha;
+
+ HDC hdcBuffer;
+ HANDLE hPaintBuffer = beginBufferedPaint(hdc, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);
+ if (hPaintBuffer)
+ {
+ if (hIml)
+ ImageList_Draw(hIml, iconId, hdc, 0, 0, ILD_TRANSPARENT);
+ else
+ DrawIconEx(hdcBuffer, 0, 0, hicon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL);
+
+ // If icon did not have an alpha channel we need to convert buffer to PARGB
+ ConvertBufferToPARGB32(hPaintBuffer, hdc, hicon, sizIcon);
+
+ // This will write the buffer contents to the destination bitmap
+ endBufferedPaint(hPaintBuffer, TRUE);
+ }
+
+ SelectObject(hdc, hbmpOld);
+ DeleteDC(hdc);
+
+ return hbmp;
+}
diff --git a/src/modules/utils/md5.cpp b/src/modules/utils/md5.cpp
new file mode 100644
index 0000000000..0fcaccb9ae
--- /dev/null
+++ b/src/modules/utils/md5.cpp
@@ -0,0 +1,374 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c 2874 2006-05-16 21:38:00Z ghazan $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+// (C) 2005 Joe @ Whale - changed to compile with Miranda
+
+#include "commonheaders.h"
+
+
+#define T_MASK ((mir_md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+//gfd*
+static void md5_process(mir_md5_state_t *pms, const mir_md5_byte_t *data /*[64]*/)
+{
+ mir_md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ mir_md5_word_t t;
+ /* Define storage for little-endian or both types of CPUs. */
+ mir_md5_word_t xbuf[16];
+ const mir_md5_word_t *X;
+
+ {
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const mir_md5_byte_t *)&w)) /* dynamic little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const mir_md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const mir_md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+ else /* dynamic big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const mir_md5_byte_t *xp = data;
+ int i;
+
+ X = xbuf; /* (dynamic only) */
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET1(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET1(a, b, c, d, 0, 7, T1);
+ SET1(d, a, b, c, 1, 12, T2);
+ SET1(c, d, a, b, 2, 17, T3);
+ SET1(b, c, d, a, 3, 22, T4);
+ SET1(a, b, c, d, 4, 7, T5);
+ SET1(d, a, b, c, 5, 12, T6);
+ SET1(c, d, a, b, 6, 17, T7);
+ SET1(b, c, d, a, 7, 22, T8);
+ SET1(a, b, c, d, 8, 7, T9);
+ SET1(d, a, b, c, 9, 12, T10);
+ SET1(c, d, a, b, 10, 17, T11);
+ SET1(b, c, d, a, 11, 22, T12);
+ SET1(a, b, c, d, 12, 7, T13);
+ SET1(d, a, b, c, 13, 12, T14);
+ SET1(c, d, a, b, 14, 17, T15);
+ SET1(b, c, d, a, 15, 22, T16);
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET2(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET2(a, b, c, d, 1, 5, T17);
+ SET2(d, a, b, c, 6, 9, T18);
+ SET2(c, d, a, b, 11, 14, T19);
+ SET2(b, c, d, a, 0, 20, T20);
+ SET2(a, b, c, d, 5, 5, T21);
+ SET2(d, a, b, c, 10, 9, T22);
+ SET2(c, d, a, b, 15, 14, T23);
+ SET2(b, c, d, a, 4, 20, T24);
+ SET2(a, b, c, d, 9, 5, T25);
+ SET2(d, a, b, c, 14, 9, T26);
+ SET2(c, d, a, b, 3, 14, T27);
+ SET2(b, c, d, a, 8, 20, T28);
+ SET2(a, b, c, d, 13, 5, T29);
+ SET2(d, a, b, c, 2, 9, T30);
+ SET2(c, d, a, b, 7, 14, T31);
+ SET2(b, c, d, a, 12, 20, T32);
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET3(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET3(a, b, c, d, 5, 4, T33);
+ SET3(d, a, b, c, 8, 11, T34);
+ SET3(c, d, a, b, 11, 16, T35);
+ SET3(b, c, d, a, 14, 23, T36);
+ SET3(a, b, c, d, 1, 4, T37);
+ SET3(d, a, b, c, 4, 11, T38);
+ SET3(c, d, a, b, 7, 16, T39);
+ SET3(b, c, d, a, 10, 23, T40);
+ SET3(a, b, c, d, 13, 4, T41);
+ SET3(d, a, b, c, 0, 11, T42);
+ SET3(c, d, a, b, 3, 16, T43);
+ SET3(b, c, d, a, 6, 23, T44);
+ SET3(a, b, c, d, 9, 4, T45);
+ SET3(d, a, b, c, 12, 11, T46);
+ SET3(c, d, a, b, 15, 16, T47);
+ SET3(b, c, d, a, 2, 23, T48);
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET4(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET4(a, b, c, d, 0, 6, T49);
+ SET4(d, a, b, c, 7, 10, T50);
+ SET4(c, d, a, b, 14, 15, T51);
+ SET4(b, c, d, a, 5, 21, T52);
+ SET4(a, b, c, d, 12, 6, T53);
+ SET4(d, a, b, c, 3, 10, T54);
+ SET4(c, d, a, b, 10, 15, T55);
+ SET4(b, c, d, a, 1, 21, T56);
+ SET4(a, b, c, d, 8, 6, T57);
+ SET4(d, a, b, c, 15, 10, T58);
+ SET4(c, d, a, b, 6, 15, T59);
+ SET4(b, c, d, a, 13, 21, T60);
+ SET4(a, b, c, d, 4, 6, T61);
+ SET4(d, a, b, c, 11, 10, T62);
+ SET4(c, d, a, b, 2, 15, T63);
+ SET4(b, c, d, a, 9, 21, T64);
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void md5_init(mir_md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void md5_append(mir_md5_state_t *pms, const mir_md5_byte_t *data, int nbytes)
+{
+ const mir_md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ mir_md5_word_t nbits = (mir_md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void md5_finish(mir_md5_state_t *pms, mir_md5_byte_t digest[16])
+{
+ static const mir_md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ mir_md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (mir_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (mir_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+void md5_hash_string(const mir_md5_byte_t *data, int len, mir_md5_byte_t digest[16])
+{
+ mir_md5_state_t state;
+ md5_init(&state);
+ md5_append(&state, data, len);
+ md5_finish(&state, digest);
+}
+
+INT_PTR GetMD5Interface(WPARAM, LPARAM lParam)
+{
+ struct MD5_INTERFACE *md5i = (struct MD5_INTERFACE*) lParam;
+ if ( md5i == NULL )
+ return 1;
+ if ( md5i->cbSize != sizeof( struct MD5_INTERFACE ))
+ return 1;
+
+ md5i->md5_init = md5_init;
+ md5i->md5_append = md5_append;
+ md5i->md5_finish = md5_finish;
+ md5i->md5_hash = md5_hash_string;
+ return 0;
+}
diff --git a/src/modules/utils/openurl.cpp b/src/modules/utils/openurl.cpp
new file mode 100644
index 0000000000..ef66d89f8c
--- /dev/null
+++ b/src/modules/utils/openurl.cpp
@@ -0,0 +1,228 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include <ctype.h>
+
+#define DDEMESSAGETIMEOUT 1000
+#define WNDCLASS_DDEMSGWINDOW _T("MirandaDdeMsgWindow")
+
+struct DdeMsgWindowData {
+ int fAcked,fData;
+ HWND hwndDde;
+};
+
+static LRESULT CALLBACK DdeMessageWindow(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ struct DdeMsgWindowData *dat;
+ ATOM hSzItem;
+ HGLOBAL hDdeData;
+
+ dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwnd,0);
+ switch(msg) {
+ case WM_DDE_ACK:
+ dat->fAcked=1;
+ dat->hwndDde=(HWND)wParam;
+ return 0;
+ case WM_DDE_DATA:
+ UnpackDDElParam(msg,lParam,(PUINT_PTR)&hDdeData,(PUINT_PTR)&hSzItem);
+ dat->fData=1;
+ if(hDdeData) {
+ DDEDATA *data;
+ int release;
+ data=(DDEDATA*)GlobalLock(hDdeData);
+ if(data->fAckReq) {
+ DDEACK ack={0};
+ PostMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,PackDDElParam(WM_DDE_ACK,*(PUINT)&ack,(UINT)hSzItem));
+ }
+ else GlobalDeleteAtom(hSzItem);
+ release=data->fRelease;
+ GlobalUnlock(hDdeData);
+ if(release) GlobalFree(hDdeData);
+ }
+ else GlobalDeleteAtom(hSzItem);
+ return 0;
+ }
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+}
+
+static int DoDdeRequest(const char *szItemName,HWND hwndDdeMsg)
+{
+ ATOM hSzItemName;
+ DWORD timeoutTick,thisTick;
+ MSG msg;
+ struct DdeMsgWindowData *dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwndDdeMsg,0);
+
+ hSzItemName=GlobalAddAtomA(szItemName);
+ if(!PostMessage(dat->hwndDde,WM_DDE_REQUEST,(WPARAM)hwndDdeMsg,MAKELPARAM(CF_TEXT,hSzItemName))) {
+ GlobalDeleteAtom(hSzItemName);
+ return 1;
+ }
+ timeoutTick=GetTickCount()+5000;
+ dat->fData=0; dat->fAcked=0;
+ do {
+ if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ if(dat->fData || dat->fAcked) break;
+ thisTick=GetTickCount();
+ if(thisTick>timeoutTick) break;
+ } while(MsgWaitForMultipleObjects(0,NULL,FALSE,timeoutTick-thisTick,QS_ALLINPUT)==WAIT_OBJECT_0);
+
+ if(!dat->fData) {
+ GlobalDeleteAtom(hSzItemName);
+ return 1;
+ }
+ return 0;
+}
+
+//see Q160957 and http://developer.netscape.com/docs/manuals/communicator/DDE/index.htm
+static int DdeOpenUrl(const char *szBrowser,char *szUrl,int newWindow,HWND hwndDdeMsg)
+{
+ ATOM hSzBrowser,hSzTopic;
+ DWORD_PTR dwResult;
+ char *szItemName;
+ struct DdeMsgWindowData *dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwndDdeMsg,0);
+
+ hSzBrowser=GlobalAddAtomA(szBrowser);
+ hSzTopic=GlobalAddAtomA("WWW_OpenURL");
+ dat->fAcked=0;
+ if(!SendMessageTimeout(HWND_BROADCAST,WM_DDE_INITIATE,(WPARAM)hwndDdeMsg,MAKELPARAM(hSzBrowser,hSzTopic),SMTO_ABORTIFHUNG|SMTO_NORMAL,DDEMESSAGETIMEOUT,&dwResult)
+ || !dat->fAcked) {
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ return 1;
+ }
+ szItemName=(char*)mir_alloc(lstrlenA(szUrl)+7);
+ wsprintfA(szItemName,"\"%s\",,%d",szUrl,newWindow?0:-1);
+ if(DoDdeRequest(szItemName,hwndDdeMsg)) {
+ mir_free(szItemName);
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ return 1;
+ }
+ PostMessage(dat->hwndDde,WM_DDE_TERMINATE,(WPARAM)hwndDdeMsg,0);
+ GlobalDeleteAtom(hSzTopic);
+ GlobalDeleteAtom(hSzBrowser);
+ mir_free(szItemName);
+ return 0;
+}
+
+typedef struct {
+ char *szUrl;
+ int newWindow;
+} TOpenUrlInfo;
+
+static void OpenURLThread(void *arg)
+{
+ TOpenUrlInfo *hUrlInfo = (TOpenUrlInfo*)arg;
+ char *szResult;
+ HWND hwndDdeMsg;
+ struct DdeMsgWindowData msgWndData={0};
+ char *pszProtocol;
+ HKEY hKey;
+ char szSubkey[80];
+ char szCommandName[MAX_PATH];
+ DWORD dataLength;
+ int success=0;
+
+ if (!hUrlInfo->szUrl) return;
+ hwndDdeMsg=CreateWindow(WNDCLASS_DDEMSGWINDOW,_T(""),0,0,0,0,0,NULL,NULL,hMirandaInst,NULL);
+ SetWindowLongPtr(hwndDdeMsg,0,(LONG_PTR)&msgWndData);
+
+ if(!_strnicmp(hUrlInfo->szUrl,"ftp:",4) || !_strnicmp(hUrlInfo->szUrl,"ftp.",4)) pszProtocol="ftp";
+ if(!_strnicmp(hUrlInfo->szUrl,"mailto:",7)) pszProtocol="mailto";
+ if(!_strnicmp(hUrlInfo->szUrl,"news:",5)) pszProtocol="news";
+ else pszProtocol="http";
+ wsprintfA(szSubkey,"%s\\shell\\open\\command",pszProtocol);
+ if(RegOpenKeyExA(HKEY_CURRENT_USER,szSubkey,0,KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS
+ || RegOpenKeyExA(HKEY_CLASSES_ROOT,szSubkey,0,KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) {
+ dataLength=SIZEOF(szCommandName);
+ if(RegQueryValueEx(hKey,NULL,NULL,NULL,(PBYTE)szCommandName,&dataLength)==ERROR_SUCCESS) {
+ _strlwr(szCommandName);
+ if(strstr(szCommandName,"mozilla") || strstr(szCommandName,"netscape"))
+ success=(DdeOpenUrl("mozilla",hUrlInfo->szUrl,hUrlInfo->newWindow,hwndDdeMsg)==0 || DdeOpenUrl("netscape",hUrlInfo->szUrl,hUrlInfo->newWindow,hwndDdeMsg)==0);
+ else if(strstr(szCommandName,"iexplore") || strstr(szCommandName,"msimn"))
+ success=0==DdeOpenUrl("iexplore",hUrlInfo->szUrl,hUrlInfo->newWindow,hwndDdeMsg);
+ else if(strstr(szCommandName,"opera"))
+ success=0==DdeOpenUrl("opera",hUrlInfo->szUrl,hUrlInfo->newWindow,hwndDdeMsg);
+ //opera's the default anyway
+ }
+ RegCloseKey(hKey);
+ }
+
+ DestroyWindow(hwndDdeMsg);
+ if(success) return;
+
+ //wack a protocol on it
+ if((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1]==':') || hUrlInfo->szUrl[0]=='\\') {
+ szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+9);
+ wsprintfA(szResult,"file:///%s",hUrlInfo->szUrl);
+ }
+ else {
+ int i;
+ for(i=0;isalpha(hUrlInfo->szUrl[i]);i++);
+ if(hUrlInfo->szUrl[i]==':') szResult=mir_strdup(hUrlInfo->szUrl);
+ else {
+ if(!_strnicmp(hUrlInfo->szUrl,"ftp.",4)) {
+ szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+7);
+ wsprintfA(szResult,"ftp://%s",hUrlInfo->szUrl);
+ }
+ else {
+ szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+8);
+ wsprintfA(szResult,"http://%s",hUrlInfo->szUrl);
+ }
+ }
+ }
+ ShellExecuteA(NULL, "open", szResult, NULL, NULL, SW_SHOWDEFAULT);
+ mir_free(szResult);
+ mir_free(hUrlInfo->szUrl);
+ mir_free(hUrlInfo);
+ return;
+}
+
+static INT_PTR OpenURL(WPARAM wParam,LPARAM lParam) {
+ TOpenUrlInfo *hUrlInfo = (TOpenUrlInfo*)mir_alloc(sizeof(TOpenUrlInfo));
+ hUrlInfo->szUrl = (char*)lParam?mir_strdup((char*)lParam):NULL;
+ hUrlInfo->newWindow = (int)wParam;
+ forkthread(OpenURLThread, 0, (void*)hUrlInfo);
+ return 0;
+}
+
+int InitOpenUrl(void)
+{
+ WNDCLASS wcl;
+ wcl.lpfnWndProc=DdeMessageWindow;
+ wcl.cbClsExtra=0;
+ wcl.cbWndExtra=sizeof(void*);
+ wcl.hInstance=hMirandaInst;
+ wcl.hCursor=NULL;
+ wcl.lpszClassName=WNDCLASS_DDEMSGWINDOW;
+ wcl.hbrBackground=NULL;
+ wcl.hIcon=NULL;
+ wcl.lpszMenuName=NULL;
+ wcl.style=0;
+ RegisterClass(&wcl);
+ CreateServiceFunction(MS_UTILS_OPENURL,OpenURL);
+ return 0;
+}
diff --git a/src/modules/utils/path.cpp b/src/modules/utils/path.cpp
new file mode 100644
index 0000000000..80333a3075
--- /dev/null
+++ b/src/modules/utils/path.cpp
@@ -0,0 +1,600 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "../database/profilemanager.h"
+#include "../srfile/file.h"
+
+extern TCHAR g_profileDir[MAX_PATH];
+
+static char szMirandaPath[MAX_PATH];
+static char szMirandaPathLower[MAX_PATH];
+
+static INT_PTR replaceVars(WPARAM wParam, LPARAM lParam);
+
+static int pathIsAbsolute(const char *path)
+{
+ if ( strlen(path) <= 2 )
+ return 0;
+ if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\'))
+ return 1;
+ return 0;
+}
+
+static INT_PTR pathToRelative(WPARAM wParam, LPARAM lParam)
+{
+ char *pSrc = (char*)wParam;
+ char *pOut = (char*)lParam;
+ if (!pSrc||!strlen(pSrc)||strlen(pSrc)>MAX_PATH) return 0;
+ if (!pathIsAbsolute(pSrc)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ else {
+ char szTmp[MAX_PATH];
+
+ mir_snprintf(szTmp, SIZEOF(szTmp), "%s", pSrc);
+ _strlwr(szTmp);
+ if (strstr(szTmp, szMirandaPathLower)) {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc+strlen(szMirandaPathLower));
+ return strlen(pOut);
+ }
+ else {
+ mir_snprintf(pOut, MAX_PATH, "%s", pSrc);
+ return strlen(pOut);
+ }
+ }
+}
+
+int pathToAbsolute(const char *pSrc, char *pOut, char* base)
+{
+ if ( !pSrc || !strlen( pSrc ) || strlen( pSrc ) > MAX_PATH )
+ return 0;
+
+ if ( base == NULL )
+ base = szMirandaPath;
+
+ char buf[MAX_PATH];
+ if ( pSrc[0] < ' ')
+ return mir_snprintf( pOut, MAX_PATH, "%s", pSrc );
+ else if ( pathIsAbsolute( pSrc ))
+ return GetFullPathNameA(pSrc, MAX_PATH, pOut, NULL);
+ else if ( pSrc[0] != '\\' )
+ mir_snprintf( buf, MAX_PATH, "%s%s", base, pSrc );
+ else
+ mir_snprintf( buf, MAX_PATH, "%s%s", base, pSrc+1 );
+
+ return GetFullPathNameA(buf, MAX_PATH, pOut, NULL);
+}
+
+static INT_PTR pathToAbsolute(WPARAM wParam, LPARAM lParam)
+{
+ return pathToAbsolute((char*)wParam, (char*)lParam, szMirandaPath);
+}
+
+void CreatePathToFile( char* szFilePath )
+{
+ char* pszLastBackslash = strrchr( szFilePath, '\\' );
+ if ( pszLastBackslash == NULL )
+ return;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTree( szFilePath );
+ *pszLastBackslash = '\\';
+}
+
+int CreateDirectoryTree( const char *szDir )
+{
+ DWORD dwAttributes;
+ char *pszLastBackslash, szTestDir[ MAX_PATH ];
+
+ lstrcpynA( szTestDir, szDir, SIZEOF( szTestDir ));
+ if (( dwAttributes = GetFileAttributesA( szTestDir )) != INVALID_FILE_ATTRIBUTES && ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ))
+ return 0;
+
+ pszLastBackslash = strrchr( szTestDir, '\\' );
+ if ( pszLastBackslash == NULL )
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTree( szTestDir );
+ *pszLastBackslash = '\\';
+ return ( CreateDirectoryA( szTestDir, NULL ) == 0 ) ? GetLastError() : 0;
+}
+
+static INT_PTR createDirTree(WPARAM, LPARAM lParam)
+{
+ if ( lParam == 0 )
+ return 1;
+
+ return CreateDirectoryTree(( char* )lParam );
+}
+
+#ifdef _UNICODE
+static TCHAR szMirandaPathW[MAX_PATH];
+static TCHAR szMirandaPathWLower[MAX_PATH];
+
+static int pathIsAbsoluteW(const TCHAR *path)
+{
+ if ( lstrlen(path) <= 2 )
+ return 0;
+ if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\'))
+ return 1;
+ return 0;
+}
+
+static INT_PTR pathToRelativeW(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR *pSrc = (TCHAR*)wParam;
+ TCHAR *pOut = (TCHAR*)lParam;
+ if ( !pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH )
+ return 0;
+
+ if ( !pathIsAbsoluteW( pSrc ))
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc);
+ else {
+ TCHAR szTmp[MAX_PATH];
+
+ mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s"), pSrc);
+ _tcslwr(szTmp);
+ if (_tcsstr(szTmp, szMirandaPathWLower))
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc+lstrlen(szMirandaPathWLower));
+ else
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc);
+ }
+ return lstrlen(pOut);
+}
+
+int pathToAbsoluteW(const TCHAR *pSrc, TCHAR *pOut, TCHAR* base)
+{
+ if ( !pSrc || !wcslen(pSrc) || wcslen(pSrc) > MAX_PATH)
+ return 0;
+
+ if ( base == NULL )
+ base = szMirandaPathW;
+
+ TCHAR buf[MAX_PATH];
+ if ( pSrc[0] < ' ')
+ return mir_sntprintf( pOut, MAX_PATH, _T("%s"), pSrc );
+ else if ( pathIsAbsoluteW( pSrc ))
+ return GetFullPathName(pSrc, MAX_PATH, pOut, NULL);
+ else if ( pSrc[0] != '\\' )
+ mir_sntprintf( buf, MAX_PATH, _T("%s%s"), base, pSrc );
+ else
+ mir_sntprintf( buf, MAX_PATH, _T("%s%s"), base, pSrc+1 );
+
+ return GetFullPathName(buf, MAX_PATH, pOut, NULL);
+}
+
+static INT_PTR pathToAbsoluteW(WPARAM wParam, LPARAM lParam)
+{
+ return pathToAbsoluteW((TCHAR*)wParam, (TCHAR*)lParam, szMirandaPathW);
+}
+
+void CreatePathToFileW( WCHAR* wszFilePath )
+{
+ WCHAR* pszLastBackslash = wcsrchr( wszFilePath, '\\' );
+ if ( pszLastBackslash == NULL )
+ return;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTreeW( wszFilePath );
+ *pszLastBackslash = '\\';
+}
+
+int CreateDirectoryTreeW( const WCHAR* szDir )
+{
+ DWORD dwAttributes;
+ WCHAR* pszLastBackslash, szTestDir[ MAX_PATH ];
+
+ lstrcpynW( szTestDir, szDir, SIZEOF( szTestDir ));
+ if (( dwAttributes = GetFileAttributesW( szTestDir )) != INVALID_FILE_ATTRIBUTES && ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ))
+ return 0;
+
+ pszLastBackslash = wcsrchr( szTestDir, '\\' );
+ if ( pszLastBackslash == NULL )
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTreeW( szTestDir );
+ *pszLastBackslash = '\\';
+ return ( CreateDirectoryW( szTestDir, NULL ) == 0 ) ? GetLastError() : 0;
+}
+
+static INT_PTR createDirTreeW(WPARAM, LPARAM lParam)
+{
+ if ( lParam == 0 )
+ return 1;
+
+ return CreateDirectoryTreeW(( WCHAR* )lParam );
+}
+
+int InitPathUtilsW(void)
+{
+ GetModuleFileName(hMirandaInst, szMirandaPathW, SIZEOF(szMirandaPathW));
+ TCHAR *p = _tcsrchr(szMirandaPathW,'\\');
+ if ( p )
+ p[1] = 0;
+ mir_sntprintf(szMirandaPathWLower, SIZEOF(szMirandaPathWLower), _T("%s"), szMirandaPathW);
+ _tcslwr(szMirandaPathWLower);
+ CreateServiceFunction(MS_UTILS_PATHTORELATIVEW, pathToRelativeW);
+ CreateServiceFunction(MS_UTILS_PATHTOABSOLUTEW, pathToAbsoluteW);
+ CreateServiceFunction(MS_UTILS_CREATEDIRTREEW, createDirTreeW);
+ return 0;
+}
+#endif
+
+TCHAR *GetContactID(HANDLE hContact)
+{
+ TCHAR *theValue = {0};
+ char *szProto = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (DBGetContactSettingByte(hContact, szProto, "ChatRoom", 0) == 1) {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(hContact, szProto, "ChatRoomID", &dbv)) {
+ theValue = (TCHAR *)mir_tstrdup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ return theValue;
+ } }
+ else {
+ CONTACTINFO ci = {0};
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ ci.szProto = szProto;
+ ci.dwFlag = CNF_UNIQUEID | CNF_TCHAR;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ switch (ci.type) {
+ case CNFT_ASCIIZ:
+ return (TCHAR *)ci.pszVal;
+ break;
+ case CNFT_DWORD:
+ return _itot(ci.dVal, (TCHAR *)mir_alloc(sizeof(TCHAR)*32), 10);
+ break;
+ } } }
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Variables parser
+
+#define XSTR(target, s) _xstrselect(target, s, _T(s))
+
+static __forceinline int _xcscmp(const char *s1, const char *s2) { return strcmp(s1, s2); }
+static __forceinline int _xcsncmp(const char *s1, const char *s2, size_t n) { return strncmp(s1, s2, n); }
+static __forceinline size_t _xcslen(const char *s1) { return strlen(s1); }
+static __forceinline char *_xcscpy(char *s1, const char *s2) { return strcpy(s1, s2); }
+static __forceinline char *_xcsncpy(char *s1, const char *s2, size_t n) { return strncpy(s1, s2, n); }
+static __forceinline char *_xstrselect(char *, char *s1, TCHAR *s2) { return s1; }
+static __forceinline char *_itox(char *, int a) { return itoa(a, (char *)mir_alloc(sizeof(char)*20), 10); }
+static __forceinline char *mir_a2x(char *, char *s) { return mir_strdup(s); }
+static __forceinline char *GetContactNickX(char *, HANDLE hContact)
+{
+ return mir_strdup((char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0));
+}
+static __forceinline char *GetContactIDX(char *, HANDLE hContact)
+{
+ TCHAR *id = GetContactID(hContact);
+ char* res = mir_t2a(id);
+ mir_free(id);
+ return res;
+}
+static __forceinline char *GetEnvironmentVariableX(char *variable)
+{
+ char result[512];
+ if (GetEnvironmentVariableA(variable, result, SIZEOF(result)))
+ return mir_strdup(result);
+ return NULL;
+}
+static __forceinline char *GetProfileDirX( char* )
+{
+ return mir_t2a( g_profileDir );
+}
+static __forceinline char *SHGetSpecialFolderPathX(int iCSIDL, char* var)
+{
+ char result[512];
+ if (shGetSpecialFolderPathA && shGetSpecialFolderPathA(NULL, result, iCSIDL, FALSE))
+ return mir_strdup(result);
+ return NULL;
+}
+static __forceinline char *GetModulePathX(char *, HMODULE hModule)
+{
+ char result[MAX_PATH];
+ GetModuleFileNameA(hModule, result, sizeof(result));
+ char* str = strrchr(result, '\\');
+ if (str) *str = 0;
+ return mir_strdup(result);
+}
+static __forceinline char *GetUserNameX(char *)
+{
+ char result[128];
+ DWORD size = SIZEOF(result);
+ if (GetUserNameA(result, &size))
+ return mir_strdup(result);
+ return NULL;
+}
+static __forceinline char *GetProfileNameX(char *)
+{
+ TCHAR szProfileName[MAX_PATH];
+ _tcscpy( szProfileName, g_profileName );
+ TCHAR *pos = _tcsrchr(szProfileName, '.');
+ if ( lstrcmp( pos, _T(".dat")) == 0 )
+ *pos = 0;
+ return mir_t2a( szProfileName );
+}
+static __forceinline char *GetPathVarX(char *, int code)
+{
+ TCHAR szFullPath[MAX_PATH], szProfileName[MAX_PATH];
+ _tcscpy( szProfileName, g_profileName );
+ _tcslwr( szProfileName );
+ TCHAR *pos = _tcsrchr(szProfileName, '.');
+ if ( lstrcmp( pos, _T(".dat")) == 0 )
+ *pos = 0;
+
+ switch( code ) {
+ case 1:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s\\AvatarCache"), g_profileDir, szProfileName);
+ break;
+ case 2:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s\\Logs"), g_profileDir, szProfileName);
+ break;
+ case 3:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s"), g_profileDir, szProfileName);
+ break;
+ }
+ return makeFileName( szFullPath );
+}
+
+#ifdef _UNICODE
+static __forceinline int _xcscmp(const TCHAR *s1, const TCHAR *s2) { return _tcscmp(s1, s2); }
+static __forceinline int _xcsncmp(const TCHAR *s1, const TCHAR *s2, size_t n) { return _tcsncmp(s1, s2, n); }
+static __forceinline size_t _xcslen(const TCHAR *s1) { return _tcslen(s1); }
+static __forceinline TCHAR *_xcscpy(TCHAR *s1, const TCHAR *s2) { return _tcscpy(s1, s2); }
+static __forceinline TCHAR *_xcsncpy(TCHAR *s1, const TCHAR *s2, size_t n) { return _tcsncpy(s1, s2, n); }
+static __forceinline TCHAR *_xstrselect(TCHAR *, char *s1, TCHAR *s2) { return s2; }
+static __forceinline TCHAR *_itox(TCHAR *, int a) { return _itot(a, (TCHAR *)mir_alloc(sizeof(TCHAR)*20), 10); }
+static __forceinline TCHAR *mir_a2x(TCHAR *, char *s) { return mir_a2t(s); }
+static __forceinline TCHAR *GetContactNickX(TCHAR *, HANDLE hContact)
+{
+ return mir_tstrdup((TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+}
+static __forceinline TCHAR *GetContactIDX(TCHAR *, HANDLE hContact)
+{
+ return GetContactID(hContact);
+}
+static __forceinline TCHAR *GetEnvironmentVariableX(TCHAR *variable)
+{
+ TCHAR result[512];
+ if (GetEnvironmentVariable(variable, result, SIZEOF(result)))
+ return mir_tstrdup(result);
+ return NULL;
+}
+static __forceinline TCHAR *SHGetSpecialFolderPathX(int iCSIDL, TCHAR* var)
+{
+ TCHAR result[512];
+ if (shGetSpecialFolderPath && shGetSpecialFolderPath(NULL, result, iCSIDL, FALSE))
+ return mir_tstrdup(result);
+ return NULL;
+}
+static __forceinline TCHAR *GetProfileDirX( TCHAR* )
+{
+ return mir_tstrdup( g_profileDir );
+}
+static __forceinline TCHAR *GetModulePathX(TCHAR *, HMODULE hModule)
+{
+ TCHAR result[MAX_PATH];
+ GetModuleFileName(hModule, result, SIZEOF(result));
+ TCHAR* str = _tcsrchr(result, '\\');
+ if (str) *str = 0;
+ return mir_tstrdup(result);
+}
+static __forceinline TCHAR *GetUserNameX(TCHAR *)
+{
+ TCHAR result[128];
+ DWORD size = SIZEOF(result);
+ if (GetUserName(result, &size))
+ return mir_tstrdup(result);
+ return NULL;
+}
+static __forceinline TCHAR *GetProfileNameX(TCHAR *)
+{
+ TCHAR szProfileName[MAX_PATH];
+ _tcscpy( szProfileName, g_profileName );
+ TCHAR *pos = _tcsrchr(szProfileName, '.');
+ if ( lstrcmp( pos, _T(".dat")) == 0 )
+ *pos = 0;
+ return mir_tstrdup( szProfileName );
+}
+static __forceinline TCHAR *GetPathVarX(TCHAR *, int code)
+{
+ TCHAR szFullPath[MAX_PATH], szProfileName[MAX_PATH];
+ _tcscpy( szProfileName, g_profileName );
+ TCHAR *pos = _tcsrchr(szProfileName, '.');
+ if ( lstrcmp( pos, _T(".dat")) == 0 )
+ *pos = 0;
+
+ switch( code ) {
+ case 1:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s\\AvatarCache"), g_profileDir, szProfileName);
+ break;
+ case 2:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s\\Logs"), g_profileDir, szProfileName);
+ break;
+ case 3:
+ mir_sntprintf(szFullPath, SIZEOF(szFullPath), _T("%s\\%s"), g_profileDir, szProfileName);
+ break;
+ }
+ return mir_tstrdup( szFullPath );
+}
+#endif
+
+template<typename XCHAR>
+XCHAR *GetInternalVariable(XCHAR *key, size_t keyLength, HANDLE hContact)
+{
+ XCHAR *theValue = NULL;
+ XCHAR *theKey = (XCHAR *)_alloca(sizeof(XCHAR) * (keyLength + 1));
+ _xcsncpy(theKey, key, keyLength);
+ theKey[keyLength] = 0;
+
+ if (hContact) {
+ if (!_xcscmp(theKey, XSTR(key, "nick")))
+ theValue = GetContactNickX(key, hContact);
+ else if (!_xcscmp(theKey, XSTR(key, "proto")))
+ theValue = mir_a2x(key, (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact,0));
+ else if (!_xcscmp(theKey, XSTR(key, "userid")))
+ theValue = GetContactIDX(key, hContact);
+ }
+
+ if (!theValue) {
+ if (!_xcscmp(theKey, XSTR(key, "miranda_path")))
+ theValue = GetModulePathX(key, NULL);
+ else if (!_xcscmp(theKey, XSTR(key, "appdata")))
+ theValue = SHGetSpecialFolderPathX(CSIDL_APPDATA, theKey);
+ else if (!_xcscmp(theKey, XSTR(key, "mydocuments")))
+ theValue = SHGetSpecialFolderPathX(CSIDL_PERSONAL, theKey);
+ else if (!_xcscmp(theKey, XSTR(key, "desktop")))
+ theValue = SHGetSpecialFolderPathX(CSIDL_DESKTOPDIRECTORY, theKey);
+ else if (!_xcscmp(theKey, XSTR(key, "miranda_profile")))
+ theValue = GetProfileDirX(key);
+ else if (!_xcscmp(theKey, XSTR(key, "miranda_profilename")))
+ theValue = GetProfileNameX(key);
+ else if (!_xcscmp(theKey, XSTR(key, "username")))
+ theValue = GetUserNameX(key);
+ else if (!_xcscmp(theKey, XSTR(key, "miranda_avatarcache")))
+ theValue = GetPathVarX(key,1);
+ else if (!_xcscmp(theKey, XSTR(key, "miranda_logpath")))
+ theValue = GetPathVarX(key,2);
+ else if (!_xcscmp(theKey, XSTR(key, "miranda_userdata")))
+ theValue = GetPathVarX(key,3);
+ }
+
+ if (!theValue)
+ theValue = GetEnvironmentVariableX(theKey);
+
+ return theValue;
+}
+
+template<typename XCHAR>
+XCHAR *GetVariableFromArray(REPLACEVARSARRAY *vars, XCHAR *key, size_t keyLength, HANDLE hContact, bool *bFree)
+{
+ *bFree = false;
+ for (REPLACEVARSARRAY *var = vars; var && var->lptzKey; ++var)
+ if ((_xcslen((XCHAR *)var->lptzKey) == keyLength) && !_xcsncmp(key, (XCHAR *)var->lptzKey, keyLength))
+ return (XCHAR *)var->lptzValue;
+
+ *bFree = true;
+ return GetInternalVariable(key, keyLength, hContact);
+}
+
+template<typename XCHAR>
+XCHAR *ReplaceVariables(XCHAR *str, REPLACEVARSDATA *data)
+{
+ if (!str)
+ return NULL;
+
+ XCHAR *p;
+ XCHAR *varStart = 0;
+ size_t length = 0;
+ bool bFree;
+
+ for (p = str; *p; ++p) {
+ if (*p == '%') {
+ if (varStart) {
+ if (p == varStart)
+ length++;
+ else if (XCHAR *value = GetVariableFromArray(data->variables, varStart, p-varStart, data->hContact, &bFree)) {
+ length += _xcslen(value);
+ if (bFree) mir_free(value);
+ }
+ else // variable not found
+ length += p-varStart+2;
+
+ varStart = 0;
+ }
+ else varStart = p+1;
+ }
+ else if (!varStart)
+ length++;
+ }
+
+ XCHAR *result = (XCHAR *)mir_alloc(sizeof(XCHAR) * (length + 1));
+ XCHAR *q = result;
+ varStart = NULL;
+
+ for (p = str; *p; ++p) {
+ if (*p == '%') {
+ if (varStart) {
+ if (p == varStart)
+ *q++ = '%';
+ else if (XCHAR *value = GetVariableFromArray(data->variables, varStart, p-varStart, data->hContact, &bFree)) {
+ _xcscpy(q, value);
+ q += _xcslen(value);
+ if (bFree) mir_free(value);
+ }
+ else {
+ // variable not found
+ _xcsncpy(q, varStart-1, p-varStart+2);
+ q += p-varStart+2;
+ }
+ varStart = 0;
+ }
+ else varStart = p+1;
+ }
+ else if (!varStart)
+ *q++ = *p;
+ }
+
+ *q = 0;
+
+ return result;
+}
+
+static INT_PTR replaceVars(WPARAM wParam, LPARAM lParam)
+{
+ REPLACEVARSDATA *data = (REPLACEVARSDATA *)lParam;
+ if (!(data->dwFlags & RVF_UNICODE))
+ return (INT_PTR)ReplaceVariables<char>((char *)wParam, data);
+
+#ifdef _UNICODE
+ return (INT_PTR)ReplaceVariables<WCHAR>((WCHAR *)wParam, data);
+#else
+ return NULL;
+#endif
+}
+
+int InitPathUtils(void)
+{
+ char *p = 0;
+ GetModuleFileNameA(hMirandaInst, szMirandaPath, SIZEOF(szMirandaPath));
+ p = strrchr(szMirandaPath,'\\');
+ if ( p )
+ p[1] = 0;
+ mir_snprintf(szMirandaPathLower, MAX_PATH, "%s", szMirandaPath);
+ _strlwr(szMirandaPathLower);
+ CreateServiceFunction(MS_UTILS_PATHTORELATIVE, pathToRelative);
+ CreateServiceFunction(MS_UTILS_PATHTOABSOLUTE, pathToAbsolute);
+ CreateServiceFunction(MS_UTILS_CREATEDIRTREE, createDirTree);
+ CreateServiceFunction(MS_UTILS_REPLACEVARS, replaceVars);
+#ifdef _UNICODE
+ return InitPathUtilsW();
+#else
+ return 0;
+#endif
+}
diff --git a/src/modules/utils/resizer.cpp b/src/modules/utils/resizer.cpp
new file mode 100644
index 0000000000..ea0daa057a
--- /dev/null
+++ b/src/modules/utils/resizer.cpp
@@ -0,0 +1,152 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+typedef struct {
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ short x;
+ short y;
+ short cx;
+ short cy;
+ DWORD id;
+} START_OF_DLGITEMTEMPLATEEX;
+
+typedef struct {
+ WORD dlgVer;
+ WORD signature;
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ WORD cDlgItems;
+ short x;
+ short y;
+ short cx;
+ short cy;
+} START_OF_DLGTEMPLATEEX;
+
+INT_PTR ResizeDialog(WPARAM, LPARAM lParam)
+{
+ UTILRESIZEDIALOG *urd=(UTILRESIZEDIALOG*)lParam;
+ HDWP hDwp;
+ int i;
+ DLGITEMTEMPLATE *pItem = NULL;
+ START_OF_DLGITEMTEMPLATEEX *pItemEx = NULL;
+ RECT rc;
+ PWORD pWord;
+ DLGTEMPLATE *pTemplate;
+ START_OF_DLGTEMPLATEEX *pTemplateEx;
+ UTILRESIZECONTROL urc;
+ int procResult;
+ int extendedDlg,itemCount;
+
+ if(urd==NULL||urd->cbSize!=sizeof(UTILRESIZEDIALOG)) return 1;
+ pTemplate=(DLGTEMPLATE*)LockResource(LoadResource(urd->hInstance,FindResourceA(urd->hInstance,urd->lpTemplate,MAKEINTRESOURCEA(5))));
+ pTemplateEx=(START_OF_DLGTEMPLATEEX*)pTemplate;
+ extendedDlg=pTemplateEx->signature==0xFFFF;
+ if(extendedDlg && pTemplateEx->dlgVer!=1)
+ return 1;
+
+ if(extendedDlg) pWord=(PWORD)(pTemplateEx+1);
+ else pWord=(PWORD)(pTemplate+1);
+ if(*pWord==0xFFFF) pWord+=2; else while(*pWord++); //menu
+ if(*pWord==0xFFFF) pWord+=2; else while(*pWord++); //class
+ while(*pWord++); //title
+ if(extendedDlg) {
+ if(pTemplateEx->style&DS_SETFONT) {
+ pWord+=3; //font size,weight,italic
+ while(*pWord++); //font name
+ }
+ }
+ else {
+ if(pTemplate->style&DS_SETFONT) {
+ pWord++; //font size
+ while(*pWord++); //font name
+ }
+ }
+
+ urc.cbSize=sizeof(UTILRESIZECONTROL);
+ rc.left=0; rc.top=0;
+ if(extendedDlg) {rc.right=pTemplateEx->cx; rc.bottom=pTemplateEx->cy;}
+ else {rc.right=pTemplate->cx; rc.bottom=pTemplate->cy;}
+ MapDialogRect(urd->hwndDlg,&rc);
+ urc.dlgOriginalSize.cx=rc.right; urc.dlgOriginalSize.cy=rc.bottom;
+ GetClientRect(urd->hwndDlg,&rc);
+ urc.dlgNewSize.cx=rc.right; urc.dlgNewSize.cy=rc.bottom;
+
+ if(extendedDlg) itemCount=pTemplateEx->cDlgItems;
+ else itemCount=pTemplate->cdit;
+ hDwp=BeginDeferWindowPos(itemCount);
+ for(i=0;i<itemCount;i++) {
+ if((UINT_PTR)pWord&2) pWord++; //dword align
+
+ if(extendedDlg) {
+ pItemEx=(START_OF_DLGITEMTEMPLATEEX*)pWord;
+ pWord=(PWORD)(pItemEx+1);
+
+ urc.wId=pItemEx->id;
+ urc.rcItem.left=pItemEx->x; urc.rcItem.top=pItemEx->y;
+ urc.rcItem.right=urc.rcItem.left+pItemEx->cx; urc.rcItem.bottom=urc.rcItem.top+pItemEx->cy;
+ }
+ else {
+ pItem=(DLGITEMTEMPLATE*)pWord;
+ pWord=(PWORD)(pItem+1);
+
+ urc.wId=pItem->id;
+ urc.rcItem.left=pItem->x; urc.rcItem.top=pItem->y;
+ urc.rcItem.right=urc.rcItem.left+pItem->cx; urc.rcItem.bottom=urc.rcItem.top+pItem->cy;
+ }
+ if(*pWord==0xFFFF) pWord+=2; else while(*pWord++); //menu
+ if(*pWord==0xFFFF) pWord+=2; else while(*pWord++); //class
+ pWord+=1+(1+*pWord)/2; //creation data
+
+ if(urc.wId==65535) continue; //using this breaks the dwp, so just ignore it
+
+ MapDialogRect(urd->hwndDlg,&urc.rcItem);
+ procResult=(urd->pfnResizer)(urd->hwndDlg,urd->lParam,&urc);
+ if(procResult&RD_ANCHORX_RIGHT) {
+ urc.rcItem.left+=urc.dlgNewSize.cx-urc.dlgOriginalSize.cx;
+ urc.rcItem.right+=urc.dlgNewSize.cx-urc.dlgOriginalSize.cx;
+ }
+ else if(procResult&RD_ANCHORX_WIDTH)
+ urc.rcItem.right+=urc.dlgNewSize.cx-urc.dlgOriginalSize.cx;
+ else if(procResult&RD_ANCHORX_CENTRE) {
+ urc.rcItem.left+=(urc.dlgNewSize.cx-urc.dlgOriginalSize.cx)/2;
+ urc.rcItem.right+=(urc.dlgNewSize.cx-urc.dlgOriginalSize.cx)/2;
+ }
+ if(procResult&RD_ANCHORY_BOTTOM) {
+ urc.rcItem.top+=urc.dlgNewSize.cy-urc.dlgOriginalSize.cy;
+ urc.rcItem.bottom+=urc.dlgNewSize.cy-urc.dlgOriginalSize.cy;
+ }
+ else if(procResult&RD_ANCHORY_HEIGHT)
+ urc.rcItem.bottom+=urc.dlgNewSize.cy-urc.dlgOriginalSize.cy;
+ else if(procResult&RD_ANCHORY_CENTRE) {
+ urc.rcItem.top+=(urc.dlgNewSize.cy-urc.dlgOriginalSize.cy)/2;
+ urc.rcItem.bottom+=(urc.dlgNewSize.cy-urc.dlgOriginalSize.cy)/2;
+ }
+ hDwp = DeferWindowPos(hDwp,GetDlgItem(urd->hwndDlg,extendedDlg?pItemEx->id:pItem->id),0,urc.rcItem.left,urc.rcItem.top,urc.rcItem.right-urc.rcItem.left,urc.rcItem.bottom-urc.rcItem.top,SWP_NOZORDER);
+ }
+ EndDeferWindowPos(hDwp);
+ return 0;
+}
diff --git a/src/modules/utils/sha1.cpp b/src/modules/utils/sha1.cpp
new file mode 100644
index 0000000000..c89a60ca46
--- /dev/null
+++ b/src/modules/utils/sha1.cpp
@@ -0,0 +1,175 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SHA 180-1 Reference Implementation (Compact version).
+ *
+ * The Initial Developer of the Original Code is
+ * Paul Kocher of Cryptography Research.
+ * Portions created by the Initial Developer are Copyright (C) 1995-9
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "commonheaders.h"
+
+static void shaHashBlock(mir_sha1_ctx *ctx);
+
+void shaInit(mir_sha1_ctx *ctx) {
+ int i;
+
+ ctx->lenW = 0;
+ ctx->sizeHi = ctx->sizeLo = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants)
+ */
+ ctx->H[0] = 0x67452301L;
+ ctx->H[1] = 0xefcdab89L;
+ ctx->H[2] = 0x98badcfeL;
+ ctx->H[3] = 0x10325476L;
+ ctx->H[4] = 0xc3d2e1f0L;
+
+ for (i = 0; i < 80; i++)
+ ctx->W[i] = 0;
+}
+
+
+void shaUpdate(mir_sha1_ctx *ctx, mir_sha1_byte_t *dataIn, int len) {
+ int i;
+
+ /* Read the data into W and process blocks as they get full
+ */
+ for (i = 0; i < len; i++) {
+ ctx->W[ctx->lenW / 4] <<= 8;
+ ctx->W[ctx->lenW / 4] |= (unsigned long)dataIn[i];
+ if ((++ctx->lenW) % 64 == 0) {
+ shaHashBlock(ctx);
+ ctx->lenW = 0;
+ }
+ ctx->sizeLo += 8;
+ ctx->sizeHi += (ctx->sizeLo < 8);
+ }
+}
+
+
+void shaFinal(mir_sha1_ctx *ctx, mir_sha1_byte_t hashout[20]) {
+ unsigned char pad0x80 = 0x80;
+ unsigned char pad0x00 = 0x00;
+ unsigned char padlen[8];
+ int i;
+
+ /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
+ */
+ padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
+ padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
+ padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
+ padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
+ padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
+ padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
+ padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
+ padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
+ shaUpdate(ctx, &pad0x80, 1);
+ while (ctx->lenW != 56)
+ shaUpdate(ctx, &pad0x00, 1);
+ shaUpdate(ctx, padlen, 8);
+
+ /* Output hash
+ */
+ for (i = 0; i < 20; i++) {
+ hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
+ ctx->H[i / 4] <<= 8;
+ }
+
+ /*
+ * Re-initialize the context (also zeroizes contents)
+ */
+ shaInit(ctx);
+}
+
+
+void shaBlock(mir_sha1_byte_t *dataIn, int len, mir_sha1_byte_t hashout[20]) {
+ mir_sha1_ctx ctx;
+
+ shaInit(&ctx);
+ shaUpdate(&ctx, dataIn, len);
+ shaFinal(&ctx, hashout);
+}
+
+
+#define SHA_ROTL(X,n) (((X) << (n)) | ((X) >> (32-(n))))
+
+static void shaHashBlock(mir_sha1_ctx *ctx) {
+ int t;
+ unsigned long A,B,C,D,E,TEMP;
+
+ for (t = 16; t <= 79; t++)
+ ctx->W[t] =
+ SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ for (t = 0; t <= 19; t++) {
+ TEMP = SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 20; t <= 39; t++) {
+ TEMP = SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 40; t <= 59; t++) {
+ TEMP = SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 60; t <= 79; t++) {
+ TEMP = SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+INT_PTR GetSHA1Interface(WPARAM, LPARAM lParam)
+{
+ struct SHA1_INTERFACE *sha1i = (struct SHA1_INTERFACE*) lParam;
+ if ( sha1i == NULL )
+ return 1;
+ if ( sha1i->cbSize != sizeof( struct SHA1_INTERFACE ))
+ return 1;
+
+ sha1i->sha1_init = shaInit;
+ sha1i->sha1_append = shaUpdate;
+ sha1i->sha1_finish = shaFinal;
+ sha1i->sha1_hash = shaBlock;
+ return 0;
+}
diff --git a/src/modules/utils/timeutils.cpp b/src/modules/utils/timeutils.cpp
new file mode 100644
index 0000000000..a0d4c02e46
--- /dev/null
+++ b/src/modules/utils/timeutils.cpp
@@ -0,0 +1,277 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+implements services to handle location - based timezones, instead of
+simple UTC offsets.
+*/
+
+#include "commonheaders.h"
+
+//KB167296
+void UnixTimeToFileTime(time_t ts, LPFILETIME pft)
+{
+ unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64;
+ pft->dwLowDateTime = (DWORD)ll;
+ pft->dwHighDateTime = ll >> 32;
+}
+
+time_t FileTimeToUnixTime(LPFILETIME pft)
+{
+ unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime;
+ ll -= 116444736000000000i64;
+ return (time_t)(ll / 10000000);
+}
+
+void FormatTime(const SYSTEMTIME *st, const TCHAR *szFormat, TCHAR *szDest, int cbDest)
+{
+ if (szDest == NULL || cbDest == 0) return;
+
+ TCHAR *pDest = szDest;
+ int destCharsLeft = cbDest - 1;
+
+ for (const TCHAR* pFormat = szFormat; *pFormat; ++pFormat)
+ {
+ DWORD fmt;
+ bool date, iso = false;
+ switch (*pFormat)
+ {
+ case 't':
+ fmt = TIME_NOSECONDS;
+ date = false;
+ break;
+
+ case 's':
+ fmt = 0;
+ date = false;
+ break;
+
+ case 'm':
+ fmt = TIME_NOMINUTESORSECONDS;
+ date = false;
+ break;
+
+ case 'd':
+ fmt = DATE_SHORTDATE;
+ date = true;
+ break;
+
+ case 'D':
+ fmt = DATE_LONGDATE;
+ date = true;
+ break;
+
+ case 'I':
+ iso = true;
+ break;
+
+ default:
+ if (destCharsLeft--)
+ *pDest++ = *pFormat;
+ continue;
+ }
+
+ TCHAR dateTimeStr[64];
+ int dateTimeStrLen;
+
+ if (iso)
+ {
+ dateTimeStrLen = mir_sntprintf(dateTimeStr, SIZEOF(dateTimeStr),
+ _T("%d-%02d-%02dT%02d:%02d:%02dZ"),
+ st->wYear, st->wMonth, st->wDay,
+ st->wHour, st->wMinute, st->wSecond) + 1;
+ }
+ else if (date)
+ dateTimeStrLen = GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, NULL,
+ dateTimeStr, SIZEOF(dateTimeStr));
+ else
+ dateTimeStrLen = GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, NULL,
+ dateTimeStr, SIZEOF(dateTimeStr));
+
+ if (dateTimeStrLen) --dateTimeStrLen;
+ if (destCharsLeft < dateTimeStrLen) dateTimeStrLen = destCharsLeft;
+ memcpy(pDest, dateTimeStr, dateTimeStrLen * sizeof(dateTimeStr[0]));
+ destCharsLeft -= dateTimeStrLen;
+ pDest += dateTimeStrLen;
+ }
+ *pDest = 0;
+}
+
+
+#ifndef _UNICODE
+void ConvertToAbsolute (const SYSTEMTIME * pstLoc, const SYSTEMTIME * pstDst, SYSTEMTIME * pstDstAbs)
+{
+ static int iDays [12] = { 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31 } ;
+ int iDay ;
+
+ // Set up the aboluste date structure except for wDay, which we must find
+
+ pstDstAbs->wYear = pstLoc->wYear ; // Notice from local date/time
+ pstDstAbs->wMonth = pstDst->wMonth ;
+ pstDstAbs->wDayOfWeek = pstDst->wDayOfWeek ;
+
+ pstDstAbs->wHour = pstDst->wHour ;
+ pstDstAbs->wMinute = pstDst->wMinute ;
+ pstDstAbs->wSecond = pstDst->wSecond ;
+ pstDstAbs->wMilliseconds = pstDst->wMilliseconds ;
+
+ // Fix the iDays array for leap years
+
+ if ((pstLoc->wYear % 4 == 0) && ((pstLoc->wYear % 100 != 0) ||
+ (pstLoc->wYear % 400 == 0)))
+ {
+ iDays[1] = 29 ;
+ }
+
+ // Find a day of the month that falls on the same
+ // day of the week as the transition.
+
+ // Suppose today is the 20th of the month (pstLoc->wDay = 20)
+ // Suppose today is a Wednesday (pstLoc->wDayOfWeek = 3)
+ // Suppose the transition occurs on a Friday (pstDst->wDayOfWeek = 5)
+ // Then iDay = 31, meaning that the 31st falls on a Friday
+ // (The 7 is this formula avoids negatives.)
+
+ iDay = pstLoc->wDay + pstDst->wDayOfWeek + 7 - pstLoc->wDayOfWeek ;
+
+ // Now shrink iDay to a value between 1 and 7.
+
+ iDay = (iDay - 1) % 7 + 1 ;
+
+ // Now iDay is a day of the month ranging from 1 to 7.
+ // Recall that the wDay field of the structure can range
+ // from 1 to 5, 1 meaning "first", 2 meaning "second",
+ // and 5 meaning "last".
+ // So, increase iDay so it's the proper day of the month.
+
+ iDay += 7 * (pstDst->wDay - 1) ;
+
+ // Could be that iDay overshot the end of the month, so
+ // fix it up using the number of days in each month
+
+ if (iDay > iDays[pstDst->wMonth - 1])
+ iDay -= 7 ;
+
+ // Assign that day to the structure.
+
+ pstDstAbs->wDay = iDay ;
+}
+
+BOOL LocalGreaterThanTransition (const SYSTEMTIME * pstLoc, const SYSTEMTIME * pstTran)
+{
+ FILETIME ftLoc, ftTran ;
+ LARGE_INTEGER liLoc, liTran ;
+ SYSTEMTIME stTranAbs ;
+
+ // Easy case: Just compare the two months
+
+ if (pstLoc->wMonth != pstTran->wMonth)
+ return (pstLoc->wMonth > pstTran->wMonth) ;
+
+ // Well, we're in a transition month. That requires a bit more work.
+
+ // Check if pstDst is in absolute or day-in-month format.
+ // (See documentation of TIME_ZONE_INFORMATION, StandardDate field.)
+
+ if (pstTran->wYear) // absolute format (haven't seen one yet!)
+ {
+ stTranAbs = * pstTran ;
+ }
+ else // day-in-month format
+ {
+ ConvertToAbsolute (pstLoc, pstTran, &stTranAbs) ;
+ }
+
+ // Now convert both date/time structures to large integers & compare
+
+ SystemTimeToFileTime (pstLoc, &ftLoc) ;
+ liLoc = * (LARGE_INTEGER *) (void *) &ftLoc ;
+
+ SystemTimeToFileTime (&stTranAbs, &ftTran) ;
+ liTran = * (LARGE_INTEGER *) (void *) &ftTran ;
+
+ return (liLoc.QuadPart > liTran.QuadPart) ;
+}
+
+BOOL MySystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION ptzi, LPSYSTEMTIME pstUtc, LPSYSTEMTIME pstLoc)
+{
+ // st is UTC
+
+ FILETIME ft ;
+ LARGE_INTEGER li ;
+ SYSTEMTIME stDst ;
+
+ if (IsWinVerNT())
+ return SystemTimeToTzSpecificLocalTime(ptzi, pstUtc, pstLoc);
+
+ // Convert time to a LARGE_INTEGER and subtract the bias
+
+ SystemTimeToFileTime (pstUtc, &ft) ;
+ li = * (LARGE_INTEGER *) (void *) &ft;
+ li.QuadPart -= (LONGLONG) 600000000 * ptzi->Bias ;
+
+ // Convert to a local date/time before application of daylight saving time.
+ // The local date/time must be used to determine when the conversion occurs.
+
+ ft = * (FILETIME *) (void *) &li ;
+ FileTimeToSystemTime (&ft, pstLoc) ;
+
+ // Find the time assuming Daylight Saving Time
+
+ li.QuadPart -= (LONGLONG) 600000000 * ptzi->DaylightBias ;
+ ft = * (FILETIME *) (void *) &li ;
+ FileTimeToSystemTime (&ft, &stDst) ;
+
+ // Now put li back the way it was
+
+ li.QuadPart += (LONGLONG) 600000000 * ptzi->DaylightBias ;
+
+ if (ptzi->StandardDate.wMonth) // ie, daylight savings time
+ {
+ // Northern hemisphere
+ if ((ptzi->DaylightDate.wMonth < ptzi->StandardDate.wMonth) &&
+
+ (stDst.wMonth >= pstLoc->wMonth) && // avoid the end of year problem
+
+ LocalGreaterThanTransition (pstLoc, &ptzi->DaylightDate) &&
+ !LocalGreaterThanTransition (&stDst, &ptzi->StandardDate))
+ {
+ li.QuadPart -= (LONGLONG) 600000000 * ptzi->DaylightBias ;
+ }
+ // Southern hemisphere
+
+ else if ((ptzi->StandardDate.wMonth < ptzi->DaylightDate.wMonth) &&
+ (!LocalGreaterThanTransition (&stDst, &ptzi->StandardDate) ||
+ LocalGreaterThanTransition (pstLoc, &ptzi->DaylightDate)))
+ {
+ li.QuadPart -= (LONGLONG) 600000000 * ptzi->DaylightBias ;
+ }
+ else
+ {
+ li.QuadPart -= (LONGLONG) 600000000 * ptzi->StandardBias ;
+ }
+ }
+
+ ft = * (FILETIME *) (void *) &li ;
+ FileTimeToSystemTime (&ft, pstLoc) ;
+ return TRUE ;
+}
+#endif
diff --git a/src/modules/utils/timezones.cpp b/src/modules/utils/timezones.cpp
new file mode 100644
index 0000000000..7d22cbec1e
--- /dev/null
+++ b/src/modules/utils/timezones.cpp
@@ -0,0 +1,662 @@
+/*
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2010 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+implements services to handle location - based timezones, instead of
+simple UTC offsets.
+*/
+
+#include <commonheaders.h>
+
+TIME_API tmi;
+
+#if _MSC_VER < 1500
+ typedef struct _TIME_DYNAMIC_ZONE_INFORMATION_T {
+ LONG Bias;
+ WCHAR StandardName[ 32 ];
+ SYSTEMTIME StandardDate;
+ LONG StandardBias;
+ WCHAR DaylightName[ 32 ];
+ SYSTEMTIME DaylightDate;
+ LONG DaylightBias;
+ WCHAR TimeZoneKeyName[ 128 ];
+ BOOLEAN DynamicDaylightTimeDisabled;
+ } DYNAMIC_TIME_ZONE_INFORMATION;
+#endif
+
+typedef DWORD (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi);
+static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation;
+
+typedef HRESULT (WINAPI *pfnSHLoadIndirectString_t)(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved);
+static pfnSHLoadIndirectString_t pfnSHLoadIndirectString;
+
+typedef LANGID (WINAPI *pfnGetUserDefaultUILanguage_t)(void);
+static pfnGetUserDefaultUILanguage_t pfnGetUserDefaultUILanguage;
+
+typedef LANGID (WINAPI *pfnGetSystemDefaultUILanguage_t)(void);
+static pfnGetSystemDefaultUILanguage_t pfnGetSystemDefaultUILanguage;
+
+typedef LPARAM (WINAPI *pfnSendMessageW_t)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
+static pfnSendMessageW_t pfnSendMessageW;
+
+typedef struct _REG_TZI_FORMAT
+{
+ LONG Bias;
+ LONG StandardBias;
+ LONG DaylightBias;
+ SYSTEMTIME StandardDate;
+ SYSTEMTIME DaylightDate;
+} REG_TZI_FORMAT;
+
+#define MIM_TZ_DISPLAYLEN 128
+
+struct MIM_TIMEZONE
+{
+ unsigned hash;
+ int offset;
+
+ TCHAR tszName[MIM_TZ_NAMELEN]; // windows name for the time zone
+ wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs)
+ // every hour should be sufficient.
+ TIME_ZONE_INFORMATION tzi;
+
+ static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2)
+ { return p2->tzi.Bias - p1->tzi.Bias; }
+};
+
+typedef struct
+{
+ DWORD timestamp; // last time updated
+ MIM_TIMEZONE myTZ; // set to my own timezone
+} TZ_INT_INFO;
+
+static TZ_INT_INFO myInfo;
+bool muiInstalled;
+
+static OBJLIST<MIM_TIMEZONE> g_timezones(55, NumericKeySortT);
+static LIST<MIM_TIMEZONE> g_timezonesBias(55, MIM_TIMEZONE::compareBias);
+
+void FormatTime (const SYSTEMTIME *st, const TCHAR *szFormat, TCHAR *szDest, int cbDest);
+void UnixTimeToFileTime(time_t ts, LPFILETIME pft);
+time_t FileTimeToUnixTime(LPFILETIME pft);
+
+#ifdef _UNICODE
+#define fnSystemTimeToTzSpecificLocalTime SystemTimeToTzSpecificLocalTime
+#else
+BOOL MySystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION ptzi, LPSYSTEMTIME pstUtc, LPSYSTEMTIME pstLoc);
+#define fnSystemTimeToTzSpecificLocalTime MySystemTimeToTzSpecificLocalTime
+#endif
+
+
+static int timeapiGetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st)
+{
+ if (st == NULL) return 1;
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == UTC_TIME_HANDLE)
+ GetSystemTime(st);
+ else if (tz && tz != &myInfo.myTZ)
+ {
+ SYSTEMTIME sto;
+ GetSystemTime(&sto);
+ return !fnSystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st);
+ }
+ else
+ GetLocalTime(st);
+
+ return 0;
+}
+
+static LPCTSTR timeapiGetTzName(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == NULL)
+ return myInfo.myTZ.tszName;
+ else if (tz == UTC_TIME_HANDLE)
+ return _T("UTC");
+
+ return tz->tszName;
+}
+
+static void CalcTsOffset(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+ GetSystemTime(&st);
+
+ FILETIME ft;
+ SystemTimeToFileTime(&st, &ft);
+ time_t ts1 = FileTimeToUnixTime(&ft);
+
+ if (!fnSystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl))
+ return;
+
+ SystemTimeToFileTime(&stl, &ft);
+ time_t ts2 = FileTimeToUnixTime(&ft);
+
+ tz->offset = ts2 - ts1;
+}
+
+static bool IsSameTime(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+
+ if (tz == &myInfo.myTZ)
+ return true;
+
+ timeapiGetTimeZoneTime(tz, &stl);
+ timeapiGetTimeZoneTime(NULL, &st);
+
+ return st.wHour == stl.wHour && st.wMinute == stl.wMinute;
+}
+
+static HANDLE timeapiGetInfoByName(LPCTSTR tszName, DWORD dwFlags)
+{
+ if (tszName == NULL)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? NULL : &myInfo.myTZ;
+
+ if (_tcscmp(myInfo.myTZ.tszName, tszName) == 0)
+ return (dwFlags & TZF_DIFONLY) ? NULL : &myInfo.myTZ;
+
+ MIM_TIMEZONE tzsearch;
+ tzsearch.hash = hashstr(tszName);
+
+ MIM_TIMEZONE *tz = g_timezones.find(&tzsearch);
+ if (tz == NULL)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? NULL : &myInfo.myTZ;
+
+ if (dwFlags & TZF_DIFONLY)
+ return IsSameTime(tz) ? NULL : tz;
+
+ return tz;
+}
+
+static HANDLE timeapiGetInfoByContact(HANDLE hContact, DWORD dwFlags)
+{
+ if (hContact == NULL)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? NULL : &myInfo.myTZ;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(hContact, "UserInfo", "TzName", &dbv))
+ {
+ HANDLE res = timeapiGetInfoByName(dbv.ptszVal, dwFlags);
+ DBFreeVariant(&dbv);
+ if (res) return res;
+ }
+
+ signed char timezone = (signed char)DBGetContactSettingByte(hContact, "UserInfo", "Timezone", -1);
+ if (timezone == -1)
+ {
+ char* szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (!DBGetContactSettingTString(hContact, szProto, "TzName", &dbv))
+ {
+ HANDLE res = timeapiGetInfoByName(dbv.ptszVal, dwFlags);
+ DBFreeVariant(&dbv);
+ if (res) return res;
+ }
+ timezone = (signed char)DBGetContactSettingByte(hContact, szProto, "Timezone", -1);
+ }
+
+ if (timezone != -1)
+ {
+ MIM_TIMEZONE tzsearch;
+ tzsearch.tzi.Bias = timezone * 30;
+ if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias)
+ {
+ if (dwFlags & TZF_DIFONLY) return NULL;
+ return &myInfo.myTZ;
+ }
+
+ int i = g_timezonesBias.getIndex(&tzsearch);
+ while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i;
+
+ int delta = LONG_MAX;
+ for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j)
+ {
+ int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth);
+ if (delta1 <= delta)
+ {
+ delta = delta1;
+ i = j;
+ }
+ }
+
+ if (i >= 0)
+ {
+ MIM_TIMEZONE *tz = g_timezonesBias[i];
+ return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? NULL : tz;
+ }
+ }
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? NULL : &myInfo.myTZ;
+}
+
+static void timeapiSetInfoByContact(HANDLE hContact, HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+
+ if (hContact == NULL) return;
+
+ if (tz)
+ {
+ DBWriteContactSettingTString(hContact, "UserInfo", "TzName", tz->tszName);
+ DBWriteContactSettingByte(hContact, "UserInfo", "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30));
+ }
+ else
+ {
+ DBDeleteContactSetting(hContact, "UserInfo", "TzName");
+ DBDeleteContactSetting(hContact, "UserInfo", "Timezone");
+ }
+}
+
+static int timeapiPrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, int cbDest, DWORD dwFlags)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == NULL && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)))
+ return 1;
+
+ SYSTEMTIME st;
+ if (timeapiGetTimeZoneTime(tz, &st))
+ return 1;
+
+ FormatTime(&st, szFormat, szDest, cbDest);
+
+ return 0;
+}
+
+static int timeapiPrintTimeStamp(HANDLE hTZ, time_t ts, LPCTSTR szFormat, LPTSTR szDest, int cbDest, DWORD dwFlags)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == NULL && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)))
+ return 1;
+
+ FILETIME ft;
+
+ if (tz == NULL) tz = &myInfo.myTZ;
+ if (tz == NULL)
+ {
+ FILETIME lft;
+
+ UnixTimeToFileTime(ts, &lft);
+ FileTimeToLocalFileTime(&lft, &ft);
+ }
+ else if (tz == UTC_TIME_HANDLE)
+ UnixTimeToFileTime(ts, &ft);
+ else
+ {
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ UnixTimeToFileTime(ts + tz->offset, &ft);
+ }
+
+ SYSTEMTIME st;
+ FileTimeToSystemTime(&ft, &st);
+
+ FormatTime(&st, szFormat, szDest, cbDest);
+
+ return 0;
+}
+
+static LPTIME_ZONE_INFORMATION timeapiGetTzi(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ return tz ? &tz->tzi : &myInfo.myTZ.tzi;
+}
+
+
+static time_t timeapiTimeStampToTimeZoneTimeStamp(HANDLE hTZ, time_t ts)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+
+ if (tz == NULL) tz = &myInfo.myTZ;
+ if (tz == NULL)
+ {
+ FILETIME ft, lft;
+
+ UnixTimeToFileTime(ts, &ft);
+ FileTimeToLocalFileTime(&ft, &lft);
+ return FileTimeToUnixTime(&lft);
+ }
+ else if (tz == UTC_TIME_HANDLE)
+ return ts;
+
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ return ts + tz->offset;
+}
+
+typedef struct
+{
+ UINT addStr, getSel, setSel, getData, setData;
+} ListMessages;
+
+static const ListMessages lbMessages =
+{ LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA };
+
+static const ListMessages cbMessages =
+{ CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA };
+
+static const ListMessages *GetListMessages(HWND hWnd, DWORD dwFlags)
+{
+ if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB)))
+ {
+ TCHAR tszClassName[128];
+ GetClassName(hWnd, tszClassName, SIZEOF(tszClassName));
+ if (!_tcsicmp(tszClassName, _T("COMBOBOX")))
+ dwFlags |= TZF_PLF_CB;
+ else if(!_tcsicmp(tszClassName, _T("LISTBOX")))
+ dwFlags |= TZF_PLF_LB;
+ }
+ if (dwFlags & TZF_PLF_CB)
+ return & cbMessages;
+ else if(dwFlags & TZF_PLF_LB)
+ return & lbMessages;
+ else
+ return NULL;
+}
+
+
+static int timeapiSelectListItem(HANDLE hContact, HWND hWnd, DWORD dwFlags)
+{
+ if (hWnd == NULL) // nothing to do
+ return -1;
+
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == NULL) return -1;
+
+ int iSelection = 0;
+ if (hContact)
+ {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingTString(hContact, "UserInfo", "TzName", &dbv))
+ {
+ unsigned hash = hashstr(dbv.ptszVal);
+ for (int i = 0; i < g_timezonesBias.getCount(); ++i)
+ {
+ if (hash == g_timezonesBias[i]->hash)
+ {
+ iSelection = i + 1;
+ break;
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ SendMessage(hWnd, lstMsg->setSel, iSelection, 0);
+ return iSelection;
+}
+
+
+static int timeapiPrepareList(HANDLE hContact, HWND hWnd, DWORD dwFlags)
+{
+ if (hWnd == NULL) // nothing to do
+ return 0;
+
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == NULL) return 0;
+
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateT("<unspecified>"));
+
+ for (int i = 0; i < g_timezonesBias.getCount(); ++i)
+ {
+ MIM_TIMEZONE *tz = g_timezonesBias[i];
+
+ if (pfnSendMessageW)
+ pfnSendMessageW(hWnd, lstMsg->addStr, 0, (LPARAM)tz->szDisplay);
+ else
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)StrConvTu(tz->szDisplay));
+
+ SendMessage(hWnd, lstMsg->setData, i + 1, (LPARAM)tz);
+ }
+
+ return timeapiSelectListItem(hContact, hWnd, dwFlags);
+}
+
+
+static void timeapiStoreListResult(HANDLE hContact, HWND hWnd, DWORD dwFlags)
+{
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == NULL) return;
+
+ LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0);
+ if (offset > 0)
+ {
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0);
+ if ((INT_PTR)tz != CB_ERR && tz != NULL)
+ timeapiSetInfoByContact(hContact, tz);
+ }
+ else
+ timeapiSetInfoByContact(hContact, NULL);
+}
+
+
+static INT_PTR GetTimeApi( WPARAM, LPARAM lParam )
+{
+ TIME_API* tmi = (TIME_API*)lParam;
+ if (tmi == NULL)
+ return FALSE;
+
+ if (tmi->cbSize != sizeof(TIME_API))
+ return FALSE;
+
+ tmi->createByName = timeapiGetInfoByName;
+ tmi->createByContact = timeapiGetInfoByContact;
+ tmi->storeByContact = timeapiSetInfoByContact;
+
+ tmi->printDateTime = timeapiPrintDateTime;
+ tmi->printTimeStamp = timeapiPrintTimeStamp;
+
+ tmi->prepareList = timeapiPrepareList;
+ tmi->selectListItem = timeapiSelectListItem;
+ tmi->storeListResults = timeapiStoreListResult;
+
+ tmi->getTimeZoneTime = timeapiGetTimeZoneTime;
+ tmi->timeStampToTimeZoneTimeStamp = timeapiTimeStampToTimeZoneTimeStamp;
+ tmi->getTzi = timeapiGetTzi;
+ tmi->getTzName = timeapiGetTzName;
+
+ return TRUE;
+}
+
+static INT_PTR TimestampToLocal(WPARAM wParam, LPARAM)
+{
+ return timeapiTimeStampToTimeZoneTimeStamp(NULL, (time_t)wParam);
+}
+
+static INT_PTR TimestampToStringT(WPARAM wParam, LPARAM lParam)
+{
+ DBTIMETOSTRINGT *tts = (DBTIMETOSTRINGT*)lParam;
+ if (tts == NULL) return 0;
+
+ timeapiPrintTimeStamp(NULL, (time_t)wParam, tts->szFormat, tts->szDest, tts->cbDest, 0);
+ return 0;
+}
+
+#ifdef _UNICODE
+static INT_PTR TimestampToStringA(WPARAM wParam, LPARAM lParam)
+{
+ DBTIMETOSTRING *tts = (DBTIMETOSTRING*)lParam;
+ if (tts == NULL) return 0;
+
+ TCHAR *szDest = (TCHAR*)alloca(tts->cbDest);
+ timeapiPrintTimeStamp(NULL, (time_t)wParam, StrConvT(tts->szFormat), szDest, tts->cbDest, 0);
+ WideCharToMultiByte(CP_ACP, 0, szDest, -1, tts->szDest, tts->cbDest, NULL, NULL);
+ return 0;
+}
+#endif
+
+void GetLocalizedString(HKEY hSubKey, const TCHAR *szName, wchar_t *szBuf, DWORD cbLen)
+{
+ szBuf[0] = 0;
+ if (muiInstalled)
+ {
+ TCHAR tszTempBuf[MIM_TZ_NAMELEN], tszName[30];
+ mir_sntprintf(tszName, SIZEOF(tszName), _T("MUI_%s"), szName);
+ DWORD dwLength = cbLen * sizeof(TCHAR);
+ if (ERROR_SUCCESS == RegQueryValueEx(hSubKey, tszName, NULL, NULL, (unsigned char *)tszTempBuf, &dwLength))
+ {
+ tszTempBuf[min(dwLength / sizeof(TCHAR), cbLen - 1)] = 0;
+ if (pfnSHLoadIndirectString)
+ pfnSHLoadIndirectString(StrConvU(tszTempBuf), szBuf, cbLen, NULL);
+ }
+ }
+ if (szBuf[0] == 0)
+ {
+ DWORD dwLength = cbLen * sizeof(wchar_t);
+
+#ifdef _UNICODE
+ RegQueryValueEx(hSubKey, szName, NULL, NULL, (unsigned char *)szBuf, &dwLength);
+ szBuf[min(dwLength / sizeof(TCHAR), cbLen - 1)] = 0;
+#else
+ char* szBufP = (char*)alloca(dwLength);
+ RegQueryValueEx(hSubKey, szName, NULL, NULL, (unsigned char *)szBufP, &dwLength);
+ szBufP[min(dwLength, cbLen * sizeof(wchar_t) - 1)] = 0;
+ MultiByteToWideChar(CP_ACP, 0, szBufP, -1, szBuf, cbLen);
+#endif
+ }
+}
+
+void RecalculateTime(void)
+{
+ GetTimeZoneInformation(&myInfo.myTZ.tzi);
+ myInfo.timestamp = time(NULL);
+ myInfo.myTZ.offset = INT_MIN;
+
+ bool found = false;
+ DYNAMIC_TIME_ZONE_INFORMATION dtzi;
+
+ if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID)
+ {
+ TCHAR *myTzKey = mir_u2t(dtzi.TimeZoneKeyName);
+ _tcscpy(myInfo.myTZ.tszName, myTzKey);
+ mir_free(myTzKey);
+ found = true;
+ }
+
+ for (int i = 0; i < g_timezones.getCount(); ++i)
+ {
+ MIM_TIMEZONE &tz = g_timezones[i];
+ if (tz.offset != INT_MIN) tz.offset = INT_MIN;
+
+ if (!found)
+ {
+ if (!wcscmp(tz.tzi.StandardName, myInfo.myTZ.tzi.StandardName) ||
+ !wcscmp(tz.tzi.DaylightName, myInfo.myTZ.tzi.DaylightName))
+ {
+ _tcscpy(myInfo.myTZ.tszName, tz.tszName);
+ found = true;
+ }
+ }
+ }
+}
+
+void InitTimeZones(void)
+{
+ REG_TZI_FORMAT tzi;
+ HKEY hKey;
+
+ const TCHAR *tszKey = IsWinVerNT() ?
+ _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones") :
+ _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones");
+
+ /*
+ * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with
+ * the registry key name, so finding our own time zone later will be MUCH easier for
+ * localized systems or systems with a MUI pack installed
+ */
+ if (IsWinVerVistaPlus())
+ pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(_T("kernel32")), "GetDynamicTimeZoneInformation");
+
+ if (IsWinVer2000Plus())
+ {
+ pfnSHLoadIndirectString = (pfnSHLoadIndirectString_t)GetProcAddress(GetModuleHandle(_T("shlwapi")), "SHLoadIndirectString");
+ pfnGetSystemDefaultUILanguage = (pfnGetSystemDefaultUILanguage_t)GetProcAddress(GetModuleHandle(_T("kernel32")), "GetSystemDefaultUILanguage");
+ pfnGetUserDefaultUILanguage = (pfnGetUserDefaultUILanguage_t)GetProcAddress(GetModuleHandle(_T("kernel32")), "GetUserDefaultUILanguage");
+ muiInstalled = pfnSHLoadIndirectString && pfnGetSystemDefaultUILanguage() != pfnGetUserDefaultUILanguage();
+ }
+
+ pfnSendMessageW = (pfnSendMessageW_t)GetProcAddress(GetModuleHandle(_T("user32")), "SendMessageW");
+
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey))
+ {
+ DWORD dwIndex = 0;
+ HKEY hSubKey;
+ TCHAR tszName[MIM_TZ_NAMELEN];
+
+ DWORD dwSize = SIZEOF(tszName);
+ while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, NULL, NULL, 0, NULL))
+ {
+ if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey))
+ {
+ dwSize = sizeof(tszName);
+
+ DWORD dwLength = sizeof(tzi);
+ if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, _T("TZI"), NULL, NULL, (unsigned char *)&tzi, &dwLength))
+ continue;
+
+ MIM_TIMEZONE *tz = new MIM_TIMEZONE;
+
+ tz->tzi.Bias = tzi.Bias;
+ tz->tzi.StandardDate = tzi.StandardDate;
+ tz->tzi.StandardBias = tzi.StandardBias;
+ tz->tzi.DaylightDate = tzi.DaylightDate;
+ tz->tzi.DaylightBias = tzi.DaylightBias;
+
+ _tcscpy(tz->tszName, tszName);
+ tz->hash = hashstr(tszName);
+ tz->offset = INT_MIN;
+
+ GetLocalizedString(hSubKey, _T("Display"), tz->szDisplay, SIZEOF(tz->szDisplay));
+ GetLocalizedString(hSubKey, _T("Std"), tz->tzi.StandardName, SIZEOF(tz->tzi.StandardName));
+ GetLocalizedString(hSubKey, _T("Dlt"), tz->tzi.DaylightName, SIZEOF(tz->tzi.DaylightName));
+
+ g_timezones.insert(tz);
+ g_timezonesBias.insert(tz);
+
+ RegCloseKey(hSubKey);
+ }
+ dwSize = SIZEOF(tszName);
+ }
+ RegCloseKey(hKey);
+ }
+
+ RecalculateTime();
+
+ CreateServiceFunction(MS_SYSTEM_GET_TMI, GetTimeApi);
+
+ CreateServiceFunction(MS_DB_TIME_TIMESTAMPTOLOCAL, TimestampToLocal);
+ CreateServiceFunction(MS_DB_TIME_TIMESTAMPTOSTRINGT, TimestampToStringT);
+#ifdef _UNICODE
+ CreateServiceFunction(MS_DB_TIME_TIMESTAMPTOSTRING, TimestampToStringA);
+#else
+ CreateServiceFunction(MS_DB_TIME_TIMESTAMPTOSTRING, TimestampToStringT);
+#endif
+
+
+ tmi.cbSize = sizeof(tmi);
+ GetTimeApi(0, (LPARAM)&tmi);
+}
+
+void UninitTimeZones(void)
+{
+ g_timezonesBias.destroy();
+ g_timezones.destroy();
+}
diff --git a/src/modules/utils/utf.cpp b/src/modules/utils/utf.cpp
new file mode 100644
index 0000000000..ad6683d2ed
--- /dev/null
+++ b/src/modules/utils/utf.cpp
@@ -0,0 +1,413 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+ Copyright 2000 Alexandre Julliard of Wine project
+ (UTF-8 conversion routines)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+
+/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
+static const char utf8_length[128] =
+{
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80-0x8f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90-0x9f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0-0xaf */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb0-0xbf */
+ 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xc0-0xcf */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xd0-0xdf */
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xe0-0xef */
+ 3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0 /* 0xf0-0xff */
+};
+
+/* first byte mask depending on UTF-8 sequence length */
+static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
+
+/* minimum Unicode value depending on UTF-8 sequence length */
+static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 };
+
+
+/* get the next char value taking surrogates into account */
+static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen)
+{
+ if (src[0] >= 0xd800 && src[0] <= 0xdfff) /* surrogate pair */
+ {
+ if (src[0] > 0xdbff || /* invalid high surrogate */
+ srclen <= 1 || /* missing low surrogate */
+ src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */
+ return 0;
+ return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff);
+ }
+ return src[0];
+}
+
+/* query necessary dst length for src string */
+static int Ucs2toUtf8Len(const wchar_t *src, unsigned int srclen)
+{
+ int len;
+ unsigned int val;
+
+ for (len = 0; srclen; srclen--, src++)
+ {
+ if (*src < 0x80) /* 0x00-0x7f: 1 byte */
+ {
+ len++;
+ continue;
+ }
+ if (*src < 0x800) /* 0x80-0x7ff: 2 bytes */
+ {
+ len += 2;
+ continue;
+ }
+ if (!(val = getSurrogateValue(src, srclen)))
+ {
+ return -2;
+ }
+ if (val < 0x10000) /* 0x800-0xffff: 3 bytes */
+ len += 3;
+ else /* 0x10000-0x10ffff: 4 bytes */
+ {
+ len += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return len;
+}
+
+int Ucs2toUtf8Len(const wchar_t *src)
+{
+ if ( src == 0 )
+ return 0;
+
+ return Ucs2toUtf8Len( src, (int)wcslen( src ));
+}
+
+/* wide char to UTF-8 string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen)
+{
+ int len;
+
+ for (len = dstlen; srclen; srclen--, src++)
+ {
+ WCHAR ch = *src;
+ unsigned int val;
+
+ if (ch < 0x80) /* 0x00-0x7f: 1 byte */
+ {
+ if (!len--) return -1; /* overflow */
+ *dst++ = ch;
+ continue;
+ }
+
+ if (ch < 0x800) /* 0x80-0x7ff: 2 bytes */
+ {
+ if ((len -= 2) < 0) return -1; /* overflow */
+ dst[1] = 0x80 | (ch & 0x3f);
+ ch >>= 6;
+ dst[0] = 0xc0 | ch;
+ dst += 2;
+ continue;
+ }
+
+ if (!(val = getSurrogateValue(src, srclen)))
+ {
+ return -2;
+ }
+
+ if (val < 0x10000) /* 0x800-0xffff: 3 bytes */
+ {
+ if ((len -= 3) < 0) return -1; /* overflow */
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xe0 | val;
+ dst += 3;
+ }
+ else /* 0x10000-0x10ffff: 4 bytes */
+ {
+ if ((len -= 4) < 0) return -1; /* overflow */
+ dst[3] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xf0 | val;
+ dst += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return dstlen - len;
+}
+
+/* helper for the various utf8 mbstowcs functions */
+static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend)
+{
+ unsigned int len = utf8_length[ch-0x80];
+ unsigned int res = ch & utf8_mask[len];
+ const char *end = *str + len;
+
+ if (end > strend) return ~0;
+ switch(len)
+ {
+ case 3:
+ if ((ch = end[-3] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+ case 2:
+ if ((ch = end[-2] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+ case 1:
+ if ((ch = end[-1] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+ if (res < utf8_minval[len]) break;
+ return res;
+ }
+ return ~0;
+}
+
+/* query necessary dst length for src string */
+static inline int Utf8toUcs2Len(const char *src, int srclen)
+{
+ int ret = 0;
+ unsigned int res;
+ const char *srcend = src + srclen;
+
+ while (src < srcend)
+ {
+ unsigned char ch = *src++;
+ if (ch < 0x80) /* special fast case for 7-bit ASCII */
+ {
+ ret++;
+ continue;
+ }
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff)
+ {
+ if (res > 0xffff) ret++;
+ ret++;
+ }
+ else return -2; /* bad char */
+ /* otherwise ignore it */
+ }
+ return ret;
+}
+
+/* UTF-8 to wide char string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int Utf8toUcs2(const char *src, int srclen, wchar_t *dst, int dstlen)
+{
+ unsigned int res;
+ const char *srcend = src + srclen;
+ wchar_t *dstend = dst + dstlen;
+
+ while ((dst < dstend) && (src < srcend))
+ {
+ unsigned char ch = *src++;
+ if (ch < 0x80) /* special fast case for 7-bit ASCII */
+ {
+ *dst++ = ch;
+ continue;
+ }
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff)
+ {
+ *dst++ = res;
+ }
+ else if (res <= 0x10ffff) /* we need surrogates */
+ {
+ if (dst == dstend - 1) return -1; /* overflow */
+ res -= 0x10000;
+ *dst++ = 0xd800 | (res >> 10);
+ *dst++ = 0xdc00 | (res & 0x3ff);
+ }
+ else return -2; /* bad char */
+ /* otherwise ignore it */
+ }
+ if (src < srcend) return -1; /* overflow */
+ return dstlen - (dstend - dst);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utf8Decode - converts UTF8-encoded string to the UCS2/MBCS format
+
+char* Utf8DecodeCP(char* str, int codepage, wchar_t** ucs2)
+{
+ int len;
+ bool needs_free = false;
+ wchar_t* tempBuf = NULL;
+ if ( ucs2 )
+ *ucs2 = NULL;
+
+ if (str == NULL)
+ return NULL;
+
+ len = (int)strlen(str);
+
+ if (len < 2) {
+ if (ucs2 != NULL) {
+ *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ MultiByteToWideChar(codepage, 0, str, len, tempBuf, len);
+ tempBuf[len] = 0;
+ }
+ return str;
+ }
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0)
+ return NULL;
+
+ if (ucs2 == NULL) {
+ __try
+ {
+ tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = NULL;
+ needs_free = true;
+ }
+ }
+
+ if ( tempBuf == NULL ) {
+ tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if ( tempBuf == NULL )
+ return NULL;
+ }
+
+ Utf8toUcs2(str, len, tempBuf, destlen);
+ tempBuf[destlen] = 0;
+ WideCharToMultiByte(codepage, 0, tempBuf, -1, str, len + 1, "?", NULL);
+
+ if (ucs2)
+ *ucs2 = tempBuf;
+ else if (needs_free)
+ mir_free(tempBuf);
+
+ return str;
+}
+
+char* Utf8Decode(char* str, wchar_t** ucs2)
+{
+ return Utf8DecodeCP(str, LangPackGetDefaultCodePage(), ucs2);
+}
+
+wchar_t* Utf8DecodeUcs2(const char* str)
+{
+ if (str == NULL)
+ return NULL;
+
+ int len = (int)strlen(str);
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0) return NULL;
+
+ wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if (ucs2 == NULL) return NULL;
+
+ if (Utf8toUcs2(str, len, ucs2, destlen) >= 0)
+ {
+ ucs2[destlen] = 0;
+ return ucs2;
+ }
+
+ mir_free(ucs2);
+
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utf8Encode - converts MBCS string to the UTF8-encoded format
+
+char* Utf8EncodeCP(const char* src, int codepage)
+{
+ int len;
+ bool needs_free = false;
+ char* result = NULL;
+ wchar_t* tempBuf;
+
+ if (src == NULL)
+ return NULL;
+
+ len = (int)strlen(src);
+
+ __try
+ {
+ tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ if (tempBuf == NULL) return NULL;
+ needs_free = true;
+ }
+
+ len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1);
+
+ int destlen = Ucs2toUtf8Len(tempBuf, len);
+ if (destlen >= 0)
+ {
+ result = (char*)mir_alloc(destlen + 1);
+ if (result)
+ {
+ Ucs2toUtf8(tempBuf, len, result, destlen);
+ result[destlen] = 0;
+ }
+ }
+
+ if (needs_free)
+ mir_free(tempBuf);
+
+ return result;
+}
+
+char* Utf8Encode(const char* src)
+{
+ return Utf8EncodeCP(src, LangPackGetDefaultCodePage());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utf8Encode - converts UCS2 string to the UTF8-encoded format
+
+char* Utf8EncodeUcs2(const wchar_t* src)
+{
+ if (src == NULL)
+ return NULL;
+
+ int len = (int)wcslen(src);
+
+ int destlen = Ucs2toUtf8Len(src, len);
+ if (destlen < 0) return NULL;
+
+ char* result = (char*)mir_alloc(destlen + 1);
+ if (result == NULL)
+ return NULL;
+
+ Ucs2toUtf8(src, len, result, destlen);
+ result[destlen] = 0;
+
+ return result;
+}
diff --git a/src/modules/utils/utils.cpp b/src/modules/utils/utils.cpp
new file mode 100644
index 0000000000..9e81b3084b
--- /dev/null
+++ b/src/modules/utils/utils.cpp
@@ -0,0 +1,587 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+INT_PTR ResizeDialog(WPARAM wParam,LPARAM lParam);
+int InitOpenUrl(void);
+int InitWindowList(void);
+void FreeWindowList(void);
+int InitHyperlink(void);
+int InitColourPicker(void);
+int InitBitmapFilter(void);
+void InitXmlApi(void);
+void InitTimeZones(void);
+void UninitTimeZones(void);
+
+INT_PTR GetMD5Interface(WPARAM, LPARAM);
+INT_PTR GetSHA1Interface(WPARAM, LPARAM);
+
+static BOOL bModuleInitialized = FALSE;
+
+static struct CountryListEntry countries[]={
+ {0 ,"Unspecified"},
+ {9999,"Other"},
+ {0xFFFF,"Unknown"},
+ {93 ,"Afghanistan"},
+ {355 ,"Albania"},
+ {213 ,"Algeria"},
+ {376 ,"Andorra"},
+ {244 ,"Angola"},
+ {1264,"Anguilla"}, /* change county code to NANP (from 101) */
+ {1268,"Antigua and Barbuda"}, /* change county code to NANP (from 1021) */
+// {5902,"Antilles"}, /* removed: it is not a country, it's a group of islands from diffrent countries (all are included in the list)*/
+ {54 ,"Argentina"},
+ {374 ,"Armenia"},
+ {297 ,"Aruba"},
+ {247 ,"Ascension Island"},
+ {61 ,"Australia"},
+ {6720 ,"Australia, Antarctic Territory"}, /* added country code 672(0)*/
+ {614 ,"Australia, Christmas Island"}, /* rename (from Christmas Island) and change to official county code 61(4) (from 672) */
+ {61891,"Australia, Cocos (Keeling) Islands"}, /* rename and change to official county code 61(891) (from 6102) */
+ {6723 ,"Australia, Norfolk Island"}, /* rename (from Norfolk Island) and change to official county code 672(3) (from 6722) */
+ {43 ,"Austria"},
+ {994 ,"Azerbaijan"},
+ {1242,"Bahamas"}, /* change county code to NANP (from 103) */
+ {973 ,"Bahrain"},
+ {880 ,"Bangladesh"},
+ {1246,"Barbados"}, /* change county code to NANP (from 103) */
+// {120 ,"Barbuda"}, /* removed: it is not a country and no special island, see Antigua and Barbuda*/
+ {375 ,"Belarus"},
+ {32 ,"Belgium"},
+ {501 ,"Belize"},
+ {229 ,"Benin"},
+ {1441,"Bermuda"}, /* change county code to NANP (from 105) */
+ {975 ,"Bhutan"},
+ {591 ,"Bolivia"},
+ {387 ,"Bosnia and Herzegovina"},
+ {267 ,"Botswana"},
+ {55 ,"Brazil"},
+ {673 ,"Brunei"},
+ {359 ,"Bulgaria"},
+ {226 ,"Burkina Faso"},
+ {257 ,"Burundi"},
+ {855 ,"Cambodia"},
+ {237 ,"Cameroon"},
+ {1002,"Canada"}, /* change county code to NANP (from 107 to virtual 1(002) -> reflect NANP*/
+ {238 ,"Cape Verde Islands"},
+ {1345,"Cayman Islands"}, /* change county code to NANP (from 108) */
+ {236 ,"Central African Republic"},
+ {235 ,"Chad"},
+ {56 ,"Chile, Republic of"},
+ {86 ,"China"},
+// {6101,"Cocos-Keeling Islands"}, /* removed (double): see Australia, Cocos (Keeling) Islands */
+ {57 ,"Colombia"},
+ {269 ,"Comoros"}, /* change county code (from 2691) */
+ {243 ,"Congo, Democratic Republic of (Zaire)"},
+ {242 ,"Congo, Republic of the"},
+ {682 ,"Cook Islands"},
+ {506 ,"Costa Rica"},
+ {225 ,"Cote d'Ivoire (Ivory Coast)"},
+ {385 ,"Croatia"},
+ {53 ,"Cuba"},
+ {357 ,"Greek, Republic of South Cyprus"}, /* rename coz Turkey, Republic of Northern Cyprus */
+ {420 ,"Czech Republic"},
+ {45 ,"Denmark"},
+ {246 ,"Diego Garcia"},
+ {253 ,"Djibouti"},
+ {1767,"Dominica"}, /* change county code to NANP (from 109) */
+ {1809,"Dominican Republic"}, /* change county code to NANP 809, 829, 849 (from 110) */
+ {593 ,"Ecuador"},
+ {20 ,"Egypt"},
+ {503 ,"El Salvador"},
+ {240 ,"Equatorial Guinea"},
+ {291 ,"Eritrea"},
+ {372 ,"Estonia"},
+ {251 ,"Ethiopia"},
+ {3883,"Europe"}, /* add county code +388 3 official European Telephony Numbering Space*/
+ {298 ,"Faeroe Islands"},
+ {500 ,"Falkland Islands"},
+ {679 ,"Fiji"},
+ {358 ,"Finland"},
+ {33 ,"France"},
+ {5901,"French Antilles"},
+ {594 ,"French Guiana"},
+ {689 ,"French Polynesia"},
+ {241 ,"Gabon"},
+ {220 ,"Gambia"},
+ {995 ,"Georgia"},
+ {49 ,"Germany"},
+ {233 ,"Ghana"},
+ {350 ,"Gibraltar"},
+ {30 ,"Greece"},
+ {299 ,"Greenland"},
+ {1473,"Grenada"}, /* change county code to NANP (from 111) */
+ {590 ,"Guadeloupe"},
+ {1671,"Guam, US Territory of"}, /* change county code to NANP (from 671) */
+ {502 ,"Guatemala"},
+ {224 ,"Guinea"},
+ {245 ,"Guinea-Bissau"},
+ {592 ,"Guyana"},
+ {509 ,"Haiti"},
+ {504 ,"Honduras"},
+ {852 ,"Hong Kong"},
+ {36 ,"Hungary"},
+ {354 ,"Iceland"},
+ {91 ,"India"},
+ {62 ,"Indonesia"},
+ {98 ,"Iran (Islamic Republic of)"},
+ {964 ,"Iraq"},
+ {353 ,"Ireland"},
+ {972 ,"Israel"},
+ {39 ,"Italy"},
+ {1876,"Jamaica"}, /* change county code to NANP (from 112) */
+ {81 ,"Japan"},
+ {962 ,"Jordan"},
+ {705 ,"Kazakhstan"},
+ {254 ,"Kenya"},
+ {686 ,"Kiribati"},
+ {850 ,"Korea, North"},
+ {82 ,"Korea, South"},
+ {965 ,"Kuwait"},
+ {996 ,"Kyrgyzstan"}, /* change county code (from 706) */
+ {856 ,"Laos"},
+ {371 ,"Latvia"},
+ {961 ,"Lebanon"},
+ {266 ,"Lesotho"},
+ {231 ,"Liberia"},
+ {218 ,"Libyan Arab Jamahiriya"},
+ {423 ,"Liechtenstein"}, /* change county code (from 4101) */
+ {370 ,"Lithuania"},
+ {352 ,"Luxembourg"},
+ {853 ,"Macau"},
+ {389 ,"Macedonia, Republic of"}, /* rename coz war */
+ {261 ,"Madagascar"},
+ {265 ,"Malawi"},
+ {60 ,"Malaysia"},
+ {960 ,"Maldives"},
+ {223 ,"Mali"},
+ {356 ,"Malta"},
+ {692 ,"Marshall Islands"},
+ {596 ,"Martinique"},
+ {222 ,"Mauritania"},
+ {230 ,"Mauritius"},
+ {262 ,"Mayotte Island"}, /* change county code coz bug (from 269) */
+ {52 ,"Mexico"},
+ {691 ,"Micronesia, Federated States of"},
+ {373 ,"Moldova, Republic of"},
+ {377 ,"Monaco"},
+ {976 ,"Mongolia"},
+ {1664,"Montserrat"}, /* change county code to NANP (from 113) */
+ {212 ,"Morocco"},
+ {258 ,"Mozambique"},
+ {95 ,"Myanmar"},
+ {264 ,"Namibia"},
+ {674 ,"Nauru"},
+ {977 ,"Nepal"},
+ {31 ,"Netherlands"},
+ {599 ,"Netherlands Antilles"}, /* dissolved 2010 */
+ {5995 ,"St. Maarten"}, /* add new country in 2010 (from Netherlands Antilles) */
+ {5999 ,"Curacao"}, /* add new country in 2010 (from Netherlands Antilles) */
+ {5997 ,"Netherlands (Bonaire Island)"}, /* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+ {59946,"Netherlands (Saba Island)"}, /* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+ {59938,"Netherlands (St. Eustatius Island)"}, /* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+ // {114 ,"Nevis"}, /* removed: it is not a country, it's part of Saint Kitts and Nevis*/
+ {687 ,"New Caledonia"},
+ {64 ,"New Zealand"},
+ {505 ,"Nicaragua"},
+ {227 ,"Niger"},
+ {234 ,"Nigeria"},
+ {683 ,"Niue"},
+ {1670,"Northern Mariana Islands, US Territory of"}, /* added NANP */
+ {47 ,"Norway"},
+ {968 ,"Oman"},
+ {92 ,"Pakistan"},
+ {680 ,"Palau"},
+ {507 ,"Panama"},
+ {675 ,"Papua New Guinea"},
+ {595 ,"Paraguay"},
+ {51 ,"Peru"},
+ {63 ,"Philippines"},
+ {48 ,"Poland"},
+ {351 ,"Portugal"},
+ {1939,"Puerto Rico"}, /* change county code to NANP 939, 787 (from 121) */
+ {974 ,"Qatar"},
+ {262 ,"Reunion Island"},
+ {40 ,"Romania"},
+// {6701,"Rota Island"}, /* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+ {7 ,"Russia"},
+ {250 ,"Rwanda"},
+ {1684,"Samoa (USA)"}, /* rename (from American Samoa) change county code to NANP (from 684) */
+ {685 ,"Samoa, Western"}, /* rename (from Western Samoa) */
+ {290 ,"Saint Helena"},
+// {115 ,"Saint Kitts"}, /* removed: it is not a country it is part of Saint Kitts and Nevis*/
+ {1869,"Saint Kitts and Nevis"}, /* change county code to NANP (from 1141) */
+ {1758,"Saint Lucia"}, /* change county code to NANP (from 122) */
+ {508 ,"Saint Pierre and Miquelon"},
+ {1784,"Saint Vincent and the Grenadines"}, /* change county code to NANP (from 116) */
+// {670 ,"Saipan Island"}, /* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+ {378 ,"San Marino"},
+ {239 ,"Sao Tome and Principe"},
+ {966 ,"Saudi Arabia"},
+ {442 ,"Scotland"},
+ {221 ,"Senegal"},
+ {248 ,"Seychelles"},
+ {232 ,"Sierra Leone"},
+ {65 ,"Singapore"},
+ {421 ,"Slovakia"},
+ {386 ,"Slovenia"},
+ {677 ,"Solomon Islands"},
+ {252 ,"Somalia"},
+ {27 ,"South Africa"},
+ {34 ,"Spain"},
+ {3492,"Spain, Canary Islands"}, /*rename and change county code to 34(92) spain + canary code*/
+ {94 ,"Sri Lanka"},
+ {249 ,"Sudan"},
+ {597 ,"Suriname"},
+ {268 ,"Swaziland"},
+ {46 ,"Sweden"},
+ {41 ,"Switzerland"},
+ {963 ,"Syrian Arab Republic"},
+ {886 ,"Taiwan"},
+ {992 ,"Tajikistan"}, /* change county code (from 708) */
+ {255 ,"Tanzania"},
+ {66 ,"Thailand"},
+// {6702,"Tinian Island"}, /* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+ {670 ,"Timor, East"}, /* added (is part off Northern Mariana Islands but not US Territory*/
+ {228 ,"Togo"},
+ {690 ,"Tokelau"},
+ {676 ,"Tonga"},
+ {1868,"Trinidad and Tobago"}, /* change county code to NANP (from 1141) */
+ {216 ,"Tunisia"},
+ {90 ,"Turkey"},
+ {90392,"Turkey, Republic of Northern Cyprus"}, /* added (is diffrent from Greek part)*/
+ {993 ,"Turkmenistan"}, /* change county code (from 709) */
+ {1649,"Turks and Caicos Islands"}, /* change county code to NANP (from 118) */
+ {688 ,"Tuvalu"},
+ {256 ,"Uganda"},
+ {380 ,"Ukraine"},
+ {971 ,"United Arab Emirates"},
+ {44 ,"United Kingdom"},
+ {598 ,"Uruguay"},
+ {1 ,"USA"},
+ {998 ,"Uzbekistan"}, /* change county code (from 711) */
+ {678 ,"Vanuatu"},
+ {379 ,"Vatican City"},
+ {58 ,"Venezuela"},
+ {84 ,"Vietnam"},
+ {1284,"Virgin Islands (UK)"}, /* change county code to NANP (from 105) - rename coz Virgin Islands (USA) */
+ {1340,"Virgin Islands (USA)"}, /* change county code to NANP (from 123) */
+ {441 ,"Wales"},
+ {681 ,"Wallis and Futuna Islands"},
+ {967 ,"Yemen"},
+ {38 ,"Yugoslavia"}, /* added for old values like birth-country */
+ {381 ,"Serbia, Republic of"}, /* rename need (from Yugoslavia)*/
+ {383 ,"Kosovo, Republic of"}, /*change country code (from 3811), rename need (from Yugoslavia - Serbia) */
+ {382 ,"Montenegro, Republic of"}, /* rename need (from Yugoslavia - Montenegro) */
+ {260 ,"Zambia"},
+ {263 ,"Zimbabwe"},
+};
+
+static INT_PTR GetCountryByNumber(WPARAM wParam, LPARAM)
+{
+ int i;
+
+ for(i=0; i < SIZEOF(countries); i++ )
+ if((int)wParam==countries[i].id) return (INT_PTR)countries[i].szName;
+ return (INT_PTR)NULL;
+}
+
+static INT_PTR GetCountryList(WPARAM wParam,LPARAM lParam)
+{
+ *(int*)wParam = SIZEOF(countries);
+ *(struct CountryListEntry**)lParam=countries;
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR SaveWindowPosition(WPARAM, LPARAM lParam)
+{
+ SAVEWINDOWPOS *swp=(SAVEWINDOWPOS*)lParam;
+ WINDOWPLACEMENT wp;
+ char szSettingName[64];
+
+ wp.length=sizeof(wp);
+ GetWindowPlacement(swp->hwnd,&wp);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sx", swp->szNamePrefix);
+ DBWriteContactSettingDword(swp->hContact,swp->szModule,szSettingName,wp.rcNormalPosition.left);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sy", swp->szNamePrefix);
+ DBWriteContactSettingDword(swp->hContact,swp->szModule,szSettingName,wp.rcNormalPosition.top);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%swidth", swp->szNamePrefix);
+ DBWriteContactSettingDword(swp->hContact,swp->szModule,szSettingName,wp.rcNormalPosition.right-wp.rcNormalPosition.left);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sheight", swp->szNamePrefix);
+ DBWriteContactSettingDword(swp->hContact,swp->szModule,szSettingName,wp.rcNormalPosition.bottom-wp.rcNormalPosition.top);
+ return 0;
+}
+
+
+static INT_PTR AssertInsideScreen(WPARAM wParam, LPARAM lParam)
+{
+ LPRECT rc = (LPRECT) wParam;
+ if (rc == NULL)
+ return -1;
+
+ RECT rcScreen;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE);
+
+ if (MyMonitorFromWindow)
+ {
+ if (MyMonitorFromRect(rc, MONITOR_DEFAULTTONULL))
+ return 0;
+
+ MONITORINFO mi = {0};
+ HMONITOR hMonitor = MyMonitorFromRect(rc, MONITOR_DEFAULTTONEAREST);
+ mi.cbSize = sizeof(mi);
+ if (MyGetMonitorInfo(hMonitor, &mi))
+ rcScreen = mi.rcWork;
+ }
+ else
+ {
+ RECT rcDest;
+ if (IntersectRect(&rcDest, &rcScreen, rc))
+ return 0;
+ }
+
+ if (rc->top >= rcScreen.bottom)
+ OffsetRect(rc, 0, rcScreen.bottom - rc->bottom);
+ else if (rc->bottom <= rcScreen.top)
+ OffsetRect(rc, 0, rcScreen.top - rc->top);
+ if (rc->left >= rcScreen.right)
+ OffsetRect(rc, rcScreen.right - rc->right, 0);
+ else if (rc->right <= rcScreen.left)
+ OffsetRect(rc, rcScreen.left - rc->left, 0);
+
+ return 1;
+}
+
+
+static INT_PTR RestoreWindowPosition(WPARAM wParam,LPARAM lParam)
+{
+ SAVEWINDOWPOS *swp=(SAVEWINDOWPOS*)lParam;
+ WINDOWPLACEMENT wp;
+ char szSettingName[64];
+ int x,y;
+
+ wp.length=sizeof(wp);
+ GetWindowPlacement(swp->hwnd,&wp);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sx", swp->szNamePrefix);
+ x=DBGetContactSettingDword(swp->hContact,swp->szModule,szSettingName,-1);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sy", swp->szNamePrefix);
+ y=(int)DBGetContactSettingDword(swp->hContact,swp->szModule,szSettingName,-1);
+ if(x==-1) return 1;
+ if(wParam&RWPF_NOSIZE) {
+ OffsetRect(&wp.rcNormalPosition,x-wp.rcNormalPosition.left,y-wp.rcNormalPosition.top);
+ }
+ else {
+ wp.rcNormalPosition.left=x;
+ wp.rcNormalPosition.top=y;
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%swidth", swp->szNamePrefix);
+ wp.rcNormalPosition.right=wp.rcNormalPosition.left+DBGetContactSettingDword(swp->hContact,swp->szModule,szSettingName,-1);
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%sheight", swp->szNamePrefix);
+ wp.rcNormalPosition.bottom=wp.rcNormalPosition.top+DBGetContactSettingDword(swp->hContact,swp->szModule,szSettingName,-1);
+ }
+ wp.flags=0;
+ if (wParam & RWPF_HIDDEN)
+ wp.showCmd = SW_HIDE;
+ if (wParam & RWPF_NOACTIVATE)
+ wp.showCmd = SW_SHOWNOACTIVATE;
+
+ if (!(wParam & RWPF_NOMOVE))
+ AssertInsideScreen((WPARAM) &wp.rcNormalPosition, 0);
+
+ SetWindowPlacement(swp->hwnd,&wp);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR RestartMiranda(WPARAM, LPARAM)
+{
+ TCHAR mirandaPath[ MAX_PATH ], cmdLine[ 100 ];
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si = { 0 };
+ si.cb = sizeof(si);
+ GetModuleFileName( NULL, mirandaPath, SIZEOF(mirandaPath));
+ mir_sntprintf( cmdLine, SIZEOF( cmdLine ), _T("/restart:%d"), GetCurrentProcessId());
+ CreateProcess( mirandaPath, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef BOOL (APIENTRY *PGENRANDOM)( PVOID, ULONG );
+
+static INT_PTR GenerateRandom(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == 0 || lParam == 0) return 0;
+
+ PGENRANDOM pfnRtlGenRandom = NULL;
+ HMODULE hModule = GetModuleHandleA("advapi32");
+ if (hModule)
+ {
+ pfnRtlGenRandom = (PGENRANDOM)GetProcAddress(hModule, "SystemFunction036");
+ if (pfnRtlGenRandom)
+ {
+ if (!pfnRtlGenRandom((PVOID)lParam, wParam))
+ pfnRtlGenRandom = NULL;
+ }
+ }
+ if (pfnRtlGenRandom == NULL)
+ {
+ srand(GetTickCount());
+ unsigned short* buf = (unsigned short*)lParam;
+ for ( ; (long)(wParam-=2) >= 0; )
+ *(buf++) = (unsigned short)rand();
+ if (lParam < 0)
+ *(char*)buf = (char)(rand() & 0xFF);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#if defined( _UNICODE )
+char* __fastcall rtrim(char* str)
+{
+ if (str == NULL) return NULL;
+ char* p = strchr(str, 0);
+ while (--p >= str)
+ {
+ switch (*p)
+ {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+#endif
+
+TCHAR* __fastcall rtrim(TCHAR *str)
+{
+ if (str == NULL) return NULL;
+ TCHAR* p = _tcschr(str, 0);
+ while (--p >= str)
+ {
+ switch (*p)
+ {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+
+char* __fastcall ltrim(char* str)
+{
+ if (str == NULL) return NULL;
+ char* p = str;
+
+ for (;;)
+ {
+ switch (*p)
+ {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ memmove(str, p, strlen(p) + 1);
+ return str;
+ }
+ }
+}
+
+char* __fastcall ltrimp(char* str)
+{
+ if (str == NULL) return NULL;
+ char* p = str;
+
+ for (;;)
+ {
+ switch (*p)
+ {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ return p;
+ }
+ }
+}
+
+bool __fastcall wildcmp(char * name, char * mask)
+{
+ char * last='\0';
+ for(;; mask++, name++)
+ {
+ if(*mask != '?' && *mask != *name) break;
+ if(*name == '\0') return ((BOOL)!*mask);
+ }
+ if(*mask != '*') return FALSE;
+ for(;; mask++, name++)
+ {
+ while(*mask == '*')
+ {
+ last = mask++;
+ if(*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if(*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if(*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int LoadUtilsModule(void)
+{
+ bModuleInitialized = TRUE;
+
+ CreateServiceFunction(MS_UTILS_RESIZEDIALOG,ResizeDialog);
+ CreateServiceFunction(MS_UTILS_SAVEWINDOWPOSITION,SaveWindowPosition);
+ CreateServiceFunction(MS_UTILS_RESTOREWINDOWPOSITION,RestoreWindowPosition);
+ CreateServiceFunction(MS_UTILS_ASSERTINSIDESCREEN,AssertInsideScreen);
+ CreateServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER,GetCountryByNumber);
+ CreateServiceFunction(MS_UTILS_GETCOUNTRYLIST,GetCountryList);
+ CreateServiceFunction(MS_UTILS_GETRANDOM,GenerateRandom);
+ CreateServiceFunction(MS_SYSTEM_RESTART,RestartMiranda);
+ CreateServiceFunction(MS_SYSTEM_GET_MD5I,GetMD5Interface);
+ CreateServiceFunction(MS_SYSTEM_GET_SHA1I,GetSHA1Interface);
+ InitOpenUrl();
+ InitWindowList();
+ InitHyperlink();
+ InitColourPicker();
+ InitBitmapFilter();
+ InitXmlApi();
+ InitTimeZones();
+ return 0;
+}
+
+void UnloadUtilsModule(void)
+{
+ if ( !bModuleInitialized ) return;
+
+ FreeWindowList();
+ UninitTimeZones();
+}
diff --git a/src/modules/utils/windowlist.cpp b/src/modules/utils/windowlist.cpp
new file mode 100644
index 0000000000..b7ea59eb58
--- /dev/null
+++ b/src/modules/utils/windowlist.cpp
@@ -0,0 +1,101 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static WINDOWLISTENTRY *windowList=NULL;
+static int windowListCount=0;
+static int nextWindowListId=1;
+
+static INT_PTR AllocWindowList(WPARAM, LPARAM)
+{
+ return nextWindowListId++;
+}
+
+static INT_PTR AddToWindowList(WPARAM, LPARAM lParam)
+{
+ windowList=(WINDOWLISTENTRY*)mir_realloc(windowList,sizeof(WINDOWLISTENTRY)*(windowListCount+1));
+ windowList[windowListCount++]=*(WINDOWLISTENTRY*)lParam;
+ return 0;
+}
+
+static INT_PTR RemoveFromWindowList(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ for(i=0;i<windowListCount;i++)
+ if(windowList[i].hwnd==(HWND)lParam && windowList[i].hList==(HANDLE)wParam) {
+ MoveMemory(&windowList[i],&windowList[i+1],sizeof(WINDOWLISTENTRY)*(windowListCount-i-1));
+ windowListCount--;
+ return 0;
+ }
+ return 1;
+}
+
+static INT_PTR FindInWindowList(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ for(i=0;i<windowListCount;i++)
+ if(windowList[i].hContact==(HANDLE)lParam && windowList[i].hList==(HANDLE)wParam)
+ return (INT_PTR)windowList[i].hwnd;
+ return (INT_PTR)(HWND)NULL;
+}
+
+static INT_PTR BroadcastToWindowList(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ MSG *msg=(MSG*)lParam;
+ for(i=0;i<windowListCount;i++)
+ if(windowList[i].hList==(HANDLE)wParam)
+ SendMessage(windowList[i].hwnd,msg->message,msg->wParam,msg->lParam);
+ return 0;
+}
+
+static INT_PTR BroadcastToWindowListAsync(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ MSG *msg=(MSG*)lParam;
+ for(i=0;i<windowListCount;i++)
+ if(windowList[i].hList==(HANDLE)wParam)
+ PostMessage(windowList[i].hwnd,msg->message,msg->wParam,msg->lParam);
+ return 0;
+}
+
+int InitWindowList(void)
+{
+ CreateServiceFunction(MS_UTILS_ALLOCWINDOWLIST,AllocWindowList);
+ CreateServiceFunction(MS_UTILS_ADDTOWINDOWLIST,AddToWindowList);
+ CreateServiceFunction(MS_UTILS_REMOVEFROMWINDOWLIST,RemoveFromWindowList);
+ CreateServiceFunction(MS_UTILS_BROADCASTTOWINDOWLIST,BroadcastToWindowList);
+ CreateServiceFunction(MS_UTILS_BROADCASTTOWINDOWLIST_ASYNC,BroadcastToWindowListAsync);
+ CreateServiceFunction(MS_UTILS_FINDWINDOWINLIST,FindInWindowList);
+ return 0;
+}
+
+void FreeWindowList(void)
+{
+ if ( windowList ) {
+ mir_free(windowList);
+ windowList = NULL;
+ }
+ windowListCount=0;
+ nextWindowListId=1;
+}
diff --git a/src/modules/visibility/visibility.cpp b/src/modules/visibility/visibility.cpp
new file mode 100644
index 0000000000..aa319cf715
--- /dev/null
+++ b/src/modules/visibility/visibility.cpp
@@ -0,0 +1,297 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+
+static void SetListGroupIcons(HWND hwndList,HANDLE hFirstItem,HANDLE hParentItem,int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[2]={1,1};
+ int childCount[2]={0,0},i;
+ int iImage;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetListGroupIcons(hwndList,hChildItem,hItem,childCount);
+ for( i=0; i < SIZEOF(iconOn); i++)
+ if(iconOn[i] && SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i)==0) iconOn[i]=0;
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ for ( i=0; i < SIZEOF(iconOn); i++) {
+ iImage=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,i);
+ if(iconOn[i] && iImage==0) iconOn[i]=0;
+ if(iImage!=0xFF) childCount[i]++;
+ }
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+ //set icons
+ for( i=0; i < SIZEOF(iconOn); i++) {
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hParentItem,MAKELPARAM(i,childCount[i]?(iconOn[i]?i+1:0):0xFF));
+ if(groupChildCount) groupChildCount[i]+=childCount[i];
+ }
+}
+
+static void SetAllChildIcons(HWND hwndList,HANDLE hFirstItem,int iColumn,int iImage)
+{
+ int typeOfFirst,iOldIcon;
+ HANDLE hItem,hChildItem;
+
+ typeOfFirst=SendMessage(hwndList,CLM_GETITEMTYPE,(WPARAM)hFirstItem,0);
+ //check groups
+ if(typeOfFirst==CLCIT_GROUP) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hFirstItem);
+ while(hItem) {
+ hChildItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_CHILD,(LPARAM)hItem);
+ if(hChildItem) SetAllChildIcons(hwndList,hChildItem,iColumn,iImage);
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTGROUP,(LPARAM)hItem);
+ }
+ //check contacts
+ if(typeOfFirst==CLCIT_CONTACT) hItem=hFirstItem;
+ else hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hFirstItem);
+ while(hItem) {
+ iOldIcon=SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,iColumn);
+ if(iOldIcon!=0xFF && iOldIcon!=iImage) SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(iColumn,iImage));
+ hItem=(HANDLE)SendMessage(hwndList,CLM_GETNEXTITEM,CLGN_NEXTCONTACT,(LPARAM)hItem);
+ }
+}
+
+static void ResetListOptions(HWND hwndList)
+{
+ int i;
+
+ SendMessage(hwndList,CLM_SETBKBITMAP,0,(LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList,CLM_SETBKCOLOR,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN,2,0);
+ SendMessage(hwndList,CLM_SETINDENT,10,0);
+ for(i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList,CLM_SETTEXTCOLOR,i,GetSysColor(COLOR_WINDOWTEXT));
+ SetWindowLongPtr(hwndList,GWL_STYLE,GetWindowLongPtr(hwndList,GWL_STYLE)|CLS_SHOWHIDDEN);
+}
+
+static void SetAllContactIcons(HWND hwndList)
+{
+ HANDLE hContact,hItem;
+ char *szProto;
+ DWORD flags;
+ WORD status;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendMessage(hwndList,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem) {
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if(szProto==NULL) {flags=0; status=0;}
+ else {
+ flags=CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_1,0);
+ status=DBGetContactSettingWord(hContact,szProto,"ApparentMode",0);
+ }
+ if(flags&PF1_INVISLIST) {
+ if(SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(0,0))==0xFF)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(0,status==ID_STATUS_ONLINE?1:0));
+ }
+ if(flags&PF1_VISLIST) {
+ if(SendMessage(hwndList,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(1,0))==0xFF)
+ SendMessage(hwndList,CLM_SETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(1,status==ID_STATUS_OFFLINE?2:0));
+ }
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+}
+
+static INT_PTR CALLBACK DlgProcVisibilityOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ static HICON hVisibleIcon,hInvisibleIcon;
+ static HANDLE hItemAll;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ { HIMAGELIST hIml;
+ hIml=ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),(IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,3,3);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_OTHER_SMALLDOT);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_STATUS_INVISIBLE);
+ ImageList_AddIcon_IconLibLoaded(hIml,SKINICON_STATUS_OFFLINE);
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRAIMAGELIST,0,(LPARAM)hIml);
+ hVisibleIcon=ImageList_GetIcon(hIml,1,ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg,IDC_VISIBLEICON,STM_SETICON,(WPARAM)hVisibleIcon,0);
+ hInvisibleIcon=ImageList_GetIcon(hIml,2,ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg,IDC_INVISIBLEICON,STM_SETICON,(WPARAM)hInvisibleIcon,0);
+ }
+
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_SETEXTRACOLUMNS,2,0);
+
+ { CLCINFOITEM cii={0};
+ cii.cbSize=sizeof(cii);
+ cii.flags=CLCIIF_GROUPFONT;
+ cii.pszText=TranslateT("** All contacts **");
+ hItemAll=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_ADDINFOITEM,0,(LPARAM)&cii);
+ }
+
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ return TRUE;
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(GetDlgItem(hwndDlg,IDC_LIST));
+ //fall through
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg,IDC_LIST),(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETNEXTITEM,CLGN_ROOT,0),hItemAll,NULL);
+ break;
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg,IDC_LIST));
+ break;
+ case NM_CLICK:
+ { HANDLE hItem;
+ NMCLISTCONTROL *nm=(NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+ int iImage;
+ int itemType;
+
+ // Make sure we have an extra column
+ if (nm->iColumn == -1)
+ break;
+
+ // Find clicked item
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x,nm->pt.y));
+ // Nothing was clicked
+ if (hItem == NULL) break;
+ // It was not a visbility icon
+ if (!(hitFlags & CLCHT_ONITEMEXTRA)) break;
+
+ // Get image in clicked column (0=none, 1=visible, 2=invisible)
+ iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, 0));
+ if (iImage == 0)
+ iImage=nm->iColumn + 1;
+ else
+ if (iImage == 1 || iImage == 2)
+ iImage = 0;
+
+ // Get item type (contact, group, etc...)
+ itemType = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+
+ // Update list, making sure that the options are mutually exclusive
+ if (itemType == CLCIT_CONTACT) { // A contact
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, iImage));
+ if (iImage && SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(nm->iColumn?0:1,0))!=0xFF)
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn?0:1, 0));
+ }
+ else if (itemType == CLCIT_INFO) { // All Contacts
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ if (iImage)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn?0:1, 0);
+ }
+ else if (itemType == CLCIT_GROUP) { // A group
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hItem) {
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ if (iImage)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn?0:1, 0);
+ }
+ }
+ // Update the all/none icons
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), hItemAll, NULL);
+
+ // Activate Apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ { HANDLE hContact,hItem;
+ int set,i,iImage;
+
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ do {
+ hItem=(HANDLE)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_FINDCONTACT,(WPARAM)hContact,0);
+ if(hItem) {
+ set=0;
+ for(i=0;i<2;i++) {
+ iImage=SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGE,(WPARAM)hItem,MAKELPARAM(i,0));
+ if(iImage==i+1) {
+ CallContactService(hContact,PSS_SETAPPARENTMODE,iImage==1?ID_STATUS_ONLINE:ID_STATUS_OFFLINE,0);
+ set=1;
+ break;
+ }
+ }
+ if(!set) CallContactService(hContact,PSS_SETAPPARENTMODE,0,0);
+ }
+ } while(hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ DestroyIcon(hVisibleIcon);
+ DestroyIcon(hInvisibleIcon);
+ { HIMAGELIST hIml=(HIMAGELIST)SendDlgItemMessage(hwndDlg,IDC_LIST,CLM_GETEXTRAIMAGELIST,0,0);
+ ImageList_Destroy(hIml);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static int VisibilityOptInitialise(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = 850000000;
+ odp.hInstance = hMirandaInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_VISIBILITY);
+ odp.pszTitle = LPGEN("Visibility");
+ odp.pszGroup = LPGEN("Status");
+ odp.pfnDlgProc = DlgProcVisibilityOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp );
+ return 0;
+}
+
+int LoadVisibilityModule(void)
+{
+ HookEvent(ME_OPT_INITIALISE,VisibilityOptInitialise);
+ return 0;
+}
diff --git a/src/modules/xml/xmlApi.cpp b/src/modules/xml/xmlApi.cpp
new file mode 100644
index 0000000000..08887dd448
--- /dev/null
+++ b/src/modules/xml/xmlApi.cpp
@@ -0,0 +1,446 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "commonheaders.h"
+#include "xmlParser.h"
+
+static HXML xmlapiCreateNode( LPCTSTR name, LPCTSTR text, char isDeclaration )
+{
+ XMLNode result = XMLNode::createXMLTopNode( name, isDeclaration );
+ if ( text )
+ result.updateText( text );
+ return result.detach();
+}
+
+static void xmlapiDestroyNode( HXML n )
+{
+ XMLNode tmp; tmp.attach(n);
+}
+
+static HXML xmlapiParseString( LPCTSTR str, int* datalen, LPCTSTR tag )
+{
+ if (str == NULL) return NULL;
+
+ XMLResults res;
+ XMLNode result = XMLNode::parseString( str, tag, &res );
+
+ if ( datalen != NULL )
+ datalen[0] += res.nChars;
+
+ return (res.error == eXMLErrorNone || (tag != NULL && res.error == eXMLErrorMissingEndTag)) ? result.detach() : NULL;
+}
+
+static HXML xmlapiAddChild( HXML _n, LPCTSTR name, LPCTSTR text )
+{
+ XMLNode result = XMLNode(_n).addChild( name );
+ if ( text != NULL )
+ result.updateText( text );
+ return result;
+}
+
+static void xmlapiAddChild2( HXML _child, HXML _parent )
+{
+ XMLNode child(_child), parent(_parent);
+ parent.addChild( child );
+}
+
+static HXML xmlapiCopyNode( HXML _n )
+{
+ XMLNode result = XMLNode(_n);
+ return result.detach();
+}
+
+static LPCTSTR xmlapiGetAttr( HXML _n, int i )
+{
+ return XMLNode(_n).getAttributeValue( i );
+}
+
+static int xmlapiGetAttrCount( HXML _n )
+{
+ return XMLNode(_n).nAttribute();
+}
+
+static LPCTSTR xmlapiGetAttrName( HXML _n, int i )
+{
+ return XMLNode(_n).getAttributeName( i );
+}
+
+static HXML xmlapiGetChild( HXML _n, int i )
+{
+ return XMLNode(_n).getChildNode( i );
+}
+
+static HXML xmlapiGetChildByAttrValue( HXML _n, LPCTSTR name, LPCTSTR attrName, LPCTSTR attrValue )
+{
+ return XMLNode(_n).getChildNodeWithAttribute( name, attrName, attrValue );
+}
+
+static int xmlapiGetChildCount( HXML _n )
+{
+ return XMLNode(_n).nChildNode();
+}
+
+static HXML xmlapiGetFirstChild( HXML _n )
+{
+ return XMLNode(_n).getChildNode( 0 );
+}
+
+static HXML xmlapiGetNthChild( HXML _n, LPCTSTR name, int i )
+{
+ return XMLNode(_n).getChildNode( name, i );
+}
+
+static HXML xmlapiGetNextChild( HXML _n, LPCTSTR name, int* i )
+{
+ return XMLNode(_n).getChildNode( name, i );
+}
+
+static HXML xmlapiGetNextNode( HXML _n )
+{
+ return XMLNode(_n).getNextNode( );
+}
+
+static HXML xmlapiGetChildByPath( HXML _n, LPCTSTR path, char createNodeIfMissing )
+{
+ return XMLNode(_n).getChildNodeByPath( path, createNodeIfMissing );
+}
+
+static LPCTSTR xmlapiGetName( HXML _n )
+{
+ return XMLNode(_n).getName();
+}
+
+static HXML xmlapiGetParent( HXML _n )
+{
+ return XMLNode(_n).getParentNode();
+}
+
+static LPCTSTR xmlapiGetText( HXML _n )
+{
+ return XMLNode(_n).getInnerText();
+}
+
+static LPCTSTR xmlapiGetAttrValue( HXML _n, LPCTSTR attrName )
+{
+ return XMLNode(_n).getAttribute( attrName );
+}
+
+static void xmlapiSetText( HXML _n, LPCTSTR _text )
+{
+ XMLNode(_n).updateText( _text );
+}
+
+static LPTSTR xmlapiToString( HXML _n, int* datalen )
+{
+ return XMLNode(_n).createXMLString( 0, datalen );
+}
+
+static void xmlapiAddAttr( HXML _n, LPCTSTR attrName, LPCTSTR attrValue )
+{
+ if ( attrName != NULL && attrValue != NULL )
+ XMLNode(_n).addAttribute( attrName, attrValue );
+}
+
+static void xmlapiAddAttrInt( HXML _n, LPCTSTR attrName, int attrValue )
+{
+ TCHAR buf[40];
+ _itot( attrValue, buf, 10 );
+ XMLNode(_n).addAttribute( attrName, buf );
+}
+
+static void xmlapiFree( void* p )
+{
+ free( p );
+}
+
+// XML API v2 methods
+static int xmlapiGetTextCount( HXML _n )
+{
+ return XMLNode(_n).nText();
+}
+
+static LPCTSTR xmlapiGetTextByIndex( HXML _n, int i )
+{
+ return XMLNode(_n).getText( i );
+}
+
+static void xmlapiSetTextByIndex( HXML _n, int i, LPCTSTR value )
+{
+ XMLNode(_n).updateText( value, i );
+}
+
+static void xmlapiAddText( HXML _n, LPCTSTR value, XML_ELEMENT_POS pos )
+{
+ XMLNode(_n).addText( value, ( XMLElementPosition )pos );
+}
+
+static LPTSTR xmlapiToStringWithFormatting( HXML _n, int* datalen )
+{
+ return XMLNode(_n).createXMLString( 1, datalen );
+}
+
+static int xmlapiGetClearCount( HXML _n )
+{
+ return XMLNode(_n).nClear();
+}
+
+static LPCTSTR xmlapiGetClear( HXML _n, int i, LPCTSTR *openTag, LPCTSTR *closeTag )
+{
+ XMLClear c = XMLNode(_n).getClear( i );
+ if ( openTag )
+ *openTag = c.lpszOpenTag;
+ if ( closeTag )
+ *closeTag = c.lpszCloseTag;
+ return c.lpszValue;
+}
+
+static void xmlapiAddClear( HXML _n, LPCTSTR lpszValue, LPCTSTR openTag, LPCTSTR closeTag, XML_ELEMENT_POS pos )
+{
+ XMLNode(_n).addClear( lpszValue, openTag, closeTag, ( XMLElementPosition )pos );
+}
+
+static void xmlapiSetClear( HXML _n, int i, LPCTSTR lpszValue )
+{
+ XMLNode(_n).updateClear( lpszValue, i );
+}
+
+static int xmlapiGetElement( HXML _n, XML_ELEMENT_POS pos, XML_ELEMENT_TYPE *type, HXML *child, LPCTSTR *value, LPCTSTR *name, LPCTSTR *openTag, LPCTSTR *closeTag )
+{
+ // reset all values
+ if ( child )
+ *child = NULL;
+ if ( value )
+ *value = NULL;
+ if ( name )
+ *name = NULL;
+ if ( openTag )
+ *openTag = NULL;
+ if ( closeTag )
+ *closeTag = NULL;
+
+ if ( !type || pos >= XMLNode(_n).nElement())
+ return false;
+ XMLNodeContents c( XMLNode(_n).enumContents( ( XMLElementPosition )pos ));
+ switch ( c.etype ) {
+ case eNodeChild:
+ {
+ *type = XML_ELEM_TYPE_CHILD;
+ if ( child )
+ *child = c.child;
+ } break;
+ case eNodeAttribute:
+ {
+ *type = XML_ELEM_TYPE_ATTRIBUTE;
+ if ( name )
+ *name = c.attrib.lpszName;
+ if ( value )
+ *value = c.attrib.lpszValue;
+ } break;
+ case eNodeText:
+ {
+ *type = XML_ELEM_TYPE_TEXT;
+ if ( value )
+ *value = c.text;
+ } break;
+ case eNodeClear:
+ {
+ *type = XML_ELEM_TYPE_CLEAR;
+ if ( value )
+ *value = c.clear.lpszValue;
+ if ( openTag )
+ *openTag = c.clear.lpszOpenTag;
+ if ( closeTag )
+ *closeTag = c.clear.lpszCloseTag;
+ } break;
+ case eNodeNULL:
+ {
+ return false;
+ } break;
+ }
+ return true;
+}
+
+static int xmlapiGetElementCount( HXML _n )
+{
+ return XMLNode(_n).nElement();
+}
+
+static char xmlapiIsDeclaration( HXML _n )
+{
+ return XMLNode(_n).isDeclaration();
+}
+
+static HXML xmlapiDeepCopy( HXML _n )
+{
+ return XMLNode(_n).deepCopy().detach();
+}
+
+static HXML xmlapiAddChildEx( HXML _n, LPCTSTR name, char isDeclaration, XML_ELEMENT_POS pos )
+{
+ return XMLNode(_n).addChild( name, isDeclaration, ( XMLElementPosition )pos );
+}
+
+static void xmlapiAddChildEx2( HXML _n, HXML parent, XML_ELEMENT_POS pos )
+{
+ XMLNode(_n).addChild( parent, ( XMLElementPosition )pos );
+}
+
+static void xmlapiSetAttrByIndex( HXML _n, int i, LPCTSTR value )
+{
+ XMLNode(_n).updateAttribute( value, NULL, i );
+}
+
+static void xmlapiSetAttrByName( HXML _n, LPCTSTR name, LPCTSTR value )
+{
+ XMLNode(_n).updateAttribute( value, NULL, name );
+}
+
+static void xmlapiDeleteNodeContent( HXML _n )
+{
+ XMLNode(_n).deleteNodeContent();
+}
+
+static void xmlapiDeleteAttrByIndex( HXML _n, int i )
+{
+ XMLNode(_n).deleteAttribute( i );
+}
+
+static void xmlapiDeleteAttrByName( HXML _n, LPCTSTR name )
+{
+ XMLNode(_n).deleteAttribute( name );
+}
+
+static void xmlapiDeleteText( HXML _n, int i )
+{
+ XMLNode(_n).deleteText( i );
+}
+
+static void xmlapiDeleteClear( HXML _n, int i )
+{
+ XMLNode(_n).deleteClear( i );
+}
+
+static XML_ELEMENT_POS xmlapiPositionOfText( HXML _n, int i )
+{
+ return ( XML_ELEMENT_POS )XMLNode(_n).positionOfText( i );
+}
+
+static XML_ELEMENT_POS xmlapiPositionOfClear( HXML _n, int i )
+{
+ return ( XML_ELEMENT_POS )XMLNode(_n).positionOfClear( i );
+}
+
+static XML_ELEMENT_POS xmlapiPositionOfChildByIndex( HXML _n, int i )
+{
+ return ( XML_ELEMENT_POS )XMLNode(_n).positionOfChildNode( i );
+}
+
+static XML_ELEMENT_POS xmlapiPositionOfChildByNode( HXML _n, HXML child )
+{
+ return ( XML_ELEMENT_POS )XMLNode(_n).positionOfChildNode( child );
+}
+
+static XML_ELEMENT_POS xmlapiPositionOfChildByName( HXML _n, LPCTSTR name, int i )
+{
+ return ( XML_ELEMENT_POS )XMLNode(_n).positionOfChildNode( name, i );
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR GetXmlApi( WPARAM, LPARAM lParam )
+{
+ XML_API* xi = ( XML_API* )lParam;
+ if ( xi == NULL )
+ return FALSE;
+
+ if ( xi->cbSize != XML_API_SIZEOF_V1 && xi->cbSize != sizeof(XML_API))
+ return FALSE;
+
+ xi->createNode = xmlapiCreateNode;
+ xi->destroyNode = xmlapiDestroyNode;
+
+ xi->parseString = xmlapiParseString;
+ xi->toString = xmlapiToString;
+ xi->freeMem = xmlapiFree;
+
+ xi->addChild = xmlapiAddChild;
+ xi->addChild2 = xmlapiAddChild2;
+ xi->copyNode = xmlapiCopyNode;
+ xi->getChild = xmlapiGetChild;
+ xi->getChildByAttrValue = xmlapiGetChildByAttrValue;
+ xi->getChildCount = xmlapiGetChildCount;
+ xi->getFirstChild = xmlapiGetFirstChild;
+ xi->getNthChild = xmlapiGetNthChild;
+ xi->getNextChild = xmlapiGetNextChild;
+ xi->getNextNode = xmlapiGetNextNode;
+ xi->getChildByPath = xmlapiGetChildByPath;
+ xi->getName = xmlapiGetName;
+ xi->getParent = xmlapiGetParent;
+ xi->getText = xmlapiGetText;
+ xi->setText = xmlapiSetText;
+
+ xi->getAttr = xmlapiGetAttr;
+ xi->getAttrCount = xmlapiGetAttrCount;
+ xi->getAttrName = xmlapiGetAttrName;
+ xi->getAttrValue = xmlapiGetAttrValue;
+ xi->addAttr = xmlapiAddAttr;
+ xi->addAttrInt = xmlapiAddAttrInt;
+
+ if ( xi->cbSize > XML_API_SIZEOF_V1 ) {
+ xi->isDeclaration = xmlapiIsDeclaration;
+ xi->toStringWithFormatting = xmlapiToStringWithFormatting;
+ xi->deepCopy = xmlapiDeepCopy;
+ xi->setAttrByIndex = xmlapiSetAttrByIndex;
+ xi->setAttrByName = xmlapiSetAttrByName;
+ xi->addChildEx = xmlapiAddChildEx;
+ xi->addChildEx2 = xmlapiAddChildEx2;
+ xi->getTextCount = xmlapiGetTextCount;
+ xi->getTextByIndex = xmlapiGetTextByIndex;
+ xi->addText = xmlapiAddText;
+ xi->setTextByIndex = xmlapiSetTextByIndex;
+ xi->getClearCount = xmlapiGetClearCount;
+ xi->getClear = xmlapiGetClear;
+ xi->addClear = xmlapiAddClear;
+ xi->setClear = xmlapiSetClear;
+ xi->getElementCount = xmlapiGetElementCount;
+ xi->getElement = xmlapiGetElement;
+
+ xi->deleteNodeContent = xmlapiDeleteNodeContent;
+ xi->deleteAttrByIndex = xmlapiDeleteAttrByIndex;
+ xi->deleteAttrByName = xmlapiDeleteAttrByName;
+ xi->deleteText = xmlapiDeleteText;
+ xi->deleteClear = xmlapiDeleteClear;
+
+ xi->positionOfChildByIndex = xmlapiPositionOfChildByIndex;
+ xi->positionOfChildByNode = xmlapiPositionOfChildByNode;
+ xi->positionOfChildByName = xmlapiPositionOfChildByName;
+ xi->positionOfText = xmlapiPositionOfText;
+ xi->positionOfClear = xmlapiPositionOfClear;
+ }
+ return TRUE;
+}
+
+void InitXmlApi( void )
+{
+ CreateServiceFunction( MS_SYSTEM_GET_XI, GetXmlApi );
+}
diff --git a/src/modules/xml/xmlParser.cpp b/src/modules/xml/xmlParser.cpp
new file mode 100644
index 0000000000..f2afae563f
--- /dev/null
+++ b/src/modules/xml/xmlParser.cpp
@@ -0,0 +1,3061 @@
+/**
+****************************************************************************
+* <P> XML.c - implementation file for basic XML parser written in ANSI C++
+* for portability. It works by using recursion and a node tree for breaking
+* down the elements of an XML document. </P>
+*
+* @version V2.43
+* @author Frank Vanden Berghen
+*
+* NOTE:
+*
+* If you add "#define STRICT_PARSING", on the first line of this file
+* the parser will see the following XML-stream:
+* <a><b>some text</b><b>other text </a>
+* as an error. Otherwise, this tring will be equivalent to:
+* <a><b>some text</b><b>other text</b></a>
+*
+* NOTE:
+*
+* If you add "#define APPROXIMATE_PARSING" on the first line of this file
+* the parser will see the following XML-stream:
+* <data name="n1">
+* <data name="n2">
+* <data name="n3" />
+* as equivalent to the following XML-stream:
+* <data name="n1" />
+* <data name="n2" />
+* <data name="n3" />
+* This can be useful for badly-formed XML-streams but prevent the use
+* of the following XML-stream (problem is: tags at contiguous levels
+* have the same names):
+* <data name="n1">
+* <data name="n2">
+* <data name="n3" />
+* </data>
+* </data>
+*
+* NOTE:
+*
+* If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file
+* the "openFileHelper" function will always display error messages inside the
+* console instead of inside a message-box-window. Message-box-windows are
+* available on windows 9x/NT/2000/XP/Vista only.
+*
+* Copyright (c) 2002, Business-Insight
+* <a href="http://www.Business-Insight.com">Business-Insight</a>
+* All rights reserved.
+* See the file "AFPL-license.txt" about the licensing terms
+*
+****************************************************************************
+*/
+
+#include "commonheaders.h"
+#include "xmlParser.h"
+
+#include <memory.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+XMLCSTR XMLNode::getVersion() { return _CXML("v2.43"); }
+void freeXMLString(XMLSTR t){if(t)free(t);}
+
+static XMLNode::XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8;
+static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1;
+
+inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; }
+
+// You can modify the initialization of the variable "XMLClearTags" below
+// to change the clearTags that are currently recognized by the library.
+// The number on the second columns is the length of the string inside the
+// first column.
+// The "<!DOCTYPE" declaration must be the second in the list.
+// The "<!--" declaration must be the third in the list.
+// All ClearTag Strings must start with the '<' character.
+typedef struct { XMLCSTR lpszOpen; int openTagLen; XMLCSTR lpszClose;} ALLXMLClearTag;
+static ALLXMLClearTag XMLClearTags[] =
+{
+ { _CXML("<![CDATA["),9, _CXML("]]>") },
+ { _CXML("<!DOCTYPE"),9, _CXML(">") },
+ { _CXML("<!--") ,4, _CXML("-->") },
+ { _CXML("<PRE>") ,5, _CXML("</PRE>") },
+ // { _CXML("<Script>") ,8, _CXML("</Script>")},
+ { NULL ,0, NULL }
+};
+
+// You can modify the initialization of the variable "XMLEntities" below
+// to change the character entities that are currently recognized by the library.
+// The number on the second columns is the length of the string inside the
+// first column. Additionally, the syntaxes "&#xA0;" and "&#160;" are recognized.
+typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity;
+static XMLCharacterEntity XMLEntities[] =
+{
+ { _CXML("&amp;" ), 5, _CXML('&' )},
+ { _CXML("&lt;" ), 4, _CXML('<' )},
+ { _CXML("&gt;" ), 4, _CXML('>' )},
+ { _CXML("&quot;"), 6, _CXML('\"')},
+ { _CXML("&apos;"), 6, _CXML('\'')},
+ { NULL , 0, '\0' }
+};
+
+// When rendering the XMLNode to a string (using the "createXMLString" function),
+// you can ask for a beautiful formatting. This formatting is using the
+// following indentation character:
+#define INDENTCHAR _CXML('\t')
+
+// The following function parses the XML errors into a user friendly string.
+// You can edit this to change the output language of the library to something else.
+XMLCSTR XMLNode::getError(XMLError xerror)
+{
+ switch (xerror)
+ {
+ case eXMLErrorNone: return _CXML("No error");
+ case eXMLErrorMissingEndTag: return _CXML("Warning: Unmatched end tag");
+ case eXMLErrorNoXMLTagFound: return _CXML("Warning: No XML tag found");
+ case eXMLErrorEmpty: return _CXML("Error: No XML data");
+ case eXMLErrorMissingTagName: return _CXML("Error: Missing start tag name");
+ case eXMLErrorMissingEndTagName: return _CXML("Error: Missing end tag name");
+ case eXMLErrorUnmatchedEndTag: return _CXML("Error: Unmatched end tag");
+ case eXMLErrorUnmatchedEndClearTag: return _CXML("Error: Unmatched clear tag end");
+ case eXMLErrorUnexpectedToken: return _CXML("Error: Unexpected token found");
+ case eXMLErrorNoElements: return _CXML("Error: No elements found");
+ case eXMLErrorFileNotFound: return _CXML("Error: File not found");
+ case eXMLErrorFirstTagNotFound: return _CXML("Error: First Tag not found");
+ case eXMLErrorUnknownCharacterEntity:return _CXML("Error: Unknown character entity");
+ case eXMLErrorCharacterCodeAbove255: return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode.");
+ case eXMLErrorCharConversionError: return _CXML("Error: unable to convert between WideChar and MultiByte chars");
+ case eXMLErrorCannotOpenWriteFile: return _CXML("Error: unable to open file for writing");
+ case eXMLErrorCannotWriteFile: return _CXML("Error: cannot write into file");
+
+ case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _CXML("Warning: Base64-string length is not a multiple of 4");
+ case eXMLErrorBase64DecodeTruncatedData: return _CXML("Warning: Base64-string is truncated");
+ case eXMLErrorBase64DecodeIllegalCharacter: return _CXML("Error: Base64-string contains an illegal character");
+ case eXMLErrorBase64DecodeBufferTooSmall: return _CXML("Error: Base64 decode output buffer is too small");
+ };
+ return _CXML("Unknown");
+}
+
+/////////////////////////////////////////////////////////////////////////
+// Here start the abstraction layer to be OS-independent //
+/////////////////////////////////////////////////////////////////////////
+
+// Here is an abstraction layer to access some common string manipulation functions.
+// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0,
+// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++.
+// If you plan to "port" the library to a new system/compiler, all you have to do is
+// to edit the following lines.
+#ifdef XML_NO_WIDE_CHAR
+char myIsTextWideChar(const void *b, int len) { return FALSE; }
+#else
+#if defined (UNDER_CE) || !defined(_XMLWINDOWS)
+char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode
+{
+#ifdef sun
+ // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer.
+ if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE;
+#endif
+ const wchar_t *s=(const wchar_t*)b;
+
+ // buffer too small:
+ if (len<(int)sizeof(wchar_t)) return FALSE;
+
+ // odd length test
+ if (len&1) return FALSE;
+
+ /* only checks the first 256 characters */
+ len=mmin(256,len/sizeof(wchar_t));
+
+ // Check for the special byte order:
+ if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE;
+ if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE
+
+ // checks for ASCII characters in the UNICODE stream
+ int i,stats=0;
+ for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++;
+ if (stats>len/2) return TRUE;
+
+ // Check for UNICODE NULL chars
+ for (i=0; i<len; i++) if (!s[i]) return TRUE;
+
+ return FALSE;
+}
+#else
+char myIsTextWideChar(const void *b,int l) { return (char)IsTextUnicode((CONST LPVOID)b,l,NULL); }
+#endif
+#endif
+
+#ifdef _XMLWINDOWS
+// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
+#ifdef _XMLWIDECHAR
+wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce)
+{
+ int i;
+ if (ce==XMLNode::char_encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,NULL,0);
+ else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,NULL,0);
+ if (i<0) return NULL;
+ wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(XMLCHAR));
+ if (ce==XMLNode::char_encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,d,i);
+ else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,d,i);
+ d[i]=0;
+ return d;
+}
+static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return _wfopen(filename,mode); }
+static inline int xstrlen(XMLCSTR c) { return (int)wcslen(c); }
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _wcsnicmp(c1,c2,l);}
+static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _wcsicmp(c1,c2); }
+static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
+static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
+#else
+char *myWideCharToMultiByte(const wchar_t *s)
+{
+ UINT codePage=CP_ACP; if (characterEncoding==XMLNode::char_encoding_UTF8) codePage=CP_UTF8;
+ int i=(int)WideCharToMultiByte(codePage, // code page
+ 0, // performance and mapping flags
+ s, // wide-character string
+ -1, // number of chars in string
+ NULL, // buffer for new string
+ 0, // size of buffer
+ NULL, // default for unmappable chars
+ NULL // set when default char used
+ );
+ if (i<0) return NULL;
+ char *d=(char*)malloc(i+1);
+ WideCharToMultiByte(codePage, // code page
+ 0, // performance and mapping flags
+ s, // wide-character string
+ -1, // number of chars in string
+ d, // buffer for new string
+ i, // size of buffer
+ NULL, // default for unmappable chars
+ NULL // set when default char used
+ );
+ d[i]=0;
+ return d;
+}
+static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
+static inline int xstrlen(XMLCSTR c) { return (int)strlen(c); }
+#ifdef __BORLANDC__
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strnicmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return stricmp(c1,c2); }
+#else
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _strnicmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _stricmp(c1,c2); }
+#endif
+static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
+static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
+static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
+#endif
+#else
+// for gcc and CC
+#ifdef XML_NO_WIDE_CHAR
+char *myWideCharToMultiByte(const wchar_t *s) { return NULL; }
+#else
+char *myWideCharToMultiByte(const wchar_t *s)
+{
+ const wchar_t *ss=s;
+ int i=(int)wcsrtombs(NULL,&ss,0,NULL);
+ if (i<0) return NULL;
+ char *d=(char *)malloc(i+1);
+ wcsrtombs(d,&s,i,NULL);
+ d[i]=0;
+ return d;
+}
+#endif
+#ifdef _XMLWIDECHAR
+wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce)
+{
+ const char *ss=s;
+ int i=(int)mbsrtowcs(NULL,&ss,0,NULL);
+ if (i<0) return NULL;
+ wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(wchar_t));
+ mbsrtowcs(d,&s,i,NULL);
+ d[i]=0;
+ return d;
+}
+int xstrlen(XMLCSTR c) { return wcslen(c); }
+#ifdef sun
+// for CC
+#include <widec.h>
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);}
+static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); }
+#else
+static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
+#ifdef __linux__
+// for gcc/linux
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); }
+#else
+#include <wctype.h>
+// for gcc/non-linux (MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 4.3.2, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin, mingw)
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2)
+{
+ wchar_t left,right;
+ do
+ {
+ left=towlower(*c1++); right=towlower(*c2++);
+ } while (left&&(left==right));
+ return (int)left-(int)right;
+}
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l)
+{
+ wchar_t left,right;
+ while(l--)
+ {
+ left=towlower(*c1++); right=towlower(*c2++);
+ if ((!left)||(left!=right)) return (int)left-(int)right;
+ }
+ return 0;
+}
+#endif
+#endif
+static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
+static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
+static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode)
+{
+ char *filenameAscii=myWideCharToMultiByte(filename);
+ FILE *f;
+ if (mode[0]==_CXML('r')) f=fopen(filenameAscii,"rb");
+ else f=fopen(filenameAscii,"wb");
+ free(filenameAscii);
+ return f;
+}
+#else
+static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
+static inline int xstrlen(XMLCSTR c) { return strlen(c); }
+static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);}
+static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
+static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); }
+static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
+static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
+#endif
+static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// the "xmltoc,xmltob,xmltoi,xmltol,xmltof,xmltoa" functions //
+///////////////////////////////////////////////////////////////////////////////
+// These 6 functions are not used inside the XMLparser.
+// There are only here as "convenience" functions for the user.
+// If you don't need them, you can delete them without any trouble.
+#ifdef _XMLWIDECHAR
+#ifdef _XMLWINDOWS
+// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0
+char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)_wtoi(t); return v; }
+int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return _wtoi(t); return v; }
+long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return _wtol(t); return v; }
+double xmltof(XMLCSTR t,double v){ if (t&&(*t)) swscanf(t, L"%lf", &v); /*v=_wtof(t);*/ return v; }
+#else
+#ifdef sun
+// for CC
+#include <widec.h>
+char xmltob(XMLCSTR t,char v){ if (t) return (char)wstol(t,NULL,10); return v; }
+int xmltoi(XMLCSTR t,int v){ if (t) return (int)wstol(t,NULL,10); return v; }
+long xmltol(XMLCSTR t,long v){ if (t) return wstol(t,NULL,10); return v; }
+#else
+// for gcc
+char xmltob(XMLCSTR t,char v){ if (t) return (char)wcstol(t,NULL,10); return v; }
+int xmltoi(XMLCSTR t,int v){ if (t) return (int)wcstol(t,NULL,10); return v; }
+long xmltol(XMLCSTR t,long v){ if (t) return wcstol(t,NULL,10); return v; }
+#endif
+double xmltof(XMLCSTR t,double v){ if (t&&(*t)) swscanf(t, L"%lf", &v); /*v=_wtof(t);*/ return v; }
+#endif
+#else
+char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)atoi(t); return v; }
+int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return atoi(t); return v; }
+long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return atol(t); return v; }
+double xmltof(XMLCSTR t,double v){ if (t&&(*t)) return atof(t); return v; }
+#endif
+XMLCSTR xmltoa(XMLCSTR t,XMLCSTR v){ if (t) return t; return v; }
+XMLCHAR xmltoc(XMLCSTR t,const XMLCHAR v){ if (t&&(*t)) return *t; return v; }
+
+/////////////////////////////////////////////////////////////////////////
+// the "openFileHelper" function //
+/////////////////////////////////////////////////////////////////////////
+
+// Since each application has its own way to report and deal with errors, you should modify & rewrite
+// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs.
+XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag)
+{
+ // guess the value of the global parameter "characterEncoding"
+ // (the guess is based on the first 200 bytes of the file).
+ FILE *f=xfopen(filename,_CXML("rb"));
+ if (f)
+ {
+ char bb[205];
+ int l=(int)fread(bb,1,200,f);
+ setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace,removeCommentsInMiddleOfText);
+ fclose(f);
+ }
+
+ // parse the file
+ XMLResults pResults;
+ XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults);
+
+ // display error message (if any)
+ if (pResults.error != eXMLErrorNone)
+ {
+ // create message
+ char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_CXML("");
+ if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; }
+ sprintf(message,
+#ifdef _XMLWIDECHAR
+ "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s"
+#else
+ "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s"
+#endif
+ ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3);
+
+ // display message
+#if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_)
+ MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST);
+#else
+ printf("%s",message);
+#endif
+ exit(255);
+ }
+ return xnode;
+}
+
+/////////////////////////////////////////////////////////////////////////
+// Here start the core implementation of the XMLParser library //
+/////////////////////////////////////////////////////////////////////////
+
+// You should normally not change anything below this point.
+
+#ifndef _XMLWIDECHAR
+// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte.
+// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes).
+// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes).
+// This table is used as lookup-table to know the length of a character (in byte) based on the
+// content of the first byte of the character.
+// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ).
+static const char XML_utf8ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
+ 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte
+ 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
+};
+static const char XML_legacyByteTable[256] =
+{
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+};
+static const char XML_sjisByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70
+ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0
+};
+static const char XML_gb2312ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90
+ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 0xa1 to 0xf7 2 bytes
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0
+ 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 // 0xf0
+};
+static const char XML_gbk_big5_ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70
+ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0xfe 2 bytes
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1 // 0xf0
+};
+static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8"
+#endif
+
+
+XMLNode XMLNode::emptyXMLNode;
+XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL};
+XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL};
+
+// Enumeration used to decipher what type a token is
+typedef enum XMLTokenTypeTag
+{
+ eTokenText = 0,
+ eTokenQuotedText,
+ eTokenTagStart, /* "<" */
+ eTokenTagEnd, /* "</" */
+ eTokenCloseTag, /* ">" */
+ eTokenEquals, /* "=" */
+ eTokenDeclaration, /* "<?" */
+ eTokenShortHandClose, /* "/>" */
+ eTokenClear,
+ eTokenError
+} XMLTokenType;
+
+// Main structure used for parsing XML
+typedef struct XML
+{
+ XMLCSTR lpXML;
+ XMLCSTR lpszText;
+ int nIndex,nIndexMissigEndTag;
+ enum XMLError error;
+ XMLCSTR lpEndTag;
+ int cbEndTag;
+ XMLCSTR lpNewElement;
+ int cbNewElement;
+ int nFirst;
+} XML;
+
+typedef struct
+{
+ ALLXMLClearTag *pClr;
+ XMLCSTR pStr;
+} NextToken;
+
+// Enumeration used when parsing attributes
+typedef enum Attrib
+{
+ eAttribName = 0,
+ eAttribEquals,
+ eAttribValue
+} Attrib;
+
+// Enumeration used when parsing elements to dictate whether we are currently
+// inside a tag
+typedef enum XMLStatus
+{
+ eInsideTag = 0,
+ eOutsideTag
+} XMLStatus;
+
+XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const
+{
+ if (!d) return eXMLErrorNone;
+ FILE *f=xfopen(filename,_CXML("wb"));
+ if (!f) return eXMLErrorCannotOpenWriteFile;
+#ifdef _XMLWIDECHAR
+ unsigned char h[2]={ 0xFF, 0xFE };
+ if (!fwrite(h,2,1,f))
+ {
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
+ {
+ if (!fwrite(L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\n",sizeof(wchar_t)*40,1,f))
+ {
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ }
+#else
+ if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
+ {
+ if (characterEncoding==char_encoding_UTF8)
+ {
+ // header so that windows recognize the file as UTF-8:
+ unsigned char h[3]={0xEF,0xBB,0xBF};
+ if (!fwrite(h,3,1,f))
+ {
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ encoding="utf-8";
+ } else if (characterEncoding==char_encoding_ShiftJIS) encoding="SHIFT-JIS";
+
+ if (!encoding) encoding="ISO-8859-1";
+ if (fprintf(f,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",encoding)<0)
+ {
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ } else
+ {
+ if (characterEncoding==char_encoding_UTF8)
+ {
+ unsigned char h[3]={0xEF,0xBB,0xBF};
+ if (!fwrite(h,3,1,f))
+ {
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ }
+ }
+#endif
+ int i;
+ XMLSTR t=createXMLString(nFormat,&i);
+ if (!fwrite(t,sizeof(XMLCHAR)*i,1,f))
+ {
+ free(t);
+ fclose(f);
+ return eXMLErrorCannotWriteFile;
+ }
+ if (fclose(f)!=0)
+ {
+ free(t);
+ return eXMLErrorCannotWriteFile;
+ }
+ free(t);
+ return eXMLErrorNone;
+}
+
+// Duplicate a given string.
+XMLSTR stringDup(XMLCSTR lpszData, int cbData)
+{
+ if (lpszData==NULL) return NULL;
+
+ XMLSTR lpszNew;
+ if (cbData==-1) cbData=(int)xstrlen(lpszData);
+ lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR));
+ if (lpszNew)
+ {
+ memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR));
+ lpszNew[cbData] = (XMLCHAR)NULL;
+ }
+ return lpszNew;
+}
+
+XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest,XMLCSTR source)
+{
+ XMLSTR dd=dest;
+ XMLCHAR ch;
+ XMLCharacterEntity *entity;
+ while ((ch=*source))
+ {
+ entity=XMLEntities;
+ do
+ {
+ if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; }
+ entity++;
+ } while(entity->s);
+#ifdef _XMLWIDECHAR
+ *(dest++)=*(source++);
+#else
+ switch(XML_ByteTable[(unsigned char)ch])
+ {
+ case 4: *(dest++)=*(source++);
+ case 3: *(dest++)=*(source++);
+ case 2: *(dest++)=*(source++);
+ case 1: *(dest++)=*(source++);
+ }
+#endif
+out_of_loop1:
+ ;
+ }
+ *dest=0;
+ return dd;
+}
+
+// private (used while rendering):
+int ToXMLStringTool::lengthXMLString(XMLCSTR source)
+{
+ int r=0;
+ XMLCharacterEntity *entity;
+ XMLCHAR ch;
+ while ((ch=*source))
+ {
+ entity=XMLEntities;
+ do
+ {
+ if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; }
+ entity++;
+ } while(entity->s);
+#ifdef _XMLWIDECHAR
+ r++; source++;
+#else
+ ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch;
+#endif
+out_of_loop1:
+ ;
+ }
+ return r;
+}
+
+ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); }
+void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
+XMLSTR ToXMLStringTool::toXML(XMLCSTR source)
+{
+ if (!source)
+ {
+ if (buflen<1) { buflen=1; buf=(XMLSTR)malloc(sizeof(XMLCHAR)); }
+ *buf=0;
+ return buf;
+ }
+ int l=lengthXMLString(source)+1;
+ if (l>buflen) { freeBuffer(); buflen=l; buf=(XMLSTR)malloc(l*sizeof(XMLCHAR)); }
+ return toXMLUnSafe(buf,source);
+}
+
+// private:
+XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML)
+{
+ // This function is the opposite of the function "toXMLString". It decodes the escape
+ // sequences &amp;, &quot;, &apos;, &lt;, &gt; and replace them by the characters
+ // &,",',<,>. This function is used internally by the XML Parser. All the calls to
+ // the XML library will always gives you back "decoded" strings.
+ //
+ // in: string (s) and length (lo) of string
+ // out: new allocated string converted from xml
+ if (!s) return NULL;
+
+ int ll=0,j;
+ XMLSTR d;
+ XMLCSTR ss=s;
+ XMLCharacterEntity *entity;
+ while ((lo>0)&&(*s))
+ {
+ if (*s==_CXML('&'))
+ {
+ if ((lo>2)&&(s[1]==_CXML('#')))
+ {
+ s+=2; lo-=2;
+ if ((*s==_CXML('X'))||(*s==_CXML('x'))) { s++; lo--; }
+ while ((*s)&&(*s!=_CXML(';'))&&((lo--)>0)) s++;
+ if (*s!=_CXML(';'))
+ {
+ pXML->error=eXMLErrorUnknownCharacterEntity;
+ return NULL;
+ }
+ s++; lo--;
+ } else
+ {
+ entity=XMLEntities;
+ do
+ {
+ if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; }
+ entity++;
+ } while(entity->s);
+ if (!entity->s)
+ {
+ pXML->error=eXMLErrorUnknownCharacterEntity;
+ return NULL;
+ }
+ }
+ } else
+ {
+#ifdef _XMLWIDECHAR
+ s++; lo--;
+#else
+ j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1;
+#endif
+ }
+ ll++;
+ }
+
+ d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR));
+ s=d;
+ while (ll-->0)
+ {
+ if (*ss==_CXML('&'))
+ {
+ if (ss[1]==_CXML('#'))
+ {
+ ss+=2; j=0;
+ if ((*ss==_CXML('X'))||(*ss==_CXML('x')))
+ {
+ ss++;
+ while (*ss!=_CXML(';'))
+ {
+ if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j<<4)+*ss-_CXML('0');
+ else if ((*ss>=_CXML('A'))&&(*ss<=_CXML('F'))) j=(j<<4)+*ss-_CXML('A')+10;
+ else if ((*ss>=_CXML('a'))&&(*ss<=_CXML('f'))) j=(j<<4)+*ss-_CXML('a')+10;
+ else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
+ ss++;
+ }
+ } else
+ {
+ while (*ss!=_CXML(';'))
+ {
+ if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j*10)+*ss-_CXML('0');
+ else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
+ ss++;
+ }
+ }
+#ifndef _XMLWIDECHAR
+ if (j>255) { free((void*)s); pXML->error=eXMLErrorCharacterCodeAbove255;return NULL;}
+#endif
+ (*d++)=(XMLCHAR)j; ss++;
+ } else
+ {
+ entity=XMLEntities;
+ do
+ {
+ if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; }
+ entity++;
+ } while(entity->s);
+ }
+ } else
+ {
+#ifdef _XMLWIDECHAR
+ *(d++)=*(ss++);
+#else
+ switch(XML_ByteTable[(unsigned char)*ss])
+ {
+ case 4: *(d++)=*(ss++); ll--;
+ case 3: *(d++)=*(ss++); ll--;
+ case 2: *(d++)=*(ss++); ll--;
+ case 1: *(d++)=*(ss++);
+ }
+#endif
+ }
+ }
+ *d=0;
+
+#ifndef _XMLWIDECHAR
+ if (characterEncoding != XMLNode::char_encoding_legacy)
+ Utf8Decode((XMLSTR)s, NULL );
+#endif
+
+ return (XMLSTR)s;
+}
+
+#define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r')))
+
+// private:
+char myTagCompare(XMLCSTR cclose, XMLCSTR copen)
+// !!!! WARNING strange convention&:
+// return 0 if equals
+// return 1 if different
+{
+ if (!cclose) return 1;
+ int l=(int)xstrlen(cclose);
+ if (xstrnicmp(cclose, copen, l)!=0) return 1;
+ const XMLCHAR c=copen[l];
+ if (XML_isSPACECHAR(c)||
+ (c==_CXML('/' ))||
+ (c==_CXML('<' ))||
+ (c==_CXML('>' ))||
+ (c==_CXML('=' ))) return 0;
+ return 1;
+}
+
+// Obtain the next character from the string.
+static inline XMLCHAR getNextChar(XML *pXML)
+{
+ XMLCHAR ch = pXML->lpXML[pXML->nIndex];
+#ifdef _XMLWIDECHAR
+ if (ch!=0) pXML->nIndex++;
+#else
+ pXML->nIndex+=XML_ByteTable[(unsigned char)ch];
+#endif
+ return ch;
+}
+
+// Find the next token in a string.
+// pcbToken contains the number of characters that have been read.
+static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType)
+{
+ NextToken result;
+ XMLCHAR ch;
+ XMLCHAR chTemp;
+ int indexStart,nFoundMatch,nIsText=FALSE;
+ result.pClr=NULL; // prevent warning
+
+ // Find next non-white space character
+ do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch);
+
+ if (ch)
+ {
+ // Cache the current string pointer
+ result.pStr = &pXML->lpXML[indexStart];
+
+ // check for standard tokens
+ switch(ch)
+ {
+ // Check for quotes
+ case _CXML('\''):
+ case _CXML('\"'):
+ // Type of token
+ *pType = eTokenQuotedText;
+ chTemp = ch;
+
+ // Set the size
+ nFoundMatch = FALSE;
+
+ // Search through the string to find a matching quote
+ while((ch = getNextChar(pXML)))
+ {
+ if (ch==chTemp) { nFoundMatch = TRUE; break; }
+ if (ch==_CXML('<')) break;
+ }
+
+ // If we failed to find a matching quote
+ if (nFoundMatch == FALSE)
+ {
+ pXML->nIndex=indexStart+1;
+ nIsText=TRUE;
+ break;
+ }
+
+ // 4.02.2002
+ // if (FindNonWhiteSpace(pXML)) pXML->nIndex--;
+
+ break;
+
+ // Equals (used with attribute values)
+ case _CXML('='):
+ *pType = eTokenEquals;
+ break;
+
+ // Close tag
+ case _CXML('>'):
+ *pType = eTokenCloseTag;
+ break;
+
+ // Check for tag start and tag end
+ case _CXML('<'):
+
+ {
+ // First check whether the token is in the clear tag list (meaning it
+ // does not need formatting).
+ ALLXMLClearTag *ctag=XMLClearTags;
+ do
+ {
+ if (!xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen))
+ {
+ result.pClr=ctag;
+ pXML->nIndex+=ctag->openTagLen-1;
+ *pType=eTokenClear;
+ return result;
+ }
+ ctag++;
+ } while(ctag->lpszOpen);
+
+ // Peek at the next character to see if we have an end tag '</',
+ // or an xml declaration '<?'
+ chTemp = pXML->lpXML[pXML->nIndex];
+
+ // If we have a tag end...
+ if (chTemp == _CXML('/'))
+ {
+ // Set the type and ensure we point at the next character
+ getNextChar(pXML);
+ *pType = eTokenTagEnd;
+ }
+
+ // If we have an XML declaration tag
+ else if (chTemp == _CXML('?'))
+ {
+
+ // Set the type and ensure we point at the next character
+ getNextChar(pXML);
+ *pType = eTokenDeclaration;
+ }
+
+ // Otherwise we must have a start tag
+ else
+ {
+ *pType = eTokenTagStart;
+ }
+ break;
+ }
+
+ // Check to see if we have a short hand type end tag ('/>').
+ case _CXML('/'):
+
+ // Peek at the next character to see if we have a short end tag '/>'
+ chTemp = pXML->lpXML[pXML->nIndex];
+
+ // If we have a short hand end tag...
+ if (chTemp == _CXML('>'))
+ {
+ // Set the type and ensure we point at the next character
+ getNextChar(pXML);
+ *pType = eTokenShortHandClose;
+ break;
+ }
+
+ // If we haven't found a short hand closing tag then drop into the
+ // text process
+
+ // Other characters
+ default:
+ nIsText = TRUE;
+ }
+
+ // If this is a TEXT node
+ if (nIsText)
+ {
+ // Indicate we are dealing with text
+ *pType = eTokenText;
+ while((ch = getNextChar(pXML)))
+ {
+ if XML_isSPACECHAR(ch)
+ {
+ indexStart++; break;
+
+ } else if (ch==_CXML('/'))
+ {
+ // If we find a slash then this maybe text or a short hand end tag
+ // Peek at the next character to see it we have short hand end tag
+ ch=pXML->lpXML[pXML->nIndex];
+ // If we found a short hand end tag then we need to exit the loop
+ if (ch==_CXML('>')) { pXML->nIndex--; break; }
+
+ } else if ((ch==_CXML('<'))||(ch==_CXML('>'))||(ch==_CXML('=')))
+ {
+ pXML->nIndex--; break;
+ }
+ }
+ }
+ *pcbToken = pXML->nIndex-indexStart;
+ } else
+ {
+ // If we failed to obtain a valid character
+ *pcbToken = 0;
+ *pType = eTokenError;
+ result.pStr=NULL;
+ }
+
+ return result;
+}
+
+XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName)
+{
+ if (!d) { free(lpszName); return NULL; }
+ if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName);
+ d->lpszName=lpszName;
+ return lpszName;
+}
+
+// private:
+XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; }
+XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration)
+{
+ d=(XMLNodeData*)malloc(sizeof(XMLNodeData));
+ d->ref_count=1;
+
+ d->lpszName=NULL;
+ d->nChild= 0;
+ d->nText = 0;
+ d->nClear = 0;
+ d->nAttribute = 0;
+
+ d->isDeclaration = isDeclaration;
+
+ d->pParent = pParent;
+ d->pChild= NULL;
+ d->pText= NULL;
+ d->pClear= NULL;
+ d->pAttribute= NULL;
+ d->pOrder= NULL;
+
+ d->pInnerText= NULL;
+
+ updateName_WOSD(lpszName);
+
+ d->lpszNS = NULL;
+ if ( lpszName && pParent && pParent->lpszName && !pParent->isDeclaration) {
+ TCHAR* p = _tcschr( lpszName, ':' );
+ if ( p ) {
+ *p = 0;
+ d->lpszNS = d->lpszName;
+ d->lpszName = p+1;
+ }
+ }
+}
+
+XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); }
+XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); }
+
+#define MEMORYINCREASE 50
+
+static inline void myFree(void *p) { if (p) free(p); }
+static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem)
+{
+ if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); }
+ if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem);
+ // if (!p)
+ // {
+ // printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220);
+ // }
+ return p;
+}
+
+// private:
+XMLElementPosition XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xxtype)
+{
+ if (index<0) return -1;
+ int i=0,j=(int)((index<<2)+xxtype),*o=d->pOrder; while (o[i]!=j) i++; return i;
+}
+
+// private:
+// update "order" information when deleting a content of a XMLNode
+int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index)
+{
+ int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t);
+ memmove(o+i, o+i+1, (n-i)*sizeof(int));
+ for (;i<n;i++)
+ if ((o[i]&3)==(int)t) o[i]-=4;
+ // We should normally do:
+ // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int));
+ // but we skip reallocation because it's too time consuming.
+ // Anyway, at the end, it will be free'd completely at once.
+ return i;
+}
+
+void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype)
+{
+ // in: *_pos is the position inside d->pOrder ("-1" means "EndOf")
+ // out: *_pos is the index inside p
+ p=myRealloc(p,(nc+1),memoryIncrease,size);
+ int n=d->nChild+d->nText+d->nClear;
+ d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int));
+ int pos=*_pos,*o=d->pOrder;
+
+ if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
+
+ int i=pos;
+ memmove(o+i+1, o+i, (n-i)*sizeof(int));
+
+ while ((pos<n)&&((o[pos]&3)!=(int)xtype)) pos++;
+ if (pos==n) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
+
+ o[i]=o[pos];
+ for (i=pos+1;i<=n;i++) if ((o[i]&3)==(int)xtype) o[i]+=4;
+
+ *_pos=pos=o[pos]>>2;
+ memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size);
+
+ return p;
+}
+
+// Add a child node to the given element.
+XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos)
+{
+ if (!lpszName) return emptyXMLNode;
+ d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
+ d->pChild[pos].d=NULL;
+ d->pChild[pos]=XMLNode(d,lpszName,isDeclaration);
+ d->nChild++;
+ return d->pChild[pos];
+}
+
+// Add an attribute to an element.
+XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev)
+{
+ if (!lpszName) return &emptyXMLAttribute;
+ if (!d) { myFree(lpszName); myFree(lpszValuev); return &emptyXMLAttribute; }
+ int nc=d->nAttribute;
+ d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute));
+ XMLAttribute *pAttr=d->pAttribute+nc;
+ pAttr->lpszName = lpszName;
+ pAttr->lpszValue = lpszValuev;
+ d->nAttribute++;
+
+ TCHAR* p = _tcschr( lpszName, ':' );
+ if ( p )
+ if ( !lstrcmp( p+1, d->lpszNS ) || ( d->pParent && !lstrcmp( p+1, d->pParent->lpszNS )))
+ *p = 0;
+
+ return pAttr;
+}
+
+// Add text to the element.
+XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos)
+{
+ if (!lpszValue) return NULL;
+ if (!d) { myFree(lpszValue); return NULL; }
+ invalidateInnerText();
+ d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText);
+ d->pText[pos]=lpszValue;
+ d->nText++;
+ return lpszValue;
+}
+
+// Add clear (unformatted) text to the element.
+XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
+{
+ if (!lpszValue) return &emptyXMLClear;
+ if (!d) { myFree(lpszValue); return &emptyXMLClear; }
+ invalidateInnerText();
+ d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear);
+ XMLClear *pNewClear=d->pClear+pos;
+ pNewClear->lpszValue = lpszValue;
+ if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen;
+ if (!lpszClose) lpszClose=XMLClearTags->lpszClose;
+ pNewClear->lpszOpenTag = lpszOpen;
+ pNewClear->lpszCloseTag = lpszClose;
+ d->nClear++;
+ return pNewClear;
+}
+
+// private:
+// Parse a clear (unformatted) type node.
+char XMLNode::parseClearTag(void *px, void *_pClear)
+{
+ XML *pXML=(XML *)px;
+ ALLXMLClearTag pClear=*((ALLXMLClearTag*)_pClear);
+ int cbTemp=0;
+ XMLCSTR lpszTemp=NULL;
+ XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex];
+ static XMLCSTR docTypeEnd=_CXML("]>");
+
+ // Find the closing tag
+ // Seems the <!DOCTYPE need a better treatment so lets handle it
+ if (pClear.lpszOpen==XMLClearTags[1].lpszOpen)
+ {
+ XMLCSTR pCh=lpXML;
+ while (*pCh)
+ {
+ if (*pCh==_CXML('<')) { pClear.lpszClose=docTypeEnd; lpszTemp=xstrstr(lpXML,docTypeEnd); break; }
+ else if (*pCh==_CXML('>')) { lpszTemp=pCh; break; }
+#ifdef _XMLWIDECHAR
+ pCh++;
+#else
+ pCh+=XML_ByteTable[(unsigned char)(*pCh)];
+#endif
+ }
+ } else lpszTemp=xstrstr(lpXML, pClear.lpszClose);
+
+ if (lpszTemp)
+ {
+ // Cache the size and increment the index
+ cbTemp = (int)(lpszTemp - lpXML);
+
+ pXML->nIndex += cbTemp+(int)xstrlen(pClear.lpszClose);
+
+ // Add the clear node to the current element
+ addClear_priv(MEMORYINCREASE,cbTemp?stringDup(lpXML,cbTemp):NULL, pClear.lpszOpen, pClear.lpszClose,-1);
+ return 0;
+ }
+
+ // If we failed to find the end tag
+ pXML->error = eXMLErrorUnmatchedEndClearTag;
+ return 1;
+}
+
+void XMLNode::exactMemory(XMLNodeData *d)
+{
+ if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int));
+ if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode));
+ if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute));
+ if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR));
+ if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear));
+}
+
+char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr)
+{
+ XML *pXML=(XML *)pa;
+ XMLCSTR lpszText=pXML->lpszText;
+ if (!lpszText) return 0;
+ if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++;
+ int cbText = (int)(tokenPStr - lpszText);
+ if (!cbText) { pXML->lpszText=NULL; return 0; }
+ if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; }
+ if (!cbText) { pXML->lpszText=NULL; return 0; }
+ XMLSTR lpt=fromXMLString(lpszText,cbText,pXML);
+ if (!lpt) return 1;
+ pXML->lpszText=NULL;
+ if (removeCommentsInMiddleOfText && d->nText && d->nClear)
+ {
+ // if the previous insertion was a comment (<!-- -->) AND
+ // if the previous previous insertion was a text then, delete the comment and append the text
+ int n=d->nChild+d->nText+d->nClear-1,*o=d->pOrder;
+ if (((o[n]&3)==eNodeClear)&&((o[n-1]&3)==eNodeText))
+ {
+ int i=o[n]>>2;
+ if (d->pClear[i].lpszOpenTag==XMLClearTags[2].lpszOpen)
+ {
+ deleteClear(i);
+ i=o[n-1]>>2;
+ n=xstrlen(d->pText[i]);
+ int n2=xstrlen(lpt)+1;
+ d->pText[i]=(XMLSTR)realloc((void*)d->pText[i],(n+n2)*sizeof(XMLCHAR));
+ if (!d->pText[i]) return 1;
+ memcpy((void*)(d->pText[i]+n),lpt,n2*sizeof(XMLCHAR));
+ free(lpt);
+ return 0;
+ }
+ }
+ }
+ addText_priv(MEMORYINCREASE,lpt,-1);
+ return 0;
+}
+// private:
+// Recursively parse an XML element.
+int XMLNode::ParseXMLElement(void *pa)
+{
+ XML *pXML=(XML *)pa;
+ int cbToken;
+ enum XMLTokenTypeTag xtype;
+ NextToken token;
+ XMLCSTR lpszTemp=NULL;
+ int cbTemp=0;
+ char nDeclaration;
+ XMLNode pNew;
+ enum XMLStatus status; // inside or outside a tag
+ enum Attrib attrib = eAttribName;
+
+ assert(pXML);
+
+ // If this is the first call to the function
+ if (pXML->nFirst)
+ {
+ // Assume we are outside of a tag definition
+ pXML->nFirst = FALSE;
+ status = eOutsideTag;
+ } else
+ {
+ // If this is not the first call then we should only be called when inside a tag.
+ status = eInsideTag;
+ }
+
+ // Iterate through the tokens in the document
+ for(;;)
+ {
+ // Obtain the next token
+ token = GetNextToken(pXML, &cbToken, &xtype);
+
+ if (xtype != eTokenError)
+ {
+ // Check the current status
+ switch(status)
+ {
+
+ // If we are outside of a tag definition
+ case eOutsideTag:
+
+ // Check what type of token we obtained
+ switch(xtype)
+ {
+ // If we have found text or quoted text
+ case eTokenText:
+ case eTokenCloseTag: /* '>' */
+ case eTokenShortHandClose: /* '/>' */
+ case eTokenQuotedText:
+ case eTokenEquals:
+ break;
+
+ // If we found a start tag '<' and declarations '<?'
+ case eTokenTagStart:
+ case eTokenDeclaration:
+
+ // Cache whether this new element is a declaration or not
+ nDeclaration = (xtype == eTokenDeclaration);
+
+ // If we have node text then add this to the element
+ if (maybeAddTxT(pXML,token.pStr)) return FALSE;
+
+ // Find the name of the tag
+ token = GetNextToken(pXML, &cbToken, &xtype);
+
+ // Return an error if we couldn't obtain the next token or
+ // it wasnt text
+ if (xtype != eTokenText)
+ {
+ pXML->error = eXMLErrorMissingTagName;
+ return FALSE;
+ }
+
+ // If we found a new element which is the same as this
+ // element then we need to pass this back to the caller..
+
+#ifdef APPROXIMATE_PARSING
+ if (d->lpszName &&
+ myTagCompare(d->lpszName, token.pStr) == 0)
+ {
+ // Indicate to the caller that it needs to create a
+ // new element.
+ pXML->lpNewElement = token.pStr;
+ pXML->cbNewElement = cbToken;
+ return TRUE;
+ } else
+#endif
+ {
+ // If the name of the new element differs from the name of
+ // the current element we need to add the new element to
+ // the current one and recurse
+ pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1);
+
+ while (!pNew.isEmpty())
+ {
+ // Callself to process the new node. If we return
+ // FALSE this means we dont have any more
+ // processing to do...
+
+ if (!pNew.ParseXMLElement(pXML)) return FALSE;
+ else
+ {
+ // If the call to recurse this function
+ // evented in a end tag specified in XML then
+ // we need to unwind the calls to this
+ // function until we find the appropriate node
+ // (the element name and end tag name must
+ // match)
+ if (pXML->cbEndTag)
+ {
+ // If we are back at the root node then we
+ // have an unmatched end tag
+ if (!d->lpszName)
+ {
+ pXML->error=eXMLErrorUnmatchedEndTag;
+ return FALSE;
+ }
+
+ // If the end tag matches the name of this
+ // element then we only need to unwind
+ // once more...
+
+ if (myTagCompare(d->lpszName, pXML->lpEndTag)==0)
+ {
+ pXML->cbEndTag = 0;
+ }
+
+ return TRUE;
+ } else
+ if (pXML->cbNewElement)
+ {
+ // If the call indicated a new element is to
+ // be created on THIS element.
+
+ // If the name of this element matches the
+ // name of the element we need to create
+ // then we need to return to the caller
+ // and let it process the element.
+
+ if (myTagCompare(d->lpszName, pXML->lpNewElement)==0)
+ {
+ return TRUE;
+ }
+
+ // Add the new element and recurse
+ pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1);
+ pXML->cbNewElement = 0;
+ }
+ else
+ {
+ // If we didn't have a new element to create
+ pNew = emptyXMLNode;
+
+ }
+ }
+ }
+ }
+ break;
+
+ // If we found an end tag
+ case eTokenTagEnd:
+
+ // If we have node text then add this to the element
+ if (maybeAddTxT(pXML,token.pStr)) return FALSE;
+
+ // Find the name of the end tag
+ token = GetNextToken(pXML, &cbTemp, &xtype);
+
+ // The end tag should be text
+ if (xtype != eTokenText)
+ {
+ pXML->error = eXMLErrorMissingEndTagName;
+ return FALSE;
+ }
+ lpszTemp = token.pStr;
+
+ // After the end tag we should find a closing tag
+ token = GetNextToken(pXML, &cbToken, &xtype);
+ if (xtype != eTokenCloseTag)
+ {
+ pXML->error = eXMLErrorMissingEndTagName;
+ return FALSE;
+ }
+ pXML->lpszText=pXML->lpXML+pXML->nIndex;
+
+ // We need to return to the previous caller. If the name
+ // of the tag cannot be found we need to keep returning to
+ // caller until we find a match
+ if (!d->lpszNS) {
+ if (myTagCompare(d->lpszName, lpszTemp) != 0)
+#ifdef STRICT_PARSING
+ {
+LBL_Error:
+ pXML->error=eXMLErrorUnmatchedEndTag;
+ pXML->nIndexMissigEndTag=pXML->nIndex;
+ return FALSE;
+ }
+#else
+ {
+LBL_Error:
+ pXML->error=eXMLErrorMissingEndTag;
+ pXML->nIndexMissigEndTag=pXML->nIndex;
+ pXML->lpEndTag = lpszTemp;
+ pXML->cbEndTag = cbTemp;
+ }
+#endif
+ }
+ else {
+ const TCHAR* p = _tcschr( lpszTemp, ':' );
+ if ( !p )
+ goto LBL_Error;
+
+ if (myTagCompare(d->lpszName, p+1) != 0)
+ goto LBL_Error;
+ }
+
+ // Return to the caller
+ exactMemory(d);
+ return TRUE;
+
+ // If we found a clear (unformatted) token
+ case eTokenClear:
+ // If we have node text then add this to the element
+ if (maybeAddTxT(pXML,token.pStr)) return FALSE;
+ if (parseClearTag(pXML, token.pClr)) return FALSE;
+ pXML->lpszText=pXML->lpXML+pXML->nIndex;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // If we are inside a tag definition we need to search for attributes
+ case eInsideTag:
+
+ // Check what part of the attribute (name, equals, value) we
+ // are looking for.
+ switch(attrib)
+ {
+ // If we are looking for a new attribute
+ case eAttribName:
+
+ // Check what the current token type is
+ switch(xtype)
+ {
+ // If the current type is text...
+ // Eg. 'attribute'
+ case eTokenText:
+ // Cache the token then indicate that we are next to
+ // look for the equals
+ lpszTemp = token.pStr;
+ cbTemp = cbToken;
+ attrib = eAttribEquals;
+ break;
+
+ // If we found a closing tag...
+ // Eg. '>'
+ case eTokenCloseTag:
+ // We are now outside the tag
+ status = eOutsideTag;
+ pXML->lpszText=pXML->lpXML+pXML->nIndex;
+ break;
+
+ // If we found a short hand '/>' closing tag then we can
+ // return to the caller
+ case eTokenShortHandClose:
+ exactMemory(d);
+ pXML->lpszText=pXML->lpXML+pXML->nIndex;
+ return TRUE;
+
+ // Errors...
+ case eTokenQuotedText: /* '"SomeText"' */
+ case eTokenTagStart: /* '<' */
+ case eTokenTagEnd: /* '</' */
+ case eTokenEquals: /* '=' */
+ case eTokenDeclaration: /* '<?' */
+ case eTokenClear:
+ pXML->error = eXMLErrorUnexpectedToken;
+ return FALSE;
+ default: break;
+ }
+ break;
+
+ // If we are looking for an equals
+ case eAttribEquals:
+ // Check what the current token type is
+ switch(xtype)
+ {
+ // If the current type is text...
+ // Eg. 'Attribute AnotherAttribute'
+ case eTokenText:
+ // Add the unvalued attribute to the list
+ addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
+ // Cache the token then indicate. We are next to
+ // look for the equals attribute
+ lpszTemp = token.pStr;
+ cbTemp = cbToken;
+ break;
+
+ // If we found a closing tag 'Attribute >' or a short hand
+ // closing tag 'Attribute />'
+ case eTokenShortHandClose:
+ case eTokenCloseTag:
+ // If we are a declaration element '<?' then we need
+ // to remove extra closing '?' if it exists
+ pXML->lpszText=pXML->lpXML+pXML->nIndex;
+
+ if (d->isDeclaration &&
+ (lpszTemp[cbTemp-1]) == _CXML('?'))
+ {
+ cbTemp--;
+ if (d->pParent && d->pParent->pParent) xtype = eTokenShortHandClose;
+ }
+
+ if (cbTemp)
+ {
+ // Add the unvalued attribute to the list
+ addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
+ }
+
+ // If this is the end of the tag then return to the caller
+ if (xtype == eTokenShortHandClose)
+ {
+ exactMemory(d);
+ return TRUE;
+ }
+
+ // We are now outside the tag
+ status = eOutsideTag;
+ break;
+
+ // If we found the equals token...
+ // Eg. 'Attribute ='
+ case eTokenEquals:
+ // Indicate that we next need to search for the value
+ // for the attribute
+ attrib = eAttribValue;
+ break;
+
+ // Errors...
+ case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/
+ case eTokenTagStart: /* 'Attribute <' */
+ case eTokenTagEnd: /* 'Attribute </' */
+ case eTokenDeclaration: /* 'Attribute <?' */
+ case eTokenClear:
+ pXML->error = eXMLErrorUnexpectedToken;
+ return FALSE;
+ default: break;
+ }
+ break;
+
+ // If we are looking for an attribute value
+ case eAttribValue:
+ // Check what the current token type is
+ switch(xtype)
+ {
+ // If the current type is text or quoted text...
+ // Eg. 'Attribute = "Value"' or 'Attribute = Value' or
+ // 'Attribute = 'Value''.
+ case eTokenText:
+ case eTokenQuotedText:
+ // If we are a declaration element '<?' then we need
+ // to remove extra closing '?' if it exists
+ if (d->isDeclaration &&
+ (token.pStr[cbToken-1]) == _CXML('?'))
+ {
+ cbToken--;
+ }
+
+ if (cbTemp)
+ {
+ // Add the valued attribute to the list
+ if (xtype==eTokenQuotedText) { token.pStr++; cbToken-=2; }
+ XMLSTR attrVal=(XMLSTR)token.pStr;
+ if (attrVal)
+ {
+ attrVal=fromXMLString(attrVal,cbToken,pXML);
+ if (!attrVal) return FALSE;
+ }
+ addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal);
+ }
+
+ // Indicate we are searching for a new attribute
+ attrib = eAttribName;
+ break;
+
+ // Errors...
+ case eTokenTagStart: /* 'Attr = <' */
+ case eTokenTagEnd: /* 'Attr = </' */
+ case eTokenCloseTag: /* 'Attr = >' */
+ case eTokenShortHandClose: /* "Attr = />" */
+ case eTokenEquals: /* 'Attr = =' */
+ case eTokenDeclaration: /* 'Attr = <?' */
+ case eTokenClear:
+ pXML->error = eXMLErrorUnexpectedToken;
+ return FALSE;
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ // If we failed to obtain the next token
+ else
+ {
+ if ((!d->isDeclaration)&&(d->pParent))
+ {
+#ifdef STRICT_PARSING
+ pXML->error=eXMLErrorUnmatchedEndTag;
+#else
+ pXML->error=eXMLErrorMissingEndTag;
+#endif
+ pXML->nIndexMissigEndTag=pXML->nIndex;
+ }
+ maybeAddTxT(pXML,pXML->lpXML+pXML->nIndex);
+ return FALSE;
+ }
+ }
+}
+
+// Count the number of lines and columns in an XML string.
+static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults)
+{
+ XMLCHAR ch;
+ assert(lpXML);
+ assert(pResults);
+
+ struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
+
+ pResults->nLine = 1;
+ pResults->nColumn = 1;
+ while (xml.nIndex<nUpto)
+ {
+ ch = getNextChar(&xml);
+ if (ch != _CXML('\n')) pResults->nColumn++;
+ else
+ {
+ pResults->nLine++;
+ pResults->nColumn=1;
+ }
+ }
+}
+
+// Parse XML and return the root element.
+XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults)
+{
+ if (!lpszXML)
+ {
+ if (pResults)
+ {
+ pResults->error=eXMLErrorNoElements;
+ pResults->nLine=0;
+ pResults->nColumn=0;
+ }
+ return emptyXMLNode;
+ }
+
+ XMLNode xnode(NULL,NULL,FALSE);
+ struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
+
+ // Create header element
+ xnode.ParseXMLElement(&xml);
+ enum XMLError error = xml.error;
+ if (!xnode.nChildNode()) error=eXMLErrorNoXMLTagFound;
+ if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node
+
+ // If no error occurred
+ if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)||(error==eXMLErrorNoXMLTagFound))
+ {
+ XMLCSTR name=xnode.getName();
+ if (tag&&(*tag)&&((!name)||(xstricmp(name,tag))))
+ {
+ xnode=xnode.getChildNode(tag);
+ if (xnode.isEmpty())
+ {
+ if (pResults)
+ {
+ pResults->error=eXMLErrorFirstTagNotFound;
+ pResults->nLine=0;
+ pResults->nColumn=0;
+ pResults->nChars=xml.nIndex;
+ }
+ return emptyXMLNode;
+ }
+ }
+ } else
+ {
+ // Cleanup: this will destroy all the nodes
+ xnode = emptyXMLNode;
+ }
+
+
+ // If we have been given somewhere to place results
+ if (pResults)
+ {
+ pResults->error = error;
+
+ // If we have an error
+ if (error!=eXMLErrorNone)
+ {
+ if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag;
+ // Find which line and column it starts on.
+ CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults);
+ }
+
+ pResults->nChars = xml.nIndex;
+ }
+ return xnode;
+}
+
+XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults)
+{
+ if (pResults) { pResults->nLine=0; pResults->nColumn=0; }
+ FILE *f=xfopen(filename,_CXML("rb"));
+ if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; }
+ fseek(f,0,SEEK_END);
+ int l=(int)ftell(f),headerSz=0;
+ if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; fclose(f); return emptyXMLNode; }
+ fseek(f,0,SEEK_SET);
+ unsigned char *buf=(unsigned char*)malloc(l+4);
+ l=(int)fread(buf,1,l,f);
+ fclose(f);
+ buf[l]=0;buf[l+1]=0;buf[l+2]=0;buf[l+3]=0;
+#ifdef _XMLWIDECHAR
+ if (guessWideCharChars)
+ {
+ if (!myIsTextWideChar(buf,l))
+ {
+ XMLNode::XMLCharEncoding ce=XMLNode::char_encoding_legacy;
+ if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) { headerSz=3; ce=XMLNode::char_encoding_UTF8; }
+ XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz),ce);
+ if (!b2)
+ {
+ // todo: unable to convert
+ }
+ free(buf); buf=(unsigned char*)b2; headerSz=0;
+ } else
+ {
+ if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
+ if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
+ }
+ } else
+ {
+ if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
+ if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
+ if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
+ }
+#else
+ if (guessWideCharChars)
+ {
+ if (myIsTextWideChar(buf,l))
+ {
+ if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
+ if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
+ char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz));
+ free(buf); buf=(unsigned char*)b2; headerSz=0;
+ } else
+ {
+ if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
+ }
+ } else
+ {
+ if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
+ if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
+ if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
+ }
+#endif
+
+ if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; }
+ XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults);
+ free(buf);
+ return x;
+}
+
+static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; }
+// private:
+// Creates an user friendly XML string from a given element with
+// appropriate white space and carriage returns.
+//
+// This recurses through all subnodes then adds contents of the nodes to the
+// string.
+int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat)
+{
+ int nResult = 0;
+ int cb=nFormat<0?0:nFormat;
+ int cbElement;
+ int nChildFormat=-1;
+ int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear;
+ int i,j;
+ if ((nFormat>=0)&&(nElementI==1)&&(pEntry->nText==1)&&(!pEntry->isDeclaration)) nFormat=-2;
+
+ assert(pEntry);
+
+#define LENSTR(lpsz) (lpsz ? xstrlen(lpsz) : 0)
+
+ // If the element has no name then assume this is the head node.
+ cbElement = (int)LENSTR(pEntry->lpszName);
+
+ if (cbElement)
+ {
+ // "<elementname "
+ if (lpszMarker)
+ {
+ if (cb) charmemset(lpszMarker, INDENTCHAR, cb);
+ nResult = cb;
+ lpszMarker[nResult++]=_CXML('<');
+ if (pEntry->isDeclaration) lpszMarker[nResult++]=_CXML('?');
+ xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
+ nResult+=cbElement;
+ lpszMarker[nResult++]=_CXML(' ');
+
+ } else
+ {
+ nResult+=cbElement+2+cb;
+ if (pEntry->isDeclaration) nResult++;
+ }
+
+ // Enumerate attributes and add them to the string
+ XMLAttribute *pAttr=pEntry->pAttribute;
+ for (i=0; i<pEntry->nAttribute; i++)
+ {
+ // "Attrib
+ cb = (int)LENSTR(pAttr->lpszName);
+ if (cb)
+ {
+ if (lpszMarker) xstrcpy(&lpszMarker[nResult], pAttr->lpszName);
+ nResult += cb;
+ // "Attrib=Value "
+ if (pAttr->lpszValue)
+ {
+ cb=(int)ToXMLStringTool::lengthXMLString(pAttr->lpszValue);
+ if (lpszMarker)
+ {
+ lpszMarker[nResult]=_CXML('=');
+ lpszMarker[nResult+1]=_CXML('"');
+ if (cb) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+2],pAttr->lpszValue);
+ lpszMarker[nResult+cb+2]=_CXML('"');
+ }
+ nResult+=cb+3;
+ }
+ if (lpszMarker) lpszMarker[nResult] = _CXML(' ');
+ nResult++;
+ }
+ pAttr++;
+ }
+
+ if (pEntry->isDeclaration)
+ {
+ if (lpszMarker)
+ {
+ lpszMarker[nResult-1]=_CXML('?');
+ lpszMarker[nResult]=_CXML('>');
+ }
+ nResult++;
+ if (nFormat!=-1)
+ {
+ if (lpszMarker) lpszMarker[nResult]=_CXML('\n');
+ nResult++;
+ }
+ } else
+ // If there are child nodes we need to terminate the start tag
+ if (nElementI)
+ {
+ if (lpszMarker) lpszMarker[nResult-1]=_CXML('>');
+ if (nFormat>=0)
+ {
+ if (lpszMarker) lpszMarker[nResult]=_CXML('\n');
+ nResult++;
+ }
+ } else nResult--;
+ }
+
+ // Calculate the child format for when we recurse. This is used to
+ // determine the number of spaces used for prefixes.
+ if (nFormat!=-1)
+ {
+ if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1;
+ else nChildFormat=nFormat;
+ }
+
+ // Enumerate through remaining children
+ for (i=0; i<nElementI; i++)
+ {
+ j=pEntry->pOrder[i];
+ switch((XMLElementType)(j&3))
+ {
+ // Text nodes
+ case eNodeText:
+ {
+ // "Text"
+ XMLCSTR pChild=pEntry->pText[j>>2];
+ cb = (int)ToXMLStringTool::lengthXMLString(pChild);
+ if (cb)
+ {
+ if (nFormat>=0)
+ {
+ if (lpszMarker)
+ {
+ charmemset(&lpszMarker[nResult],INDENTCHAR,nFormat+1);
+ ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+nFormat+1],pChild);
+ lpszMarker[nResult+nFormat+1+cb]=_CXML('\n');
+ }
+ nResult+=cb+nFormat+2;
+ } else
+ {
+ if (lpszMarker) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult], pChild);
+ nResult += cb;
+ }
+ }
+ break;
+ }
+
+ // Clear type nodes
+ case eNodeClear:
+ {
+ XMLClear *pChild=pEntry->pClear+(j>>2);
+ // "OpenTag"
+ cb = (int)LENSTR(pChild->lpszOpenTag);
+ if (cb)
+ {
+ if (nFormat!=-1)
+ {
+ if (lpszMarker)
+ {
+ charmemset(&lpszMarker[nResult], INDENTCHAR, nFormat+1);
+ xstrcpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag);
+ }
+ nResult+=cb+nFormat+1;
+ }
+ else
+ {
+ if (lpszMarker)xstrcpy(&lpszMarker[nResult], pChild->lpszOpenTag);
+ nResult += cb;
+ }
+ }
+
+ // "OpenTag Value"
+ cb = (int)LENSTR(pChild->lpszValue);
+ if (cb)
+ {
+ if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszValue);
+ nResult += cb;
+ }
+
+ // "OpenTag Value CloseTag"
+ cb = (int)LENSTR(pChild->lpszCloseTag);
+ if (cb)
+ {
+ if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszCloseTag);
+ nResult += cb;
+ }
+
+ if (nFormat!=-1)
+ {
+ if (lpszMarker) lpszMarker[nResult] = _CXML('\n');
+ nResult++;
+ }
+ break;
+ }
+
+ // Element nodes
+ case eNodeChild:
+ {
+ // Recursively add child nodes
+ nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, nChildFormat);
+ break;
+ }
+ default: break;
+ }
+ }
+
+ if ((cbElement)&&(!pEntry->isDeclaration))
+ {
+ // If we have child entries we need to use long XML notation for
+ // closing the element - "<elementname>blah blah blah</elementname>"
+ if (nElementI)
+ {
+ // "</elementname>\0"
+ if (lpszMarker)
+ {
+ if (nFormat >=0)
+ {
+ charmemset(&lpszMarker[nResult], INDENTCHAR,nFormat);
+ nResult+=nFormat;
+ }
+
+ lpszMarker[nResult]=_CXML('<'); lpszMarker[nResult+1]=_CXML('/');
+ nResult += 2;
+ xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
+ nResult += cbElement;
+
+ lpszMarker[nResult]=_CXML('>');
+ if (nFormat == -1) nResult++;
+ else
+ {
+ lpszMarker[nResult+1]=_CXML('\n');
+ nResult+=2;
+ }
+ } else
+ {
+ if (nFormat>=0) nResult+=cbElement+4+nFormat;
+ else if (nFormat==-1) nResult+=cbElement+3;
+ else nResult+=cbElement+4;
+ }
+ } else
+ {
+ // If there are no children we can use shorthand XML notation -
+ // "<elementname/>"
+ // "/>\0"
+ if (lpszMarker)
+ {
+ lpszMarker[nResult]=_CXML('/'); lpszMarker[nResult+1]=_CXML('>');
+ if (nFormat != -1) lpszMarker[nResult+2]=_CXML('\n');
+ }
+ nResult += nFormat == -1 ? 2 : 3;
+ }
+ }
+
+ return nResult;
+}
+
+#undef LENSTR
+
+// Create an XML string
+// @param int nFormat - 0 if no formatting is required
+// otherwise nonzero for formatted text
+// with carriage returns and indentation.
+// @param int *pnSize - [out] pointer to the size of the
+// returned string not including the
+// NULL terminator.
+// @return XMLSTR - Allocated XML string, you must free
+// this with free().
+XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const
+{
+ if (!d) { if (pnSize) *pnSize=0; return NULL; }
+
+ XMLSTR lpszResult = NULL;
+ int cbStr;
+
+ // Recursively Calculate the size of the XML string
+ if (!dropWhiteSpace) nFormat=0;
+ nFormat = nFormat ? 0 : -1;
+ cbStr = CreateXMLStringR(d, 0, nFormat);
+ // Alllocate memory for the XML string + the NULL terminator and
+ // create the recursively XML string.
+ lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR));
+ CreateXMLStringR(d, lpszResult, nFormat);
+ lpszResult[cbStr]=_CXML('\0');
+ if (pnSize) *pnSize = cbStr;
+ return lpszResult;
+}
+
+int XMLNode::detachFromParent(XMLNodeData *d)
+{
+ XMLNode *pa=d->pParent->pChild;
+ int i=0;
+ while (((void*)(pa[i].d))!=((void*)d)) i++;
+ d->pParent->nChild--;
+ if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode));
+ else { free(pa); d->pParent->pChild=NULL; }
+ return removeOrderElement(d->pParent,eNodeChild,i);
+}
+
+XMLNode::~XMLNode()
+{
+ if (!d) return;
+ d->ref_count--;
+ emptyTheNode(0);
+}
+void XMLNode::deleteNodeContent()
+{
+ if (!d) return;
+ if (d->pParent) { detachFromParent(d); d->pParent=NULL; d->ref_count--; }
+ emptyTheNode(1);
+}
+void XMLNode::emptyTheNode(char force)
+{
+ XMLNodeData *dd=d; // warning: must stay this way!
+ if ((dd->ref_count==0)||force)
+ {
+ if (d->pParent) detachFromParent(d);
+ int i;
+ XMLNode *pc;
+ for(i=0; i<dd->nChild; i++)
+ {
+ pc=dd->pChild+i;
+ pc->d->pParent=NULL;
+ pc->d->ref_count--;
+ pc->emptyTheNode(force);
+ }
+ myFree(dd->pChild);
+ for(i=0; i<dd->nText; i++) free((void*)dd->pText[i]);
+ myFree(dd->pText);
+ for(i=0; i<dd->nClear; i++) free((void*)dd->pClear[i].lpszValue);
+ myFree(dd->pClear);
+ for(i=0; i<dd->nAttribute; i++)
+ {
+ free((void*)dd->pAttribute[i].lpszName);
+ if (dd->pAttribute[i].lpszValue) free((void*)dd->pAttribute[i].lpszValue);
+ }
+ myFree(dd->pAttribute);
+ myFree(dd->pOrder);
+ myFree(dd->pInnerText);
+ if (dd->lpszNS)
+ myFree((void*)dd->lpszNS);
+ else
+ myFree((void*)dd->lpszName);
+ dd->nChild=0; dd->nText=0; dd->nClear=0; dd->nAttribute=0;
+ dd->pChild=NULL; dd->pText=NULL; dd->pClear=NULL; dd->pAttribute=NULL;
+ dd->pOrder=NULL; dd->pInnerText=NULL; dd->lpszNS=dd->lpszName=NULL; dd->pParent=NULL;
+ }
+ if (dd->ref_count==0)
+ {
+ free(dd);
+ d=NULL;
+ }
+}
+void XMLNode::invalidateInnerText()
+{
+ if (!d) return;
+ myFree(d->pInnerText);
+ d->pInnerText= NULL;
+}
+
+XMLNode& XMLNode::operator=( const XMLNode& A )
+{
+ // shallow copy
+ if (this != &A)
+ {
+ if (d) { d->ref_count--; emptyTheNode(0); }
+ d=A.d;
+ if (d) (d->ref_count) ++ ;
+ }
+ return *this;
+}
+
+XMLNode::XMLNode(const XMLNode &A)
+{
+ // shallow copy
+ d=A.d;
+ if (d) (d->ref_count)++ ;
+}
+
+XMLNode XMLNode::deepCopy() const
+{
+ if (!d) return XMLNode::emptyXMLNode;
+ XMLNode x(NULL,stringDup(d->lpszName),d->isDeclaration);
+ XMLNodeData *p=x.d;
+ int n=d->nAttribute;
+ if (n)
+ {
+ p->nAttribute=n; p->pAttribute=(XMLAttribute*)malloc(n*sizeof(XMLAttribute));
+ while (n--)
+ {
+ p->pAttribute[n].lpszName=stringDup(d->pAttribute[n].lpszName);
+ p->pAttribute[n].lpszValue=stringDup(d->pAttribute[n].lpszValue);
+ }
+ }
+ if (d->pOrder)
+ {
+ n=(d->nChild+d->nText+d->nClear)*sizeof(int); p->pOrder=(int*)malloc(n); memcpy(p->pOrder,d->pOrder,n);
+ }
+ n=d->nText;
+ if (n)
+ {
+ p->nText=n; p->pText=(XMLCSTR*)malloc(n*sizeof(XMLCSTR));
+ while(n--) p->pText[n]=stringDup(d->pText[n]);
+ }
+ n=d->nClear;
+ if (n)
+ {
+ p->nClear=n; p->pClear=(XMLClear*)malloc(n*sizeof(XMLClear));
+ while (n--)
+ {
+ p->pClear[n].lpszCloseTag=d->pClear[n].lpszCloseTag;
+ p->pClear[n].lpszOpenTag=d->pClear[n].lpszOpenTag;
+ p->pClear[n].lpszValue=stringDup(d->pClear[n].lpszValue);
+ }
+ }
+ n=d->nChild;
+ if (n)
+ {
+ p->nChild=n; p->pChild=(XMLNode*)malloc(n*sizeof(XMLNode));
+ while (n--)
+ {
+ p->pChild[n].d=NULL;
+ p->pChild[n]=d->pChild[n].deepCopy();
+ p->pChild[n].d->pParent=p;
+ }
+ }
+ return x;
+}
+
+XMLNode XMLNode::addChild(XMLNode childNode, int pos)
+{
+ XMLNodeData *dc=childNode.d;
+ if ((!dc)||(!d)) return childNode;
+ if (!dc->lpszName)
+ {
+ // this is a root node: todo: correct fix
+ int j=pos;
+ while (dc->nChild)
+ {
+ addChild(dc->pChild[0],j);
+ if (pos>=0) j++;
+ }
+ return childNode;
+ }
+ if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++;
+ dc->pParent=d;
+ // int nc=d->nChild;
+ // d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode));
+ d->pChild=(XMLNode*)addToOrder(0,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
+ d->pChild[pos].d=dc;
+ d->nChild++;
+ return childNode;
+}
+
+void XMLNode::deleteAttribute(int i)
+{
+ if ((!d)||(i<0)||(i>=d->nAttribute)) return;
+ d->nAttribute--;
+ XMLAttribute *p=d->pAttribute+i;
+ free((void*)p->lpszName);
+ if (p->lpszValue) free((void*)p->lpszValue);
+ if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; }
+}
+
+void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); }
+void XMLNode::deleteAttribute(XMLCSTR lpszName)
+{
+ int j=0;
+ getAttribute(lpszName,&j);
+ if (j) deleteAttribute(j-1);
+}
+
+XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,int i)
+{
+ if (!d) { if (lpszNewValue) free(lpszNewValue); if (lpszNewName) free(lpszNewName); return NULL; }
+ if (i>=d->nAttribute)
+ {
+ if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
+ return NULL;
+ }
+ XMLAttribute *p=d->pAttribute+i;
+ if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue);
+ p->lpszValue=lpszNewValue;
+ if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; };
+ return p;
+}
+
+XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
+{
+ if (oldAttribute) return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,(XMLSTR)newAttribute->lpszName,oldAttribute->lpszName);
+ return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,(XMLSTR)newAttribute->lpszValue);
+}
+
+XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName)
+{
+ int j=0;
+ getAttribute(lpszOldName,&j);
+ if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1);
+ else
+ {
+ if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
+ else return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue);
+ }
+}
+
+int XMLNode::indexText(XMLCSTR lpszValue) const
+{
+ if (!d) return -1;
+ int i,l=d->nText;
+ if (!lpszValue) { if (l) return 0; return -1; }
+ XMLCSTR *p=d->pText;
+ for (i=0; i<l; i++) if (lpszValue==p[i]) return i;
+ return -1;
+}
+
+void XMLNode::deleteText(int i)
+{
+ if ((!d)||(i<0)||(i>=d->nText)) return;
+ invalidateInnerText();
+ d->nText--;
+ XMLCSTR *p=d->pText+i;
+ free((void*)*p);
+ if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; }
+ removeOrderElement(d,eNodeText,i);
+}
+
+void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); }
+
+XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i)
+{
+ if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; }
+ if (i>=d->nText) return addText_WOSD(lpszNewValue);
+ invalidateInnerText();
+ XMLCSTR *p=d->pText+i;
+ if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; }
+ return lpszNewValue;
+}
+
+XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue)
+{
+ if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; }
+ int i=indexText(lpszOldValue);
+ if (i>=0) return updateText_WOSD(lpszNewValue,i);
+ return addText_WOSD(lpszNewValue);
+}
+
+void XMLNode::deleteClear(int i)
+{
+ if ((!d)||(i<0)||(i>=d->nClear)) return;
+ invalidateInnerText();
+ d->nClear--;
+ XMLClear *p=d->pClear+i;
+ free((void*)p->lpszValue);
+ if (d->nClear) memmove(p,p+1,(d->nClear-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; }
+ removeOrderElement(d,eNodeClear,i);
+}
+
+int XMLNode::indexClear(XMLCSTR lpszValue) const
+{
+ if (!d) return -1;
+ int i,l=d->nClear;
+ if (!lpszValue) { if (l) return 0; return -1; }
+ XMLClear *p=d->pClear;
+ for (i=0; i<l; i++) if (lpszValue==p[i].lpszValue) return i;
+ return -1;
+}
+
+void XMLNode::deleteClear(XMLCSTR lpszValue) { deleteClear(indexClear(lpszValue)); }
+void XMLNode::deleteClear(XMLClear *a) { if (a) deleteClear(a->lpszValue); }
+
+XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i)
+{
+ if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; }
+ if (i>=d->nClear) return addClear_WOSD(lpszNewContent);
+ invalidateInnerText();
+ XMLClear *p=d->pClear+i;
+ if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; }
+ return p;
+}
+
+XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, XMLCSTR lpszOldValue)
+{
+ if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; }
+ int i=indexClear(lpszOldValue);
+ if (i>=0) return updateClear_WOSD(lpszNewContent,i);
+ return addClear_WOSD(lpszNewContent);
+}
+
+XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP)
+{
+ if (oldP) return updateClear_WOSD((XMLSTR)newP->lpszValue,(XMLSTR)oldP->lpszValue);
+ return NULL;
+}
+
+int XMLNode::nChildNode(XMLCSTR name) const
+{
+ if (!d) return 0;
+ int i,j=0,n=d->nChild;
+ XMLNode *pc=d->pChild;
+ for (i=0; i<n; i++)
+ {
+ if (xstricmp(pc->d->lpszName, name)==0) j++;
+ pc++;
+ }
+ return j;
+}
+
+XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const
+{
+ if (!d) return emptyXMLNode;
+ int i=0,n=d->nChild;
+ if (j) i=*j;
+ XMLNode *pc=d->pChild+i;
+ for (; i<n; i++)
+ {
+ if (!xstricmp(pc->d->lpszName, name))
+ {
+ if (j) *j=i+1;
+ return *pc;
+ }
+ pc++;
+ }
+ return emptyXMLNode;
+}
+
+XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const
+{
+ if (!d) return emptyXMLNode;
+ if (j>=0)
+ {
+ int i=0;
+ while (j-->0) getChildNode(name,&i);
+ return getChildNode(name,&i);
+ }
+ int i=d->nChild;
+ while (i--) if (!xstricmp(name,d->pChild[i].d->lpszName)) break;
+ if (i<0) return emptyXMLNode;
+ return getChildNode(i);
+}
+
+XMLNode XMLNode::getNextNode() const
+{
+ if (!d) return emptyXMLNode;
+ XMLNodeDataTag *par=d->pParent;
+ if (!par) return emptyXMLNode;
+ int i,n=par->nChild;
+ for (i=0; i<n; ++i)
+ {
+ if (par->pChild[i].d == d) break;
+ }
+ return XMLNode(par).getChildNode(d->lpszName, &++i);
+}
+
+XMLNode XMLNode::getChildNodeByPath(XMLCSTR _path, char createMissing, XMLCHAR sep)
+{
+ XMLSTR path=stringDup(_path);
+ XMLNode x=getChildNodeByPathNonConst(path,createMissing,sep);
+ if (path) free(path);
+ return x;
+}
+
+XMLNode XMLNode::getChildNodeByPathNonConst(XMLSTR path, char createIfMissing, XMLCHAR sep)
+{
+ if ((!path)||(!(*path))) return *this;
+ XMLNode xn,xbase=*this;
+ XMLCHAR *tend1,sepString[2]; sepString[0]=sep; sepString[1]=0;
+ tend1=xstrstr(path,sepString);
+ while(tend1)
+ {
+ *tend1=0;
+ xn=xbase.getChildNode(path);
+ if (xn.isEmpty())
+ {
+ if (createIfMissing) xn=xbase.addChild(path);
+ else { *tend1=sep; return XMLNode::emptyXMLNode; }
+ }
+ *tend1=sep;
+ xbase=xn;
+ path=tend1+1;
+ tend1=xstrstr(path,sepString);
+ }
+ xn=xbase.getChildNode(path);
+ if (xn.isEmpty()&&createIfMissing) xn=xbase.addChild(path);
+ return xn;
+}
+
+XMLElementPosition XMLNode::positionOfText (int i) const { if (i>=d->nText ) i=d->nText-1; return findPosition(d,i,eNodeText ); }
+XMLElementPosition XMLNode::positionOfClear (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); }
+XMLElementPosition XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); }
+XMLElementPosition XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); }
+XMLElementPosition XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); }
+XMLElementPosition XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); }
+XMLElementPosition XMLNode::positionOfChildNode(XMLNode x) const
+{
+ if ((!d)||(!x.d)) return -1;
+ XMLNodeData *dd=x.d;
+ XMLNode *pc=d->pChild;
+ int i=d->nChild;
+ while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild);
+ return -1;
+}
+XMLElementPosition XMLNode::positionOfChildNode(XMLCSTR name, int count) const
+{
+ if (!name) return positionOfChildNode(count);
+ int j=0;
+ do { getChildNode(name,&j); if (j<0) return -1; } while (count--);
+ return findPosition(d,j-1,eNodeChild);
+}
+
+XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const
+{
+ int i=0,j;
+ if (k) i=*k;
+ XMLNode x;
+ XMLCSTR t;
+ do
+ {
+ x=getChildNode(name,&i);
+ if (!x.isEmpty())
+ {
+ if (attributeValue)
+ {
+ j=0;
+ do
+ {
+ t=x.getAttribute(attributeName,&j);
+ if (t&&(xstricmp(attributeValue,t)==0)) { if (k) *k=i; return x; }
+ } while (t);
+ } else
+ {
+ if (x.isAttributeSet(attributeName)) { if (k) *k=i; return x; }
+ }
+ }
+ } while (!x.isEmpty());
+ return emptyXMLNode;
+}
+
+// Find an attribute on an node.
+XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const
+{
+ if (!d) return NULL;
+ int i=0,n=d->nAttribute;
+ if (j) i=*j;
+ XMLAttribute *pAttr=d->pAttribute+i;
+ for (; i<n; i++)
+ {
+ if (xstricmp(pAttr->lpszName, lpszAttrib)==0)
+ {
+ if (j) *j=i+1;
+ return pAttr->lpszValue;
+ }
+ pAttr++;
+ }
+ return NULL;
+}
+
+char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const
+{
+ if (!d) return FALSE;
+ int i,n=d->nAttribute;
+ XMLAttribute *pAttr=d->pAttribute;
+ for (i=0; i<n; i++)
+ {
+ if (xstricmp(pAttr->lpszName, lpszAttrib)==0)
+ {
+ return TRUE;
+ }
+ pAttr++;
+ }
+ return FALSE;
+}
+
+XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const
+{
+ if (!d) return NULL;
+ int i=0;
+ while (j-->0) getAttribute(name,&i);
+ return getAttribute(name,&i);
+}
+
+XMLNodeContents XMLNode::enumContents(int i) const
+{
+ XMLNodeContents c;
+ if (!d) { c.etype=eNodeNULL; return c; }
+ if (i<d->nAttribute)
+ {
+ c.etype=eNodeAttribute;
+ c.attrib=d->pAttribute[i];
+ return c;
+ }
+ i-=d->nAttribute;
+ c.etype=(XMLElementType)(d->pOrder[i]&3);
+ i=(d->pOrder[i])>>2;
+ switch (c.etype)
+ {
+ case eNodeChild: c.child = d->pChild[i]; break;
+ case eNodeText: c.text = d->pText[i]; break;
+ case eNodeClear: c.clear = d->pClear[i]; break;
+ default: break;
+ }
+ return c;
+}
+
+XMLCSTR XMLNode::getInnerText() const
+{
+ if (!d) return NULL;
+ if (nText() <= 1 && nClear() == 0) return getText();
+ if (d->pInnerText) return d->pInnerText;
+
+ int count = nElement();
+ int i, length = 1;
+ for (i = 0; i < count; ++i)
+ {
+ XMLNodeContents c = enumContents(i);
+ switch (c.etype)
+ {
+ case eNodeText:
+ length += xstrlen(c.text);
+ break;
+ case eNodeClear:
+ length += xstrlen(c.clear.lpszValue);
+ break;
+ }
+ }
+ XMLCHAR *buf = (XMLCHAR *)malloc(sizeof(XMLCHAR) * length);
+ XMLCHAR *pos = buf;
+ for (i = 0; i < count; ++i)
+ {
+ XMLNodeContents c = enumContents(i);
+ switch (c.etype)
+ {
+ case eNodeText:
+ xstrcpy(pos, c.text);
+ pos += xstrlen(c.text);
+ break;
+ case eNodeClear:
+ xstrcpy(pos, c.clear.lpszValue);
+ pos += xstrlen(c.clear.lpszValue);
+ break;
+ }
+ }
+ return d->pInnerText = buf;
+}
+
+XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName; }
+int XMLNode::nText() const { if (!d) return 0; return d->nText; }
+int XMLNode::nChildNode() const { if (!d) return 0; return d->nChild; }
+int XMLNode::nAttribute() const { if (!d) return 0; return d->nAttribute; }
+int XMLNode::nClear() const { if (!d) return 0; return d->nClear; }
+int XMLNode::nElement() const { if (!d) return 0; return d->nAttribute+d->nChild+d->nText+d->nClear; }
+XMLClear XMLNode::getClear (int i) const { if ((!d)||(i>=d->nClear )) return emptyXMLClear; return d->pClear[i]; }
+XMLAttribute XMLNode::getAttribute (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; }
+XMLCSTR XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszName; }
+XMLCSTR XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszValue; }
+XMLCSTR XMLNode::getText (int i) const { if ((!d)||(i>=d->nText )) return NULL; return d->pText[i]; }
+XMLNode XMLNode::getChildNode (int i) const { if ((!d)||(i>=d->nChild )) return emptyXMLNode; return d->pChild[i]; }
+XMLNode XMLNode::getParentNode ( ) const { if ((!d)||(!d->pParent )) return emptyXMLNode; return XMLNode(d->pParent); }
+char XMLNode::isDeclaration ( ) const { if (!d) return 0; return d->isDeclaration; }
+char XMLNode::isEmpty ( ) const { return (d==NULL); }
+XMLNode XMLNode::emptyNode ( ) { return XMLNode::emptyXMLNode; }
+
+XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, XMLElementPosition pos)
+{ return addChild_priv(0,stringDup(lpszName),isDeclaration,pos); }
+XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration, XMLElementPosition pos)
+{ return addChild_priv(0,lpszName,isDeclaration,pos); }
+XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue)
+{ return addAttribute_priv(0,stringDup(lpszName),stringDup(lpszValue)); }
+XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev)
+{ return addAttribute_priv(0,lpszName,lpszValuev); }
+XMLCSTR XMLNode::addText(XMLCSTR lpszValue, XMLElementPosition pos)
+{ return addText_priv(0,stringDup(lpszValue),pos); }
+XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos)
+{ return addText_priv(0,lpszValue,pos); }
+XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos)
+{ return addClear_priv(0,stringDup(lpszValue),lpszOpen,lpszClose,pos); }
+XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos)
+{ return addClear_priv(0,lpszValue,lpszOpen,lpszClose,pos); }
+XMLCSTR XMLNode::updateName(XMLCSTR lpszName)
+{ return updateName_WOSD(stringDup(lpszName)); }
+XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
+{ return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); }
+XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i)
+{ return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); }
+XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName)
+{ return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); }
+XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i)
+{ return updateText_WOSD(stringDup(lpszNewValue),i); }
+XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
+{ return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); }
+XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i)
+{ return updateClear_WOSD(stringDup(lpszNewContent),i); }
+XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
+{ return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); }
+XMLClear *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP)
+{ return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); }
+
+char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding, char _guessWideCharChars,
+ char _dropWhiteSpace, char _removeCommentsInMiddleOfText)
+{
+ guessWideCharChars=_guessWideCharChars; dropWhiteSpace=_dropWhiteSpace; removeCommentsInMiddleOfText=_removeCommentsInMiddleOfText;
+#ifdef _XMLWIDECHAR
+ if (_characterEncoding) characterEncoding=_characterEncoding;
+#else
+ switch(_characterEncoding)
+ {
+ case char_encoding_UTF8: characterEncoding=_characterEncoding; XML_ByteTable=XML_utf8ByteTable; break;
+ case char_encoding_legacy: characterEncoding=_characterEncoding; XML_ByteTable=XML_legacyByteTable; break;
+ case char_encoding_ShiftJIS: characterEncoding=_characterEncoding; XML_ByteTable=XML_sjisByteTable; break;
+ case char_encoding_GB2312: characterEncoding=_characterEncoding; XML_ByteTable=XML_gb2312ByteTable; break;
+ case char_encoding_Big5:
+ case char_encoding_GBK: characterEncoding=_characterEncoding; XML_ByteTable=XML_gbk_big5_ByteTable; break;
+ default: return 1;
+ }
+#endif
+ return 0;
+}
+
+XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf,int l, char useXMLEncodingAttribute)
+{
+#ifdef _XMLWIDECHAR
+ return (XMLCharEncoding)0;
+#else
+ if (l<25) return (XMLCharEncoding)0;
+ if (guessWideCharChars&&(myIsTextWideChar(buf,l))) return (XMLCharEncoding)0;
+ unsigned char *b=(unsigned char*)buf;
+ if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return char_encoding_UTF8;
+
+ // Match utf-8 model ?
+ XMLCharEncoding bestGuess=char_encoding_UTF8;
+ int i=0;
+ while (i<l)
+ switch (XML_utf8ByteTable[b[i]])
+ {
+ case 4: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ?
+ case 3: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ?
+ case 2: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ?
+ case 1: i++; break;
+ case 0: i=l;
+ }
+ if (!useXMLEncodingAttribute) return bestGuess;
+ // if encoding is specified and different from utf-8 than it's non-utf8
+ // otherwise it's utf-8
+ char bb[201];
+ l=mmin(l,200);
+ memcpy(bb,buf,l); // copy buf into bb to be able to do "bb[l]=0"
+ bb[l]=0;
+ b=(unsigned char*)strstr(bb,"encoding");
+ if (!b) return bestGuess;
+ b+=8; while XML_isSPACECHAR(*b) b++; if (*b!='=') return bestGuess;
+ b++; while XML_isSPACECHAR(*b) b++; if ((*b!='\'')&&(*b!='"')) return bestGuess;
+ b++; while XML_isSPACECHAR(*b) b++;
+
+ if ((xstrnicmp((char*)b,"utf-8",5)==0)||
+ (xstrnicmp((char*)b,"utf8",4)==0))
+ {
+ if (bestGuess==char_encoding_legacy) return char_encoding_error;
+ return char_encoding_UTF8;
+ }
+
+ if ((xstrnicmp((char*)b,"shiftjis",8)==0)||
+ (xstrnicmp((char*)b,"shift-jis",9)==0)||
+ (xstrnicmp((char*)b,"sjis",4)==0)) return char_encoding_ShiftJIS;
+
+ if (xstrnicmp((char*)b,"GB2312",6)==0) return char_encoding_GB2312;
+ if (xstrnicmp((char*)b,"Big5",4)==0) return char_encoding_Big5;
+ if (xstrnicmp((char*)b,"GBK",3)==0) return char_encoding_GBK;
+
+ return char_encoding_legacy;
+#endif
+}
+#undef XML_isSPACECHAR
+
+//////////////////////////////////////////////////////////
+// Here starts the base64 conversion functions. //
+//////////////////////////////////////////////////////////
+
+static const char base64Fillchar = _CXML('='); // used to mark partial words at the end
+
+// this lookup table defines the base64 encoding
+XMLCSTR base64EncodeTable=_CXML("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
+// Decode Table gives the index of any valid base64 character in the Base64 table]
+// 96: '=' - 97: space char - 98: illegal char - 99: end of string
+const unsigned char base64DecodeTable[] = {
+ 99,98,98,98,98,98,98,98,98,97, 97,98,98,97,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //00 -29
+ 98,98,97,98,98,98,98,98,98,98, 98,98,98,62,98,98,98,63,52,53, 54,55,56,57,58,59,60,61,98,98, //30 -59
+ 98,96,98,98,98, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24, //60 -89
+ 25,98,98,98,98,98,98,26,27,28, 29,30,31,32,33,34,35,36,37,38, 39,40,41,42,43,44,45,46,47,48, //90 -119
+ 49,50,51,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //120 -149
+ 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //150 -179
+ 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //180 -209
+ 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //210 -239
+ 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98 //240 -255
+};
+
+XMLParserBase64Tool::~XMLParserBase64Tool(){ freeBuffer(); }
+
+void XMLParserBase64Tool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
+
+int XMLParserBase64Tool::encodeLength(int inlen, char formatted)
+{
+ unsigned int i=((inlen-1)/3*4+4+1);
+ if (formatted) i+=inlen/54;
+ return i;
+}
+
+XMLSTR XMLParserBase64Tool::encode(unsigned char *inbuf, unsigned int inlen, char formatted)
+{
+ int i=encodeLength(inlen,formatted),k=17,eLen=inlen/3,j;
+ alloc(i*sizeof(XMLCHAR));
+ XMLSTR curr=(XMLSTR)buf;
+ for(i=0;i<eLen;i++)
+ {
+ // Copy next three bytes into lower 24 bits of int, paying attention to sign.
+ j=(inbuf[0]<<16)|(inbuf[1]<<8)|inbuf[2]; inbuf+=3;
+ // Encode the int into four chars
+ *(curr++)=base64EncodeTable[ j>>18 ];
+ *(curr++)=base64EncodeTable[(j>>12)&0x3f];
+ *(curr++)=base64EncodeTable[(j>> 6)&0x3f];
+ *(curr++)=base64EncodeTable[(j )&0x3f];
+ if (formatted) { if (!k) { *(curr++)=_CXML('\n'); k=18; } k--; }
+ }
+ eLen=inlen-eLen*3; // 0 - 2.
+ if (eLen==1)
+ {
+ *(curr++)=base64EncodeTable[ inbuf[0]>>2 ];
+ *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F];
+ *(curr++)=base64Fillchar;
+ *(curr++)=base64Fillchar;
+ } else if (eLen==2)
+ {
+ j=(inbuf[0]<<8)|inbuf[1];
+ *(curr++)=base64EncodeTable[ j>>10 ];
+ *(curr++)=base64EncodeTable[(j>> 4)&0x3f];
+ *(curr++)=base64EncodeTable[(j<< 2)&0x3f];
+ *(curr++)=base64Fillchar;
+ }
+ *(curr++)=0;
+ return (XMLSTR)buf;
+}
+
+unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe)
+{
+ if (!data) return 0;
+ if (xe) *xe=eXMLErrorNone;
+ int size=0;
+ unsigned char c;
+ //skip any extra characters (e.g. newlines or spaces)
+ while (*data)
+ {
+#ifdef _XMLWIDECHAR
+ if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
+#endif
+ c=base64DecodeTable[(unsigned char)(*data)];
+ if (c<97) size++;
+ else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
+ data++;
+ }
+ if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4;
+ if (size==0) return 0;
+ do { data--; size--; } while(*data==base64Fillchar); size++;
+ return (unsigned int)((size*3)/4);
+}
+
+unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe)
+{
+ if (!data) return 0;
+ if (xe) *xe=eXMLErrorNone;
+ int i=0,p=0;
+ unsigned char d,c;
+ for(;;)
+ {
+
+#ifdef _XMLWIDECHAR
+#define BASE64DECODE_READ_NEXT_CHAR(c) \
+ do { \
+ if (data[i]>255){ c=98; break; } \
+ c=base64DecodeTable[(unsigned char)data[i++]]; \
+ }while (c==97); \
+ if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
+#else
+#define BASE64DECODE_READ_NEXT_CHAR(c) \
+ do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97); \
+ if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
+#endif
+
+ BASE64DECODE_READ_NEXT_CHAR(c)
+ if (c==99) { return 2; }
+ if (c==96)
+ {
+ if (p==(int)len) return 2;
+ if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;
+ return 1;
+ }
+
+ BASE64DECODE_READ_NEXT_CHAR(d)
+ if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
+ if (p==(int)len) { if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; }
+ buf[p++]=(unsigned char)((c<<2)|((d>>4)&0x3));
+
+ BASE64DECODE_READ_NEXT_CHAR(c)
+ if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
+ if (p==(int)len)
+ {
+ if (c==96) return 2;
+ if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
+ return 0;
+ }
+ if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
+ buf[p++]=(unsigned char)(((d<<4)&0xf0)|((c>>2)&0xf));
+
+ BASE64DECODE_READ_NEXT_CHAR(d)
+ if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
+ if (p==(int)len)
+ {
+ if (d==96) return 2;
+ if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
+ return 0;
+ }
+ if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
+ buf[p++]=(unsigned char)(((c<<6)&0xc0)|d);
+ }
+}
+#undef BASE64DECODE_READ_NEXT_CHAR
+
+void XMLParserBase64Tool::alloc(int newsize)
+{
+ if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; }
+ if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; }
+}
+
+unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe)
+{
+ if (xe) *xe=eXMLErrorNone;
+ if (!data) { *outlen=0; return (unsigned char*)""; }
+ unsigned int len=decodeSize(data,xe);
+ if (outlen) *outlen=len;
+ if (!len) return NULL;
+ alloc(len+1);
+ if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; }
+ return (unsigned char*)buf;
+}
+
+//////////////////////////////////////////////////////////
+// Helpers for external C APIs. //
+//////////////////////////////////////////////////////////
+
+XMLNode::XMLNode( HXML h ) :
+d(( XMLNodeDataTag* )h )
+{
+ if (d)
+ d->ref_count++;
+}
+
+void XMLNode::attach( HXML h )
+{
+ d = ( XMLNodeDataTag* )h;
+}
+
+HXML XMLNode::detach()
+{
+ HXML res = (HXML)d;
+ d = NULL;
+ return res;
+}
diff --git a/src/modules/xml/xmlParser.h b/src/modules/xml/xmlParser.h
new file mode 100644
index 0000000000..5a216ab0cb
--- /dev/null
+++ b/src/modules/xml/xmlParser.h
@@ -0,0 +1,746 @@
+/****************************************************************************/
+/*! \mainpage XMLParser library
+ * \section intro_sec Introduction
+ *
+ * This is a basic XML parser written in ANSI C++ for portability.
+ * It works by using recursion and a node tree for breaking
+ * down the elements of an XML document.
+ *
+ * @version V2.43
+ * @author Frank Vanden Berghen
+ *
+ * Copyright (c) 2002, Business-Insight
+ * <a href="http://www.Business-Insight.com">Business-Insight</a>
+ * All rights reserved.
+ * See the file <a href="../../AFPL-license.txt">AFPL-license.txt</a> about the licensing terms
+ *
+ * \section tutorial First Tutorial
+ * You can follow a simple <a href="../../xmlParser.html">Tutorial</a> to know the basics...
+ *
+ * \section usage General usage: How to include the XMLParser library inside your project.
+ *
+ * The library is composed of two files: <a href="../../xmlParser.cpp">xmlParser.cpp</a> and
+ * <a href="../../xmlParser.h">xmlParser.h</a>. These are the ONLY 2 files that you need when
+ * using the library inside your own projects.
+ *
+ * All the functions of the library are documented inside the comments of the file
+ * <a href="../../xmlParser.h">xmlParser.h</a>. These comments can be transformed in
+ * full-fledged HTML documentation using the DOXYGEN software: simply type: "doxygen doxy.cfg"
+ *
+ * By default, the XMLParser library uses (char*) for string representation.To use the (wchar_t*)
+ * version of the library, you need to define the "_UNICODE" preprocessor definition variable
+ * (this is usually done inside your project definition file) (This is done automatically for you
+ * when using Visual Studio).
+ *
+ * \section example Advanced Tutorial and Many Examples of usage.
+ *
+ * Some very small introductory examples are described inside the Tutorial file
+ * <a href="../../xmlParser.html">xmlParser.html</a>
+ *
+ * Some additional small examples are also inside the file <a href="../../xmlTest.cpp">xmlTest.cpp</a>
+ * (for the "char*" version of the library) and inside the file
+ * <a href="../../xmlTestUnicode.cpp">xmlTestUnicode.cpp</a> (for the "wchar_t*"
+ * version of the library). If you have a question, please review these additionnal examples
+ * before sending an e-mail to the author.
+ *
+ * To build the examples:
+ * - linux/unix: type "make"
+ * - solaris: type "make -f makefile.solaris"
+ * - windows: Visual Studio: double-click on xmlParser.dsw
+ * (under Visual Studio .NET, the .dsp and .dsw files will be automatically converted to .vcproj and .sln files)
+ *
+ * In order to build the examples you need some additional files:
+ * - linux/unix: makefile
+ * - solaris: makefile.solaris
+ * - windows: Visual Studio: *.dsp, xmlParser.dsw and also xmlParser.lib and xmlParser.dll
+ *
+ * \section debugging Debugging with the XMLParser library
+ *
+ * \subsection debugwin Debugging under WINDOWS
+ *
+ * Inside Visual C++, the "debug versions" of the memory allocation functions are
+ * very slow: Do not forget to compile in "release mode" to get maximum speed.
+ * When I had to debug a software that was using the XMLParser Library, it was usually
+ * a nightmare because the library was sooOOOoooo slow in debug mode (because of the
+ * slow memory allocations in Debug mode). To solve this
+ * problem, during all the debugging session, I am now using a very fast DLL version of the
+ * XMLParser Library (the DLL is compiled in release mode). Using the DLL version of
+ * the XMLParser Library allows me to have lightening XML parsing speed even in debug!
+ * Other than that, the DLL version is useless: In the release version of my tool,
+ * I always use the normal, ".cpp"-based, XMLParser Library (I simply include the
+ * <a href="../../xmlParser.cpp">xmlParser.cpp</a> and
+ * <a href="../../xmlParser.h">xmlParser.h</a> files into the project).
+ *
+ * The file <a href="../../XMLNodeAutoexp.txt">XMLNodeAutoexp.txt</a> contains some
+ * "tweaks" that improve substancially the display of the content of the XMLNode objects
+ * inside the Visual Studio Debugger. Believe me, once you have seen inside the debugger
+ * the "smooth" display of the XMLNode objects, you cannot live without it anymore!
+ *
+ * \subsection debuglinux Debugging under LINUX/UNIX
+ *
+ * The speed of the debug version of the XMLParser library is tolerable so no extra
+ * work.has been done.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_XML_NODE__
+#define __INCLUDE_XML_NODE__
+
+#include <stdlib.h>
+
+#ifdef _UNICODE
+// If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters).
+// This is useful when you get error messages like:
+// 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *'
+// The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable
+// must be defined) or utf8-mode(the pre-processor variable must be undefined).
+#define _XMLWIDECHAR
+#endif
+
+#if defined(WIN32) || defined(UNDER_CE) || defined(_WIN32) || defined(WIN64) || defined(__BORLANDC__)
+// comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) or Borland
+#define _XMLWINDOWS
+#endif
+
+#ifdef XMLDLLENTRY
+#undef XMLDLLENTRY
+#endif
+#ifdef _USE_XMLPARSER_DLL
+#ifdef _DLL_EXPORTS_
+#define XMLDLLENTRY __declspec(dllexport)
+#else
+#define XMLDLLENTRY __declspec(dllimport)
+#endif
+#else
+#define XMLDLLENTRY
+#endif
+
+// uncomment the next line if you want no support for wchar_t* (no need for the <wchar.h> or <tchar.h> libraries anymore to compile)
+//#define XML_NO_WIDE_CHAR
+
+#ifdef XML_NO_WIDE_CHAR
+#undef _XMLWINDOWS
+#undef _XMLWIDECHAR
+#endif
+
+#ifdef _XMLWINDOWS
+#include <tchar.h>
+#else
+#define XMLDLLENTRY
+#ifndef XML_NO_WIDE_CHAR
+#include <wchar.h> // to have 'wcsrtombs' for ANSI version
+ // to have 'mbsrtowcs' for WIDECHAR version
+#endif
+#endif
+
+// Some common types for char set portable code
+#ifdef _XMLWIDECHAR
+ #define _CXML(c) L ## c
+ #define XMLCSTR const wchar_t *
+ #define XMLSTR wchar_t *
+ #define XMLCHAR wchar_t
+#else
+ #define _CXML(c) c
+ #define XMLCSTR const char *
+ #define XMLSTR char *
+ #define XMLCHAR char
+#endif
+#ifndef FALSE
+ #define FALSE 0
+#endif /* FALSE */
+#ifndef TRUE
+ #define TRUE 1
+#endif /* TRUE */
+
+
+/// Enumeration for XML parse errors.
+typedef enum XMLError
+{
+ eXMLErrorNone = 0,
+ eXMLErrorMissingEndTag,
+ eXMLErrorNoXMLTagFound,
+ eXMLErrorEmpty,
+ eXMLErrorMissingTagName,
+ eXMLErrorMissingEndTagName,
+ eXMLErrorUnmatchedEndTag,
+ eXMLErrorUnmatchedEndClearTag,
+ eXMLErrorUnexpectedToken,
+ eXMLErrorNoElements,
+ eXMLErrorFileNotFound,
+ eXMLErrorFirstTagNotFound,
+ eXMLErrorUnknownCharacterEntity,
+ eXMLErrorCharacterCodeAbove255,
+ eXMLErrorCharConversionError,
+ eXMLErrorCannotOpenWriteFile,
+ eXMLErrorCannotWriteFile,
+
+ eXMLErrorBase64DataSizeIsNotMultipleOf4,
+ eXMLErrorBase64DecodeIllegalCharacter,
+ eXMLErrorBase64DecodeTruncatedData,
+ eXMLErrorBase64DecodeBufferTooSmall
+} XMLError;
+
+
+/// Enumeration used to manage type of data. Use in conjunction with structure XMLNodeContents
+typedef enum XMLElementType
+{
+ eNodeChild=0,
+ eNodeAttribute=1,
+ eNodeText=2,
+ eNodeClear=3,
+ eNodeNULL=4
+} XMLElementType;
+
+/// Structure used to obtain error details if the parse fails.
+typedef struct XMLResults
+{
+ enum XMLError error;
+ int nLine,nColumn,nChars;
+} XMLResults;
+
+/// Structure for XML clear (unformatted) node (usually comments)
+typedef struct XMLClear {
+ XMLCSTR lpszValue; XMLCSTR lpszOpenTag; XMLCSTR lpszCloseTag;
+} XMLClear;
+
+/// Structure for XML attribute.
+typedef struct XMLAttribute {
+ XMLCSTR lpszName; XMLCSTR lpszValue;
+} XMLAttribute;
+
+/// XMLElementPosition are not interchangeable with simple indexes
+typedef int XMLElementPosition;
+
+struct XMLNodeContents;
+
+/** @defgroup XMLParserGeneral The XML parser */
+
+/// Main Class representing a XML node
+/**
+ * All operations are performed using this class.
+ * \note The constructors of the XMLNode class are protected, so use instead one of these four methods to get your first instance of XMLNode:
+ * <ul>
+ * <li> XMLNode::parseString </li>
+ * <li> XMLNode::parseFile </li>
+ * <li> XMLNode::openFileHelper </li>
+ * <li> XMLNode::createXMLTopNode (or XMLNode::createXMLTopNode_WOSD)</li>
+ * </ul> */
+typedef struct XMLDLLENTRY XMLNode
+{
+private:
+
+ struct XMLNodeDataTag;
+
+ /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode
+ XMLNode(struct XMLNodeDataTag *pParent, XMLSTR lpszName, char isDeclaration);
+ /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode
+ XMLNode(struct XMLNodeDataTag *p);
+
+public:
+ static XMLCSTR getVersion();///< Return the XMLParser library version number
+
+ /** @defgroup conversions Parsing XML files/strings to an XMLNode structure and Rendering XMLNode's to files/string.
+ * @ingroup XMLParserGeneral
+ * @{ */
+
+ /// Parse an XML string and return the root of a XMLNode tree representing the string.
+ static XMLNode parseString (XMLCSTR lpXMLString, XMLCSTR tag=NULL, XMLResults *pResults=NULL);
+ /**< The "parseString" function parse an XML string and return the root of a XMLNode tree. The "opposite" of this function is
+ * the function "createXMLString" that re-creates an XML string from an XMLNode tree. If the XML document is corrupted, the
+ * "parseString" method will initialize the "pResults" variable with some information that can be used to trace the error.
+ * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the
+ * beginning of the "xmlParser.cpp" file.
+ *
+ * @param lpXMLString the XML string to parse
+ * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>).
+ * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function.
+ */
+
+ /// Parse an XML file and return the root of a XMLNode tree representing the file.
+ static XMLNode parseFile (XMLCSTR filename, XMLCSTR tag=NULL, XMLResults *pResults=NULL);
+ /**< The "parseFile" function parse an XML file and return the root of a XMLNode tree. The "opposite" of this function is
+ * the function "writeToFile" that re-creates an XML file from an XMLNode tree. If the XML document is corrupted, the
+ * "parseFile" method will initialize the "pResults" variable with some information that can be used to trace the error.
+ * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the
+ * beginning of the "xmlParser.cpp" file.
+ *
+ * @param filename the path to the XML file to parse
+ * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>).
+ * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function.
+ */
+
+ /// Parse an XML file and return the root of a XMLNode tree representing the file. A very crude error checking is made. An attempt to guess the Char Encoding used in the file is made.
+ static XMLNode openFileHelper(XMLCSTR filename, XMLCSTR tag=NULL);
+ /**< The "openFileHelper" function reports to the screen all the warnings and errors that occurred during parsing of the XML file.
+ * This function also tries to guess char Encoding (UTF-8, ASCII or SHIT-JIS) based on the first 200 bytes of the file. Since each
+ * application has its own way to report and deal with errors, you should rather use the "parseFile" function to parse XML files
+ * and program yourself thereafter an "error reporting" tailored for your needs (instead of using the very crude "error reporting"
+ * mechanism included inside the "openFileHelper" function).
+ *
+ * If the XML document is corrupted, the "openFileHelper" method will:
+ * - display an error message on the console (or inside a messageBox for windows).
+ * - stop execution (exit).
+ *
+ * I strongly suggest that you write your own "openFileHelper" method tailored to your needs. If you still want to parse
+ * the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the beginning of the "xmlParser.cpp" file.
+ *
+ * @param filename the path of the XML file to parse.
+ * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>).
+ */
+
+ static XMLCSTR getError(XMLError error); ///< this gives you a user-friendly explanation of the parsing error
+
+ /// Create an XML string starting from the current XMLNode.
+ XMLSTR createXMLString(int nFormat=1, int *pnSize=NULL) const;
+ /**< The returned string should be free'd using the "freeXMLString" function.
+ *
+ * If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element
+ * with appropriate white spaces and carriage returns. if pnSize is given it returns the size in character of the string. */
+
+ /// Save the content of an xmlNode inside a file
+ XMLError writeToFile(XMLCSTR filename,
+ const char *encoding=NULL,
+ char nFormat=1) const;
+ /**< If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element with appropriate white spaces and carriage returns.
+ * If the global parameter "characterEncoding==encoding_UTF8", then the "encoding" parameter is ignored and always set to "utf-8".
+ * If the global parameter "characterEncoding==encoding_ShiftJIS", then the "encoding" parameter is ignored and always set to "SHIFT-JIS".
+ * If "_XMLWIDECHAR=1", then the "encoding" parameter is ignored and always set to "utf-16".
+ * If no "encoding" parameter is given the "ISO-8859-1" encoding is used. */
+ /** @} */
+
+ /** @defgroup navigate Navigate the XMLNode structure
+ * @ingroup XMLParserGeneral
+ * @{ */
+ XMLCSTR getName() const; ///< name of the node
+ XMLCSTR getText(int i=0) const; ///< return ith text field
+ XMLCSTR getInnerText() const;
+ int nText() const; ///< nbr of text field
+ XMLNode getParentNode() const; ///< return the parent node
+ XMLNode getChildNode(int i=0) const; ///< return ith child node
+ XMLNode getChildNode(XMLCSTR name, int i) const; ///< return ith child node with specific name (return an empty node if failing). If i==-1, this returns the last XMLNode with the given name.
+ XMLNode getChildNode(XMLCSTR name, int *i=NULL) const; ///< return next child node with specific name (return an empty node if failing)
+ XMLNode getChildNodeWithAttribute(XMLCSTR tagName,
+ XMLCSTR attributeName,
+ XMLCSTR attributeValue=NULL,
+ int *i=NULL) const; ///< return child node with specific name/attribute (return an empty node if failing)
+ XMLNode getChildNodeByPath(XMLSTR path, char createNodeIfMissing=0, XMLCHAR sep='/');
+ ///< return the first child node with specific path. WARNING: the value of the parameter "path" is destroyed!
+ XMLNode getChildNodeByPath(XMLCSTR path, char createNodeIfMissing=0, XMLCHAR sep='/');
+ ///< return the first child node with specific path
+ XMLNode getChildNodeByPathNonConst(XMLSTR path, char createNodeIfMissing=0, XMLCHAR sep='/');
+ ///< return the first child node with specific path.
+ XMLNode getNextNode() const;
+
+ int nChildNode(XMLCSTR name) const; ///< return the number of child node with specific name
+ int nChildNode() const; ///< nbr of child node
+ XMLAttribute getAttribute(int i=0) const; ///< return ith attribute
+ XMLCSTR getAttributeName(int i=0) const; ///< return ith attribute name
+ XMLCSTR getAttributeValue(int i=0) const; ///< return ith attribute value
+ char isAttributeSet(XMLCSTR name) const; ///< test if an attribute with a specific name is given
+ XMLCSTR getAttribute(XMLCSTR name, int i) const; ///< return ith attribute content with specific name (return a NULL if failing)
+ XMLCSTR getAttribute(XMLCSTR name, int *i=NULL) const; ///< return next attribute content with specific name (return a NULL if failing)
+ int nAttribute() const; ///< nbr of attribute
+ XMLClear getClear(int i=0) const; ///< return ith clear field (comments)
+ int nClear() const; ///< nbr of clear field
+ XMLNodeContents enumContents(XMLElementPosition i) const; ///< enumerate all the different contents (attribute,child,text, clear) of the current XMLNode. The order is reflecting the order of the original file/string. NOTE: 0 <= i < nElement();
+ int nElement() const; ///< nbr of different contents for current node
+ char isEmpty() const; ///< is this node Empty?
+ char isDeclaration() const; ///< is this node a declaration <? .... ?>
+ XMLNode deepCopy() const; ///< deep copy (duplicate/clone) a XMLNode
+ static XMLNode emptyNode(); ///< return XMLNode::emptyXMLNode;
+ /** @} */
+
+ ~XMLNode();
+ XMLNode(const XMLNode &A); ///< to allow shallow/fast copy:
+ XMLNode& operator=( const XMLNode& A ); ///< to allow shallow/fast copy:
+
+ XMLNode(): d(NULL){};
+ static XMLNode emptyXMLNode;
+ static XMLClear emptyXMLClear;
+ static XMLAttribute emptyXMLAttribute;
+
+ /** helpers for external C applications **/
+ XMLNode( HXML h );
+ void attach( HXML h );
+ HXML detach();
+ operator HXML() const { return (HXML)d; }
+
+ /** @defgroup xmlModify Create or Update the XMLNode structure
+ * @ingroup XMLParserGeneral
+ * The functions in this group allows you to create from scratch (or update) a XMLNode structure. Start by creating your top
+ * node with the "createXMLTopNode" function and then add new nodes with the "addChild" function. The parameter 'pos' gives
+ * the position where the childNode, the text or the XMLClearTag will be inserted. The default value (pos=-1) inserts at the
+ * end. The value (pos=0) insert at the beginning (Insertion at the beginning is slower than at the end). <br>
+ *
+ * REMARK: 0 <= pos < nChild()+nText()+nClear() <br>
+ */
+
+ /** @defgroup creation Creating from scratch a XMLNode structure
+ * @ingroup xmlModify
+ * @{ */
+ static XMLNode createXMLTopNode(XMLCSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure
+ XMLNode addChild(XMLCSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node
+ XMLNode addChild(XMLNode nodeToAdd, XMLElementPosition pos=-1); ///< If the "nodeToAdd" has some parents, it will be detached from it's parents before being attached to the current XMLNode
+ XMLAttribute *addAttribute(XMLCSTR lpszName, XMLCSTR lpszValuev); ///< Add a new attribute
+ XMLCSTR addText(XMLCSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content
+ XMLClear *addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1);
+ /**< Add a new clear tag
+ * @param lpszOpen default value "<![CDATA["
+ * @param lpszClose default value "]]>"
+ */
+ /** @} */
+
+ /** @defgroup xmlUpdate Updating Nodes
+ * @ingroup xmlModify
+ * Some update functions:
+ * @{
+ */
+ XMLCSTR updateName(XMLCSTR lpszName); ///< change node's name
+ XMLAttribute *updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added
+ XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added
+ XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName);///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added
+ XMLCSTR updateText(XMLCSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added
+ XMLCSTR updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added
+ XMLClear *updateClear(XMLCSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added
+ XMLClear *updateClear(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added
+ XMLClear *updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added
+ /** @} */
+
+ /** @defgroup xmlDelete Deleting Nodes or Attributes
+ * @ingroup xmlModify
+ * Some deletion functions:
+ * @{
+ */
+ /// The "deleteNodeContent" function forces the deletion of the content of this XMLNode and the subtree.
+ void deleteNodeContent();
+ /**< \note The XMLNode instances that are referring to the part of the subtree that has been deleted CANNOT be used anymore!!. Unexpected results will occur if you continue using them. */
+ void deleteAttribute(int i=0); ///< Delete the ith attribute of the current XMLNode
+ void deleteAttribute(XMLCSTR lpszName); ///< Delete the attribute with the given name (the "strcmp" function is used to find the right attribute)
+ void deleteAttribute(XMLAttribute *anAttribute); ///< Delete the attribute with the name "anAttribute->lpszName" (the "strcmp" function is used to find the right attribute)
+ void deleteText(int i=0); ///< Delete the Ith text content of the current XMLNode
+ void deleteText(XMLCSTR lpszValue); ///< Delete the text content "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the right text)
+ void deleteClear(int i=0); ///< Delete the Ith clear tag inside the current XMLNode
+ void deleteClear(XMLCSTR lpszValue); ///< Delete the clear tag "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the clear tag)
+ void deleteClear(XMLClear *p); ///< Delete the clear tag "p" inside the current XMLNode (direct "pointer-to-pointer" comparison on the lpszName of the clear tag is used to find the clear tag)
+ /** @} */
+
+ /** @defgroup xmlWOSD ???_WOSD functions.
+ * @ingroup xmlModify
+ * The strings given as parameters for the "add" and "update" methods that have a name with
+ * the postfix "_WOSD" (that means "WithOut String Duplication")(for example "addText_WOSD")
+ * will be free'd by the XMLNode class. For example, it means that this is incorrect:
+ * \code
+ * xNode.addText_WOSD("foo");
+ * xNode.updateAttribute_WOSD("#newcolor" ,NULL,"color");
+ * \endcode
+ * In opposition, this is correct:
+ * \code
+ * xNode.addText("foo");
+ * xNode.addText_WOSD(stringDup("foo"));
+ * xNode.updateAttribute("#newcolor" ,NULL,"color");
+ * xNode.updateAttribute_WOSD(stringDup("#newcolor"),NULL,"color");
+ * \endcode
+ * Typically, you will never do:
+ * \code
+ * char *b=(char*)malloc(...);
+ * xNode.addText(b);
+ * free(b);
+ * \endcode
+ * ... but rather:
+ * \code
+ * char *b=(char*)malloc(...);
+ * xNode.addText_WOSD(b);
+ * \endcode
+ * ('free(b)' is performed by the XMLNode class)
+ * @{ */
+ static XMLNode createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure
+ XMLNode addChild_WOSD(XMLSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node
+ XMLAttribute *addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValue); ///< Add a new attribute
+ XMLCSTR addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content
+ XMLClear *addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1); ///< Add a new clear Tag
+
+ XMLCSTR updateName_WOSD(XMLSTR lpszName); ///< change node's name
+ XMLAttribute *updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added
+ XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added
+ XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName); ///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added
+ XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added
+ XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added
+ XMLClear *updateClear_WOSD(XMLSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added
+ XMLClear *updateClear_WOSD(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added
+ XMLClear *updateClear_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added
+ /** @} */
+
+ /** @defgroup xmlPosition Position helper functions (use in conjunction with the update&add functions
+ * @ingroup xmlModify
+ * These are some useful functions when you want to insert a childNode, a text or a XMLClearTag in the
+ * middle (at a specified position) of a XMLNode tree already constructed. The value returned by these
+ * methods is to be used as last parameter (parameter 'pos') of addChild, addText or addClear.
+ * @{ */
+ XMLElementPosition positionOfText(int i=0) const;
+ XMLElementPosition positionOfText(XMLCSTR lpszValue) const;
+ XMLElementPosition positionOfClear(int i=0) const;
+ XMLElementPosition positionOfClear(XMLCSTR lpszValue) const;
+ XMLElementPosition positionOfClear(XMLClear *a) const;
+ XMLElementPosition positionOfChildNode(int i=0) const;
+ XMLElementPosition positionOfChildNode(XMLNode x) const;
+ XMLElementPosition positionOfChildNode(XMLCSTR name, int i=0) const; ///< return the position of the ith childNode with the specified name if (name==NULL) return the position of the ith childNode
+ /** @} */
+
+ /// Enumeration for XML character encoding.
+ typedef enum XMLCharEncoding
+ {
+ char_encoding_error=0,
+ char_encoding_UTF8=1,
+ char_encoding_legacy=2,
+ char_encoding_ShiftJIS=3,
+ char_encoding_GB2312=4,
+ char_encoding_Big5=5,
+ char_encoding_GBK=6 // this is actually the same as Big5
+ } XMLCharEncoding;
+
+ /** \addtogroup conversions
+ * @{ */
+
+ /// Sets the global options for the conversions
+ static char setGlobalOptions(XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8, char guessWideCharChars=1,
+ char dropWhiteSpace=1, char removeCommentsInMiddleOfText=1);
+ /**< The "setGlobalOptions" function allows you to change four global parameters that affect string & file
+ * parsing. First of all, you most-probably will never have to change these 3 global parameters.
+ *
+ * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in WideChar mode, then the
+ * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains ASCII
+ * characters. If this is the case, then the file will be loaded and converted in memory to
+ * WideChar before being parsed. If 0, no conversion will be performed.
+ *
+ * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in ASCII/UTF8/char* mode, then the
+ * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains WideChar
+ * characters. If this is the case, then the file will be loaded and converted in memory to
+ * ASCII/UTF8/char* before being parsed. If 0, no conversion will be performed.
+ *
+ * @param characterEncoding This parameter is only meaningful when compiling in char* mode (multibyte character mode).
+ * In wchar_t* (wide char mode), this parameter is ignored. This parameter should be one of the
+ * three currently recognized encodings: XMLNode::encoding_UTF8, XMLNode::encoding_ascii,
+ * XMLNode::encoding_ShiftJIS.
+ *
+ * @param dropWhiteSpace In most situations, text fields containing only white spaces (and carriage returns)
+ * are useless. Even more, these "empty" text fields are annoying because they increase the
+ * complexity of the user's code for parsing. So, 99% of the time, it's better to drop
+ * the "empty" text fields. However The XML specification indicates that no white spaces
+ * should be lost when parsing the file. So to be perfectly XML-compliant, you should set
+ * dropWhiteSpace=0. A note of caution: if you set "dropWhiteSpace=0", the parser will be
+ * slower and your code will be more complex.
+ *
+ * @param removeCommentsInMiddleOfText To explain this parameter, let's consider this code:
+ * \code
+ * XMLNode x=XMLNode::parseString("<a>foo<!-- hello -->bar<!DOCTYPE world >chu</a>","a");
+ * \endcode
+ * If removeCommentsInMiddleOfText=0, then we will have:
+ * \code
+ * x.getText(0) -> "foo"
+ * x.getText(1) -> "bar"
+ * x.getText(2) -> "chu"
+ * x.getClear(0) --> "<!-- hello -->"
+ * x.getClear(1) --> "<!DOCTYPE world >"
+ * \endcode
+ * If removeCommentsInMiddleOfText=1, then we will have:
+ * \code
+ * x.getText(0) -> "foobar"
+ * x.getText(1) -> "chu"
+ * x.getClear(0) --> "<!DOCTYPE world >"
+ * \endcode
+ *
+ * \return "0" when there are no errors. If you try to set an unrecognized encoding then the return value will be "1" to signal an error.
+ *
+ * \note Sometime, it's useful to set "guessWideCharChars=0" to disable any conversion
+ * because the test to detect the file-type (ASCII/UTF8/char* or WideChar) may fail (rarely). */
+
+ /// Guess the character encoding of the string (ascii, utf8 or shift-JIS)
+ static XMLCharEncoding guessCharEncoding(void *buffer, int bufLen, char useXMLEncodingAttribute=1);
+ /**< The "guessCharEncoding" function try to guess the character encoding. You most-probably will never
+ * have to use this function. It then returns the appropriate value of the global parameter
+ * "characterEncoding" described in the XMLNode::setGlobalOptions. The guess is based on the content of a buffer of length
+ * "bufLen" bytes that contains the first bytes (minimum 25 bytes; 200 bytes is a good value) of the
+ * file to be parsed. The XMLNode::openFileHelper function is using this function to automatically compute
+ * the value of the "characterEncoding" global parameter. There are several heuristics used to do the
+ * guess. One of the heuristic is based on the "encoding" attribute. The original XML specifications
+ * forbids to use this attribute to do the guess but you can still use it if you set
+ * "useXMLEncodingAttribute" to 1 (this is the default behavior and the behavior of most parsers).
+ * If an inconsistency in the encoding is detected, then the return value is "0". */
+ /** @} */
+
+private:
+ // these are functions and structures used internally by the XMLNode class (don't bother about them):
+
+ typedef struct XMLNodeDataTag // to allow shallow copy and "intelligent/smart" pointers (automatic delete):
+ {
+ XMLCSTR lpszName; // Element name (=NULL if root)
+ XMLCSTR lpszNS; // Namespace
+ int nChild, // Number of child nodes
+ nText, // Number of text fields
+ nClear, // Number of Clear fields (comments)
+ nAttribute; // Number of attributes
+ char isDeclaration; // Whether node is an XML declaration - '<?xml ?>'
+ struct XMLNodeDataTag *pParent; // Pointer to parent element (=NULL if root)
+ XMLNode *pChild; // Array of child nodes
+ XMLCSTR *pText; // Array of text fields
+ XMLClear *pClear; // Array of clear fields
+ XMLAttribute *pAttribute; // Array of attributes
+ int *pOrder; // order of the child_nodes,text_fields,clear_fields
+ int ref_count; // for garbage collection (smart pointers)
+ XMLSTR pInnerText; // cached value of inner text, for memory manadgement purposes
+ } XMLNodeData;
+ XMLNodeData *d;
+
+ char parseClearTag(void *px, void *pa);
+ char maybeAddTxT(void *pa, XMLCSTR tokenPStr);
+ int ParseXMLElement(void *pXML);
+ void *addToOrder(int memInc, int *_pos, int nc, void *p, int size, XMLElementType xtype);
+ int indexText(XMLCSTR lpszValue) const;
+ int indexClear(XMLCSTR lpszValue) const;
+ XMLNode addChild_priv(int,XMLSTR,char,int);
+ XMLAttribute *addAttribute_priv(int,XMLSTR,XMLSTR);
+ XMLCSTR addText_priv(int,XMLSTR,int);
+ XMLClear *addClear_priv(int,XMLSTR,XMLCSTR,XMLCSTR,int);
+ void emptyTheNode(char force);
+ void invalidateInnerText();
+ static inline XMLElementPosition findPosition(XMLNodeData *d, int index, XMLElementType xtype);
+ static int CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat);
+ static int removeOrderElement(XMLNodeData *d, XMLElementType t, int index);
+ static void exactMemory(XMLNodeData *d);
+ static int detachFromParent(XMLNodeData *d);
+} XMLNode;
+
+/// This structure is given by the function XMLNode::enumContents.
+typedef struct XMLNodeContents
+{
+ /// This dictates what's the content of the XMLNodeContent
+ enum XMLElementType etype;
+ /**< should be an union to access the appropriate data. Compiler does not allow union of object with constructor... too bad. */
+ XMLNode child;
+ XMLAttribute attrib;
+ XMLCSTR text;
+ XMLClear clear;
+
+} XMLNodeContents;
+
+/** @defgroup StringAlloc String Allocation/Free functions
+* @ingroup xmlModify
+* @{ */
+/// Duplicate (copy in a new allocated buffer) the source string.
+XMLDLLENTRY XMLSTR stringDup(XMLCSTR source, int cbData=-1);
+/**< This is
+* a very handy function when used with all the "XMLNode::*_WOSD" functions (\link xmlWOSD \endlink).
+* @param cbData If !=0 then cbData is the number of chars to duplicate. New strings allocated with
+* this function should be free'd using the "freeXMLString" function. */
+
+/// to free the string allocated inside the "stringDup" function or the "createXMLString" function.
+XMLDLLENTRY void freeXMLString(XMLSTR t); // {free(t);}
+/** @} */
+
+/** @defgroup atoX ato? like functions
+* @ingroup XMLParserGeneral
+* The "xmlto?" functions are equivalents to the atoi, atol, atof functions.
+* The only difference is: If the variable "xmlString" is NULL, than the return value
+* is "defautValue". These 6 functions are only here as "convenience" functions for the
+* user (they are not used inside the XMLparser). If you don't need them, you can
+* delete them without any trouble.
+*
+* @{ */
+XMLDLLENTRY char xmltob(XMLCSTR xmlString,char defautValue=0);
+XMLDLLENTRY int xmltoi(XMLCSTR xmlString,int defautValue=0);
+XMLDLLENTRY long xmltol(XMLCSTR xmlString,long defautValue=0);
+XMLDLLENTRY double xmltof(XMLCSTR xmlString,double defautValue=.0);
+XMLDLLENTRY XMLCSTR xmltoa(XMLCSTR xmlString,XMLCSTR defautValue=_CXML(""));
+XMLDLLENTRY XMLCHAR xmltoc(XMLCSTR xmlString,const XMLCHAR defautValue=_CXML('\0'));
+/** @} */
+
+/** @defgroup ToXMLStringTool Helper class to create XML files using "printf", "fprintf", "cout",... functions.
+* @ingroup XMLParserGeneral
+* @{ */
+/// Helper class to create XML files using "printf", "fprintf", "cout",... functions.
+/** The ToXMLStringTool class helps you creating XML files using "printf", "fprintf", "cout",... functions.
+* The "ToXMLStringTool" class is processing strings so that all the characters
+* &,",',<,> are replaced by their XML equivalent:
+* \verbatim &amp;, &quot;, &apos;, &lt;, &gt; \endverbatim
+* Using the "ToXMLStringTool class" and the "fprintf function" is THE most efficient
+* way to produce VERY large XML documents VERY fast.
+* \note If you are creating from scratch an XML file using the provided XMLNode class
+* you must not use the "ToXMLStringTool" class (because the "XMLNode" class does the
+* processing job for you during rendering).*/
+typedef struct XMLDLLENTRY ToXMLStringTool
+{
+public:
+ ToXMLStringTool(): buf(NULL),buflen(0){}
+ ~ToXMLStringTool();
+ void freeBuffer();///<call this function when you have finished using this object to release memory used by the internal buffer.
+
+ XMLSTR toXML(XMLCSTR source);///< returns a pointer to an internal buffer that contains a XML-encoded string based on the "source" parameter.
+
+ /** The "toXMLUnSafe" function is deprecated because there is a possibility of
+ * "destination-buffer-overflow". It converts the string
+ * "source" to the string "dest". */
+ static XMLSTR toXMLUnSafe(XMLSTR dest,XMLCSTR source); ///< deprecated: use "toXML" instead
+ static int lengthXMLString(XMLCSTR source); ///< deprecated: use "toXML" instead
+
+private:
+ XMLSTR buf;
+ int buflen;
+} ToXMLStringTool;
+/** @} */
+
+/** @defgroup XMLParserBase64Tool Helper class to include binary data inside XML strings using "Base64 encoding".
+* @ingroup XMLParserGeneral
+* @{ */
+/// Helper class to include binary data inside XML strings using "Base64 encoding".
+/** The "XMLParserBase64Tool" class allows you to include any binary data (images, sounds,...)
+* into an XML document using "Base64 encoding". This class is completely
+* separated from the rest of the xmlParser library and can be removed without any problem.
+* To include some binary data into an XML file, you must convert the binary data into
+* standard text (using "encode"). To retrieve the original binary data from the
+* b64-encoded text included inside the XML file, use "decode". Alternatively, these
+* functions can also be used to "encrypt/decrypt" some critical data contained inside
+* the XML (it's not a strong encryption at all, but sometimes it can be useful). */
+typedef struct XMLDLLENTRY XMLParserBase64Tool
+{
+public:
+ XMLParserBase64Tool(): buf(NULL),buflen(0){}
+ ~XMLParserBase64Tool();
+ void freeBuffer();///< Call this function when you have finished using this object to release memory used by the internal buffer.
+
+ /**
+ * @param formatted If "formatted"=true, some space will be reserved for a carriage-return every 72 chars. */
+ static int encodeLength(int inBufLen, char formatted=0); ///< return the length of the base64 string that encodes a data buffer of size inBufLen bytes.
+
+ /**
+ * The "base64Encode" function returns a string containing the base64 encoding of "inByteLen" bytes
+ * from "inByteBuf". If "formatted" parameter is true, then there will be a carriage-return every 72 chars.
+ * The string will be free'd when the XMLParserBase64Tool object is deleted.
+ * All returned strings are sharing the same memory space. */
+ XMLSTR encode(unsigned char *inByteBuf, unsigned int inByteLen, char formatted=0); ///< returns a pointer to an internal buffer containing the base64 string containing the binary data encoded from "inByteBuf"
+
+ /// returns the number of bytes which will be decoded from "inString".
+ static unsigned int decodeSize(XMLCSTR inString, XMLError *xe=NULL);
+
+ /**
+ * The "decode" function returns a pointer to a buffer containing the binary data decoded from "inString"
+ * The output buffer will be free'd when the XMLParserBase64Tool object is deleted.
+ * All output buffer are sharing the same memory space.
+ * @param inString If "instring" is malformed, NULL will be returned */
+ unsigned char* decode(XMLCSTR inString, int *outByteLen=NULL, XMLError *xe=NULL); ///< returns a pointer to an internal buffer containing the binary data decoded from "inString"
+
+ /**
+ * decodes data from "inString" to "outByteBuf". You need to provide the size (in byte) of "outByteBuf"
+ * in "inMaxByteOutBuflen". If "outByteBuf" is not large enough or if data is malformed, then "FALSE"
+ * will be returned; otherwise "TRUE". */
+ static unsigned char decode(XMLCSTR inString, unsigned char *outByteBuf, int inMaxByteOutBuflen, XMLError *xe=NULL); ///< deprecated.
+
+private:
+ void *buf;
+ int buflen;
+ void alloc(int newsize);
+}XMLParserBase64Tool;
+/** @} */
+
+#undef XMLDLLENTRY
+
+#endif