summaryrefslogtreecommitdiff
path: root/plugins/UserInfoEx/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/UserInfoEx/src')
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp326
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_countrylistext.h39
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_flags.cpp765
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_flags.h102
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp456
-rw-r--r--plugins/UserInfoEx/src/Flags/svc_flagsicons.h43
-rw-r--r--plugins/UserInfoEx/src/classMAnnivDate.cpp836
-rw-r--r--plugins/UserInfoEx/src/classMAnnivDate.h132
-rw-r--r--plugins/UserInfoEx/src/classMTime.cpp468
-rw-r--r--plugins/UserInfoEx/src/classMTime.h125
-rw-r--r--plugins/UserInfoEx/src/classPsTree.cpp993
-rw-r--r--plugins/UserInfoEx/src/classPsTreeItem.cpp697
-rw-r--r--plugins/UserInfoEx/src/commonheaders.cpp180
-rw-r--r--plugins/UserInfoEx/src/commonheaders.h234
-rw-r--r--plugins/UserInfoEx/src/ctrl_annivedit.cpp636
-rw-r--r--plugins/UserInfoEx/src/ctrl_annivedit.h104
-rw-r--r--plugins/UserInfoEx/src/ctrl_base.cpp301
-rw-r--r--plugins/UserInfoEx/src/ctrl_base.h238
-rw-r--r--plugins/UserInfoEx/src/ctrl_button.cpp708
-rw-r--r--plugins/UserInfoEx/src/ctrl_button.h36
-rw-r--r--plugins/UserInfoEx/src/ctrl_combo.cpp277
-rw-r--r--plugins/UserInfoEx/src/ctrl_combo.h80
-rw-r--r--plugins/UserInfoEx/src/ctrl_contact.cpp1539
-rw-r--r--plugins/UserInfoEx/src/ctrl_contact.h86
-rw-r--r--plugins/UserInfoEx/src/ctrl_edit.cpp381
-rw-r--r--plugins/UserInfoEx/src/ctrl_edit.h80
-rw-r--r--plugins/UserInfoEx/src/ctrl_tzcombo.cpp306
-rw-r--r--plugins/UserInfoEx/src/ctrl_tzcombo.h69
-rw-r--r--plugins/UserInfoEx/src/dlg_anniversarylist.cpp1120
-rw-r--r--plugins/UserInfoEx/src/dlg_anniversarylist.h43
-rw-r--r--plugins/UserInfoEx/src/dlg_msgbox.cpp856
-rw-r--r--plugins/UserInfoEx/src/dlg_msgbox.h103
-rw-r--r--plugins/UserInfoEx/src/dlg_propsheet.cpp1867
-rw-r--r--plugins/UserInfoEx/src/dlg_propsheet.h281
-rw-r--r--plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp581
-rw-r--r--plugins/UserInfoEx/src/ex_import/classExImContactBase.h103
-rw-r--r--plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp1141
-rw-r--r--plugins/UserInfoEx/src/ex_import/classExImContactXML.h103
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp464
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h39
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp371
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h39
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp244
-rw-r--r--plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h56
-rw-r--r--plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h371
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp547
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImINI.h39
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp1364
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h103
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp453
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImXML.h75
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp382
-rw-r--r--plugins/UserInfoEx/src/ex_import/svc_ExImport.h62
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinystr.cpp143
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinystr.h335
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinyxml.cpp1978
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinyxml.h1599
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp76
-rw-r--r--plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp1613
-rw-r--r--plugins/UserInfoEx/src/init.cpp315
-rw-r--r--plugins/UserInfoEx/src/mir_contactqueue.cpp425
-rw-r--r--plugins/UserInfoEx/src/mir_contactqueue.h221
-rw-r--r--plugins/UserInfoEx/src/mir_db.cpp1334
-rw-r--r--plugins/UserInfoEx/src/mir_db.h229
-rw-r--r--plugins/UserInfoEx/src/mir_icolib.cpp401
-rw-r--r--plugins/UserInfoEx/src/mir_icolib.h148
-rw-r--r--plugins/UserInfoEx/src/mir_menuitems.cpp651
-rw-r--r--plugins/UserInfoEx/src/mir_menuitems.h47
-rw-r--r--plugins/UserInfoEx/src/mir_string.cpp167
-rw-r--r--plugins/UserInfoEx/src/mir_string.h88
-rw-r--r--plugins/UserInfoEx/src/psp_about.cpp124
-rw-r--r--plugins/UserInfoEx/src/psp_anniversary.cpp347
-rw-r--r--plugins/UserInfoEx/src/psp_base.cpp111
-rw-r--r--plugins/UserInfoEx/src/psp_base.h51
-rw-r--r--plugins/UserInfoEx/src/psp_company.cpp76
-rw-r--r--plugins/UserInfoEx/src/psp_contact.cpp347
-rw-r--r--plugins/UserInfoEx/src/psp_general.cpp211
-rw-r--r--plugins/UserInfoEx/src/psp_options.cpp1570
-rw-r--r--plugins/UserInfoEx/src/psp_options.h43
-rw-r--r--plugins/UserInfoEx/src/psp_origin.cpp176
-rw-r--r--plugins/UserInfoEx/src/psp_profile.cpp1464
-rw-r--r--plugins/UserInfoEx/src/resdefines.h5
-rw-r--r--plugins/UserInfoEx/src/resource.h333
-rw-r--r--plugins/UserInfoEx/src/svc_avatar.cpp247
-rw-r--r--plugins/UserInfoEx/src/svc_avatar.h41
-rw-r--r--plugins/UserInfoEx/src/svc_constants.cpp436
-rw-r--r--plugins/UserInfoEx/src/svc_constants.h195
-rw-r--r--plugins/UserInfoEx/src/svc_contactinfo.cpp798
-rw-r--r--plugins/UserInfoEx/src/svc_contactinfo.h36
-rw-r--r--plugins/UserInfoEx/src/svc_email.cpp409
-rw-r--r--plugins/UserInfoEx/src/svc_email.h39
-rw-r--r--plugins/UserInfoEx/src/svc_gender.cpp284
-rw-r--r--plugins/UserInfoEx/src/svc_gender.h40
-rw-r--r--plugins/UserInfoEx/src/svc_homepage.cpp337
-rw-r--r--plugins/UserInfoEx/src/svc_homepage.h38
-rw-r--r--plugins/UserInfoEx/src/svc_phone.cpp309
-rw-r--r--plugins/UserInfoEx/src/svc_phone.h38
-rw-r--r--plugins/UserInfoEx/src/svc_refreshci.cpp936
-rw-r--r--plugins/UserInfoEx/src/svc_refreshci.h36
-rw-r--r--plugins/UserInfoEx/src/svc_reminder.cpp1216
-rw-r--r--plugins/UserInfoEx/src/svc_reminder.h117
-rw-r--r--plugins/UserInfoEx/src/svc_timezone.cpp83
-rw-r--r--plugins/UserInfoEx/src/svc_timezone.h51
-rw-r--r--plugins/UserInfoEx/src/svc_timezone_old.cpp645
-rw-r--r--plugins/UserInfoEx/src/svc_timezone_old.h102
-rw-r--r--plugins/UserInfoEx/src/version.h51
106 files changed, 42156 insertions, 0 deletions
diff --git a/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp b/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp
new file mode 100644
index 0000000000..fabb2749f2
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp
@@ -0,0 +1,326 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_countrylistext.cpp $
+Revision : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_countrylistext.h"
+
+/************************* Services *******************************/
+
+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"},
+ {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"}, /* UK (St. Helena, Ascension and Tristan da Cunha) */
+ {247 ,"Ascension Island"}, /* UK (St. Helena, Ascension and Tristan da Cunha) */
+ {2897,"Tristan da Cunha"}, /* UK (St. Helena, Ascension and Tristan da Cunha) */
+// {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"},
+};
+
+INT_PTR ServiceGetCountryByNumber(WPARAM wParam,LPARAM lParam)
+{
+ int i;
+ UNREFERENCED_PARAMETER(lParam);
+ for(i=0; i<SIZEOF(countries); ++i)
+ if ((int)wParam==countries[i].id)
+ return (INT_PTR)countries[i].szName;
+ return NULL;
+}
+
+INT_PTR ServiceGetCountryList(WPARAM wParam,LPARAM lParam)
+{
+ if ((int*)wParam==NULL || (void*)lParam==NULL) return 1;
+ *(int*)wParam=SIZEOF(countries);
+ *(struct CountryListEntry**)lParam=countries;
+ return 0;
+}
+
+/************************* Misc ***********************************/
+
+VOID InitCountryListExt()
+{
+ /* hack to replace built-in country list */
+ if (!myDestroyServiceFunction(MS_UTILS_GETCOUNTRYLIST))
+ CreateServiceFunction(MS_UTILS_GETCOUNTRYLIST,ServiceGetCountryList);
+ if (!myDestroyServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER))
+ CreateServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER,ServiceGetCountryByNumber);
+}
diff --git a/plugins/UserInfoEx/src/Flags/svc_countrylistext.h b/plugins/UserInfoEx/src/Flags/svc_countrylistext.h
new file mode 100644
index 0000000000..a49a289542
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_countrylistext.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_countrylistext.h $
+Revision : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_COUNTRYLIST_H_INCLUDED_
+#define _UINFOEX_COUNTRYLIST_H_INCLUDED_
+
+VOID InitCountryListExt();
+INT_PTR ServiceGetCountryByNumber(WPARAM wParam,LPARAM lParam);
+
+#endif /* _UINFOEX_COUNTRYLIST_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/Flags/svc_flags.cpp b/plugins/UserInfoEx/src/Flags/svc_flags.cpp
new file mode 100644
index 0000000000..8999df2cca
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flags.cpp
@@ -0,0 +1,765 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flags.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_flags.h"
+#include "svc_flagsicons.h"
+#include "svc_countrylistext.h"
+
+#define M_ENABLE_SUBCTLS (WM_APP+1)
+
+FLAGSOPTIONS gFlagsOpts;
+
+/* Misc */
+int nCountriesCount;
+struct CountryListEntry *countries;
+static HANDLE *phExtraImageList = NULL; //return value(s) from MS_CLIST_EXTRA_ADD_ICON
+static HANDLE hExtraIconSvc = INVALID_HANDLE_VALUE;
+/* hook */
+static HANDLE hRebuildIconsHook = NULL;
+static HANDLE hApplyIconHook = NULL;
+static HANDLE hMsgWndEventHook = NULL;
+static HANDLE hIconsChangedHook = NULL;
+static HANDLE hSettingChangedHook = NULL;
+
+static HANDLE hExtraIconSvcHook = NULL;
+static HANDLE hOptInitHook = NULL;
+
+static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam);
+
+/***********************************************************************************************************
+ * Buffered functions
+ ***********************************************************************************************************/
+
+struct BufferedCallData {
+ DWORD startTick;
+ UINT uElapse;
+ BUFFEREDPROC pfnBuffProc;
+ LPARAM lParam;
+ #ifdef _DEBUG
+ const char *pszProcName;
+ #endif
+};
+
+static UINT_PTR idBufferedTimer;
+static struct BufferedCallData *callList;
+static int nCallListCount;
+
+// always gets called in main message loop
+static void CALLBACK BufferedProcTimer(HWND hwnd, UINT msg, UINT idTimer, DWORD currentTick) {
+ int i;
+ struct BufferedCallData *buf;
+ UINT uElapsed, uElapseNext = USER_TIMER_MAXIMUM;
+ BUFFEREDPROC pfnBuffProc;
+ LPARAM lParam;
+ #ifdef _DEBUG
+ char szDbgLine[256];
+ const char *pszProcName;
+ #endif
+ UNREFERENCED_PARAMETER(msg);
+
+ for(i=0;i<nCallListCount;++i) {
+ /* find elapsed procs */
+ uElapsed=currentTick-callList[i].startTick; /* wraparound works */
+ if ((uElapsed+USER_TIMER_MINIMUM)>=callList[i].uElapse) {
+ /* call elapsed proc */
+ pfnBuffProc=callList[i].pfnBuffProc;
+ lParam=callList[i].lParam;
+ #ifdef _DEBUG
+ pszProcName=callList[i].pszProcName;
+ #endif
+ /* resize storage array */
+ if ((i+1)<nCallListCount)
+ MoveMemory(&callList[i],&callList[i+1],((nCallListCount-i-1)*sizeof(struct BufferedCallData)));
+ --nCallListCount;
+ --i; /* reiterate current */
+ if(nCallListCount) {
+ buf=(struct BufferedCallData*)mir_realloc(callList,nCallListCount*sizeof(struct BufferedCallData));
+ if(buf!=NULL) callList=buf;
+ } else {
+ mir_free(callList);
+ callList=NULL;
+ }
+ #ifdef _DEBUG
+ mir_snprintf(szDbgLine,sizeof(szDbgLine),"buffered call: %s(0x%X)\n",pszProcName,lParam); /* all ascii */
+ OutputDebugStringA(szDbgLine);
+ #endif
+ CallFunctionAsync((void (CALLBACK *)(void*))pfnBuffProc,(void*)lParam); /* compatible */
+ }
+ /* find next timer delay */
+ else if ((callList[i].uElapse-uElapsed)<uElapseNext)
+ uElapseNext=callList[i].uElapse-uElapsed;
+ }
+
+ /* set next timer */
+ if(nCallListCount) {
+ #ifdef _DEBUG
+ mir_snprintf(szDbgLine,sizeof(szDbgLine),"next buffered timeout: %ums\n",uElapseNext); /* all ascii */
+ OutputDebugStringA(szDbgLine);
+ #endif
+ idBufferedTimer=SetTimer(hwnd, idBufferedTimer, uElapseNext, (TIMERPROC)BufferedProcTimer); /* will be reset */
+ } else {
+ KillTimer(hwnd,idTimer);
+ idBufferedTimer=0;
+ #ifdef _DEBUG
+ OutputDebugStringA("empty buffered queue\n");
+ #endif
+ }
+}
+
+// assumes to be called in context of main thread
+#ifdef _DEBUG
+void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,const char *pszProcName,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse)
+#else
+void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse)
+#endif
+{
+ struct BufferedCallData *data=NULL;
+ int i;
+
+ /* find existing */
+ for(i=0;i<nCallListCount;++i)
+ if(callList[i].pfnBuffProc==pfnBuffProc)
+ if (!fAccumulateSameParam || callList[i].lParam==lParam) {
+ data=&callList[i];
+ break;
+ }
+ /* append new */
+ if(data==NULL) {
+ /* resize storage array */
+ data=(struct BufferedCallData*)mir_realloc(callList,(nCallListCount+1)*sizeof(struct BufferedCallData));
+ if(data==NULL) return;
+ callList=data;
+ data=&callList[nCallListCount];
+ ++nCallListCount;
+ }
+ /* set delay */
+ data->startTick=GetTickCount();
+ data->uElapse=uElapse;
+ data->lParam=lParam;
+ data->pfnBuffProc=pfnBuffProc;
+ #ifdef _DEBUG
+ { char szDbgLine[256];
+ data->pszProcName=pszProcName;
+ mir_snprintf(szDbgLine,sizeof(szDbgLine),"buffered queue: %s(0x%X)\n",pszProcName,lParam); /* all ascii */
+ OutputDebugStringA(szDbgLine);
+ if (!idBufferedTimer) {
+ mir_snprintf(szDbgLine,sizeof(szDbgLine),"next buffered timeout: %ums\n",uElapse); /* all ascii */
+ OutputDebugStringA(szDbgLine);
+ }
+ }
+ #endif
+ /* set next timer */
+ if(idBufferedTimer) uElapse=USER_TIMER_MINIMUM; /* will get recalculated */
+ idBufferedTimer=SetTimer(NULL,idBufferedTimer,uElapse,(TIMERPROC)BufferedProcTimer);
+}
+
+// assumes to be called in context of main thread
+void PrepareBufferedFunctions()
+{
+ idBufferedTimer=0;
+ nCallListCount=0;
+ callList=NULL;
+}
+
+// assumes to be called in context of main thread
+void KillBufferedFunctions()
+{
+ if(idBufferedTimer) KillTimer(NULL,idBufferedTimer);
+ nCallListCount=0;
+ mir_free(callList); /* does NULL check */
+}
+
+/***********************************************************************************************************
+ * service functions
+ ***********************************************************************************************************/
+
+static INT_PTR ServiceDetectContactOriginCountry(WPARAM wParam,LPARAM lParam)
+{
+ WORD countryNumber;
+ char *pszProto =
+ pszProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ /* UserinfoEx */
+ if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_ORIGIN_COUNTRY,0))
+ return (INT_PTR)countryNumber;
+ else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_COUNTRY,0))
+ return (INT_PTR)countryNumber;
+ else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_COMPANY_COUNTRY,0))
+ return (INT_PTR)countryNumber;
+ /* fallback proto settings */
+ else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,pszProto,"Country",0))
+ return (INT_PTR)countryNumber;
+ else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,pszProto,"CompanyCountry",0))
+ return (INT_PTR)countryNumber;
+ /* fallback ip detect
+ else if(countryNumber==0xFFFF && DBGetContactSettingByte(NULL,"Flags","UseIpToCountry",SETTING_USEIPTOCOUNTRY_DEFAULT)) {
+ countryNumber=ServiceIpToCountry(DBGetContactSettingDword((HANDLE)wParam,pszProto,"RealIP",0),0);
+ }*/
+
+ return (INT_PTR)0xFFFF;
+}
+
+/***********************************************************************************************************
+ * Clist Extra Image functions
+ ***********************************************************************************************************/
+
+static void CALLBACK SetExtraImage(LPARAM lParam) {
+ /* get contact's country */
+ int countryNumber = ServiceDetectContactOriginCountry((WPARAM)lParam,0);;
+ //use ExtraIconsService ?
+ if (myGlobals.ExtraIconsServiceExist) {
+ EXTRAICON ico;
+ ico.cbSize = sizeof(ico);
+ ico.hContact = (HANDLE)lParam;
+ ico.hExtraIcon = hExtraIconSvc;
+ ico.icoName = (char*)0; //preset
+ if(countryNumber!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+ char szId[20];
+ mir_snprintf(szId, SIZEOF(szId), (countryNumber==0xFFFF)?"%s_0x%X":"%s_%i","flags",countryNumber); /* buffer safe */
+ ico.icoName = szId;
+ }
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ //use Clist ExtraImageService
+ else if(gFlagsOpts.bShowExtraImgFlag) {
+ int index;
+ IconExtraColumn iec;
+ iec.cbSize = sizeof(iec);
+ iec.ColumnType = gFlagsOpts.idExtraColumn;
+ iec.hImage = INVALID_HANDLE_VALUE; //preset
+ /* get icon for contact */
+ if(phExtraImageList!=NULL) { /* too early? */
+ if(countryNumber!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+ index = CountryNumberToIndex(countryNumber);
+ /* icon not add to clist extra image list? */
+ if(phExtraImageList[index]==INVALID_HANDLE_VALUE) {
+ HICON hIcon=LoadFlag(countryNumber); // Returned HICON SHOULDN'T be destroyed, it is managed by IcoLib
+ if(hIcon!=NULL) {
+ phExtraImageList[index]=(HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON,(WPARAM)hIcon,0);
+ Skin_ReleaseIcon(hIcon); /* does NULL check */
+ }
+ }
+ iec.hImage=phExtraImageList[index];
+ }
+ }
+ //Set icon for contact at needed column
+ CallService(MS_CLIST_EXTRA_SET_ICON,(WPARAM)lParam,(LPARAM)&iec);
+ }
+}
+
+static void CALLBACK RemoveExtraImages(LPARAM lParam) {
+ register HANDLE hContact;
+ //use ExtraIconsService ?
+ if (myGlobals.ExtraIconsServiceExist) {
+ EXTRAICON ico;
+ ico.cbSize = sizeof(ico);
+ ico.hExtraIcon = hExtraIconSvc;
+ ico.icoName = (char *)0; /* invalidate icon for contact*/
+ /* enum all contacts */
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+ ico.hContact = hContact;
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ }
+ //use Clist ExtraImageService
+ else {
+ IconExtraColumn iec;
+ iec.cbSize = sizeof(iec);
+ iec.ColumnType = gFlagsOpts.idExtraColumn;
+ iec.hImage = INVALID_HANDLE_VALUE;
+ /* enum all contacts */
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+ /* invalidate icon for contact*/
+ CallService(MS_CLIST_EXTRA_SET_ICON,(WPARAM)hContact,(LPARAM)&iec);
+ }
+ }
+}
+
+// always call in context of main thread
+void EnsureExtraImages() //garantieren - sicherstellen - updaten
+{
+ register HANDLE hContact;
+ //use Clist ExtraImageService?
+ if (!myGlobals.ExtraIconsServiceExist) {
+ BYTE idExtraColumnNew = DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn",SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+ if(idExtraColumnNew != gFlagsOpts.idExtraColumn) {
+ /* clear previous column */
+ RemoveExtraImages(0);
+ gFlagsOpts.idExtraColumn = idExtraColumnNew;
+ /* clear new column */
+ RemoveExtraImages(0);
+ }
+ }
+ /* enum all contacts */
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+ /* update icon */
+ CallFunctionBuffered(SetExtraImage,(LPARAM)hContact,TRUE,EXTRAIMAGE_REFRESHDELAY);
+ }
+}
+
+static void CALLBACK UpdateExtraImages(LPARAM lParam) {
+ if (!lParam)
+ RemoveExtraImages(0);
+ else EnsureExtraImages();
+
+/* if (!myGlobals.ExtraIconsServiceExist && !gFlagsOpts.bShowExtraImgFlag)
+ RemoveExtraImages();
+ else EnsureExtraImages(); */
+}
+
+//hookProc ME_CLIST_EXTRA_LIST_REBUILD
+static int OnCListRebuildIcons(WPARAM wParam,LPARAM lParam) {
+ OutputDebugStringA("REBUILD EXTRA\n");
+ //use ExtraIconsService ?
+ if(myGlobals.ExtraIconsServiceExist) return 0; //delete ?
+ //use Clist ExtraImageService
+ if(phExtraImageList!=NULL) {
+ /* invalidate icons */
+ for(int i=0;i<nCountriesCount;++i)
+ phExtraImageList[i]=INVALID_HANDLE_VALUE;
+ }
+ /* update column */
+ gFlagsOpts.idExtraColumn = DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn",SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+ return 0;
+}
+
+//hookProc ME_CLIST_EXTRA_IMAGE_APPLY
+static int OnCListApplyIcons(WPARAM wParam,LPARAM lParam) {
+ //OutputDebugStringA("APPLY EXTRA\n");
+ if(myGlobals.ExtraIconsServiceExist || gFlagsOpts.bShowExtraImgFlag)
+ SetExtraImage((LPARAM)wParam); /* unbuffered */
+ return 0;
+}
+
+//hookProc (ME_DB_CONTACT_SETTINGCHANGED) - workaround for missing event from ExtraIconSvc
+static int OnExtraIconSvcChanged(WPARAM wParam,LPARAM lParam) {
+ DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam;
+ if ((HANDLE)wParam!=NULL)return 0;
+ if (!lstrcmpA(dbcws->szModule, "ExtraIcons") &&
+ !lstrcmpA(dbcws->szSetting,"Slot_Flags")) {
+ BOOL bEnable;
+ switch (dbcws->value.type) {
+ case DBVT_BYTE:
+ bEnable = dbcws->value.bVal != (BYTE)-1;
+ break;
+ case DBVT_WORD:
+ bEnable = dbcws->value.wVal != (WORD)-1;
+ break;
+ case DBVT_DWORD:
+ bEnable = dbcws->value.dVal != (DWORD)-1;
+ break;
+ default:
+ bEnable = -1;
+ break;
+ }
+ if(bEnable == -1) {
+ return 0;
+ }
+ else if(bEnable && !hApplyIconHook) {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons);
+ }
+ else if (!bEnable && hApplyIconHook) {
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ }
+ CallFunctionBuffered(UpdateExtraImages,(LPARAM)bEnable,FALSE,EXTRAIMAGE_REFRESHDELAY);
+ }
+ return 0;
+}
+
+VOID SvcFlagsEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB) {
+ if (!myGlobals.HaveCListExtraIcons) return;
+ gFlagsOpts.bShowExtraImgFlag = (bColumn!=((BYTE)-1));
+ if (bUpdateDB) {
+ if(gFlagsOpts.bShowExtraImgFlag) DB::Setting::WriteByte(MODNAMEFLAGS,"ExtraImgFlagColumn", bColumn);
+ DB::Setting::WriteByte(MODNAMEFLAGS,"ShowExtraImgFlag", bColumn!=(BYTE)-1);
+ }
+
+ // Flags is on
+ if (gFlagsOpts.bShowExtraImgFlag) {
+ //use ExtraIconsService ?
+ if(myGlobals.ExtraIconsServiceExist) {
+ if(hExtraIconSvc == INVALID_HANDLE_VALUE) {
+ char szId[20];
+ //get local langID for descIcon (try to use user local Flag as icon)
+ DWORD langid = 0;
+ int r = GetLocaleInfo(
+ LOCALE_USER_DEFAULT,
+ LOCALE_ICOUNTRY | LOCALE_RETURN_NUMBER ,
+ (LPTSTR)&langid, sizeof(langid)/sizeof(TCHAR));
+ if (!CallService(MS_UTILS_GETCOUNTRYBYNUMBER,langid,0)) langid = 1;
+
+ EXTRAICON_INFO ico = {0};
+ ico.cbSize = sizeof(ico);
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.name = "Flags";
+ ico.description = "Flags (uinfoex)";
+ mir_snprintf(szId, SIZEOF(szId), (langid==0xFFFF)?"%s_0x%X":"%s_%i","flags",langid); /* buffer safe */
+ ico.descIcon = szId;
+ hExtraIconSvc=(HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ if(hExtraIconSvc)
+ hExtraIconSvcHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnExtraIconSvcChanged);
+
+ }
+ }
+ //use Clist ExtraImageService
+ else {
+ if(phExtraImageList == NULL){
+ phExtraImageList = (HANDLE*)mir_alloc(nCountriesCount*sizeof(HANDLE));
+ /* invalidate icons */
+ if(phExtraImageList!=NULL)
+ for(int i=0;i<nCountriesCount;++i)
+ phExtraImageList[i]=INVALID_HANDLE_VALUE;
+ }
+ if (!hRebuildIconsHook) {
+ hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+ }
+ }
+ //init hooks
+ if (!hApplyIconHook) {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons);
+ }
+ if (!hSettingChangedHook) {
+ hSettingChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
+ }
+ }
+ // Flags is off
+ else {
+ //use ExtraIconsService ?
+ if (myGlobals.ExtraIconsServiceExist) {
+ //nothing to do, until plugin has a hookable event for status
+ return;
+ }
+ //use Clist ExtraImageService
+ //unhook
+ if (hRebuildIconsHook) {
+ UnhookEvent(hRebuildIconsHook); hRebuildIconsHook = NULL;
+ }
+ if (hApplyIconHook) {
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ }
+ if (hSettingChangedHook && !gFlagsOpts.bShowStatusIconFlag) {
+ UnhookEvent(hSettingChangedHook); hSettingChangedHook = NULL;
+ }
+ //SvcFlagsApplyCListIcons();
+ //RemoveExtraImages();
+ CallFunctionBuffered(RemoveExtraImages,0,FALSE,EXTRAIMAGE_REFRESHDELAY);
+ }
+}
+
+/***********************************************************************************************************
+ * message winsow status icon functions
+ ***********************************************************************************************************/
+
+MsgWndData::MsgWndData(HWND hwnd, HANDLE hContact) {
+ m_hwnd = hwnd;
+ m_hContact = hContact;
+ m_contryID = (int)ServiceDetectContactOriginCountry((WPARAM)m_hContact,0);
+ FlagsIconUpdate();
+}
+
+MsgWndData::~MsgWndData() {
+ FlagsIconUnset(); //check if realy need
+}
+
+void
+MsgWndData::FlagsIconSet() {
+ HICON hIcon = NULL;
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODNAMEFLAGS;
+ /* ensure status icon is registered */
+ if ( m_contryID!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+ /* copy icon as status icon API will call DestroyIcon() on it */
+ hIcon = LoadFlagIcon(m_contryID);
+ sid.hIcon = (hIcon!=NULL)?CopyIcon(hIcon):NULL;
+ Skin_ReleaseIcon(hIcon); /* does NULL check */
+ hIcon = sid.hIcon;
+ sid.dwId = (DWORD)m_contryID;
+ sid.hIconDisabled = sid.hIcon/*NULL*/;
+ sid.szTooltip = Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,m_contryID,0));
+ sid.flags = 0;
+ if(CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid) !=0) /* not yet registered? */
+ CallService(MS_MSG_ADDICON,0,(LPARAM)&sid);
+ }
+ sid.hIcon = NULL;
+ sid.szTooltip = NULL;
+ sid.hIconDisabled = NULL;
+ for(int i=0;i<nCountriesCount;++i) {
+ sid.dwId = (DWORD)countries[i].id;
+ sid.flags = (m_contryID==countries[i].id && hIcon!=NULL)? 0:MBF_HIDDEN;
+ CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid);
+ }
+}
+
+void
+MsgWndData::FlagsIconUnset() {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = MODNAMEFLAGS;
+ sid.dwId = (DWORD)m_contryID;
+ sid.flags = MBF_HIDDEN;
+ CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid);
+ /* can't call MS_MSG_REMOVEICON here as the icon might be
+ * in use by other contacts simultanously, removing them all at exit */
+}
+
+static int CompareMsgWndData(const MsgWndData* p1, const MsgWndData* p2)
+{
+ return (int)((INT_PTR)p1->m_hContact - (INT_PTR)p2->m_hContact);
+}
+static LIST<MsgWndData> gMsgWndList(10, CompareMsgWndData);
+
+static int CompareIconListData(const IconList* p1, const IconList* p2)
+{
+ return (int)((INT_PTR)p1->m_ID - (INT_PTR)p2->m_ID);
+}
+static OBJLIST<IconList> gIListMW(10, CompareIconListData);
+
+IconList::IconList(StatusIconData *sid) {
+ m_StatusIconData.cbSize = sid->cbSize;
+ m_StatusIconData.szModule = mir_strdup(sid->szModule);
+ m_StatusIconData.dwId = sid->dwId;
+ m_StatusIconData.hIcon = CopyIcon(sid->hIcon);
+ m_StatusIconData.hIconDisabled = sid->hIconDisabled;
+ m_StatusIconData.flags = sid->flags;
+ m_StatusIconData.szTooltip = mir_strdup(sid->szTooltip);
+
+ m_ID = sid->dwId;
+ CallService(MS_MSG_ADDICON,0,(LPARAM)sid);
+
+}
+IconList::~IconList() {
+ mir_free(m_StatusIconData.szModule);
+ mir_free(m_StatusIconData.szTooltip);
+}
+// const char *pszName; // [Optional] Name of an icon registered with icolib to be used in GUI.
+static __inline int MessageAPI_AddIcon(const char* pszName, const char* szModul/*StatusIconData *sid*/,int ID, int flags, const char* szTooltip)
+{
+ HICON hIcon = Skin_GetIcon(pszName);
+
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = (char*)szModul;
+ sid.dwId = (DWORD)ID;
+ sid.hIcon = (hIcon!=NULL)?CopyIcon(hIcon):NULL;
+ Skin_ReleaseIcon(hIcon); /* does NULL check */
+// sid.hIconDisabled = sid.hIcon/*NULL*/;
+ sid.flags = 0;
+ sid.szTooltip = Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,ID,0));
+
+ int res = -1;
+ IconList* p = new IconList(&sid);
+ if (!p->m_ID)delete p;
+ else res = gIListMW.insert(p);
+ if(res == -1)delete p;
+ return res;
+}
+
+void CALLBACK UpdateStatusIcons(LPARAM lParam) {
+ if (!lParam) {
+ /* enum all opened message windows */
+ for (int i = 0; i < gMsgWndList.getCount(); i++)
+ gMsgWndList[i]->FlagsIconUpdate();
+ }
+ else {
+ int i = gMsgWndList.getIndex((MsgWndData*)&lParam);
+ if (i!= -1) gMsgWndList[i]->FlagsIconUpdate();
+ }
+}
+
+//hookProc ME_MSG_WINDOWEVENT
+static int OnMsgWndEvent(WPARAM wParam,LPARAM lParam) {
+ MessageWindowEventData *msgwe=(MessageWindowEventData*)lParam;
+ /* sanity check */
+ if(msgwe->hContact==NULL)
+ return 0;
+
+ switch(msgwe->uType) {
+ case MSG_WINDOW_EVT_OPENING:
+ {
+ MsgWndData* msgwnd = gMsgWndList.find((MsgWndData*)&msgwe->hContact);
+ if(msgwnd == NULL) {
+ msgwnd = new MsgWndData(msgwe->hwndWindow, msgwe->hContact);
+ gMsgWndList.insert(msgwnd);
+ }
+ }
+ break;
+
+ case MSG_WINDOW_EVT_CLOSE:
+ {
+ int i = gMsgWndList.getIndex((MsgWndData*)&msgwe->hContact);
+ if (i != -1) {
+ delete gMsgWndList[i];
+ gMsgWndList.remove(i);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+//hookProc ME_SKIN2_ICONSCHANGED
+static int OnStatusIconsChanged(WPARAM wParam,LPARAM lParam) {
+ if(myGlobals.MsgAddIconExist && gFlagsOpts.bShowStatusIconFlag)
+ CallFunctionBuffered(UpdateStatusIcons,0,FALSE,STATUSICON_REFRESHDELAY);
+ return 0;
+}
+
+/***********************************************************************************************************
+ * option page (not used yet)
+ ***********************************************************************************************************/
+static INT_PTR CALLBACK ExtraImgOptDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ return FALSE;
+}
+
+//hookProc ME_OPT_INITIALISE
+static int ExtraImgOptInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ return 0;
+}
+
+/***********************************************************************************************************
+ * misc functions
+ ***********************************************************************************************************/
+//hookProc ME_DB_CONTACT_SETTINGCHANGED
+static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam) {
+ if ((HANDLE)wParam==NULL) return 0;
+ DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam;
+
+ /* user details update */
+ if (/*!lstrcmpA(dbcws->szSetting,"RealIP") || */
+ !lstrcmpA(dbcws->szSetting,SET_CONTACT_COUNTRY) ||
+ !lstrcmpA(dbcws->szSetting,SET_CONTACT_ORIGIN_COUNTRY) ||
+ !lstrcmpA(dbcws->szSetting,SET_CONTACT_COMPANY_COUNTRY))
+ {
+ /* Extra Image */
+ if(myGlobals.HaveCListExtraIcons)
+ CallFunctionBuffered(SetExtraImage,(LPARAM)wParam,TRUE,EXTRAIMAGE_REFRESHDELAY);
+ /* Status Icon */
+ if(hMsgWndEventHook) {
+ int i = gMsgWndList.getIndex((MsgWndData*)&wParam);
+ if (i != -1) {
+ gMsgWndList[i]->ContryIDchange((int)ServiceDetectContactOriginCountry(wParam,0));
+ gMsgWndList[i]->FlagsIconUpdate();
+ }
+ }
+ }
+ return 0;
+}
+
+
+/***********************************************************************************************************
+ * module loading & unloading
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads all required stuff for Flags.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+void SvcFlagsLoadModule()
+{
+ PrepareBufferedFunctions();
+ InitCountryListExt(); /* hack to replace core country list */
+ if(CallService(MS_UTILS_GETCOUNTRYLIST,(WPARAM)&nCountriesCount,(LPARAM)&countries))
+ nCountriesCount=0;
+ InitIcons(); /* load in iconlib */
+ //InitIpToCountry(); /* not implementet */
+ CreateServiceFunction(MS_FLAGS_DETECTCONTACTORIGINCOUNTRY,ServiceDetectContactOriginCountry);
+ //init settings
+ gFlagsOpts.bShowExtraImgFlag = DB::Setting::GetByte(MODNAMEFLAGS,"ShowExtraImgFlag", SETTING_SHOWEXTRAIMGFLAG_DEFAULT);
+ gFlagsOpts.bUseUnknownFlag = DB::Setting::GetByte(MODNAMEFLAGS,"UseUnknownFlag", SETTING_USEUNKNOWNFLAG_DEFAULT);
+ gFlagsOpts.idExtraColumn = DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn", SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+ gFlagsOpts.bShowStatusIconFlag = DB::Setting::GetByte(MODNAMEFLAGS,"ShowStatusIconFlag", SETTING_SHOWSTATUSICONFLAG_DEFAULT);
+
+ hOptInitHook = HookEvent(ME_OPT_INITIALISE,ExtraImgOptInit);
+ hIconsChangedHook = HookEvent(ME_SKIN2_ICONSCHANGED,OnStatusIconsChanged);
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+void SvcFlagsOnModulesLoaded() {
+ //use ExtraIconsService ?
+ if ( myGlobals.ExtraIconsServiceExist) {
+ SvcFlagsEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_FLAGS2, 0), FALSE);
+ }
+ //use Clist ExtraImageService
+ else {
+ SvcFlagsEnableExtraIcons(gFlagsOpts.bShowExtraImgFlag? gFlagsOpts.idExtraColumn : -1, FALSE);
+ }
+ /* Status Icon */
+ if(myGlobals.MsgAddIconExist)
+ hMsgWndEventHook = HookEvent(ME_MSG_WINDOWEVENT, OnMsgWndEvent);
+}
+
+/**
+ * This function unloads the module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+void SvcFlagsUnloadModule() {
+ KillBufferedFunctions();
+ //Uninit ExtraImg
+ UnhookEvent(hRebuildIconsHook);
+ UnhookEvent(hApplyIconHook);
+ UnhookEvent(hIconsChangedHook);
+ UnhookEvent(hExtraIconSvcHook);
+ mir_free(phExtraImageList); /* does NULL check */
+ //Uninit message winsow
+ UnhookEvent(hMsgWndEventHook);
+ for(int i = 0; i < gMsgWndList.getCount(); i++) {
+ //this should not happen
+ delete gMsgWndList[i];
+ gMsgWndList.remove(i);
+ }
+ gMsgWndList.destroy();
+ gIListMW.destroy();
+ //Uninit misc
+ UnhookEvent(hSettingChangedHook);
+ UnhookEvent(hOptInitHook);
+ UninitIcons();
+
+ //UninitIpToCountry(); /* not implementet */
+}
+
diff --git a/plugins/UserInfoEx/src/Flags/svc_flags.h b/plugins/UserInfoEx/src/Flags/svc_flags.h
new file mode 100644
index 0000000000..e7f97ab963
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flags.h
@@ -0,0 +1,102 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flags.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_FLAGS_H_INCLUDED_
+#define _UINFOEX_FLAGS_H_INCLUDED_
+
+#define EXTRAIMAGE_REFRESHDELAY 100 /* time for which setting changes are buffered */
+#define STATUSICON_REFRESHDELAY 100 /* time for which setting changes are buffered */
+
+typedef struct _FLAGSOPTIONS
+{
+ BYTE bShowExtraImgFlag;
+ BYTE bUseUnknownFlag;
+ BYTE idExtraColumn;
+// BYTE bUseIpToCountry;
+ BYTE bShowStatusIconFlag;
+} FLAGSOPTIONS, *LPFLAGSOPTIONS;
+
+extern int nCountriesCount;
+extern struct CountryListEntry *countries;
+extern FLAGSOPTIONS gFlagsOpts;
+
+class MsgWndData {
+ public:
+ HANDLE m_hContact;
+ HWND m_hwnd;
+ int m_contryID;
+
+ MsgWndData(HWND hwnd, HANDLE hContact);
+ ~MsgWndData();
+
+ void FlagsIconSet();
+ void FlagsIconUnset();
+ void FlagsIconUpdate() {
+ gFlagsOpts.bShowStatusIconFlag ? FlagsIconSet():FlagsIconUnset();
+ };
+ void ContryIDchange(int ID) {
+ m_contryID = ID; FlagsIconUpdate();
+ };
+};
+
+class IconList {
+ public:
+ int m_ID;
+ HANDLE m_hIcon; //register
+ HANDLE m_hImage; //return value from MS_CLIST_EXTRA_ADD_ICON -INVALID_HANDLE_VALUE; //preset
+ BYTE m_TypeFlag;
+ StatusIconData m_StatusIconData;
+
+ IconList(StatusIconData* sid);
+// IconList(HWND hwnd, HANDLE hContact);
+ ~IconList();
+
+};
+
+typedef void (CALLBACK *BUFFEREDPROC)(LPARAM lParam);
+#ifdef _DEBUG
+ void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,const char *pszProcName,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse);
+ #define CallFunctionBuffered(proc,param,acc,elapse) _CallFunctionBuffered(proc,#proc,param,acc,elapse)
+#else
+ void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse);
+ #define CallFunctionBuffered(proc,param,acc,elapse) _CallFunctionBuffered(proc,param,acc,elapse)
+#endif
+
+void EnsureExtraImages();
+VOID SvcFlagsEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB);
+void CALLBACK UpdateStatusIcons(LPARAM lParam);
+
+void SvcFlagsLoadModule();
+void SvcFlagsOnModulesLoaded();
+void SvcFlagsUnloadModule();
+
+#endif /* _UINFOEX_FLAGS_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp b/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp
new file mode 100644
index 0000000000..d7119cd75e
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp
@@ -0,0 +1,456 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flagsicons.cpp $
+Revision : $Revision: 206 $
+Last change on : $Date: 2010-09-28 18:43:50 +0400 (Вт, 28 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_flags.h"
+#include "svc_flagsicons.h"
+
+/************************* Bitmap Access **************************/
+
+static HANDLE *phIconHandles = NULL;
+
+static int CountryNumberToBitmapIndex(int countryNumber)
+{
+ /* country number indices (same order as in flags.bmp) */
+ const int BitmapIndexMap[239]={
+ 0, 1, 7, 20, 27, 30, 31, 32, 33, 34,
+ 36, 38, 39, 40, 41, 43, 44, 45, 46, 47,
+ 48, 49, 51, 52, 53, 54, 55, 56, 57, 58,
+ 60, 61, 62, 63, 64, 65, 66, 81, 82, 84,
+ 86, 90, 91, 92, 93, 94, 95, 98, 212, 213,
+ 216, 218, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 260, 261, 263, 264, 265, 266, 267, 268, 269,
+ 290, 291, 297, 298, 299, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 370, 371, 372, 373, 374,
+ 375, 376, 377, 378, 379, 380, 381, 382, 383, 385,
+ 386, 387, 389, 420, 421, 423, 441, 442, 500, 501,
+ 502, 503, 504, 505, 506, 507, 508, 509, 591, 592,
+ 593, 595, 597, 598, 599, 614, 670, 673, 674, 675,
+ 676, 677, 678, 679, 680, 681, 682, 683, 685, 686,
+ 688, 689, 690, 691, 692, 705, 850, 852, 853, 855,
+ 856, 880, 886, 960, 961, 962, 963, 964, 965, 966,
+ 967, 968, 971, 972, 973, 974, 975, 976, 977, 992,
+ 993, 994, 995, 996, 998, 1002, 1242, 1246, 1264, 1268,
+ 1284, 1340, 1345, 1441, 1473, 1649, 1664, 1670, 1671, 1684,
+ 1758, 1767, 1784, 1809, 1868, 1869, 1876, 1939, 2897, 3492,
+ 3883, 5995, 5999, 6720, 6723, 9999, 61891, 65535, 90392
+ };
+ /* shared flags by multiple countries */
+ switch(countryNumber) {
+ case 9999: /* Other */
+ case 65535: /* 0xFFFF,"Unknown" */
+ countryNumber=0; /* Unspecified */
+ break;
+ case 262: /* Reunion Island */
+ case 594: /* French Guiana */
+ case 5901: /* remove French Antilles */
+ countryNumber=33; /* France */
+ break;
+ case 120: /* remove Barbuda */
+ countryNumber=1268; /* Antigua and Barbuda */
+ break;
+ case 6702: /* removed Tinian Island */
+ case 6701: /* removed Rota Island */
+ case 670: /* removed Saipan Island */
+ countryNumber=1670; /* Northern Mariana Islands, US Territory of*/
+ break;
+ case 115: /* removed Saint Kitts */
+ case 114: /* removed Nevis */
+ countryNumber=1869; /* Saint Kitts and Nevis */
+ break;
+ case 247: /* Ascension Island */
+ countryNumber=44; /* United Kingdom */
+ break;
+ case 6720: /* Australian Antarctic Territory */
+ countryNumber=61; /* Australia */
+ break;
+ case 5997: /* Netherlands (Bonaire Island)*/
+ case 59946: /* Netherlands (Saba Island) */
+ case 59938: /* Netherlands (St. Eustatius Island) */
+ countryNumber=599; /* Netherlands Antilles (dissolved 2010) */
+ //countryNumber=31; /* Netherlands */
+ break;
+ case 5995: /* St. Maarten (new country in 2010 (from Netherlands Antilles) new country in 2010 (from Netherlands Antilles)) */
+ countryNumber=599; /* Netherlands Antilles (dissolved 2010) */
+ break;
+ case 5999: /* Curacao (new country in 2010 (from Netherlands Antilles) new country in 2010 (from Netherlands Antilles)) */
+ countryNumber=599; /* Netherlands Antilles (dissolved 2010) */
+ break;
+ case 5399: /* missing Guantanamo Bay */
+ countryNumber=1; /* USA */
+ }
+ /* binary search in index array */
+ if (countryNumber > 0) {
+ int i,high/*,low=0*/;
+ high=SIZEOF(BitmapIndexMap);
+ /*old code need sortet BitmapIndexMap*/
+ /*if(countryNumber<=BitmapIndexMap[high])
+ while(low<=high) {
+ i=low+((high-low)/2);
+ // never happens
+ if(i<0 || i>=SIZEOF(BitmapIndexMap)) DebugBreak();
+ if(BitmapIndexMap[i]==countryNumber) return i;
+ if(countryNumber>BitmapIndexMap[i]) low=i+1;
+ else high=i-1;
+ }*/
+ for ( i=0; i < high; i++ ) {
+ if(BitmapIndexMap[i]==countryNumber) return i;
+ }
+ }
+ /* Other,Unknown,Unspecified */
+ return 0;
+}
+
+/************************* Utils **********************************/
+
+HICON LoadFlag(int countryNumber)
+{
+ char szId[20],*szCountry;
+ /* create identifier */
+ szCountry=(char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,countryNumber,0);
+ if(szCountry == NULL)
+ szCountry=(char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,countryNumber=0xFFFF,0);
+
+ wsprintfA(szId,(countryNumber==0xFFFF)?"%s_0x%X":"%s_%i","flags",countryNumber); /* buffer safe */
+ return Skin_GetIcon(szId);
+}
+
+int CountryNumberToIndex(int countryNumber)
+{
+ int i,nf=0;
+ for(i=0;i<nCountriesCount;++i) {
+ if(countries[i].id==countryNumber) return i;
+ if(countries[i].id==0xFFFF) nf=i;
+ }
+ return nf; /* Unknown */
+}
+
+FIBITMAP* ConvertTo(FIBITMAP* dib, UINT destBits, bool greyscale)
+{
+ FIBITMAP* dib_res = NULL;
+ switch (destBits) {
+ case 8:
+ // convert to 8Bits
+ if(greyscale) {
+ dib_res = FIP->FI_ConvertTo8Bits(dib);
+ }
+ else {
+ FIBITMAP* dib_tmp = FIP->FI_ConvertTo24Bits(dib);
+ if(dib_tmp) {
+ dib_res = FIP->FI_ColorQuantize(dib_tmp, FIQ_WUQUANT/*FIQ_NNQUANT*/);
+ FIP->FI_Unload(dib_tmp);
+ }
+ } break;
+ case 16:
+ // convert to 16Bits
+ dib_res = FIP->FI_ConvertTo16Bits555(dib);
+ break;
+ case 24:
+ // convert to 24Bits
+ dib_res = FIP->FI_ConvertTo24Bits(dib);
+ break;
+ case 32:
+ // convert to 32Bits
+ dib_res = FIP->FI_ConvertTo32Bits(dib);
+ break;
+ default:
+ break;
+ }
+ FIP->FI_Unload(dib);
+ return dib_res;
+}
+
+FIBITMAP* LoadResource(UINT ID, LPTSTR lpType)
+{
+ FIBITMAP *dib = NULL;
+ if(lpType) {
+ HRSRC hResInfo = FindResource(ghInst,MAKEINTRESOURCE(ID),lpType);
+ DWORD ResSize = SizeofResource(ghInst,hResInfo);
+ HGLOBAL hRes = LoadResource(ghInst,hResInfo);
+ BYTE* buffer = (BYTE*)LockResource(hRes);
+ if (buffer)
+ {
+ // attach the binary data to a memory stream
+ FIMEMORY *hmem = FIP->FI_OpenMemory(buffer, ResSize);
+ // get the file type
+ FREE_IMAGE_FORMAT fif = FIP->FI_GetFileTypeFromMemory(hmem, 0);
+ // load an image from the memory stream
+ dib = FIP->FI_LoadFromMemory(fif, hmem, 0);
+ // always close the memory stream
+ FIP->FI_CloseMemory(hmem);
+
+ UnlockResource(buffer);
+ }
+ FreeResource(hRes);
+ }
+ else {
+ HBITMAP hScrBM = 0;
+ if(NULL == (hScrBM = (HBITMAP)LoadImage(ghInst,MAKEINTRESOURCE(ID), IMAGE_BITMAP, 0, 0,LR_SHARED)))
+ return dib;
+ dib = FIP->FI_CreateDIBFromHBITMAP(hScrBM);
+ DeleteObject(hScrBM);
+ }
+ return dib;
+}
+
+/************************* Services *******************************/
+
+static INT_PTR ServiceLoadFlagIcon(WPARAM wParam,LPARAM lParam)
+{
+ /* return handle */
+ if ((BOOL)lParam) {
+ if(phIconHandles==NULL) return NULL;
+ return (INT_PTR)phIconHandles[CountryNumberToIndex((int)wParam)];
+ }
+ /* return icon */
+ return (INT_PTR)LoadFlag(wParam);
+}
+
+static INT_PTR ServiceCreateMergedFlagIcon(WPARAM wParam,LPARAM lParam)
+{
+ //TODO: use freeimage to create merget icon and add RGB(A) support
+ HICON hUpperIcon,hLowerIcon;
+ ICONINFO icoi;
+ BITMAP bm;
+ HDC hdc;
+ POINT aptTriangle[3];
+ HICON hIcon=NULL;
+ HRGN hrgn;
+ HBITMAP hbmPrev;
+ /* load both icons */
+ if(NULL == (hLowerIcon=(HICON)ServiceLoadFlagIcon((WPARAM)lParam,0))) return NULL;
+ hUpperIcon=(HICON)ServiceLoadFlagIcon(wParam,0);
+ /* merge them */
+ if(GetIconInfo(hLowerIcon,&icoi)) {
+ if(hUpperIcon!=NULL && GetObject(icoi.hbmColor,sizeof(bm),&bm)) {
+ hdc=CreateCompatibleDC(NULL);
+ if(hdc!=NULL) {
+ ZeroMemory(&aptTriangle,sizeof(aptTriangle));
+ aptTriangle[1].y=bm.bmHeight-1;
+ aptTriangle[2].x=bm.bmWidth-1;
+ hrgn=CreatePolygonRgn(aptTriangle,SIZEOF(aptTriangle),WINDING);
+ if(hrgn!=NULL) {
+ SelectClipRgn(hdc,hrgn);
+ DeleteObject(hrgn);
+ hbmPrev=(HBITMAP)SelectObject(hdc,icoi.hbmColor);
+ if(hbmPrev!=NULL) { /* error on select? */
+ if(DrawIconEx(hdc,0,0,hUpperIcon,bm.bmWidth,bm.bmHeight,0,NULL,DI_NOMIRROR|DI_IMAGE)) {
+ if(SelectObject(hdc,icoi.hbmMask)!=NULL) /* error on select? */
+ DrawIconEx(hdc,0,0,hUpperIcon,bm.bmWidth,bm.bmHeight,0,NULL,DI_NOMIRROR|DI_MASK);
+ }
+ SelectObject(hdc,hbmPrev);
+ }
+ DeleteObject(hrgn);
+ }
+ DeleteDC(hdc);
+ }
+ }
+ /* create icon */
+ hIcon=CreateIconIndirect(&icoi);
+ DeleteObject(icoi.hbmColor);
+ DeleteObject(icoi.hbmMask);
+ }
+ return (INT_PTR)hIcon;
+}
+
+/************************* Misc ***********************************/
+
+VOID InitIcons()
+{
+ HIMAGELIST himl = {0};
+ HBITMAP hScrBM = 0;
+ HBITMAP hbmMask = 0;
+ FIBITMAP *dib = NULL,
+ *dib_ico = NULL;
+ UINT bitDest = ILC_COLOR8,
+ bit = 24;
+
+// BOOL res;
+// FREE_IMAGE_FORMAT fif = FIP->FI_GetFIFFromFilename("dummy.bmp");
+
+ /* all those flag icons storing in a large 24bit opaque bitmap to reduce file size */
+ if(NULL == (dib = LoadResource(IDB_FLAGSPNG,_T("PNG"))))
+ return;
+
+ if (IsWinVerXPPlus()) bitDest = bit = ILC_COLOR32;
+ else if (IsWinVer2000Plus()) bitDest = ILC_COLOR24;
+ else if (IsWinVerMEPlus()) bitDest = ILC_COLOR16;
+ /*else preset bitDest = ILC_COLOR8;*/
+
+ //we work always with 24bit if bitDest < xp+
+ //coz we cannot perform paste operations between palettized images,
+ //unless both src and dst images use the same palette.
+
+ if (FIP->FI_GetBPP(dib) != bit)
+ if (NULL == (dib = ConvertTo(dib, bit, 0))) return;
+
+ //create new dib
+ if (NULL == (dib_ico = FIP->FI_Allocate(FIP->FI_GetWidth(dib), 16, bit,0,0,0))) {
+ FIP->FI_Unload(dib);
+ return;
+ }
+
+// res = FIP->FI_IsTransparent(dib_ico);
+ if(bit<32) {
+ //disable transparency
+ FIP->FI_SetTransparent(dib, FALSE);
+ FIP->FI_SetTransparent(dib_ico, FALSE);
+ }
+
+ UINT h = FIP->FI_GetHeight(dib_ico);
+ UINT w = FIP->FI_GetWidth(dib_ico);
+ UINT t = ((h - FIP->FI_GetHeight(dib))/2)+1;
+ UINT b = t + FIP->FI_GetHeight(dib);
+
+ //copy dib to new dib_ico (centered)
+ if(FIP->FI_Paste(dib_ico, dib, 0, t-1, 255+1)) {
+ FIP->FI_Unload(dib); dib = NULL;
+ switch (bitDest) {
+ case 8:
+ case 16:
+ case 24:
+ {//transparency by 1bit monocrome icon mask (for bit < 32)
+ BYTE value;
+ FIBITMAP *dib_mask;
+ if(NULL == (dib_mask = FIP->FI_Allocate(w, h, 1, 0, 0, 0))) {
+ FIP->FI_Unload(dib_ico);
+ return;
+ }
+ for(unsigned y = 0; y < h; y++)
+ for(unsigned x = 0; x < w; x++) {
+ value = ((y<t || y>=b)? 0:1);
+ FIP->FI_SetPixelIndex(dib_mask, x, y, &value);
+ }
+ hbmMask = FIP->FI_CreateHBITMAPFromDIB(dib_mask);
+ FIP->FI_Unload(dib_mask);
+
+ //convert to target resolution
+ if (!hbmMask || !(dib_ico = ConvertTo(dib_ico, bitDest, 0))) {
+ FIP->FI_Unload(dib_ico);
+ if(hbmMask) DeleteObject(hbmMask);
+ return;
+ }
+ } break;
+ case 32:
+ {//transparency by alpha schannel
+ //Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+ int bytespp = FIP->FI_GetLine(dib_ico) / w;
+ //set alpha schannel
+ for(unsigned y = 0; y < h; y++) {
+ BYTE *bits = FIP->FI_GetScanLine(dib_ico, y);
+ for(unsigned x = 0; x < w; x++) {
+ bits[FI_RGBA_ALPHA] = (y<t || y>=b)? 0:255;
+ // jump to next pixel
+ bits += bytespp;
+ }
+ }
+ } break;
+ default:
+ FIP->FI_Unload(dib_ico);
+ return;
+ }
+ //FIP->FI_SaveU(fif,dib_ico,_T("d:\\CD-Archiv\\Miranda\\trunk\\miranda\\bin9\\Debug Unicode\\Plugins\\dib_ico.bmp"),0);
+ }
+ else {
+ FIP->FI_Unload(dib);
+ FIP->FI_Unload(dib_ico);
+ return;
+ }
+
+ hScrBM = FIP->FI_CreateHBITMAPFromDIB(dib_ico);
+ FIP->FI_Unload(dib_ico);
+
+ if (!hScrBM) {
+ DeleteObject(hbmMask);
+ return;
+ }
+
+ //create ImageList
+ himl = ImageList_Create(16, 16 , bitDest | ILC_MASK, 0, nCountriesCount);
+ ImageList_Add(himl, hScrBM, hbmMask);
+ DeleteObject(hScrBM); hScrBM = NULL;
+ DeleteObject(hbmMask); hbmMask = NULL;
+
+ if(himl!=NULL) {
+ phIconHandles=(HANDLE*)mir_alloc(nCountriesCount*sizeof(HANDLE));
+ if(phIconHandles!=NULL) {
+ char szId[20];
+ int i,index;
+ SKINICONDESC skid;
+
+ /* register icons */
+ ZeroMemory(&skid,sizeof(skid));
+ skid.cbSize = sizeof(skid);
+ skid.ptszSection = LPGENT("Country Flags");
+ skid.pszName = szId; // name to refer to icon when playing and in db
+ //skid.pszDefaultFile = NULL; // default icon file to use
+ //skid.iDefaultIndex = NULL; // index of icon in default file
+ skid.cx = GetSystemMetrics(SM_CXSMICON);
+ skid.cy = GetSystemMetrics(SM_CYSMICON);
+ skid.flags = SIDF_SORTED|SIDF_TCHAR;
+
+ for(i=0;i<nCountriesCount;++i) {
+ skid.ptszDescription = mir_a2t(LPGEN(countries[i].szName));
+ /* create identifier */
+ wsprintfA(szId,(countries[i].id==0xFFFF)?"%s0x%X":"%s%i","flags_",countries[i].id); /* buffer safe */
+ index = CountryNumberToBitmapIndex(countries[i].id);
+ /* create icon */
+ skid.hDefaultIcon=ImageList_ExtractIcon(NULL, himl, index);
+ index = CountryNumberToIndex(countries[i].id);
+
+ phIconHandles[index] = Skin_AddIcon(&skid);
+ if(skid.hDefaultIcon!=NULL) DestroyIcon(skid.hDefaultIcon);
+ mir_free(skid.ptszDescription); skid.ptszDescription = NULL;
+ }
+ }
+ ImageList_Destroy(himl);
+ }
+
+ /* create services */
+ CreateServiceFunction(MS_FLAGS_LOADFLAGICON,ServiceLoadFlagIcon);
+ CreateServiceFunction(MS_FLAGS_CREATEMERGEDFLAGICON,ServiceCreateMergedFlagIcon);
+}
+
+VOID UninitIcons()
+{
+ for(int i=0;i<nCountriesCount;++i) {
+ /* create identifier */
+ char szId[20];
+ wsprintfA(szId,(countries[i].id==0xFFFF)?"%s0x%X":"%s%i","flags_",countries[i].id); /* buffer safe */
+ Skin_RemoveIcon(szId);
+ }
+ mir_free(phIconHandles); /* does NULL check */
+}
diff --git a/plugins/UserInfoEx/src/Flags/svc_flagsicons.h b/plugins/UserInfoEx/src/Flags/svc_flagsicons.h
new file mode 100644
index 0000000000..7f29330a11
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flagsicons.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright 2006-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flagsicons.h $
+Revision : $Revision: 204 $
+Last change on : $Date: 2010-09-28 16:48:38 +0400 (Вт, 28 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_FLAGSICONS_H_INCLUDED_
+#define _UINFOEX_FLAGSICONS_H_INCLUDED_
+
+int CountryNumberToIndex(int countryNumber);
+HICON LoadFlag(int countryNumber);
+
+VOID InitIcons();
+//VOID SvcFlagsOnModulesLoaded();
+VOID UninitIcons();
+
+#endif /* _UINFOEX_FLAGSICONS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/classMAnnivDate.cpp b/plugins/UserInfoEx/src/classMAnnivDate.cpp
new file mode 100644
index 0000000000..fb487f61d2
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMAnnivDate.cpp
@@ -0,0 +1,836 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMAnnivDate.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_Reminder.h"
+
+/**
+ * name: MAnnivDate
+ * class: MAnnivDate
+ * desc: default constructor
+ * param: none
+ * return: nothing
+ **/
+MAnnivDate::MAnnivDate()
+{
+ Clear();
+}
+
+/**
+ * name: MAnnivDate
+ * class: MAnnivDate
+ * desc: constructor, which duplicates an existing anniversary date
+ * param: mda - anniversary date to copy
+ * return: nothing
+ **/
+MAnnivDate::MAnnivDate(MAnnivDate &mda)
+{
+ SetDate(mda);
+}
+
+/**
+ * name: Clear
+ * class: MAnnivDate
+ * desc: set all attributes to default value
+ * param: none
+ * return: nothing
+ **/
+VOID MAnnivDate::Clear()
+{
+ ZeroDate();
+ _wID = ANID_NONE;
+ _strDesc.clear();
+ _strModule.clear();
+ _wFlags = MADF_NONE;
+ _bRemind = BST_INDETERMINATE;
+ _wDaysEarlier = (WORD)-1;
+}
+
+/**
+ * name: Set
+ * class: MAnnivDate
+ * desc: set new date
+ * param: none
+ * return: nothing
+ **/
+VOID MAnnivDate::SetDate(SYSTEMTIME &st)
+{
+ ZeroDate();
+ Year(st.wYear);
+ Month(st.wMonth);
+ Day(st.wDay);
+}
+
+/**
+ * name: Set
+ * class: MAnnivDate
+ * desc: duplicates the given anniversary date class
+ * param: none
+ * return: nothing
+ **/
+VOID MAnnivDate::SetDate(MAnnivDate &mda)
+{
+ SetDate(mda.SystemTime());
+ _wID = mda.Id();
+ _strDesc = mda.Description();
+ _strModule = mda.Module();
+ _wFlags = mda.Flags();
+ _bRemind = mda.RemindOption();
+ _wDaysEarlier = mda.RemindOffset();
+}
+
+/**
+ * name: IsValid
+ * class: MAnnivDate
+ * desc: compare the current date with the given one in st
+ * param: st - SYSTEMTIME to compare with
+ * return: number of days the st differs from the class value
+ **/
+__inline BOOLEAN MAnnivDate::IsValid() const
+{
+ return (
+ Year() > 1600 &&
+ Month() > 0 && Month() < 13 &&
+ Day() > 0 && Day() <= DaysInMonth(Month())
+ );
+}
+
+/**
+ * name: CompareDays
+ * class: MAnnivDate
+ * desc: compare the current date with the given one in st
+ * param: mt - MTime to compare with
+ * return: number of days the mt differs from the class value
+ **/
+INT MAnnivDate::CompareDays(MTime mt) const
+{
+ mt.Year(Year());
+ return DayOfYear() - mt.DayOfYear();
+}
+
+/**
+ * name: Compare
+ * class: MAnnivDate
+ * desc: compare the current date with the given one in st
+ * param: st - SYSTEMTIME to compare with
+ * return: number of days the st differs from the class value
+ **/
+BOOLEAN MAnnivDate::IsEqual(const SYSTEMTIME &st) const
+{
+ return (
+ Day() == st.wDay &&
+ Month() == st.wMonth &&
+ Year() == st.wYear
+ );
+}
+
+/**
+ * name: DateStamp
+ * class: MAnnivDate
+ * desc: return the current date encoded as an DWORD
+ * param: nothing
+ * return: DWORD encoded date
+ **/
+DWORD MAnnivDate::DateStamp() const
+{
+ DWORD dwStamp;
+
+ if (!IsValid()) return 0;
+
+ dwStamp = (Day() << 24) & 0xFF000000;
+ dwStamp |= (Month() << 16) & 0x00FF0000;
+ dwStamp |= Year() & 0x0000FFFF;
+ return dwStamp;
+}
+
+/**
+ * name: DateStamp
+ * class: MAnnivDate
+ * desc: set the date according to a datestamp
+ * param: dwStamp - the dword encoded date
+ * return: nothing
+ **/
+VOID MAnnivDate::DateStamp(const DWORD dwStamp)
+{
+ Day((const WORD)((dwStamp & 0xFF000000) >> 24));
+ Month((const WORD)((dwStamp & 0x00FF0000) >> 16));
+ Year((const WORD)(dwStamp & 0x0000FFFF));
+}
+
+/**
+ * name: Age
+ * class: MAnnivDate
+ * desc: calculates the age according to the date of the class and current date
+ * param: pNow - optional pointer to a MTime class to specify a certain time to use for age calculation
+ * return: number of years the anniversary differs from now
+ **/
+INT MAnnivDate::Age(MTime *pNow)
+{
+ INT age = 0;
+ MTime now;
+
+ if (!IsValid()) return -1;
+
+ if (pNow) now = *pNow;
+ else now.GetLocalTime();
+
+ age = now.Year() - Year();
+ if (age > 1 && CompareDays(now) > 0)
+ age--;
+ return age;
+}
+
+/**
+ * name: Zodiac
+ * class: MAnnivDate
+ * desc: returns the zodiac icon and text for the date
+ * param: none
+ * return: structure, holding zodiac information
+ **/
+MZodiac MAnnivDate::Zodiac()
+{
+ static const struct TZodiac {
+ const WORD startDays;
+ const WORD endDays;
+ LPCTSTR szZodiac;
+ LPCSTR szZodiacIcon;
+ } zodiac[] = {
+ { 80, 110, LPGENT("Aries") , ICO_ZOD_ARIES }, // Widder
+ { 111, 140, LPGENT("Taurus") , ICO_ZOD_TAURUS }, // Stier
+ { 141, 172, LPGENT("Gemini") , ICO_ZOD_GEMINI }, // Zwillinge
+ { 173, 203, LPGENT("Cancer") , ICO_ZOD_CANCER }, // Krebs
+ { 204, 235, LPGENT("Leo") , ICO_ZOD_LEO }, // Lwe
+ { 236, 266, LPGENT("Virgo") , ICO_ZOD_VIRGO }, // Jungfrau
+ { 267, 296, LPGENT("Libra") , ICO_ZOD_LIBRA }, // Waage
+ { 297, 326, LPGENT("Scorpio") , ICO_ZOD_SCORPIO }, // Scorpion
+ { 327, 355, LPGENT("Sagittarius") , ICO_ZOD_SAGITTARIUS }, // Schtze
+ { 356, 364, LPGENT("Capricorn") , ICO_ZOD_CAPRICORN }, // Steinbock
+ { 1, 19, LPGENT("Capricorn") , ICO_ZOD_CAPRICORN }, // Steinbock
+ { 20, 49, LPGENT("Aquarius") , ICO_ZOD_AQUARIUS }, // Wassermann
+ { 50, 79, LPGENT("Pisces") , ICO_ZOD_PISCES }, // Fische
+ //{ 0, 0, LPGENT("Unknown") , ICO_ZOD_UNKNOWN }, // not found
+ { 0, 0, NULL , "" } // end of array
+ };
+ const WORD wDays = DayOfYear();
+ BYTE i;
+ MZodiac mZodiac;
+
+ for (i = 0; i < 13 && (wDays < zodiac[i].startDays || wDays > zodiac[i].endDays); i++);
+
+ mZodiac.hIcon = IcoLib_GetIcon(zodiac[i].szZodiacIcon);
+ mZodiac.pszName = zodiac[i].szZodiac;
+ return mZodiac;
+}
+
+/***********************************************************************************************************
+ * reading and writing options
+ ***********************************************************************************************************/
+
+/**
+ * name: DBGetReminderOpts
+ * class: MAnnivDate
+ * desc: read reminder options for previously read date from database
+ * param: hContact - handle to a contact to read the date from
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetReminderOpts(HANDLE hContact)
+{
+ if (!hContact || hContact == INVALID_HANDLE_VALUE)
+ return 1;
+ if (_wID == ANID_BIRTHDAY) {
+ _bRemind = DB::Setting::GetByte(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED, BST_INDETERMINATE);
+ _wDaysEarlier = DB::Setting::GetWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET, (WORD)-1);
+ }
+ else if (_wID <= ANID_LAST) {
+ CHAR pszSetting[MAXSETTING];
+
+ // read reminder option
+ mir_snprintf(pszSetting, MAXSETTING, "Anniv%dReminder", _wID);
+ _bRemind = DB::Setting::GetByte(hContact, Module(), pszSetting, BST_INDETERMINATE);
+ // read offset
+ mir_snprintf(pszSetting, MAXSETTING, "Anniv%dOffset", _wID);
+ _wDaysEarlier = DB::Setting::GetWord(hContact, Module(), pszSetting, (WORD)-1);
+ }
+ else {
+ _bRemind = BST_INDETERMINATE;
+ _wDaysEarlier = (WORD)-1;
+ }
+ return 0;
+}
+
+/**
+ * name: DBWriteReminderOpts
+ * class: MAnnivDate
+ * desc: write reminder options for date to database
+ * param: hContact - handle to a contact to read the date from
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteReminderOpts(HANDLE hContact)
+{
+ if (!hContact || hContact == INVALID_HANDLE_VALUE)
+ return 1;
+ if (_wID == ANID_BIRTHDAY) {
+ if (_bRemind == BST_INDETERMINATE) DB::Setting::Delete(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED);
+ else DB::Setting::WriteByte(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED, _bRemind);
+
+ if (_wDaysEarlier == (WORD)-1) DB::Setting::Delete(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET);
+ else DB::Setting::WriteWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET, _wDaysEarlier);
+ }
+ else if (_wID <= ANID_LAST) {
+ CHAR pszSetting[MAXSETTING];
+ // read reminder option
+ mir_snprintf(pszSetting, MAXSETTING, "Anniv%dReminder", _wID);
+ if (_bRemind == BST_INDETERMINATE) DB::Setting::Delete(hContact, USERINFO, pszSetting);
+ else DB::Setting::WriteByte(hContact, USERINFO, pszSetting, _bRemind);
+
+ // read offset
+ mir_snprintf(pszSetting, MAXSETTING, "Anniv%dOffset", _wID);
+ if (_wDaysEarlier == (WORD)-1) DB::Setting::Delete(hContact, USERINFO, pszSetting);
+ else DB::Setting::WriteWord(hContact, USERINFO, pszSetting, _wDaysEarlier);
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting general date
+ ***********************************************************************************************************/
+
+/**
+ * name: DBGetDate
+ * class: MAnnivDate
+ * desc: read a certain date from database
+ * param: hContact - handle to a contact to read the date from
+ * pszModule - module holding the date
+ * szDay - setting of the day to read
+ * szMonth - setting of the month to read
+ * szYear - setting of the year to read
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear)
+{
+ WORD wtmp;
+
+ ZeroDate();
+
+ wtmp = DB::Setting::GetWord(hContact, pszModule, szYear, 0);
+ if (wtmp < 1601) return 1;
+ Year(wtmp);
+
+ wtmp = DB::Setting::GetWord(hContact, pszModule, szMonth, 0);
+ if (wtmp > 0 && wtmp < 13) {
+ Month(wtmp);
+
+ wtmp = DB::Setting::GetWord(hContact, pszModule, szDay, 0);
+ if (wtmp > 0 && wtmp <= DaysInMonth(Month())) {
+ Day(wtmp);
+ // date was correctly read from db
+ _strModule = pszModule;
+ return 0;
+ }
+ }
+ ZeroDate();
+ return 1;
+}
+
+/**
+ * name: DBWriteDate
+ * class: MAnnivDate
+ * desc: write a certain date from database
+ * param: hContact - handle to a contact to read the date from
+ * pszModule - module holding the date
+ * szDay - setting of the day to read
+ * szMonth - setting of the month to read
+ * szYear - setting of the year to read
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear)
+{
+ return (
+ DB::Setting::WriteByte(hContact, pszModule, szDay, (BYTE)Day()) ||
+ DB::Setting::WriteByte(hContact, pszModule, szMonth, (BYTE)Month()) ||
+ DB::Setting::WriteWord(hContact, pszModule, szYear, Year())
+);
+}
+
+/**
+ * name: DBDeleteDate
+ * class: MAnnivDate
+ * desc: delete a certain date from database
+ * param: hContact - handle to a contact to read the date from
+ * pszModule - module holding the date
+ * szDay - setting of the day to read
+ * szMonth - setting of the month to read
+ * szYear - setting of the year to read
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBDeleteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear) const
+{
+ INT ret;
+
+ ret = DB::Setting::Delete(hContact, pszModule, szDay);
+ ret &= DB::Setting::Delete(hContact, pszModule, szMonth);
+ ret &= DB::Setting::Delete(hContact, pszModule, szYear);
+ return ret;
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting general datestamp
+ ***********************************************************************************************************/
+
+/**
+ * name: DBGetDateStamp
+ * class: MAnnivDate
+ * desc: Read a datestamp from database. A datestamp is an DWORD of the form <ddmmyyyy>.
+ * param: hContact - handle to a contact to read the datestamp from
+ * pszModule - module to read the datestamp from
+ * pszSetting - key used to identify the datestamp
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+
+ if (DB::Setting::GetAsIs(hContact, pszModule, pszSetting, &dbv))
+ return 1;
+ if (dbv.type != DBVT_DWORD) {
+ DB::Variant::Free(&dbv);
+ return 1;
+ }
+ DateStamp(dbv.dVal);
+ return IsValid() == 0;
+}
+
+/**
+ * name: DBWriteDateStamp
+ * class: MAnnivDate
+ * desc: Write a datestamp to database. A datestamp is an DWORD of the form <ddmmyyyy>.
+ * param: hContact - handle to a contact to write the datestamp to
+ * pszModule - module to write the datestamp to
+ * pszSetting - key used to save the datestamp
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DWORD dwStamp = DateStamp();
+
+ if (hContact == INVALID_HANDLE_VALUE ||
+ pszModule == 0 || *pszModule == 0 ||
+ pszSetting == 0 || *pszSetting == 0 ||
+ dwStamp == 0)
+ {
+ return 1;
+ }
+ return DB::Setting::WriteDWord(hContact, pszModule, pszSetting, dwStamp);
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting birthday
+ ***********************************************************************************************************/
+
+/**
+ * name: DBGetBirthDate
+ * class: MAnnivDate
+ * desc: try to read birthday date from all known modules
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetBirthDate(HANDLE hContact, LPSTR pszProto)
+{
+ Clear();
+
+ // try to get birthday from any custom module
+ if ( !DBGetDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+ !DBGetDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+ !DBGetDate(hContact, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+ !DBGetDate(hContact, USERINFO, SET_CONTACT_DOBD, SET_CONTACT_DOBM, SET_CONTACT_DOBY))
+ {
+ SetFlags(MADF_HASCUSTOM);
+ }
+ // if pszProto is set to NULL, this will be scaned only incase the birthday date has not been found yet
+ else if (pszProto || (pszProto = DB::Contact::Proto(hContact)) != NULL)
+ {
+ // try to get birthday from basic protocol
+ if (!DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+ {
+ SetFlags(MADF_HASPROTO);
+ }
+ // try to get birthday date from metacontact's subcontact
+ else if (DB::Module::IsMetaAndScan(pszProto))
+ {
+ const INT def = DB::MetaContact::SubDefNum(hContact);
+ HANDLE hSubContact;
+
+ // try to get setting from the default subcontact first
+ if (def > -1 && def < INT_MAX)
+ {
+ hSubContact = DB::MetaContact::Sub(hContact, def);
+ if (hSubContact != NULL && !DBGetBirthDate(hSubContact, NULL))
+ {
+ RemoveFlags(MADF_HASCUSTOM);
+ SetFlags(MADF_HASMETA);
+ }
+ }
+
+ // scan all subcontacts for the setting
+ if (_wFlags == 0)
+ {
+ const INT cnt = DB::MetaContact::SubCount(hContact);
+
+ if (cnt < INT_MAX)
+ {
+ INT i;
+ for (i = 0; i < cnt; i++)
+ {
+ if (i != def)
+ {
+ hSubContact = DB::MetaContact::Sub(hContact, i);
+ if (hSubContact != NULL && !DBGetBirthDate(hSubContact, NULL))
+ {
+ RemoveFlags(MADF_HASCUSTOM);
+ SetFlags(MADF_HASMETA);
+ break;
+ } } } } } }
+ }
+
+ if (_wFlags != 0)
+ {
+ _wID = ANID_BIRTHDAY;
+ _strDesc = TranslateT("Birthday");
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * name: DBMoveBirthDate
+ * class: MAnnivDate
+ * desc: keep the database clean
+ * param: hContact - handle to a contact to read the date from
+ * bOld - byte RemindBirthModule src
+ * bNew - byte RemindBirthModule dest
+ * return: 0 on success, 1 otherwise
+ **/
+//
+INT MAnnivDate::DBMoveBirthDate(HANDLE hContact, BYTE bOld, BYTE bNew) {
+ Clear();
+ switch(bOld) {
+ case 0: //MOD_MBIRTHDAY
+ if (!DBGetDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+ if(DBWriteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+ return 1;
+ DBDeleteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ DB::Setting::Delete(hContact, MOD_MBIRTHDAY, "BirthMode");
+ }
+ break;
+ case 1: //USERINFO
+ if (!DBGetDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+ if(DBWriteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+ return 1;
+ DB::Setting::WriteByte(hContact, MOD_MBIRTHDAY, "BirthMode", 2);
+ DBDeleteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ }
+ break;
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * name: DBWriteBirthDate
+ * class: MAnnivDate
+ * desc: write birthday date to desired module
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteBirthDate(HANDLE hContact)
+{
+ INT rc = 0;
+ LPCSTR pszModule = SvcReminderGetMyBirthdayModule();
+
+ rc = DBWriteDate(hContact, pszModule, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ if (!rc)
+ {
+ if (!mir_strcmp(pszModule, MOD_MBIRTHDAY))
+ {
+ DB::Setting::WriteByte(hContact, MOD_MBIRTHDAY, "BirthMode", 2);
+ }
+
+ if (
+ // only delete values from current contact's custom modules
+ !(_wFlags & (MADF_HASPROTO|MADF_HASMETA)) &&
+ // check whether user wants this feature
+ DB::Setting::GetByte(SET_REMIND_SECUREBIRTHDAY, TRUE) &&
+ !myGlobals.UseDbxTree)
+ {
+ // keep the database clean
+
+ if (mir_strcmp(pszModule, MOD_MBIRTHDAY)!= 0)
+ {
+ DBDeleteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ DB::Setting::Delete(hContact, MOD_MBIRTHDAY, "BirthMode");
+ }
+ else if (mir_strcmp(pszModule, USERINFO) !=0)
+ {
+ DBDeleteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ }
+ DBDeleteDate(hContact, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+ DBDeleteDate(hContact, USERINFO, SET_CONTACT_DOBD, SET_CONTACT_DOBM, SET_CONTACT_DOBY);
+ }
+
+ rc = DB::Setting::WriteWord(hContact, USERINFO, SET_CONTACT_AGE, Age());
+ }
+ return rc;
+}
+
+/**
+ * name: DBDeleteBirthDate
+ * class: MAnnivDate
+ * desc: delete birthday date from desired module
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBDeleteBirthDate(HANDLE hContact)
+{
+ return DBDeleteDate(hContact, Module(), SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting anniversary
+ ***********************************************************************************************************/
+
+/**
+ * name: DBGetAnniversaryDate
+ * class: MAnnivDate
+ * desc: try to read anniversary date from userinfo module
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetAnniversaryDate(HANDLE hContact, WORD iIndex)
+{
+ CHAR szStamp[MAXSETTING];
+ DBVARIANT dbv;
+ INT rc;
+
+ Clear();
+
+ // read date and convert older versions
+ mir_snprintf(szStamp, SIZEOF(szStamp), "Anniv%dDate", iIndex);
+ rc = DBGetDateStamp(hContact, USERINFO, szStamp);
+ if (!rc)
+ {
+ _strModule = USERINFO;
+ _wFlags |= MADF_HASCUSTOM;
+ _wID = iIndex;
+
+ // read description
+ mir_snprintf(szStamp, SIZEOF(szStamp), "Anniv%dDesc", iIndex);
+ if (!DB::Setting::GetTString(hContact, USERINFO, szStamp, &dbv))
+ {
+ _strDesc = dbv.ptszVal;
+ DB::Variant::Free(&dbv);
+ }
+ }
+ return rc;
+}
+
+/**
+ * name: DBWriteAnniversaryDate
+ * class: MAnnivDate
+ * desc: write birthday date to desired module
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteAnniversaryDate(HANDLE hContact, WORD wIndex)
+{
+ INT ret = 0;
+
+ // date can only be written to db as anniversary if it is not marked as birthday
+ if (wIndex <= ANID_LAST && _wID != ANID_BIRTHDAY)
+ {
+ CHAR pszSetting[MAXSETTING];
+
+ _wID = wIndex;
+
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), "Anniv%dDate", wIndex);
+ if (!DBWriteDateStamp(hContact, USERINFO, pszSetting))
+ {
+ // write description
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), "Anniv%dDesc", wIndex);
+ DB::Setting::WriteTString(hContact, USERINFO, pszSetting, (LPTSTR)Description());
+ return 0;
+ }
+ // delete date if written incompletely
+ DB::Setting::Delete(hContact, USERINFO, pszSetting);
+ }
+ return 1;
+}
+
+/***********************************************************************************************************
+ * automatic backup service
+ ***********************************************************************************************************/
+
+static WORD AskUser(HANDLE hContact, MAnnivDate *pOldCustomDate, MAnnivDate *pNewProtoDate)
+{
+ MSGBOX MB;
+ TCHAR szMsg[MAXDATASIZE];
+ TCHAR szDate[MAX_PATH];
+ TCHAR szoldDate[MAX_PATH];
+
+ pOldCustomDate->DateFormat(szoldDate, SIZEOF(szoldDate));
+ pNewProtoDate->DateFormat(szDate, SIZEOF(szDate));
+
+ mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s provides a new birthday via protocol.\nIt is %s. The old one was %s.\n\nDo you want to use this as the new birthday for this contact?"),
+ DB::Contact::DisplayName(hContact), szDate, szoldDate
+ );
+
+ MB.cbSize = sizeof(MSGBOX);
+ MB.hParent = NULL;
+ MB.hiLogo = IcoLib_GetIcon(ICO_DLG_ANNIVERSARY);
+ MB.hiMsg = NULL;
+ MB.uType = MB_YESALLNO|MB_ICON_QUESTION|MB_INFOBAR|MB_NOPOPUP;
+ MB.ptszTitle = LPGENT("Update custom birthday");
+ MB.ptszInfoText = LPGENT("Keeps your custom birthday up to date.");
+ MB.ptszMsg = szMsg;
+ return MsgBoxService(NULL, (LPARAM)&MB);
+}
+
+/**
+ * name: BackupBirthday
+ * class: MAnnivDate
+ * desc: tries to read birthday date from protocol and compares it with the classes date
+ * param: hContact - handle to a contact to read the date from
+ * pszProto - basic protocol module
+ * return: 0 if backup was done, 1 otherwise
+ **/
+INT MAnnivDate::BackupBirthday(HANDLE hContact, LPSTR pszProto, const BOOLEAN bDontIgnoreAnything, PWORD lastAnswer)
+{
+ if (hContact)
+ {
+ // This birthday is a protocol based or metasubcontact's anniversary and no custom information exist,
+ // so directly back it up under all circumstances!
+ if ((_wFlags & MADF_HASPROTO) || (_wFlags & MADF_HASMETA))
+ {
+ DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+ DBWriteBirthDate(hContact);
+ }
+ // A custom birthday was set by user before and is not to be ignored
+ else if ((_wFlags & MADF_HASCUSTOM) && (bDontIgnoreAnything || !lastAnswer || (*lastAnswer != IDNONE)))
+ {
+ if (!pszProto)
+ {
+ pszProto = DB::Contact::Proto(hContact);
+ }
+ if (pszProto)
+ {
+ BOOLEAN bIsMeta = DB::Module::IsMeta(pszProto);
+ BOOLEAN bIsMetaSub = !bIsMeta && DB::MetaContact::IsSub(hContact);
+ BOOLEAN bWantBackup = FALSE;
+ MAnnivDate mdbNewProto;
+ MAnnivDate mdbIgnore;
+ HANDLE hSubContact;
+
+ const INT nSubContactCount = (bIsMeta) ? DB::MetaContact::SubCount(hContact) : 0;
+
+ bWantBackup = !mdbNewProto.DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)
+ && !IsEqual(mdbNewProto.SystemTime())
+ && (bDontIgnoreAnything || (DB::Setting::GetDWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED, 0) != mdbNewProto.DateStamp()))
+ && !bIsMetaSub;
+
+ // allow backup only, if the custom setting differs from all meta subcontacts' protocol based settings, too.
+ for (INT i = 0; (i < nSubContactCount) && bWantBackup && bIsMeta; i++)
+ {
+ hSubContact = DB::MetaContact::Sub(hContact, i);
+ if (hSubContact && !mdbIgnore.DBGetDate(hSubContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+ {
+ bWantBackup = bWantBackup
+ && !IsEqual(mdbIgnore.SystemTime())
+ && (bDontIgnoreAnything || (DB::Setting::GetDWord(hSubContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED, 0) != mdbIgnore.DateStamp()));
+ }
+ }
+ if (bWantBackup)
+ {
+ if (!lastAnswer || *lastAnswer != IDALL)
+ {
+ WORD rc = AskUser(hContact, this, &mdbNewProto);
+ if (lastAnswer)
+ {
+ *lastAnswer = rc;
+ }
+ if (IDYES != rc && IDALL != rc)
+ {
+ // special handling for metasubcontacts required?!
+ mdbNewProto.DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+ bWantBackup = FALSE;
+ }
+ }
+ if (bWantBackup)
+ {
+ Set(mdbNewProto);
+ DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+ DBWriteBirthDate(hContact);
+
+ // update metasubcontacts
+ for (INT i = 0; i < nSubContactCount; i++)
+ {
+ hSubContact = DB::MetaContact::Sub(hContact, i);
+ if (hSubContact != NULL)
+ {
+ if (!mdbIgnore.DBGetDate(hSubContact, DB::Contact::Proto(hSubContact), SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+ {
+ mdbIgnore.DBWriteDateStamp(hSubContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+ }
+ DBWriteBirthDate(hSubContact);
+ }
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ /*
+ else if (mir_stricmp(Module(), SvcReminderGetMyBirthdayModule()))
+ {
+ DBWriteBirthDate(hContact);
+ }
+ */
+ }
+ return 1;
+} \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/classMAnnivDate.h b/plugins/UserInfoEx/src/classMAnnivDate.h
new file mode 100644
index 0000000000..4e2d447a13
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMAnnivDate.h
@@ -0,0 +1,132 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMAnnivDate.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#pragma once
+
+#define ANID_LAST 0xFFFC
+#define ANID_BIRTHDAY 0xFFFE
+#define ANID_NONE 0xFFFF
+
+struct MZodiac {
+ HICON hIcon;
+ LPCTSTR pszName;
+};
+
+class MAnnivDate : public MTime
+{
+public:
+ typedef enum {
+ MADF_NONE = 0,
+ MADF_CHANGED = 1, // date has been edited (used, if date is used in controls)
+ MADF_HASPROTO = 2, // basic protocol module contains date information
+ MADF_HASCUSTOM = 4, // date is customized or read from a custom module
+ MADF_HASMETA = 8, // date is read from a metacontact's subcontact
+ MADF_REMINDER_CHANGED = 16 // reminder options have changed
+ } EFlags;
+
+private:
+ WORD _wID; // index to anniversary in database or ANID_BIRTHDAY
+ tstring _strDesc; // descripes the anniversary (e.g. birthday)
+ string _strModule; // the module the anniversary has been read from
+ WORD _wFlags; // the flags
+ BYTE _bRemind; // per user setting for reminder (0 - disabled, 1 - use local offset, 2 - use global offset)
+ WORD _wDaysEarlier; // number of days to the anniversary the user wants to be reminded of this anniversary
+
+ INT DBWriteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear);
+ INT DBDeleteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear) const;
+
+public:
+ MAnnivDate();
+ MAnnivDate(MAnnivDate &mda);
+
+ // basic access to attributes
+ __inline LPCTSTR Description() const { return _strDesc.c_str(); };
+ __inline VOID Description(LPCTSTR pszDesc) { if (pszDesc) _strDesc = pszDesc; };
+ __inline LPCSTR Module() const { return _strModule.c_str(); };
+ __inline VOID Module(LPCSTR pszModule) { if (pszModule) _strModule = pszModule; else _strModule.clear(); };
+ __inline BYTE RemindOption() const { return _bRemind; };
+ __inline VOID RemindOption(BYTE bRemind) { if (bRemind <= BST_INDETERMINATE) _bRemind = bRemind; };
+ __inline WORD RemindOffset() const { return _wDaysEarlier; };
+ __inline VOID RemindOffset(WORD wOffset) { _wDaysEarlier = wOffset; };
+ __inline WORD Id() const { return _wID; };
+ __inline VOID Id(WORD wId) { if (_wID == ANID_NONE) _wID = wId; };
+
+ DWORD DateStamp() const;
+ VOID DateStamp(const DWORD dwStamp);
+
+ // basic checks
+ __inline BOOLEAN IsValid() const;
+ __inline BOOLEAN IsChanged() const { return (_wFlags & MADF_CHANGED); };
+ __inline BOOLEAN IsReminderChanged() const { return (_wFlags & MADF_REMINDER_CHANGED); };
+ __inline BOOLEAN IsEqual(const MAnnivDate &mda) const { return IsEqual(mda.SystemTime()); };
+ BOOLEAN IsEqual(const SYSTEMTIME &st) const;
+
+ // handling flags
+ __inline WORD Flags() const { return _wFlags; };
+ __inline VOID Flags(WORD wFlags) { _wFlags = wFlags; };
+ __inline VOID SetFlags(WORD wFlag) { _wFlags |= wFlag; };
+ __inline VOID RemoveFlags(WORD wFlag) { _wFlags &= ~wFlag; };
+
+ // return diffence of days, ignoring the date
+ INT CompareDays(MTime mt) const;
+
+ MZodiac Zodiac();
+ INT Age(MTime *pNow = NULL);
+ VOID Clear();
+
+ // read date from database
+ INT DBGetDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear);
+ INT DBGetDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ INT DBGetAnniversaryDate(HANDLE hContact, WORD iIndex);
+ INT DBGetBirthDate(HANDLE hContact, LPSTR pszProto = NULL);
+ INT DBGetReminderOpts(HANDLE hContact);
+
+ // write date to database
+ INT DBWriteDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ INT DBWriteAnniversaryDate(HANDLE hContact, WORD wIndex);
+ INT DBWriteBirthDate(HANDLE hContact);
+ INT DBWriteReminderOpts(HANDLE hContact);
+
+ // delete date from database
+ INT DBDeleteBirthDate(HANDLE hContact);
+
+ INT DBMoveBirthDate(HANDLE hContact, BYTE bOld, BYTE bNew);
+ INT BackupBirthday (HANDLE hContact, LPSTR pszProto = NULL, const BOOLEAN bDontIgnoreAnything = FALSE, PWORD lastAnswer = NULL);
+
+ // setting values
+ VOID SetDate(SYSTEMTIME &st);
+ VOID SetDate(MAnnivDate &mda);
+
+ BOOLEAN operator == (const SYSTEMTIME &st) { return IsEqual(st); };
+ BOOLEAN operator == (const MAnnivDate &mda) { return IsEqual(mda); };
+
+ VOID operator = (SYSTEMTIME &st) { SetDate(st); };
+ VOID operator = (MAnnivDate &mda) { SetDate(mda); };
+};
diff --git a/plugins/UserInfoEx/src/classMTime.cpp b/plugins/UserInfoEx/src/classMTime.cpp
new file mode 100644
index 0000000000..3b1dd2e081
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMTime.cpp
@@ -0,0 +1,468 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMTime.cpp $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_Timezone.h"
+
+/******************************************************************************************
+ * class MTime
+ *
+ *****************************************************************************************/
+
+/*********************************************
+ * construction
+ *********************************************/
+
+MTime::MTime()
+{
+ ZeroDate();
+}
+
+MTime::MTime(SYSTEMTIME &st, const BOOLEAN bIsLocal)
+{
+ _SysTime = st;
+ _isLocal = bIsLocal != FALSE;
+}
+
+MTime::MTime(FILETIME &ft, const BOOLEAN bIsLocal)
+{
+ ZeroDate();
+ Set(ft, bIsLocal);
+}
+
+MTime::MTime(LARGE_INTEGER &li, const BOOLEAN bIsLocal)
+{
+ ZeroDate();
+ Set(li, bIsLocal);
+}
+
+MTime::MTime(DWORD dwStamp)
+{
+ ZeroDate();
+ FromStampAsUTC(dwStamp);
+}
+
+MTime::MTime(const MTime& mtime)
+{
+ Set(mtime);
+}
+
+VOID MTime::ZeroDate()
+{
+ _isLocal = FALSE;
+ ZeroMemory(&_SysTime, sizeof(_SysTime));
+}
+
+/*********************************************
+ * validation / checks
+ *********************************************/
+
+BOOLEAN MTime::IsValid() const
+{
+ return (
+ _SysTime.wYear > 1600 &&
+ _SysTime.wMonth > 0 && _SysTime.wMonth < 13 &&
+ _SysTime.wDay > 0 && _SysTime.wDay <= DaysInMonth(_SysTime.wMonth) &&
+ _SysTime.wHour < 25 &&
+ _SysTime.wMinute < 60 &&
+ _SysTime.wSecond < 60 );
+}
+
+BOOLEAN MTime::IsLeapYear() const
+{
+ return (!(((_SysTime.wYear) % 4 != 0) || (((_SysTime.wYear) % 100 == 0) && ((_SysTime.wYear) % 400 != 0))));
+}
+
+LONG MTime::Compare(const DWORD dwTimeStamp) const
+{
+ return (LONG)(TimeStamp() - dwTimeStamp);
+}
+
+/**
+ * name: Compare
+ * desc: compare a filetime with the value of current object and return difference as number of seconds
+ * param: ft - FILETIME to compare with
+ * return: number of seconds the ft differs from the class value
+ **/
+LONG MTime::Compare(const FILETIME &ft) const
+{
+ const FILETIME ft1 = FileTime();
+ return (LONG)((*(__int64*)&ft1 - *(__int64*)&ft) / 10000000i64);
+}
+
+/**
+ * name: Compare
+ * desc: compare a systemtime with the value of current object and return difference as number of seconds it handles some strange, too.
+ * param: st - SYSTEMTIME to compare with
+ * return: number of seconds the st differs from the class value
+ **/
+LONG MTime::Compare(SYSTEMTIME st) const
+{
+ FILETIME ft2;
+
+ //strange day-in-month thing
+ if (st.wYear == 0) {
+ if (Month() < st.wMonth) return -1;
+ if (Month() > st.wMonth) return 1;
+
+ MTime mtTmp(st, FALSE);
+
+ mtTmp.Year(Year());
+ mtTmp.Day(1);
+ mtTmp.Set(mtTmp.FileTime(), FALSE); //gets the day of week of the first of the month
+ mtTmp.Day(1 + (7 + st.wDayOfWeek - mtTmp.DayOfWeek()) % 7);
+
+ //last wDayOfWeek in month
+ if (st.wDay == 5) {
+ mtTmp.Day(mtTmp.Day() + 7 * 3); //can't be less than 4 of that day in the month
+ if (mtTmp.Day() + 7 <= mtTmp.DaysInMonth(st.wMonth - 1))
+ mtTmp.Day(mtTmp.Day() + 7);
+ }
+ else
+ mtTmp.Day(7 * (st.wDay - 1)); //nth of month
+
+ ft2 = mtTmp.FileTime();
+ }
+ else {
+ SystemTimeToFileTime(&st, &ft2);
+ }
+ return Compare(ft2);
+}
+
+/**
+ * name: Compare
+ * desc: compare a MTime with the value of current object and return difference as number of seconds it handles some strange, too.
+ * param: mt - MTime to compare with
+ * return: number of seconds the st differs from the class value
+ **/
+LONG MTime::Compare(const MTime &mt) const
+{
+ return Compare(mt.SystemTime());
+}
+
+/*********************************************
+ * conversions
+ *********************************************/
+
+VOID MTime::UTCToLocal()
+{
+ if (!IsLocal()) {
+ TIME_ZONE_INFORMATION tzInfo;
+
+ ZeroMemory(&tzInfo, sizeof(TIME_ZONE_INFORMATION));
+ GetTimeZoneInformation(&tzInfo);
+ UTCToTzSpecificLocal(&tzInfo);
+ }
+}
+
+VOID MTime::UTCToTzSpecificLocal(INT tzh)
+{
+
+ TIME_ZONE_INFORMATION tzInfo;
+
+ if (IsLocal()) LocalToUTC();
+ ZeroMemory(&tzInfo, sizeof(TIME_ZONE_INFORMATION));
+
+ if (tzh > 24) tzh = 24;
+ if (tzh < -24)tzh = -24;
+
+ GetTimeZoneInformation(&tzInfo);
+ tzInfo.Bias = tzh * 30i64;
+ UTCToTzSpecificLocal(&tzInfo);
+}
+
+LONG MTime::_Offset(TIME_ZONE_INFORMATION *tzi)
+{
+ LONG offset = tzi->Bias;
+ // daylight saving times
+ if (tzi->StandardDate.wMonth != 0) {
+ if (tzi->DaylightDate.wMonth < tzi->StandardDate.wMonth) {
+ //northern hemisphere
+ if (Compare(tzi->DaylightDate) < 0 || Compare(tzi->StandardDate) > 0)
+ offset += tzi->StandardBias;
+ else
+ offset += tzi->DaylightBias;
+ }
+ else {
+ //southern hemisphere
+ if (Compare(tzi->StandardDate) < 0 || Compare(tzi->DaylightDate) > 0)
+ offset += tzi->DaylightBias;
+ else
+ offset += tzi->StandardBias;
+ }
+ }
+ return offset;
+}
+
+VOID MTime::UTCToTzSpecificLocal(TIME_ZONE_INFORMATION *tzi)
+{
+ LARGE_INTEGER liFiletime;
+
+ // do not transform to local twice
+ if (tzi && !_isLocal) {
+ liFiletime = LargeInt();
+ liFiletime.QuadPart -= _Offset(tzi) * 60 * 10000000i64;
+ Set(liFiletime, TRUE);
+ }
+}
+
+VOID MTime::TzSpecificLocalToUTC(TIME_ZONE_INFORMATION *tzi)
+{
+ LARGE_INTEGER liFiletime;
+
+ // do not transform to utc twice
+ if (tzi && _isLocal) {
+ liFiletime = LargeInt();
+ liFiletime.QuadPart += _Offset(tzi) * 60 * 10000000i64;
+ Set(liFiletime, TRUE);
+ }
+}
+
+VOID MTime::LocalToUTC()
+{
+ TIME_ZONE_INFORMATION tzInfo;
+
+ GetTimeZoneInformation(&tzInfo);
+ TzSpecificLocalToUTC(&tzInfo);
+}
+
+/*********************************************
+ * return values
+ *********************************************/
+
+LARGE_INTEGER MTime::LargeInt() const
+{
+ LARGE_INTEGER liFileTime = { 0 };
+
+ SystemTimeToFileTime(&_SysTime, (LPFILETIME)&liFileTime);
+ return liFileTime;
+}
+
+FILETIME MTime::FileTime() const
+{
+ FILETIME ftFileTime;
+
+ SystemTimeToFileTime(&_SysTime, &ftFileTime);
+ return ftFileTime;
+}
+
+DWORD MTime::TimeStamp() const
+{
+ LARGE_INTEGER li;
+
+ if (IsLocal()) {
+ MTime mt(*this);
+ mt.LocalToUTC();
+ li = mt.LargeInt();
+ }
+ else
+ li = LargeInt();
+
+ li.QuadPart /= 10000000i64;
+ li.QuadPart -= 11644473600i64;
+
+ if (li.QuadPart < 0)
+ return 0;
+
+ return (DWORD)li.QuadPart;
+}
+
+WORD MTime::DaysInMonth(const WORD &wMonth) const
+{
+ static const WORD wDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+ if (wMonth > 12) return 0;
+ return (IsLeapYear() && wMonth == 2) ? wDaysInMonth[wMonth] + 1 : wDaysInMonth[wMonth];
+}
+
+WORD MTime::DaysInYear(BOOLEAN bIgnoreLeap) const
+{
+ return ((!bIgnoreLeap && IsLeapYear()) ? 366 : 365);
+};
+
+WORD MTime::DayOfYear() const
+{
+ WORD daysResult = 0;
+ WORD i;
+
+ for (i = 0; i < _SysTime.wMonth; i++)
+ daysResult += DaysInMonth(i);
+ daysResult += _SysTime.wDay;
+ return daysResult;
+}
+
+WORD MTime::AdjustYear(const INT nDiffDays)
+{
+ const INT nDay = DayOfYear() + nDiffDays;
+
+ if (nDay > DaysInYear())
+ return _SysTime.wYear + 1;
+ else if (nDay < 0)
+ return _SysTime.wYear - 1;
+ return _SysTime.wYear;
+}
+
+WORD MTime::TimeFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+ if (!ptszTimeFormat || !cchTimeFormat)
+ return 0;
+ if ((cchTimeFormat = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+ *ptszTimeFormat = 0;
+ return 0;
+ }
+ return cchTimeFormat;
+}
+
+WORD MTime::DateFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+ if (!ptszTimeFormat || !cchTimeFormat)
+ return 0;
+ if ((cchTimeFormat = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+ *ptszTimeFormat = 0;
+ return 0;
+ }
+ return cchTimeFormat;
+}
+
+WORD MTime::DateFormatLong(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+ if (!ptszTimeFormat || !cchTimeFormat)
+ return 0;
+ if ((cchTimeFormat = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+ *ptszTimeFormat = 0;
+ return 0;
+ }
+ return cchTimeFormat;
+}
+
+/*********************************************
+ * set class value
+ *********************************************/
+
+VOID MTime::FromStampAsUTC(const DWORD dwTimeStamp)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = (dwTimeStamp + 11644473600i64) * 10000000i64;
+ Set(li, FALSE);
+}
+
+VOID MTime::FromStampAsLocal(const DWORD dwTimeStamp)
+{
+ FromStampAsUTC(dwTimeStamp);
+ UTCToLocal();
+}
+
+VOID MTime::Set(LARGE_INTEGER liFileTime, const BOOLEAN bIsLocal)
+{
+ if (liFileTime.QuadPart < 0i64) liFileTime.QuadPart = 0;
+ FileTimeToSystemTime((LPFILETIME)&liFileTime, &_SysTime);
+ _isLocal = bIsLocal != FALSE;
+}
+
+VOID MTime::Set(FILETIME &ftFileTime, const BOOLEAN bIsLocal)
+{
+ FileTimeToSystemTime(&ftFileTime, &_SysTime);
+ _isLocal = bIsLocal != FALSE;
+}
+
+VOID MTime::Set(const MTime &mt)
+{
+ Set(mt.SystemTime(), mt.IsLocal());
+}
+
+VOID MTime::Set(SYSTEMTIME &st, const BOOLEAN bIsLocal)
+{
+ memcpy(&_SysTime, &st, sizeof(SYSTEMTIME));
+ _isLocal = bIsLocal != FALSE;
+}
+
+VOID MTime::GetTimeUTC()
+{
+ _isLocal = FALSE;
+ ::GetSystemTime(&_SysTime);
+}
+
+VOID MTime::GetLocalTime()
+{
+ _isLocal = TRUE;
+ ::GetLocalTime(&_SysTime);
+}
+
+VOID MTime::GetLocalTime(HANDLE hContact)
+{
+ TIME_ZONE_INFORMATION tzi;
+
+ GetTimeUTC();
+
+ if (!GetContactTimeZoneInformation((WPARAM)hContact, (LPARAM)&tzi)) {
+ UTCToTzSpecificLocal(&tzi);
+ }
+}
+
+/*********************************************
+ * read and write time to miranda's database
+ *********************************************/
+
+INT MTime::DBGetStamp (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DWORD dwTimeStamp;
+
+ if (hContact == INVALID_HANDLE_VALUE ||
+ pszModule == NULL || pszModule[0] == 0 ||
+ pszSetting == NULL || pszSetting[0] == 0)
+ {
+ ZeroDate();
+ return 1;
+ }
+
+ dwTimeStamp = DB::Setting::GetDWord(hContact, pszModule, pszSetting, 0);
+
+ if (dwTimeStamp == 0) {
+ ZeroDate();
+ return 1;
+ }
+ FromStampAsUTC(dwTimeStamp);
+ _isLocal = FALSE;
+ return 0;
+}
+
+INT MTime::DBWriteStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ if (hContact == INVALID_HANDLE_VALUE ||
+ pszModule == NULL || pszModule[0] == 0 ||
+ pszSetting == NULL || pszSetting[0] == 0)
+ {
+ return 1;
+ }
+ return DB::Setting::WriteDWord(hContact, pszModule, pszSetting, TimeStamp());
+}
diff --git a/plugins/UserInfoEx/src/classMTime.h b/plugins/UserInfoEx/src/classMTime.h
new file mode 100644
index 0000000000..9d30c4782e
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMTime.h
@@ -0,0 +1,125 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMTime.h $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+class MTime {
+ SYSTEMTIME _SysTime;
+ BOOLEAN _isLocal;
+
+ LONG _Offset(TIME_ZONE_INFORMATION *tzi);
+
+public:
+ // contruction
+ MTime();
+ MTime(SYSTEMTIME &st, const BOOLEAN bIsLocal);
+ MTime(FILETIME &ft, const BOOLEAN bIsLocal);
+ MTime(LARGE_INTEGER &li, const BOOLEAN bIsLocal);
+ MTime(DWORD dwStamp);
+ MTime(const MTime& mtime);
+
+ // checks
+ __inline BOOLEAN IsLocal() const { return _isLocal; };
+ BOOLEAN IsValid() const;
+ BOOLEAN IsLeapYear() const;
+
+ // compare by seconds
+ LONG Compare(SYSTEMTIME st) const;
+ LONG Compare(const FILETIME &ft) const;
+ LONG Compare(const MTime &mt) const;
+ LONG Compare(const DWORD dwTimeStamp) const;
+
+ // get value from class
+ LARGE_INTEGER LargeInt() const;
+ FILETIME FileTime() const;
+ DWORD TimeStamp() const;
+ SYSTEMTIME SystemTime() const { return _SysTime; };
+ WORD DaysInMonth(const WORD &wMonth) const;
+ WORD DaysInYear(BOOLEAN bIgnoreLeap = FALSE) const;
+ WORD DayOfYear() const;
+ WORD AdjustYear(const INT nDiffDays);
+
+ WORD TimeFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+ WORD TimeFormat(tstring& str);
+ WORD DateFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+ WORD DateFormatLong(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+
+ // return single attributes
+ __inline WORD DayOfWeek() const { return _SysTime.wDayOfWeek; };
+ __inline WORD Day() const { return _SysTime.wDay; };
+ __inline WORD Month() const { return _SysTime.wMonth; };
+ __inline WORD Year() const { return _SysTime.wYear; };
+ __inline WORD Hour() const { return _SysTime.wHour; };
+ __inline WORD Minute() const { return _SysTime.wMinute; };
+ __inline WORD Second() const { return _SysTime.wSecond; };
+
+ // set single values
+ __inline VOID Minute(const WORD wMinute) { if (wMinute <= 59) _SysTime.wMinute = wMinute; };
+ __inline VOID Hour(const WORD wHour) { if (wHour <= 24) _SysTime.wHour = wHour; };
+ __inline VOID Day(const WORD wDay) { if (wDay <= 31) _SysTime.wDay = wDay; };
+ __inline VOID Month(const WORD wMonth) { if (wMonth <= 12) _SysTime.wMonth = wMonth; };
+ __inline VOID Year(const WORD wYear) { _SysTime.wYear = wYear; };
+
+ // set value to class
+ VOID ZeroDate();
+ VOID FromStampAsUTC(const DWORD dwTimeStamp);
+ VOID FromStampAsLocal(const DWORD dwTimeStamp);
+ VOID Set(FILETIME &ftFileTime, const BOOLEAN bIsLocal);
+ VOID Set(LARGE_INTEGER liFileTime, const BOOLEAN bIsLocal);
+ VOID Set(SYSTEMTIME &st, const BOOLEAN bIsLocal);
+ VOID Set(const MTime &mt);
+
+ // get current time
+ VOID GetTimeUTC();
+ VOID GetLocalTime();
+ VOID GetLocalTime(HANDLE hContact);
+
+ // conversions
+ VOID UTCToLocal();
+ VOID UTCToTzSpecificLocal(INT tzh);
+ VOID UTCToTzSpecificLocal(TIME_ZONE_INFORMATION *tzi);
+ VOID LocalToUTC();
+ VOID TzSpecificLocalToUTC(TIME_ZONE_INFORMATION *tzi);
+
+ // read and write from and to db
+ INT DBGetStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ INT DBWriteStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+
+ // operatoren
+ VOID operator = (DWORD& dwTimeStamp) { FromStampAsUTC(dwTimeStamp); };
+ VOID operator = (FILETIME &ftFileTime) { Set(ftFileTime, FALSE); };
+ VOID operator = (LARGE_INTEGER &liFileTime) { Set(liFileTime, FALSE); };
+ VOID operator = (SYSTEMTIME &st) { Set(st, FALSE); };
+ VOID operator = (const MTime &mt) { Set(mt); };
+};
+
+/**
+ * prototypes
+ **/
+VOID UserTime_LoadModule(VOID);
diff --git a/plugins/UserInfoEx/src/classPsTree.cpp b/plugins/UserInfoEx/src/classPsTree.cpp
new file mode 100644
index 0000000000..ac88130d17
--- /dev/null
+++ b/plugins/UserInfoEx/src/classPsTree.cpp
@@ -0,0 +1,993 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classPsTree.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+
+
+static WNDPROC DefEditProc;
+
+/***********************************************************************************************************
+ * construction and destruction
+ ***********************************************************************************************************/
+
+/**
+ * name: CPsTree
+ * class: CPsTree
+ * desc: constructor
+ * param: none
+ * return: none
+ **/
+CPsTree::CPsTree(LPPS pPs)
+{
+ _hWndTree = NULL;
+ _hImages = NULL;
+
+ _pItems = NULL;
+ _numItems = 0;
+ _curItem = -1;
+ _dwFlags = 0;
+ _hLabelEdit = NULL;
+ _hDragItem = NULL;
+ _isDragging = FALSE;
+ _pPs = pPs;
+}
+
+/**
+ * name: ~CPsTree
+ * class: CPsTree
+ * desc: frees up all memory, used by the tree control
+ * return: nothing
+ **/
+CPsTree::~CPsTree()
+{
+ if (_hLabelEdit)
+ {
+ DestroyWindow(_hLabelEdit);
+ _hLabelEdit = NULL;
+ }
+ if (_pItems)
+ {
+ for (INT i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i])
+ {
+ delete _pItems[i];
+ _pItems[i] = NULL;
+ }
+ }
+ MIR_FREE(_pItems);
+ _pItems = NULL;
+ _numItems = NULL;
+ }
+ ImageList_Destroy(_hImages);
+ _hImages = NULL;
+}
+
+/**
+ * name: CPsTree
+ * class: CPsTree
+ * desc: constructor
+ * param: none
+ * return: none
+ **/
+BOOLEAN CPsTree::Create(HWND hWndTree, CPsHdr* pPsh)
+{
+ BOOLEAN rc;
+
+ if (hWndTree && pPsh->_hImages && pPsh->_pPages && pPsh->_numPages)
+ {
+ _hWndTree = hWndTree;
+ _hImages = pPsh->_hImages;
+ _pItems = pPsh->_pPages;
+ _numItems = pPsh->_numPages;
+ _dwFlags = pPsh->_dwFlags;
+
+ TreeView_SetImageList(_hWndTree, _hImages, TVSIL_NORMAL);
+ TreeView_SetItemHeight(_hWndTree, TreeView_GetItemHeight(_hWndTree) + 4);
+ SetUserData(_hWndTree, this);
+ rc = TRUE;
+ }
+ else
+ {
+ rc = FALSE;
+ }
+ return rc;
+}
+
+/**
+ * name: AddDummyItem
+ * class: CPsTree
+ * desc: insert an empty tree item group
+ * param: pszGroup - utf8 encoded string of the item to add
+ * return: index of the new item or -1 if failed to add
+ **/
+INT CPsTree::AddDummyItem(LPCSTR pszGroup)
+{
+ if (mir_stricmp(pszGroup, TREE_ROOTITEM))
+ {
+ CPsHdr psh;
+ psh._hContact = _pPs->hContact;
+ psh._pszProto = _pPs->pszProto;
+ psh._hImages = _hImages;
+ psh._pPages = _pItems;
+ psh._numPages = _numItems;
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = ghInst;
+ odp.flags = ODPF_TCHAR;
+ odp.ptszTitle = mir_utf8decodeT(pszGroup);
+
+ INT_PTR rc = UserInfo_AddPage((WPARAM)&psh, &odp);
+ mir_free(odp.ptszTitle);
+ if (!rc) {
+ _pItems = psh._pPages;
+ _numItems = psh._numPages;
+ return _numItems - 1;
+ }
+ }
+ return -1;
+}
+
+/**
+ * name: InitTreeItems()
+ * desc: initialize the tree control's datastructure
+ * param: needWidth - width to expand the tree by
+ * return: TRUE if initialization is ok, FALSE otherwise
+ **/
+BOOLEAN CPsTree::InitTreeItems(LPWORD needWidth)
+{
+ INT i;
+ DBVARIANT dbv;
+
+ if (!_hWndTree || !_pItems)
+ {
+ return FALSE;
+ }
+
+ if (!DB::Setting::GetUString(NULL, MODNAME, SET_LASTITEM, &dbv))
+ {
+ _curItem = FindItemIndexByName(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ }
+
+ // init the groups
+ if ((_dwFlags & PSTVF_GROUPS) || (!_pPs->hContact && myGlobals.CanChangeDetails))
+ {
+ LPSTR pszGroup;
+
+ // set treeview styles
+ TreeView_SetIndent(_hWndTree, 3);
+ SetWindowLongPtr(_hWndTree, GWL_STYLE, GetWindowLongPtr(_hWndTree, GWL_STYLE)|TVS_HASBUTTONS);
+
+ // init the iParent member for all the items
+ for (i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i] && (pszGroup = _pItems[i]->ParentItemName()) != NULL)
+ {
+ INT iParent = FindItemIndexByName(pszGroup);
+
+ // need to add an empty parent item
+ if (iParent == -1)
+ {
+ iParent = AddDummyItem(pszGroup);
+ }
+ _pItems[i]->Parent(iParent);
+ mir_free(pszGroup);
+ }
+ }
+ }
+
+ if (needWidth)
+ {
+ *needWidth = 0;
+ }
+ ShowWindow(_hWndTree, SW_HIDE);
+ for (i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i]->State() != DBTVIS_INVISIBLE)
+ {
+ ShowItem(i, needWidth);
+ }
+ }
+ ShowWindow(_hWndTree, SW_SHOW);
+ return TRUE;
+}
+
+/***********************************************************************************************************
+ * finding items
+ ***********************************************************************************************************/
+
+/**
+ * name: FindItemIndexByHandle
+ * class: CPsTree
+ * desc: returns the treeitem with specified handle
+ * param: hItem - handle of the treeview's treeitem
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+INT CPsTree::FindItemIndexByHandle(HTREEITEM hItem)
+{
+ INT i;
+ for (i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i] && hItem == _pItems[i]->Hti())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * name: FindItemIndexByHandle
+ * class: CPsTree
+ * desc: returns the treeitem with specified handle
+ * param: hItem - handle of the treeview's treeitem
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+INT CPsTree::FindItemIndexByName(LPCSTR pszName)
+{
+ INT i;
+ for (i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i] && _pItems[i]->HasName(pszName))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * name: FindItemByHandle
+ * class: CPsTree
+ * desc: returns the treeitem with specified handle
+ * param: hItem - handle of the treeview's treeitem
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByHandle(HTREEITEM hItem)
+{
+ INT i;
+
+ if ((i = FindItemIndexByHandle(hItem)) > -1)
+ {
+ return _pItems[i];
+ }
+ return NULL;
+}
+
+/**
+ * name: FindItemByName
+ * class: CPsTree
+ * desc: returns the treeitem with specified name
+ * param: pszName - name of the item to search for
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByName(LPCSTR pszName)
+{
+ INT i;
+
+ if ((i = FindItemIndexByName(pszName)) > -1)
+ {
+ return _pItems[i];
+ }
+ return NULL;
+}
+
+/**
+ * name: FindItemByHandle
+ * class: CPsTree
+ * desc: returns the treeitem with specified handle
+ * param: hItem - handle of the treeview's treeitem
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByResource(HINSTANCE hInst, INT idDlg)
+{
+ INT i;
+ for (i = 0; i < _numItems; i++)
+ {
+ if (_pItems[i] && _pItems[i]->Inst() == hInst && _pItems[i]->DlgId() == idDlg)
+ {
+ return _pItems[i];
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * name: FindItemHandleByName
+ * class: CPsTree
+ * desc: returns the treeitem with specified name
+ * param: pszName - name of the item to search for
+ * return: HTREEITEM if item exists or NULL otherwise
+ **/
+HTREEITEM CPsTree::FindItemHandleByName(LPCSTR pszName)
+{
+ INT i;
+
+ if ((i = FindItemIndexByName(pszName)) > -1)
+ {
+ return _pItems[i]->Hti();
+ }
+ return NULL;
+}
+
+/***********************************************************************************************************
+ * public methods
+ ***********************************************************************************************************/
+
+
+/**
+ * name: HideItem
+ * class: CPsTree
+ * desc: is called if icolib's icons have changed
+ * param: iPageIndex - the index of the treeitem in the array.
+ * return: nothing
+ **/
+VOID CPsTree::HideItem(const INT iPageIndex)
+{
+ if (IsIndexValid(iPageIndex))
+ {
+ TreeView_DeleteItem(_hWndTree, _pItems[iPageIndex]->Hti());
+ _pItems[iPageIndex]->Hti(0);
+ _dwFlags |= PSTVF_STATE_CHANGED;
+ }
+}
+
+/**
+ * name: ShowItem
+ * class: CPsTree
+ * desc: displays on of the items in the treeview
+ * param: iPageIndex - the index of the treeitem in the array.
+ * needWidth - gives and takes the width, the treeview must have to show all items properly
+ * return: TRUE if item was added successfully, FALSE otherwise
+ **/
+HTREEITEM CPsTree::ShowItem(const INT iPageIndex, LPWORD needWidth)
+{
+ TVINSERTSTRUCT tvii;
+ CPsTreeItem *pti;
+
+ // check parameters
+ if (!_hWndTree ||
+ !IsIndexValid(iPageIndex) ||
+ !(pti = _pItems[iPageIndex]) ||
+ !pti->Name() ||
+ !pti->Label())
+ {
+ MsgErr(GetParent(_hWndTree), LPGENT("Due to a parameter error, one of the treeitems can't be added!"));
+ return NULL;
+ }
+ // item is visible at the moment
+ if ((tvii.itemex.hItem = pti->Hti()) == NULL)
+ {
+ RECT rc;
+ const INT iParent = pti->Parent();
+
+ // init the rest of the treeitem
+ tvii.hParent = IsIndexValid(iParent) ? ShowItem(iParent, needWidth) : NULL;
+ tvii.hInsertAfter = (_dwFlags & PSTVF_SORTTREE) ? TVI_SORT : TVI_LAST;
+ tvii.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE;
+ tvii.itemex.pszText = pti->Label();
+ tvii.itemex.state = pti->State() == DBTVIS_EXPANDED ? TVIS_EXPANDED : 0;
+ tvii.itemex.stateMask = TVIS_EXPANDED;
+ tvii.itemex.lParam = iPageIndex;
+ // set images
+ if ((tvii.itemex.iImage = tvii.itemex.iSelectedImage = pti->Image()) != -1)
+ {
+ tvii.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ }
+ // insert item into tree if set visible
+ if ((tvii.itemex.hItem = TreeView_InsertItem(_hWndTree, &tvii)) == NULL)
+ {
+ MsgErr(GetParent(_hWndTree), LPGENT("An fatal error occured on adding a property sheet page!\nDialog creation aborted!"));
+ return NULL;
+ }
+ pti->Hti(tvii.itemex.hItem);
+ // calculate width of treeview
+ if (needWidth && TreeView_GetItemRect(_hWndTree, pti->Hti(), &rc, TRUE) && rc.right > *needWidth)
+ {
+ *needWidth = (WORD)rc.right;
+ }
+ }
+ return tvii.itemex.hItem;
+}
+
+/**
+ * name: MoveItem()
+ * class: CPsTree
+ * desc: moves a treeitem and its children to a new position
+ * param: pPs - datastructure of the propertysheetpage
+ * hItem - the HTREEITEM to move
+ * hInsertAfter - the HTREEITEM to insert hItem after
+ * bAsChild - tells, whether to try to add hItem as child of hInsertAfter or not
+ * return: handle to new (moved) treeitem if successful or NULL otherwise
+ **/
+HTREEITEM CPsTree::MoveItem(HTREEITEM hItem, HTREEITEM hInsertAfter, BOOLEAN bAsChild)
+{
+ TVINSERTSTRUCT tvis;
+ HTREEITEM hParent, hChild, hNewItem;
+ INT iItemIndex;
+
+ if (!hItem || !hInsertAfter)
+ return NULL;
+ if (hItem == hInsertAfter)
+ return hItem;
+
+ switch ((ULONG_PTR)hInsertAfter) {
+ case TVI_ROOT:
+ case TVI_FIRST:
+ case TVI_LAST:
+ hParent = NULL;
+ bAsChild = FALSE;
+ break;
+ default:
+ hParent = TreeView_GetParent(_hWndTree, hInsertAfter);
+ break;
+ }
+ // do not move a parent next to its own children!
+ if (hItem == hParent)
+ return hItem;
+ // get detailed information about the item to move
+ if (FAILED(iItemIndex = FindItemIndexByHandle(hItem)))
+ return hItem;
+
+ // item should be inserted as the first child of an existing root item
+ if (bAsChild) {
+ tvis.hParent = hInsertAfter;
+ tvis.hInsertAfter = (_dwFlags & PSTVF_SORTTREE) ? TVI_SORT : ((bAsChild == 2) ? TVI_LAST : TVI_FIRST);
+ }
+ // item should be inserted after an existing item
+ else {
+ tvis.hParent = hParent;
+ tvis.hInsertAfter = hInsertAfter;
+ }
+
+ // don't move subitems of a protocol to root as this would mean them not to be unique anymore
+ if (!_pPs->hContact && (_pItems[iItemIndex]->Flags() & PSPF_PROTOPREPENDED) && !tvis.hParent)
+ return hItem;
+
+ // prepare the insert structure
+ tvis.itemex.mask = TVIF_PARAM|TVIF_TEXT;
+ tvis.itemex.state = TVIS_EXPANDED;
+ tvis.itemex.stateMask = TVIS_EXPANDED;
+ tvis.itemex.pszText = _pItems[iItemIndex]->Label();
+ tvis.itemex.lParam = (LPARAM)iItemIndex;
+ if ((tvis.itemex.iImage = tvis.itemex.iSelectedImage = _pItems[iItemIndex]->Image()) >= 0)
+ tvis.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+
+ // insert the item
+ if (!(hNewItem = TreeView_InsertItem(_hWndTree, &tvis)))
+ return hItem;
+ // update handle pointer in the page structure
+ _pItems[iItemIndex]->Hti(hNewItem);
+ // get the index of the parent
+ _pItems[iItemIndex]->Parent(FindItemIndexByHandle(tvis.hParent));
+ // move children
+ hInsertAfter = hNewItem;
+ while (hChild = TreeView_GetChild(_hWndTree, hItem)) {
+ MoveItem(hChild, hInsertAfter, 2);
+ }
+ // delete old tree
+ TreeView_DeleteItem(_hWndTree, hItem);
+ _dwFlags |= PSTVF_POS_CHANGED;
+
+ TreeView_SelectItem(_hWndTree, hNewItem);
+ TreeView_Expand(_hWndTree, hNewItem, TVE_EXPAND);
+ return hNewItem;
+}
+
+/**
+ * name: SaveItemsState
+ * class: CPsTree
+ * desc: saves the tree's visible items to database
+ * param: pszGroup - name of the parent item of the current subtree
+ * hRootItem - the root of the current subtree
+ * iItem - index of the current item for position saving
+ * return: 0 on success or 1 otherwise
+ **/
+WORD CPsTree::SaveItemsState(LPCSTR pszGroup, HTREEITEM hRootItem, INT& iItem)
+{
+ TVITEMEX tvi;
+ WORD numErrors = 0;
+
+ tvi.mask = TVIF_CHILDREN|TVIF_STATE|TVIF_PARAM;
+ tvi.state = 0;
+ tvi.stateMask = TVIS_EXPANDED;
+ tvi.lParam = (LPARAM)-1;
+
+ // save all visible items
+ for (tvi.hItem = TreeView_GetChild(_hWndTree, hRootItem);
+ TreeView_GetItem(_hWndTree, &tvi);
+ tvi.hItem = TreeView_GetNextSibling(_hWndTree, tvi.hItem))
+ {
+ numErrors += _pItems[tvi.lParam]->DBSaveItemState(pszGroup, iItem++, tvi.state, _dwFlags);
+ if (tvi.cChildren) numErrors += SaveItemsState(_pItems[tvi.lParam]->Name(), tvi.hItem, iItem);
+ }
+ return numErrors;
+}
+
+/**
+ * name: SaveState
+ * class: CPsTree
+ * desc: saves the current tree to database
+ * param: none
+ * return: nothing
+ **/
+VOID CPsTree::SaveState()
+{
+ CPsTreeItem *pti = CurrentItem();
+
+ if (_hWndTree && (_dwFlags & (PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED|PSTVF_STATE_CHANGED))) {
+ SHORT i;
+ INT iItem = 0;
+
+ // save all visible items
+ WORD numErrors = SaveItemsState(TREE_ROOTITEM, TVGN_ROOT, iItem);
+
+ // save all invisible items of the current subtree
+ for (i = 0; i < _numItems; i++) {
+ if (!_pItems[i]->Hti()) {
+ LPSTR pszGroup;
+
+ if (!IsIndexValid(_pItems[i]->Parent()) || !(pszGroup = _pItems[_pItems[i]->Parent()]->Name()))
+ pszGroup = TREE_ROOTITEM;
+ numErrors += _pItems[i]->DBSaveItemState(pszGroup, iItem++, DBTVIS_INVISIBLE, _dwFlags);
+ }
+ }
+ // remove changed flags
+ RemoveFlags(PSTVF_STATE_CHANGED|PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED);
+ }
+
+ // save current selected item
+ if (pti) DB::Setting::WriteUString(SET_LASTITEM, pti->Name());
+ else DB::Setting::Delete(NULL, MODNAME, SET_LASTITEM);
+}
+
+/**
+ * name: DBResetState
+ * class: CPsTree
+ * desc: delete all treesettings from database
+ * param: pszGroup - name of the parent item of the current subtree
+ * hRootItem - the root of the current subtree
+ * iItem - index of the current item for position saving
+ * return: 0 on success or 1 otherwise
+ **/
+VOID CPsTree::DBResetState()
+{
+ DB::CEnumList Settings;
+
+ if (!Settings.EnumSettings(NULL, MODNAME))
+ {
+ INT i;
+ LPSTR s;
+ LPCSTR p;
+ INT_PTR c;
+
+ p = (_pPs->pszProto[0]) ? _pPs->pszProto : "Owner";
+ c = mir_strlen(p);
+
+ for (i = 0; i < Settings.getCount(); i++)
+ {
+ s = Settings[i];
+
+ if (s && *s == '{' && !mir_strnicmp(s + 1, p, c))
+ {
+ DB::Setting::Delete(NULL, MODNAME, s);
+ }
+ }
+ // keep only these flags
+ _dwFlags &= PSTVF_SORTTREE|PSTVF_GROUPS;
+ }
+}
+
+/***********************************************************************************************************
+ * public methods for handling label editing
+ ***********************************************************************************************************/
+
+/**
+ * name: TPropsheetTree_LabelEditProc()
+ * desc: subclussproc of the label editcontrol
+ * return: 0
+ **/
+static LRESULT CALLBACK TPropsheetTree_LabelEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_KEYDOWN:
+ switch(wParam)
+ {
+ case VK_RETURN:
+ return ((CPsTree*)GetUserData(hwnd))->EndLabelEdit(TRUE);
+ case VK_TAB:
+ case VK_ESCAPE:
+ return ((CPsTree*)GetUserData(hwnd))->EndLabelEdit(FALSE);
+ }
+ break;
+ case WM_KILLFOCUS:
+ ((CPsTree*)GetUserData(hwnd))->EndLabelEdit(FALSE);
+ break;
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS | CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam );
+ }
+ return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
+}
+
+/**
+ * name: BeginLabelEdit
+ * class: CPsTree
+ * desc: begins the labeledit mode
+ * param: hItem - handle of the treeitm whose label to edit
+ * return: 0
+ **/
+INT CPsTree::BeginLabelEdit(HTREEITEM hItem)
+{
+ CPsTreeItem* pti;
+
+ // tree is readonly
+ if (DB::Setting::GetByte(SET_PROPSHEET_READONLYLABEL, 0))
+ return 0;
+
+ // get item text
+ if (!hItem && !(hItem = TreeView_GetSelection(_hWndTree)))
+ return 0;
+ if (pti = FindItemByHandle(hItem))
+ {
+ RECT rc, rcTree;
+
+ // create the edit control
+ GetClientRect(_hWndTree, &rcTree);
+ TreeView_GetItemRect(_hWndTree, hItem, &rc, TRUE);
+ _hLabelEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY|WS_EX_CLIENTEDGE,
+ _T( "EDIT" ),
+ pti->Label(),
+ WS_VISIBLE|ES_AUTOHSCROLL|WS_CHILD,
+ rc.left, rc.top,
+ rcTree.right - rc.left, rc.bottom - rc.top,
+ _hWndTree,
+ NULL,
+ ghInst,
+ NULL );
+ if(_hLabelEdit)
+ {
+ _hDragItem = hItem;
+ SetUserData(_hLabelEdit, this);
+ DefEditProc = (WNDPROC)SetWindowLongPtr(_hLabelEdit,GWLP_WNDPROC, (LONG_PTR)TPropsheetTree_LabelEditProc );
+ SendMessage(_hLabelEdit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0 );
+ Edit_SetSel(_hLabelEdit, 0, -1);
+ Edit_LimitText(_hLabelEdit, MAX_TINAME);
+ SetFocus(_hLabelEdit);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * name: EndLabelEdit
+ * class: CPsTree
+ * desc: exits the labeledit mode
+ * bSave - tell whether to save changes or not
+ * return: 0
+ **/
+INT CPsTree::EndLabelEdit(const BOOLEAN bSave)
+{
+ TCHAR szEdit[MAX_TINAME];
+ TVITEM tvi;
+ WORD cchEdit;
+
+ if (bSave && (cchEdit = Edit_GetText(_hLabelEdit, szEdit, MAX_TINAME)) > 0)
+ {
+ tvi.hItem = _hDragItem;
+ tvi.mask = TVIF_TEXT|TVIF_HANDLE;
+ tvi.pszText = szEdit;
+ if (TreeView_SetItem(_hWndTree, &tvi))
+ {
+ CPsTreeItem* pti;
+ if (pti = FindItemByHandle(_hDragItem))
+ {
+ pti->Rename(szEdit);
+ }
+ _dwFlags |= PSTVF_LABEL_CHANGED;
+ }
+ }
+ DestroyWindow(_hLabelEdit);
+ _hLabelEdit = NULL;
+ _hDragItem = NULL;
+ return 0;
+}
+
+/***********************************************************************************************************
+ * public methods for handling other commands
+ ***********************************************************************************************************/
+
+VOID CPsTree::PopupMenu()
+{
+ HMENU hPopup;
+ MENUITEMINFO mii;
+ TVHITTESTINFO hti;
+ TVITEM tvi;
+ POINT pt;
+ INT iItem, i;
+
+ // init popup menu
+ if (!(hPopup = CreatePopupMenu()))
+ return;
+ ZeroMemory(&mii, sizeof(MENUITEMINFO));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STRING|MIIM_ID;
+
+ // get cursor postion
+ GetCursorPos(&pt);
+ hti.pt = pt;
+ ScreenToClient(_hWndTree, &hti.pt);
+
+ tvi.mask = TVIF_PARAM|TVIF_CHILDREN;
+ // find treeitem under cursor
+ TreeView_HitTest(_hWndTree, &hti);
+ if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ tvi.hItem = hti.hItem;
+ TreeView_GetItem(_hWndTree, &tvi);
+
+ if (!DB::Setting::GetByte(SET_PROPSHEET_READONLYLABEL, FALSE)) {
+ mii.dwTypeData = TranslateT("Rename Item");
+ mii.wID = 32001;
+ InsertMenuItem(hPopup, 0, FALSE, &mii);
+ }
+
+ // do not allow hiding groups
+ if (tvi.cChildren) {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ }
+ mii.dwTypeData = TranslateT("Hide Item");
+ mii.wID = 32000;
+ InsertMenuItem(hPopup, 0, FALSE, &mii);
+ }
+ else {
+ // add hidden items to menu
+ mii.wID = 0;
+ for (i = 0; i < _numItems; i++) {
+ if (!_pItems[i]->Hti()) {
+ mii.dwTypeData = _pItems[i]->Label();
+ mii.wID = 100 + i;
+ InsertMenuItem(hPopup, 0, FALSE, &mii);
+ }
+ }
+ // add headline
+ if (mii.wID > 0) {
+ mii.fMask |= MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+ mii.dwTypeData = TranslateT("Show Items:");
+ mii.wID = 0;
+ InsertMenuItem(hPopup, 0, TRUE, &mii);
+ mii.fMask |= MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(hPopup, 1, TRUE, &mii);
+ InsertMenuItem(hPopup, ++i, TRUE, &mii);
+ }
+ mii.fMask &= ~(MIIM_FTYPE|MIIM_STATE);
+ mii.dwTypeData = TranslateT("Reset to defaults");
+ mii.wID = 32004;
+ InsertMenuItem(hPopup, ++i, TRUE, &mii);
+ }
+ // show the popup menu
+ iItem = TrackPopupMenu(hPopup, TPM_RETURNCMD, pt.x, pt.y, 0, _hWndTree, NULL);
+ DestroyMenu(hPopup);
+
+ switch (iItem) {
+ // hide the item
+ case 32000:
+ HideItem(tvi.lParam);
+ break;
+ // rename the item
+ case 32001:
+ BeginLabelEdit(tvi.hItem);
+ break;
+ // reset current tree
+ case 32004:
+ DBResetState();
+ break;
+ // show a hidden item
+ default:
+ if ((iItem -= 100) >= 0 && ShowItem(iItem, NULL))
+ AddFlags(PSTVF_STATE_CHANGED|PSTVF_POS_CHANGED);
+ break;
+ }
+}
+
+/***********************************************************************************************************
+ * public event handlers
+ ***********************************************************************************************************/
+
+/**
+ * name: OnIconsChanged
+ * class: CPsTree
+ * desc: is called if icolib's icons have changed
+ * param: none
+ * return: nothing
+ **/
+VOID CPsTree::OnIconsChanged()
+{
+ for (INT i = 0; i < _numItems; i++) {
+ _pItems[i]->OnIconsChanged(this);
+ }
+}
+
+/**
+ * name: OnInfoChanged
+ * class: CPsTree
+ * desc: contact information have changed and pages need an update
+ * param: none
+ * return: TRUE if any page holds changed information
+ **/
+BOOLEAN CPsTree::OnInfoChanged()
+{
+ PSHNOTIFY pshn;
+ INT i;
+ BYTE bChanged = 0;
+
+ pshn.hdr.idFrom = 0;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ for (i = 0; i < _numItems; i++) {
+ pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+ if (pshn.hdr.hwndFrom != NULL) {
+ pshn.lParam = (LPARAM)_pItems[i]->hContact();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ if (PSP_CHANGED == GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT))
+ bChanged |= 1;
+ else
+ _pItems[i]->RemoveFlags(PSPF_CHANGED);
+ }
+ }
+ return bChanged;
+}
+
+/**
+ * name: OnSelChanging
+ * class: CPsTree
+ * desc: the displayed page is up to change
+ * param: none
+ * return: nothing
+ **/
+BOOLEAN CPsTree::OnSelChanging()
+{
+ CPsTreeItem *pti = CurrentItem();
+
+ if (pti != NULL) {
+ TreeView_SetItemState(_hWndTree, pti->Hti(), 0, TVIS_SELECTED);
+ if (pti->Wnd() != NULL) {
+ PSHNOTIFY pshn;
+
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = pti->Wnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)pti->hContact();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ SetWindowLongPtr(_pPs->hDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * name: OnSelChanged
+ * class: CPsTree
+ * desc: it handles the change of displayed page
+ * param: lpnmtv - notification structure
+ * return: nothing
+ **/
+VOID CPsTree::OnSelChanged(LPNMTREEVIEW lpnmtv)
+{
+ CPsTreeItem *oldPage = CurrentItem();
+ CPsTreeItem *pti;
+
+ _curItem = (INT)lpnmtv->itemNew.lParam;
+ if (pti = CurrentItem()) {
+ if (pti->Wnd() == NULL) {
+ pti->CreateWnd(_pPs);
+ }
+ }
+ // hide old page even if new item has no valid one
+ if (oldPage && oldPage->Wnd() != NULL)
+ ShowWindow(oldPage->Wnd(), SW_HIDE);
+ if (pti)
+ ShowWindow(pti->Wnd(), SW_SHOW);
+ if (lpnmtv->action != TVC_BYMOUSE)
+ SetFocus(_hWndTree);
+}
+
+/**
+ * name: OnCancel
+ * class: CPsTree
+ * desc: asks pages to reset their information
+ * param: none
+ * return: nothing
+ **/
+VOID CPsTree::OnCancel()
+{
+ PSHNOTIFY pshn;
+ INT i;
+
+ pshn.hdr.idFrom = 0;
+ pshn.hdr.code = PSN_RESET;
+ for (i = 0; i < _numItems; i++) {
+ pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+ if (pshn.hdr.hwndFrom && (_pItems[i]->Flags() & PSPF_CHANGED)) {
+ pshn.lParam = (LPARAM)_pItems[i]->hContact();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ }
+}
+
+/**
+ * name: OnApply
+ * class: CPsTree
+ * desc: saves settings of all pages
+ * param: none
+ * return: 0 if ready to continue, 1 if need to abort
+ **/
+INT CPsTree::OnApply()
+{
+ CPsTreeItem *pti = CurrentItem();
+
+ if (pti != NULL) {
+ PSHNOTIFY pshn;
+ INT i;
+
+ pshn.hdr.idFrom = 0;
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = pti->Wnd();
+ if (!SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ // save everything to database
+ pshn.hdr.code = PSN_APPLY;
+ for (i = 0; i < _numItems; i++) {
+ pshn.lParam = (LPARAM)_pItems[i]->hContact();
+ if (_pItems[i] && _pItems[i]->Wnd() && _pItems[i]->Flags() & PSPF_CHANGED) {
+ pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn) == PSNRET_INVALID_NOCHANGEPAGE) {
+ CPsTreeItem *pti;
+ if (pti = CurrentItem())
+ ShowWindow(pti->Wnd(), SW_HIDE);
+ _curItem = i;
+ ShowWindow(_pItems[i]->Wnd(), SW_SHOW);
+ return 1;
+ }
+ _pItems[i]->RemoveFlags(PSPF_CHANGED);
+ }
+ }
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/plugins/UserInfoEx/src/classPsTreeItem.cpp b/plugins/UserInfoEx/src/classPsTreeItem.cpp
new file mode 100644
index 0000000000..4f8b641691
--- /dev/null
+++ b/plugins/UserInfoEx/src/classPsTreeItem.cpp
@@ -0,0 +1,697 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classPsTreeItem.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "m_skin.h"
+
+/**
+ * name: BoldGroupTitlesEnumChildren()
+ * desc: set font of groupboxes to bold
+ *
+ * return: 0
+ **/
+BOOL CALLBACK BoldGroupTitlesEnumChildren(HWND hWnd, LPARAM lParam)
+{
+ TCHAR szClass[64];
+ GetClassName(hWnd, szClass, 64);
+ if (!mir_tcscmp(szClass, _T("Button")) && (GetWindowLongPtr(hWnd, GWL_STYLE) & 0x0F) == BS_GROUPBOX)
+ SendMessage(hWnd, WM_SETFONT, lParam, NULL);
+ return TRUE;
+}
+
+/**
+ * name: CPsTreeItem
+ * class: CPsTreeItem
+ * desc: default constructor
+ * param: pPsh - propertysheet's init structure
+ * odp - optiondialogpage structure with the info about the item to add
+ * return: nothing
+ **/
+CPsTreeItem::CPsTreeItem()
+{
+ _idDlg = NULL;
+ _pTemplate = NULL;
+ _hInst = NULL;
+ _pfnDlgProc = NULL;
+ _hWnd = NULL;
+ _dwFlags = NULL;
+ _hItem = NULL; // handle to the treeview item
+ _iParent = -1; // index to the parent item
+ _iImage = -1; // index of treeview item's image
+ _bState = NULL; // initial state of this treeitem
+ _pszName = NULL; // original name, given by plugin (not customized)
+ _ptszLabel = NULL;
+ _pszProto = NULL;
+ _pszPrefix = NULL;
+ _hContact = NULL;
+}
+
+/**
+ * name: ~CPsTreeItem
+ * class: CPsTreeItem
+ * desc: default destructor
+ * param: none
+ * return: nothing
+ **/
+CPsTreeItem::~CPsTreeItem()
+{
+ if (_hWnd) DestroyWindow(_hWnd);
+ if (_pszName) mir_free(_pszName);
+ if (_ptszLabel) mir_free(_ptszLabel);
+ if (_pszProto) mir_free((LPVOID)_pszProto);
+}
+
+/**
+ * name: PropertyKey
+ * class: CPsTreeItem
+ * desc: merge the treeitem's unique name with a prefix to form a setting string
+ * param: pszProperty - the prefix to add
+ * return: pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::PropertyKey(LPCSTR pszProperty)
+{
+ static CHAR pszSetting[MAXSETTING];
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), "{%s\\%s}_%s", _pszPrefix, _pszName, pszProperty);
+ return pszSetting;
+}
+
+/**
+ * name: GlobalName
+ * class: CPsTreeItem
+ * desc: return item name without prepended protocol name
+ * param: nothing
+ * return: item name without protocol name
+ **/
+LPCSTR CPsTreeItem::GlobalName()
+{
+ LPSTR pgn = NULL;
+
+ if (_dwFlags & PSPF_PROTOPREPENDED)
+ {
+ pgn = mir_strchr(_pszName, '\\');
+ if (pgn && pgn[1]) pgn++;
+ }
+ return (!pgn || !*pgn) ?_pszName : pgn;
+}
+
+/**
+ * name: GlobalPropertyKey
+ * class: CPsTreeItem
+ * desc: merge the treeitem's unique name with a prefix to form a setting string
+ * param: pszProperty - the prefix to add
+ * return: pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::GlobalPropertyKey(LPCSTR pszProperty)
+{
+ static CHAR pszSetting[MAXSETTING];
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), "{Global\\%s}_%s", GlobalName(), pszProperty);
+ return pszSetting;
+}
+
+/**
+ * name: IconKey
+ * class: CPsTreeItem
+ * desc: merge the treeitem's unique name with a prefix to form a setting string
+ * param: pszProperty - the prefix to add
+ * return: pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::IconKey()
+{
+ LPCSTR pszIconName = GlobalName();
+ if (pszIconName)
+ {
+ static CHAR pszSetting[MAXSETTING];
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), MODNAME"_{%s}", pszIconName);
+ return pszSetting;
+ }
+ return NULL;
+}
+
+/**
+ * name: ParentItemName()
+ * class: CPsTreeItem
+ * desc: returns the unique name for the parent item
+ * param: nothing
+ * return: length of group name
+ **/
+LPSTR CPsTreeItem::ParentItemName()
+{
+ DBVARIANT dbv;
+ LPSTR result;
+
+ // try to read the parent item from the database
+ if (!DB::Setting::GetAString(NULL, MODNAME, PropertyKey(SET_ITEM_GROUP), &dbv))
+ {
+ result = dbv.pszVal;
+ }
+ else
+ {
+ const CHAR* p = mir_strrchr(_pszName, '\\');
+
+ if (p)
+ {
+ INT cchGroup = p - _pszName + 1;
+ result = mir_strncpy((LPSTR)mir_alloc(cchGroup), _pszName, cchGroup);
+ }
+ else
+ {
+ result = NULL;
+ }
+ }
+ return result;
+}
+
+/**
+ * name: SetName
+ * class: CPsTreeItem
+ * desc: set the unique name for this item from a given title as it comes with OPTIONDIALOGPAGE
+ * param: ptszTitle - the title which is the base for the unique name
+ * bIsUnicode - if TRUE the title is unicode
+ * return: 0 on success, 1 to 4 indicating the failed operation
+ **/
+INT CPsTreeItem::Name(LPTSTR ptszTitle, const BOOLEAN bIsUnicode)
+{
+ // convert title to utf8
+ _pszName = (bIsUnicode) ? mir_utf8encodeW((LPWSTR)ptszTitle) : mir_utf8encode((LPSTR)ptszTitle);
+ if (_pszName)
+ {
+ // convert disallowed characters
+ for (DWORD i = 0; _pszName[i] != 0; i++)
+ {
+ switch (_pszName[i])
+ {
+ case '{': _pszName[i] = '('; break;
+ case '}': _pszName[i] = ')'; break;
+ }
+ }
+ }
+ return _pszName == NULL;
+}
+
+/**
+ * name: HasName
+ * class: CPsTreeItem
+ * desc: returns true, if current item has the name provided by the parameter
+ * params: pszName - the name to match the item with
+ * return: nothing
+ **/
+BOOLEAN CPsTreeItem::HasName(const LPCSTR pszName) const
+{
+ return !mir_stricmp(_pszName, pszName);
+};
+
+/**
+ * name: Rename
+ * class: CPsTreeItem
+ * desc: Rename label for the treeitem
+ * params: pszLabel - the desired new label
+ * return: nothing
+ **/
+VOID CPsTreeItem::Rename(const LPTSTR pszLabel)
+{
+ if(pszLabel && *pszLabel)
+ {
+ LPTSTR pszDup = mir_tcsdup(pszLabel);
+ if(pszDup)
+ {
+ if(_ptszLabel)
+ {
+ mir_free(_ptszLabel);
+ }
+ _ptszLabel = pszDup;
+ // convert disallowed characters
+ while(*pszDup)
+ {
+ switch(*pszDup)
+ {
+ case '{': *pszDup = '('; break;
+ case '}': *pszDup = ')'; break;
+ }
+ pszDup++;
+ }
+ AddFlags(PSTVF_LABEL_CHANGED);
+ }
+ }
+}
+
+/**
+ * name: ItemLabel
+ * class: CPsTreeItem
+ * desc: returns the label for a given item. The returned value must be freed after use!
+ * param: pszName - uniquely identifiing string for a propertypage encoded with utf8 (e.g.: {group\item})
+ * return: Label in a newly allocated piece of memory
+ **/
+INT CPsTreeItem::ItemLabel(const BOOLEAN bReadDBValue)
+{
+ DBVARIANT dbv;
+
+ // clear existing
+ if (_ptszLabel)
+ {
+ mir_free(_ptszLabel);
+ }
+
+ // try to get custom label from database
+ if (!bReadDBValue || DB::Setting::GetTString(NULL, MODNAME, GlobalPropertyKey(SET_ITEM_LABEL), &dbv) || (_ptszLabel = dbv.ptszVal) == NULL)
+ {
+ // extract the name
+ LPSTR pszName = mir_strrchr(_pszName, '\\');
+ if (pszName && pszName[1])
+ {
+ pszName++;
+ }
+ else
+ {
+ pszName = _pszName;
+ }
+
+ LPTSTR ptszLabel = mir_utf8decodeT(pszName);
+ if (ptszLabel)
+ {
+ _ptszLabel = mir_tcsdup(TranslateTS(ptszLabel));
+ mir_free(ptszLabel);
+ }
+ }
+ // return nonezero if label is invalid
+ return _ptszLabel == NULL;
+}
+
+/**
+ * name: ProtoIcon
+ * class: CPsTreeItem
+ * desc: check if current tree item name is a protocol name and return its icon if so
+ * params: none
+ * return: nothing
+ **/
+HICON CPsTreeItem::ProtoIcon()
+{
+ PROTOACCOUNT **pa;
+ INT ProtoCount, i;
+
+ if (!CallService(MS_PROTO_ENUMACCOUNTS, (WPARAM)&ProtoCount, (LPARAM)&pa))
+ {
+ if (_pszName)
+ {
+ for (i = 0; i < ProtoCount; i++)
+ {
+ if (pa[i]->type == PROTOTYPE_PROTOCOL)
+ {
+ TCHAR *ptszName = mir_a2t(_pszName);
+ if (!mir_tcsnicmp(pa[i]->tszAccountName, ptszName, mir_tcslen(pa[i]->tszAccountName)))
+ {
+ HICON hIco;
+ CHAR szIconID[MAX_PATH];
+
+ mir_snprintf(szIconID, SIZEOF(szIconID), "core_status_%s1", pa[i]->szModuleName);
+ hIco = IcoLib_GetIcon(szIconID);
+ if (!hIco)
+ {
+ hIco = (HICON)CallProtoService(pa[i]->szModuleName, PS_LOADICON, PLI_PROTOCOL, NULL);
+ }
+ MIR_FREE (ptszName);
+ return hIco;
+ }
+ MIR_FREE (ptszName);
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * name: Icon
+ * class: CPsTreeItem
+ * desc: load the icon, add to icolib if required and add to imagelist of treeview
+ * params: hIml - treeview's imagelist to add the icon to
+ * odp - pointer to OPTIONSDIALOGPAGE providing the information about the icon to load
+ * hDefaultIcon - default icon to use
+ * return: nothing
+ **/
+INT CPsTreeItem::Icon(HIMAGELIST hIml, OPTIONSDIALOGPAGE *odp, BOOLEAN bInitIconsOnly)
+{
+ HICON hIcon;
+
+ // check parameter
+ if (!_pszName || !odp)
+ return 1;
+
+ // load the icon if no icolib is installed or creating the required settingname failed
+ LPCSTR pszIconName = IconKey();
+
+ // use icolib to handle icons
+ if (!(hIcon = IcoLib_GetIcon(pszIconName)))
+ {
+ SKINICONDESC sid;
+
+ ZeroMemory(&sid, sizeof(sid));
+ sid.cbSize = sizeof(sid);
+ sid.flags = SIDF_ALL_TCHAR;
+ sid.cx = GetSystemMetrics(SM_CXSMICON);
+ sid.cy = GetSystemMetrics(SM_CYSMICON);
+ sid.pszName = (LPSTR)pszIconName;
+ sid.ptszDescription = _ptszLabel;
+ sid.ptszSection = LPGENT(SECT_TREE);
+
+ // the item to insert brings along an icon?
+ if (odp->flags & ODPF_ICON) {
+ // is it uinfoex item?
+ if (odp->hInstance == ghInst) {
+
+ // the pszGroup holds the iconfile for items added by uinfoex
+ sid.ptszDefaultFile = odp->ptszGroup;
+
+ // icon library exists?
+ if (sid.ptszDefaultFile) {
+ sid.iDefaultIndex = (INT_PTR)odp->hIcon;
+ }
+ // no valid icon library
+ else {
+ sid.hDefaultIcon = ImageList_GetIcon(hIml, 0, ILD_NORMAL);;
+ sid.iDefaultIndex = -1;
+ }
+ }
+ // default icon is delivered by the page to add
+ else {
+ sid.hDefaultIcon = (odp->hIcon) ? odp->hIcon : ImageList_GetIcon(hIml, 0, ILD_NORMAL);
+ sid.iDefaultIndex = -1;
+ }
+ }
+ // no icon to add, use default
+ else {
+ sid.iDefaultIndex = -1;
+ sid.hDefaultIcon = ProtoIcon();
+ if (!sid.hDefaultIcon) sid.hDefaultIcon = ImageList_GetIcon(hIml, 0, ILD_NORMAL);
+ }
+ // add file to icolib
+ Skin_AddIcon(&sid);
+
+ if (!bInitIconsOnly)
+ hIcon = IcoLib_GetIcon(pszIconName);
+ }
+
+ if (!bInitIconsOnly && hIml) {
+ // set custom icon to image list
+ if (hIcon) return ((_iImage = ImageList_AddIcon(hIml, hIcon)) == -1);
+ _iImage = 0;
+ }
+ else
+ _iImage = -1;
+ return 0;
+}
+
+/**
+ * name: OnAddPage
+ * class: CPsTreeItem
+ * desc: inits the treeitem's attributes
+ * params: pPsh - pointer to the property page's header structure
+ * odp - OPTIONSDIALOGPAGE structure with the information about the page to add
+ * return: 0 on success, 1 on failure
+ **/
+INT CPsTreeItem::Create(CPsHdr* pPsh, OPTIONSDIALOGPAGE *odp)
+{
+ INT err;
+ TCHAR szTitle[ MAXSETTING ];
+
+ // check parameter
+ if (pPsh && pPsh->_dwSize == sizeof(CPsHdr) && odp && PtrIsValid(odp->hInstance))
+ {
+ // instance value
+ _hInst = odp->hInstance;
+ _dwFlags = odp->flags;
+ _initParam = odp->dwInitParam;
+
+ // init page owning contact
+ _hContact = pPsh->_hContact;
+ _pszProto = mir_strdup(pPsh->_pszProto);
+
+ // global settings prefix for current contact (is dialog owning contact's protocol by default)
+ _pszPrefix = (pPsh->_pszPrefix) ? pPsh->_pszPrefix : "Owner";
+
+ if (pPsh->_dwFlags & PSF_PROTOPAGESONLY)
+ {
+ if (_dwFlags & ODPF_USERINFOTAB)
+ {
+ mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s %d\\%s"), odp->ptszTitle, pPsh->_nSubContact+1, odp->ptszTab);
+ }
+ else
+ {
+ mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s %d"), odp->ptszTitle, pPsh->_nSubContact+1);
+ }
+ }
+ else
+ {
+ if (_dwFlags & ODPF_USERINFOTAB)
+ {
+ mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s\\%s"), odp->ptszTitle, odp->ptszTab);
+ }
+ else
+ {
+ mir_tcscpy(szTitle, odp->ptszTitle);
+ }
+ }
+ // set the unique utf8 encoded name for the item
+ if (err = Name(szTitle, (_dwFlags & ODPF_UNICODE) == ODPF_UNICODE))
+ {
+ MsgErr(NULL, LPGENT("Creating unique name for a page failed with %d and error code %d"), err, GetLastError());
+ }
+ // read label from database or create it
+ else if (err = ItemLabel(TRUE))
+ {
+ MsgErr(NULL, LPGENT("Creating the label for a page failed with %d and error code %d"), err, GetLastError());
+ }
+ else
+ {
+ // load icon for the item
+ Icon(pPsh->_hImages, odp, (pPsh->_dwFlags & PSTVF_INITICONS) == PSTVF_INITICONS);
+
+ // the rest is not needed if only icons are loaded
+ if (pPsh->_dwFlags & PSTVF_INITICONS)
+ {
+ return 0;
+ }
+
+ // load custom order
+ if (!(pPsh->_dwFlags & PSTVF_SORTTREE))
+ {
+ _iPosition = (INT)DB::Setting::GetByte(PropertyKey(SET_ITEM_POS), odp->position);
+ if ((_iPosition < 0) && (_iPosition > 0x800000A))
+ _iPosition = 0;
+ }
+ // read visibility state
+ _bState = DB::Setting::GetByte(PropertyKey(SET_ITEM_STATE), DBTVIS_EXPANDED);
+
+ // error for no longer supported dialog template type
+ if (((UINT_PTR)odp->pszTemplate & 0xFFFF0000))
+ {
+ MsgErr(NULL, LPGENT("The dialog template type is no longer supported"));
+ }
+ else
+ {
+ // fetch dialog resource id
+ _idDlg = (INT_PTR)odp->pszTemplate;
+ // dialog procedure
+ _pfnDlgProc = odp->pfnDlgProc;
+
+ // is dummy item?
+ if (!_idDlg && !_pfnDlgProc)
+ return 0;
+
+ if (_idDlg && _pfnDlgProc)
+ {
+ // lock the property pages dialog resource
+ _pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(_hInst, FindResource(_hInst, (LPCTSTR)(UINT_PTR)_idDlg, RT_DIALOG)));
+ if (_pTemplate)
+ {
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/**
+ * name: DBSaveItemState
+ * class: CPsTreeItem
+ * desc: saves the current treeitem to database
+ * param: pszGroup - name of the parent item
+ * iItemPosition - iterated index to remember the order of the tree
+ * iState - expanded|collapsed|invisible
+ * dwFlags - tells what to save
+ * return: handle to new (moved) treeitem if successful or NULL otherwise
+ **/
+WORD CPsTreeItem::DBSaveItemState(LPCSTR pszGroup, INT iItemPosition, UINT iState, DWORD dwFlags)
+{
+ WORD numErrors = 0;
+
+ // save group
+ if ((dwFlags & PSTVF_GROUPS) && (dwFlags & PSTVF_POS_CHANGED)) {
+ numErrors += DB::Setting::WriteUString(PropertyKey(SET_ITEM_GROUP), (LPSTR)pszGroup);
+ }
+ // save label
+ if ((dwFlags & PSTVF_LABEL_CHANGED) && (_dwFlags & PSTVF_LABEL_CHANGED)) {
+ numErrors += DB::Setting::WriteTString(GlobalPropertyKey(SET_ITEM_LABEL), Label());
+ }
+ // save position
+ if ((dwFlags & PSTVF_POS_CHANGED) && !(dwFlags & PSTVF_SORTTREE)) {
+ numErrors += DB::Setting::WriteByte(PropertyKey(SET_ITEM_POS), iItemPosition);
+ }
+ // save state
+ if (dwFlags & PSTVF_STATE_CHANGED) {
+ numErrors += DB::Setting::WriteByte(PropertyKey(SET_ITEM_STATE),
+ _hItem ? ((iState & TVIS_EXPANDED) ? DBTVIS_EXPANDED : DBTVIS_NORMAL) : DBTVIS_INVISIBLE);
+ }
+ RemoveFlags(PSTVF_STATE_CHANGED|PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED);
+ return numErrors;
+}
+
+/**
+ * name: CreateWnd
+ * class: CPsTreeItem
+ * desc: create the dialog for the propertysheet page
+ * params: pPs - propertysheet's datastructure
+ * hDlg - windowhandle of the propertysheet
+ * return: windowhandle of the dialog if successful
+ **/
+HWND CPsTreeItem::CreateWnd(LPPS pPs)
+{
+ if (pPs && !_hWnd && _pTemplate && _pfnDlgProc)
+ {
+ _hWnd = CreateDialogIndirectParam(_hInst, _pTemplate, pPs->hDlg, _pfnDlgProc, (LPARAM)_hContact);
+ if (_hWnd != NULL)
+ {
+ PSHNOTIFY pshn;
+
+ pshn.hdr.code = PSN_PARAMCHANGED;
+ pshn.hdr.hwndFrom = _hWnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)_initParam;
+ SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn);
+
+ // force child window (mainly for AIM property page)
+ SetWindowLongPtr(_hWnd, GWL_STYLE, (GetWindowLongPtr(_hWnd, GWL_STYLE) & ~(WS_POPUP|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME)) | WS_CHILD);
+ SetWindowLongPtr(_hWnd, GWL_EXSTYLE, GetWindowLongPtr(_hWnd, GWL_EXSTYLE) & ~(WS_EX_APPWINDOW|WS_EX_STATICEDGE|WS_EX_CLIENTEDGE));
+ SetParent(_hWnd, pPs->hDlg);
+
+ // move dialog into the display area
+ SetWindowPos(_hWnd, HWND_TOP,
+ pPs->rcDisplay.left, pPs->rcDisplay.top,
+ pPs->rcDisplay.right - pPs->rcDisplay.left,
+ pPs->rcDisplay.bottom - pPs->rcDisplay.top, FALSE);
+ // set bold titles
+ if (_dwFlags & ODPF_BOLDGROUPS)
+ {
+ EnumChildWindows(_hWnd, BoldGroupTitlesEnumChildren, (LPARAM)pPs->hBoldFont);
+ }
+
+ // some initial notifications
+ OnInfoChanged();
+ OnPageIconsChanged();
+ return _hWnd;
+ }
+ }
+ return NULL;
+}
+
+/***********************************************************************************************************
+ * public event handlers
+ ***********************************************************************************************************/
+
+/**
+ * name: OnInfoChanged
+ * class: CPsTreeItem
+ * desc: Notifies the propertypage of changed contact information
+ * params: none
+ * return: nothing
+ **/
+VOID CPsTreeItem::OnInfoChanged()
+{
+ if (_hWnd) {
+ PSHNOTIFY pshn;
+
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = _hWnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)_hContact;
+ if (PSP_CHANGED != SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ _dwFlags &= ~PSPF_CHANGED;
+ }
+ }
+}
+
+/**
+ * name: OnPageIconsChanged
+ * class: CPsTreeItem
+ * desc: Notifies the propertypage of changed icons in icolib
+ * params: none
+ * return: nothing
+ **/
+VOID CPsTreeItem::OnPageIconsChanged()
+{
+ if (_hWnd) {
+ PSHNOTIFY pshn;
+
+ pshn.hdr.code = PSN_ICONCHANGED;
+ pshn.hdr.hwndFrom = _hWnd;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = (LPARAM)_hContact;
+ SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+/**
+ * name: OnIconsChanged
+ * class: CPsTreeItem
+ * desc: Handles reloading icons if changed by icolib
+ * params: none
+ * return: nothing
+ **/
+VOID CPsTreeItem::OnIconsChanged(CPsTree *pTree)
+{
+ HICON hIcon;
+ RECT rc;
+
+ // update tree item icons
+ if (pTree->ImageList() && (hIcon = IcoLib_GetIcon(IconKey())) != NULL) {
+ _iImage = (_iImage > 0)
+ ? ImageList_ReplaceIcon(pTree->ImageList(), _iImage, hIcon)
+ : ImageList_AddIcon(pTree->ImageList(), hIcon);
+
+ if (_hItem && TreeView_GetItemRect(pTree->Window(), _hItem, &rc, 0)) {
+ InvalidateRect(pTree->Window(), &rc, TRUE);
+ }
+ }
+ // update pages icons
+ OnPageIconsChanged();
+}
+
diff --git a/plugins/UserInfoEx/src/commonheaders.cpp b/plugins/UserInfoEx/src/commonheaders.cpp
new file mode 100644
index 0000000000..608d7ddbfc
--- /dev/null
+++ b/plugins/UserInfoEx/src/commonheaders.cpp
@@ -0,0 +1,180 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/commonheaders.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+// global:
+HINSTANCE ghInst = NULL;
+TIME_API tmi; //timezone interface
+FI_INTERFACE *FIP = NULL; //freeimage interface
+CLIST_INTERFACE *pcli = NULL;
+
+MGLOBAL myGlobals;
+pfnDwmIsCompositionEnabled dwmIsCompositionEnabled;
+
+/**
+ * Calculates an unique DWORD number from a string.
+ * It's the same as used in langpack.
+ *
+ * @param szStr - string to calculate the hash value for
+ * @return the unique id for the szStr
+ **/
+
+#if __GNUC__
+#define NOINLINEASM
+#endif
+
+DWORD hashSetting(LPCSTR szStr)
+{
+#if defined _M_IX86 && !defined _NUMEGA_BC_FINALCHECK && !defined NOINLINEASM
+ __asm
+ {
+ xor edx,edx
+ xor eax,eax
+ mov esi,szStr
+ mov al,[esi]
+ dec esi
+ xor cl,cl
+ lph_top: //only 4 of 9 instructions in here don't use AL, so optimal pipe use is impossible
+ xor edx,eax
+ inc esi
+ and cl,31
+ movzx eax,byte ptr [esi]
+ add cl,5
+ test al,al
+ rol eax,cl //rol is u-pipe only, but pairable
+ //rol doesn't touch z-flag
+ jnz lph_top //5 clock tick loop. not bad.
+
+ xor eax,edx
+ }
+#else
+ DWORD hash = 0;
+ int i;
+ int shift = 0;
+ for (i = 0; szStr[i]; i++)
+ {
+ hash ^= szStr[i] << shift;
+ if (shift > 24)
+ {
+ hash ^= (szStr[i] >> (32 - shift)) & 0x7F;
+ }
+ shift = (shift + 5) & 0x1F;
+ }
+ return hash;
+#endif
+}
+
+// MurmurHash2
+#ifdef _DEBUG
+#pragma optimize( "gt", on )
+#endif
+unsigned int __fastcall hash_M2(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 hashSettingW_M2(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_M2(buf, len);
+}
+
+unsigned int hashSetting_M2(const char * key)
+{
+ if (key == NULL) return 0;
+ const unsigned int len = (unsigned int)strlen((const char*)key);
+ return hash_M2(key, len);
+}
+
+unsigned int hashSetting_M2(const wchar_t * key)
+{
+ if (key == NULL) return 0;
+ const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+ return hash_M2(key, len * sizeof(wchar_t));
+}
+
+#ifdef _DEBUG
+#pragma optimize( "", on )
+#endif
+
+INT_PTR myDestroyServiceFunction(const char * key) {
+ //DestroyServiceFunction always return 0 therfore we must call ServiceExists to enshure it is delete
+ if (!ServiceExists(key)) return 0;
+ DestroyServiceFunction((HANDLE)(INT_PTR)hashSetting(key)); //old hash
+ if (!ServiceExists(key)) return 0;
+ DestroyServiceFunction((HANDLE)(INT_PTR)hashSetting_M2(key)); //new MurmurHash2
+ if (!ServiceExists(key)) return 0;
+ return 1;
+}
diff --git a/plugins/UserInfoEx/src/commonheaders.h b/plugins/UserInfoEx/src/commonheaders.h
new file mode 100644
index 0000000000..b998884e65
--- /dev/null
+++ b/plugins/UserInfoEx/src/commonheaders.h
@@ -0,0 +1,234 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/commonheaders.h $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+/***********************************************************************************************************
+ * some compiler definitions
+ ***********************************************************************************************************/
+
+#define _WIN32_WINNT 0x0501
+#define _WIN32_IE 0x0500
+#define WIN32_LEAN_AND_MEAN
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 0
+
+#pragma warning(disable:4995) // disable warning about strcpy, ... is old in VC2005
+#pragma warning(disable:4996) // disable warning about strcpy, ... is old in VC2005
+
+/***********************************************************************************************************
+ * standard windows includes
+ ***********************************************************************************************************/
+
+#include <windows.h>
+#include <windowsx.h>
+#include <winnt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <malloc.h>
+#include <string>
+#include <list>
+#include <Richedit.h>
+
+using namespace std;
+typedef std::basic_string<TCHAR> tstring;
+
+/***********************************************************************************************************
+ * Miranda IM SDK includes and macros
+ ***********************************************************************************************************/
+
+#define MIRANDA_VER 0x0A00
+
+#include <newpluginapi.h> // This must be included first
+#include <m_stdhdr.h>
+#include <m_button.h>
+#include <m_clui.h>
+#include <m_clist.h>
+#include <m_clistint.h>
+#include <m_cluiframes.h>
+#include <m_database.h>
+#include <m_genmenu.h>
+#include <m_hotkeys.h>
+#include <m_langpack.h>
+#include <m_protomod.h>
+#include <m_protosvc.h>
+#include <m_options.h>
+#include <m_contacts.h>
+#include <m_utils.h>
+#include <m_system.h> // memory interface
+#include <m_system_cpp.h> // list template
+#include <m_xml.h> // XML API
+#include <m_timezones.h> // timezone interface
+#include <m_imgsrvc.h>
+#include <m_message.h>
+#include <m_userinfo.h>
+#include <win2k.h>
+#include <msapi/vsstyle.h>
+#include <msapi/vssym32.h>
+
+/***********************************************************************************************************
+ * Used Plugins SDK includes and macros
+ ***********************************************************************************************************/
+
+#include <m_popup.h>
+#include "m_popup2.h"
+#include "m_flags.h"
+#include "m_metacontacts.h"
+#include "m_magneticwindows.h"
+#include "m_toptoolbar.h"
+#include "m_userinfoex.h"
+
+#include <m_extraicons.h> //change this to match extraicons header location
+
+/***********************************************************************************************************
+ * UserInfoEx plugin includes and macros
+ ***********************************************************************************************************/
+
+#pragma intrinsic(memcmp, memcpy, memset, strcmp, strlen)
+
+#ifndef MIR_OK
+#define MIR_OK 0 // success value of a miranda service function
+#define MIR_FAIL 1 // general failure value of a miranda service function
+#endif
+
+#define MIRSUCCEEDED(f) ((f)==MIR_OK)
+#define MIRFAILED(f) ((f)!=MIR_OK)
+#define MIREXISTS(f) ((int)(f)!=CALLSERVICE_NOTFOUND)
+
+#define PtrIsValid(p) (((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE))
+#define FREE(p) {if (PtrIsValid(p)){free((VOID*)p);(p)=NULL;}}
+#define MIR_DELETE(p) {LPVOID ptr = (LPVOID)(p);if (PtrIsValid(ptr)){delete(ptr);(ptr)=NULL;}}
+#define MIR_FREE(p) {if (PtrIsValid(p)){mir_free((VOID*)p);(p)=NULL;}}
+
+#define GetUserData(p) GetWindowLongPtr((p), GWLP_USERDATA)
+#define SetUserData(p, l) SetWindowLongPtr((p), GWLP_USERDATA, (LONG_PTR) (l))
+
+#include "resource.h"
+#include "../IconPacks/default/src/icons.h"
+#include "../IconPacks/ice/src/icons.h"
+
+#include "svc_constants.h"
+
+#include "mir_contactqueue.h"
+#include "mir_db.h"
+#include "mir_string.h"
+#include "mir_icolib.h"
+#include "dlg_msgbox.h"
+#include "classMTime.h"
+#include "classMAnnivDate.h"
+
+/***********************************************************************************************************
+ * UserInfoEx global variables
+ ***********************************************************************************************************/
+
+typedef struct _MGLOBAL
+{
+ DWORD mirandaVersion; // mirandaVersion
+ BOOLEAN CanChangeDetails : 1; // is service to upload own contact information for icq present?
+ BOOLEAN HaveCListExtraIcons : 1; // are extra icons supported by current clist
+ BOOLEAN ExtraIconsServiceExist : 1; // Extra Icon plugin / service exist
+ BOOLEAN MsgAddIconExist : 1; // Messsage Window support status Icon
+ BOOLEAN TzIndexExist : 1; // Win Reg has Timzone Index Info
+ BOOLEAN PopUpActionsExist : 1; // Popup++ or MS_POPUP_REGISTERACTIONS exist
+ BOOLEAN ShowPropsheetColours : 1; // cached SET_PROPSHEET_SHOWCOLOURS database value
+ BOOLEAN WantAeroAdaption : 1; // reserved for later use
+ BOOLEAN UseDbxTree : 1; // use dbx_tree ?
+ LPCSTR szMetaProto;
+
+} MGLOBAL, *LPMGLOBAL;
+
+extern HINSTANCE ghInst;
+extern MGLOBAL myGlobals;
+extern FI_INTERFACE* FIP;
+
+/***********************************************************************************************************
+ * MIRANDA_CPP_PLUGIN_API
+ ***********************************************************************************************************/
+
+/**
+ * These macros provide an interface for classes to use member
+ * function as services and event hooks.
+ *
+ * @note This requires Miranda Core 0.8+!
+ *
+ **/
+#define MIRANDA_CPP_PLUGIN_API(CCoreClass) \
+ typedef INT (__cdecl CCoreClass::*EVENTHOOK)(WPARAM, LPARAM); \
+ typedef INT (__cdecl CCoreClass::*EVENTHOOKPARAM)(WPARAM, LPARAM, LPARAM); \
+ typedef INT (__cdecl CCoreClass::*SERVICEFUNC)(WPARAM, LPARAM); \
+ typedef INT (__cdecl CCoreClass::*SERVICEFUNCPARAM)(WPARAM, LPARAM, LPARAM); \
+ \
+ HANDLE ThisHookEvent(const char* szEvent, EVENTHOOK pfnEvent) \
+ { return (HANDLE) ::HookEventObj(szEvent, (MIRANDAHOOKOBJ) (*(PVOID*) &pfnEvent), (PVOID)this);} \
+ HANDLE ThisHookEventParam(const char* szEvent, EVENTHOOKPARAM pfnEvent, LPARAM lParam) \
+ { return (HANDLE) ::HookEventObjParam(szEvent, (MIRANDAHOOKOBJPARAM) (*(PVOID*) &pfnEvent), (PVOID)this, lParam); } \
+ \
+ HANDLE ThisCreateService(const char* szService, SERVICEFUNC pfnService) \
+ { return (HANDLE) ::CreateServiceFunctionObj(szService, (MIRANDASERVICEOBJ) (*(PVOID*) &pfnService), (PVOID)this); } \
+ HANDLE ThisCreateServiceParam(const char* szService, SERVICEFUNCPARAM pfnService, LPARAM lParam) \
+ { return (HANDLE) ::CreateServiceFunctionObjParam(szService, (MIRANDASERVICEOBJPARAM) (*(PVOID*) &pfnService), (PVOID)this, lParam); } \
+
+/***********************************************************************************************************
+ * UserInfoEx common used functions
+ ***********************************************************************************************************/
+
+DWORD hashSetting(LPCSTR szStr); //old miranda hash
+
+unsigned int hashSetting_M2(const wchar_t * key); //new Murma2 hash
+unsigned int hashSetting_M2(const char * key); //new Murma2 hash
+unsigned int hashSettingW_M2(const char * key); //new Murma2 hash
+
+INT_PTR myDestroyServiceFunction(const char * key);
+
+static FORCEINLINE BOOL IsProtoOnline(LPSTR pszProto)
+{
+ return pszProto && pszProto[0] && CallProtoService(pszProto, PS_GETSTATUS, NULL, NULL) >= ID_STATUS_ONLINE;
+}
+static FORCEINLINE BOOL IsProtoLoaded(LPSTR pszProto)
+{
+ return (CallService(MS_PROTO_ISPROTOCOLLOADED, NULL, (LPARAM)pszProto) != NULL);
+}
+static FORCEINLINE BOOL IsProtoAccountEnabled(PROTOACCOUNT *pAcc)
+{
+ return ((pAcc->type == PROTOTYPE_PROTOCOL) && pAcc->bIsEnabled && IsProtoLoaded(pAcc->szModuleName));
+}
+
+typedef HRESULT (STDAPICALLTYPE *pfnDwmIsCompositionEnabled)(BOOL *);
+extern pfnDwmIsCompositionEnabled dwmIsCompositionEnabled;
+static FORCEINLINE BOOLEAN IsAeroMode()
+{
+ BOOL result;
+ return myGlobals.WantAeroAdaption && dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
diff --git a/plugins/UserInfoEx/src/ctrl_annivedit.cpp b/plugins/UserInfoEx/src/ctrl_annivedit.cpp
new file mode 100644
index 0000000000..db809906a1
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_annivedit.cpp
@@ -0,0 +1,636 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_annivedit.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_annivedit.h"
+#include "svc_Reminder.h"
+#include "psp_base.h"
+
+CBaseCtrl* CAnnivEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+ CAnnivEditCtrl *ctrl = NULL;
+
+ ctrl = new CAnnivEditCtrl(hDlg, idCtrl, pszSetting);
+ if (ctrl)
+ {
+ }
+ return (ctrl);
+}
+
+CAnnivEditCtrl::CAnnivEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+ : CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+ _hwndDlg = hDlg;
+ _hBtnAdd = GetDlgItem(hDlg, BTN_ADD);
+ _hBtnDel = GetDlgItem(hDlg, BTN_DELETE);
+ _hBtnEdit = GetDlgItem(hDlg, BTN_EDIT);
+ _hBtnMenu = GetDlgItem(hDlg, BTN_MENU);
+ _hwndDate = GetDlgItem(hDlg, EDIT_ANNIVERSARY_DATE);
+ _ReminderEnabled = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+
+ _pDates = NULL;
+ _curDate = 0;
+ _numDates = 0;
+
+ // set button tooltips
+ SendMessage(_hBtnAdd, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Add a new anniversary"), MBF_TCHAR);
+ SendMessage(_hBtnDel, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete an existing anniversary"), MBF_TCHAR);
+
+ // limit textinput
+ SendDlgItemMessage(_hwndDlg, EDIT_REMIND, EM_LIMITTEXT, 2, 0);
+ SendDlgItemMessage(_hwndDlg, SPIN_REMIND, UDM_SETRANGE32, 0, 50);
+
+ // birthday is shown as an item in any case
+ {
+ MAnnivDate mdb;
+
+ mdb.Id(ANID_BIRTHDAY);
+ mdb.Description(TranslateT("Birthday"));
+ AddDate(mdb);
+ }
+}
+
+CAnnivEditCtrl::~CAnnivEditCtrl()
+{
+ WORD i;
+
+ if (_pDates != NULL)
+ {
+ for (i = 0; i < _numDates; i++)
+ {
+ delete _pDates[i];
+ }
+ mir_free(_pDates);
+ _pDates = NULL;
+ }
+}
+
+VOID CAnnivEditCtrl::Release()
+{
+ delete this;
+}
+
+
+/**
+ * name: CAnnivEditCtrl
+ * class: ItemValid
+ * desc: tests whether the item pointed to by the wIndex is valid or not
+ * param: wIndex - index to desired item
+ * return: TRUE if item is valid, FALSE otherwise
+ **/
+BOOLEAN CAnnivEditCtrl::ItemValid(WORD wIndex) const
+{
+ return (_pDates != NULL && wIndex < _numDates && _pDates[wIndex] != NULL);
+}
+
+/**
+ * name: CAnnivEditCtrl
+ * class: CurrentItemValid
+ * desc: checks, whether currently selected item is valid
+ * param: none
+ * return: TRUE if item is valid, FALSE otherwise
+ **/
+BOOLEAN CAnnivEditCtrl::CurrentItemValid() const
+{
+ return ItemValid(_curDate);
+}
+
+/**
+ * name: CAnnivEditCtrl
+ * class: EnableReminderCtrl
+ * desc: enables or disables reminder controls
+ * param: none
+ * return: TRUE if item is valid, FALSE otherwise
+ **/
+VOID CAnnivEditCtrl::EnableReminderCtrl(BOOLEAN bEnabled)
+{
+ bEnabled &= _ReminderEnabled != REMIND_OFF;
+ EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND1), bEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND2), bEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND3), bEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), bEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), bEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, TXT_REMIND), bEnabled);
+}
+
+/**
+ * name: EnableCurrentItem
+ * class: CAnnivEditCtrl
+ * desc: make control readonly if required
+ * param: none
+ * return: nothing
+ **/
+VOID CAnnivEditCtrl::EnableCurrentItem()
+{
+ MAnnivDate *pCurrent = Current();
+
+ if (pCurrent) {
+ HANDLE hContact;
+
+ PSGetContact(_hwndDlg, hContact);
+
+ const BOOLEAN bEnabled
+ = !hContact ||
+ (pCurrent->Flags() & CTRLF_HASCUSTOM) ||
+ !(pCurrent->Flags() & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
+ !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+
+ EnableWindow(_hBtnEdit, bEnabled);
+ EnableWindow(_hBtnDel, bEnabled);
+ EnableWindow(_hwndDate, bEnabled);
+ }
+}
+
+/**
+ * name: FindDateById
+ * class: CAnnivEditCtrl
+ * desc: returns an iterator to an item with the given id
+ * param: wId - id the returned item must have
+ * return: if an date with the wId was found - iterator to item,
+ * NULL otherwise
+ **/
+MAnnivDate* CAnnivEditCtrl::FindDateById(const WORD wId)
+{
+ WORD i;
+
+ if (_pDates != NULL) {
+ for (i = 0; i < _numDates; i++) {
+ if (_pDates[i]->Id() < ANID_NONE && _pDates[i]->Id() == wId) {
+ return _pDates[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * name: AddDate
+ * class: CAnnivEditCtrl
+ * desc: Add a new item to the array of dates
+ * param: mda - the date to add
+ * return: 0 on success, -1 on failure, 1 if the item to change was edited before and the new item was not set
+ **/
+INT_PTR CAnnivEditCtrl::AddDate(MAnnivDate &mda)
+{
+ MAnnivDate *pmda, **pmd;
+
+ // if a date with wID exists, replace it
+ if ((pmda = FindDateById(mda.Id())) != NULL) {
+ BOOLEAN bChanged = pmda->IsChanged(),
+ bRemindChanged = pmda->IsReminderChanged();
+
+ if (!bChanged) {
+ pmda->Set(mda);
+ pmda->Module(mda.Module());
+ pmda->Description(mda.Description());
+ pmda->Flags(mda.Flags());
+ }
+ if (!bRemindChanged) {
+ pmda->RemindOption(mda.RemindOption());
+ pmda->RemindOffset(mda.RemindOffset());
+ }
+ return bChanged || bRemindChanged;
+ }
+ if (mda.Id() == ANID_NONE)
+ mda.Id(_numDates - 1);
+
+ if (pmd = (MAnnivDate **)mir_realloc(_pDates, (_numDates + 1) * sizeof(pmda))) {
+ _pDates = pmd;
+ if (_pDates[_numDates] = new MAnnivDate(mda)) {
+ _numDates++;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * name: DeleteDate
+ * class: CAnnivEditCtrl
+ * desc: Delete the item on the position identified by wIndex
+ * param: pDateCtrl - pointer to the date control's data structure
+ * wIndex - index of the item to delete
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DeleteDate(WORD wIndex)
+{
+ if (!ItemValid(wIndex)) return 1;
+
+ // only delete values, but not the item
+ if (_pDates[wIndex]->Id() == ANID_BIRTHDAY) {
+ HANDLE hContact;
+ LPCSTR pszProto;
+
+ PSGetContact(_hwndDlg, hContact);
+ PSGetBaseProto(_hwndDlg, pszProto);
+
+ // protocol value exists?
+ if (_pDates[wIndex]->DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+ _pDates[wIndex]->SetFlags(MAnnivDate::MADF_HASPROTO);
+ }
+ else {
+ _pDates[wIndex]->ZeroDate();
+ }
+
+ _pDates[wIndex]->RemindOption(BST_INDETERMINATE);
+ _pDates[wIndex]->RemindOffset((BYTE)-1);
+
+ _pDates[wIndex]->RemoveFlags(MAnnivDate::MADF_HASCUSTOM);
+ _pDates[wIndex]->SetFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED);
+ }
+ else {
+ delete _pDates[wIndex];
+ _numDates--;
+ if (wIndex < _numDates)
+ memmove(_pDates + wIndex, _pDates + wIndex + 1, (_numDates - wIndex) * sizeof(*_pDates));
+ ZeroMemory(_pDates + _numDates, sizeof(*_pDates));
+ if (_curDate >= _numDates)
+ _curDate = _numDates - 1;
+ }
+ SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+ SetCurSel(_curDate);
+ return 0;
+}
+
+/**
+ * name: DateCtrl_DBGetBirthDay
+ * desc:
+ * param:
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBGetBirthDay(HANDLE hContact, LPCSTR pszProto)
+{
+ MAnnivDate mdb;
+
+ if (!mdb.DBGetBirthDate(hContact, (char *)pszProto)) {
+ mdb.DBGetReminderOpts(hContact);
+ return AddDate(mdb) > 0;
+ }
+ return 0;
+}
+
+/**
+ * name: DateCtrl_DBGetBirthDay
+ * desc:
+ * param:
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBGetAnniversaries(HANDLE hContact)
+{
+ MAnnivDate mda;
+
+ WORD i;
+ BOOLEAN bChanged = FALSE;
+
+ for (i = 0; i < ANID_LAST && !mda.DBGetAnniversaryDate(hContact, i); i++) {
+ mda.DBGetReminderOpts(hContact);
+ switch (AddDate(mda)) {
+ case -1:
+ return bChanged;
+ case 1:
+ bChanged |= 1;
+ break;
+ }
+ }
+ return bChanged;
+}
+
+/**
+ * name: DBWriteBirthDay
+ * class: CAnnivEditCtrl
+ * desc: writes the birthday for a contact to database
+ * param: hContact - the contact to write the anniversaries to
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBWriteBirthDay(HANDLE hContact)
+{
+ MAnnivDate *pmdb;
+
+ if ((pmdb = FindDateById(ANID_BIRTHDAY)) == NULL)
+ return 1;
+
+ if (pmdb->IsChanged()) {
+ // save birthday, to mBirthday module by default
+ if (pmdb->Flags() & pmdb->MADF_HASCUSTOM)
+ pmdb->DBWriteBirthDate(hContact);
+ else
+ pmdb->DBDeleteBirthDate(hContact);
+ }
+
+ if (pmdb->IsReminderChanged()) {
+ pmdb->DBWriteReminderOpts(hContact);
+ }
+ pmdb->RemoveFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED);
+ return 0;
+}
+
+/**
+ * name: DBWriteAnniversaries
+ * class: CAnnivEditCtrl
+ * desc: write all anniversaries to the database
+ * param: hContact - the contact to write the anniversaries to
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBWriteAnniversaries(HANDLE hContact)
+{
+ const LPCSTR szPrefix[] = { "Reminder", "Offset", "Desc", "Day", "Month", "Year", "Stamp", "Date" };
+ CHAR szSet0[MAXSETTING];
+ WORD i, ret, ofs, wIndex = 0;
+
+ for (i = 0; i < _numDates; i++) {
+ if (
+ _pDates[i] != NULL &&
+ !_pDates[i]->DBWriteAnniversaryDate(hContact, wIndex) &&
+ !_pDates[i]->DBWriteReminderOpts(hContact)
+ )
+ {
+ wIndex++;
+ }
+ }
+ // delete reluctant items
+ do {
+ ofs = mir_snprintf(szSet0, SIZEOF(szSet0), "Anniv%d", wIndex);
+ ret = 1;
+ for (i = 0; i < SIZEOF(szPrefix); i++) {
+ mir_strncpy(szSet0 + ofs, szPrefix[i], SIZEOF(szSet0) - ofs);
+ ret &= DB::Setting::Delete(hContact, USERINFO, szSet0);
+ }
+ }
+ while (wIndex++ <= ANID_LAST && !ret);
+ return 0;
+}
+
+/**
+ * name: SetCurSel
+ * class: CAnnivEditCtrl
+ * desc: shows the item, identified by wIndex
+ * param: pDateCtrl - pointer to the date control's data structure
+ * wIndex - index of the item to delete
+ * return: 0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::SetCurSel(WORD wIndex)
+{
+ BOOLEAN bEnabled = ItemValid(wIndex);
+
+ EnableWindow(_hwndDate, bEnabled);
+ EnableWindow(_hBtnEdit, bEnabled);
+ EnableWindow(_hBtnDel, bEnabled && _pDates[wIndex]->IsValid());
+ if (!bEnabled) {
+ EnableReminderCtrl(FALSE);
+ return 1;
+ }
+ _curDate = wIndex;
+
+ // set date of date control
+ if (_pDates[wIndex]->IsValid()) {
+ SYSTEMTIME st = _pDates[wIndex]->SystemTime();
+ DateTime_SetSystemtime(_hwndDate, GDT_VALID, &st);
+ DateTime_SetFormat(_hwndDate, NULL);
+ }
+ else {
+ TCHAR szText[MAX_DESC];
+ mir_sntprintf(szText, MAX_DESC, _T("'%s'"), TranslateT("Unspecified"));
+ DateTime_SetSystemtime(_hwndDate, GDT_NONE, NULL);
+ DateTime_SetFormat(_hwndDate, szText);
+ }
+ // set edit button's caption
+ SetWindowText(_hBtnEdit, _pDates[wIndex]->Description());
+
+ // set reminder options
+ CheckDlgButton(_hwndDlg, RADIO_REMIND1, _pDates[wIndex]->RemindOption() == BST_INDETERMINATE);
+ CheckDlgButton(_hwndDlg, RADIO_REMIND2, _pDates[wIndex]->RemindOption() == BST_CHECKED);
+ CheckDlgButton(_hwndDlg, RADIO_REMIND3, _pDates[wIndex]->RemindOption() == BST_UNCHECKED);
+
+ OnReminderChecked();
+ EnableCurrentItem();
+ return 0;
+}
+
+/**
+ * name: OnMenuPopup
+ * class: CAnnivEditCtrl
+ * desc: is called to show a popup menu for all anniversaries of a contact
+ * param: none
+ * return: nothing
+ **/
+VOID CAnnivEditCtrl::OnMenuPopup()
+{
+ POINT pt = { 0, 0 };
+ RECT rc;
+ MENUITEMINFO mii;
+ HMENU hMenu;
+ WORD i;
+
+ if (hMenu = CreatePopupMenu()) {
+ SetFocus(_hBtnMenu);
+
+ ZeroMemory(&mii, sizeof(MENUITEMINFO));
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE;
+
+ // insert the items
+ for (i = 0; i < _numDates; i++) {
+ mii.fState = _pDates[i]->IsValid() ? MFS_CHECKED : MFS_UNCHECKED;
+ mii.dwTypeData = (LPTSTR)_pDates[i]->Description();
+ mii.wID = WM_USER + i;
+ if (!InsertMenuItem(hMenu, i, TRUE, &mii)) {
+ DestroyMenu(hMenu);
+ return;
+ }
+ }
+ ClientToScreen(_hBtnMenu, &pt);
+ GetClientRect(_hBtnMenu, &rc);
+ i = TrackPopupMenuEx(hMenu, TPM_RIGHTALIGN|TPM_RETURNCMD, pt.x + rc.right, pt.y + rc.bottom, _hwndDlg, NULL);
+ DestroyMenu(hMenu);
+ SendMessage(_hBtnMenu, BM_SETCHECK, NULL, NULL);
+ if (i >= WM_USER) SetCurSel(i - WM_USER);
+ }
+}
+
+/**
+ * name: OnMenuPopup
+ * class: CAnnivEditCtrl
+ * desc: is called to show a popup menu for all anniversaries of a contact
+ * param: none
+ * return: nothing
+ **/
+VOID CAnnivEditCtrl::OnDateChanged(LPNMDATETIMECHANGE lpChange)
+{
+ MAnnivDate *pCurrent = Current();
+
+ if (pCurrent && !pCurrent->IsEqual(lpChange->st))
+ {
+ HWND hPs = GetParent(_hwndDlg);
+
+ // save the new date to the structure
+ DateTime_SetFormat(_hwndDate, NULL);
+ pCurrent->Set(lpChange->st, TRUE);
+ pCurrent->SetFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_HASCUSTOM);
+
+ // notify parent of the change
+ SendMessage(hPs, PSM_CHANGED, NULL, NULL);
+ EnableWindow(_hBtnDel, TRUE);
+
+ // update the age and zodiac controls on the general propertysheetpage
+ if (pCurrent->Id() == ANID_BIRTHDAY)
+ {
+ SetZodiacAndAge(pCurrent);
+ }
+ }
+}
+
+/**
+ * name: OnRemindEditChanged
+ * class: CAnnivEditCtrl
+ * desc: is called, if reminder edit control was changed
+ * param: none
+ * return: nothing
+ **/
+VOID CAnnivEditCtrl::OnRemindEditChanged()
+{
+ MAnnivDate *pCurrent = Current();
+
+ if (pCurrent)
+ {
+ UINT iVal = GetDlgItemInt(_hwndDlg, EDIT_REMIND, NULL, FALSE);
+ if (iVal != pCurrent->RemindOffset() && IsDlgButtonChecked(_hwndDlg, RADIO_REMIND2) == BST_CHECKED)
+ {
+ SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+ pCurrent->SetFlags(MAnnivDate::MADF_REMINDER_CHANGED);
+ pCurrent->RemindOffset(iVal);
+ }
+ }
+}
+
+/**
+ * name: OnReminderChecked
+ * class: CAnnivEditCtrl
+ * desc: is called if reminder checkbox's state was changed
+ * param: none
+ * return: nothing
+ **/
+VOID CAnnivEditCtrl::OnReminderChecked()
+{
+ HANDLE hContact;
+ LPCSTR pszProto;
+ INT state;
+ TCHAR buf[6];
+ MAnnivDate *pCurrent = Current();
+
+ PSGetContact(_hwndDlg, hContact);
+ if (!hContact || !PSGetBaseProto(_hwndDlg, pszProto) || !pCurrent)
+ {
+ EnableReminderCtrl(FALSE);
+ }
+ else
+ {
+ if (IsDlgButtonChecked(_hwndDlg, RADIO_REMIND1))
+ {
+ _itot(DB::Setting::GetByte(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), buf, 10);
+ EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), FALSE);
+ EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), FALSE);
+ state = BST_INDETERMINATE;
+ }
+ else if (IsDlgButtonChecked(_hwndDlg, RADIO_REMIND2))
+ {
+ if (pCurrent->RemindOffset() == (BYTE)-1)
+ {
+ _itot(DB::Setting::GetByte(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), buf, 10);
+ }
+ else
+ {
+ _itot(pCurrent->RemindOffset(), buf, 10);
+ }
+ EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), _ReminderEnabled);
+ EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), _ReminderEnabled);
+ state = BST_CHECKED;
+ }
+ else
+ {
+ *buf = 0;
+ EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), FALSE);
+ EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), FALSE);
+ state = BST_UNCHECKED;
+ }
+ if (pCurrent->RemindOption() != state)
+ {
+ pCurrent->RemindOption(state);
+ if (!PspIsLocked(_hwndDlg))
+ {
+ pCurrent->SetFlags(MAnnivDate::MADF_REMINDER_CHANGED);
+ SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+ }
+ }
+ SetDlgItemText(_hwndDlg, EDIT_REMIND, buf);
+ }
+}
+
+VOID CAnnivEditCtrl::SetZodiacAndAge(MAnnivDate *mt)
+{
+ if (PtrIsValid(mt))
+ {
+ INT age;
+ MZodiac zod;
+
+ zod = mt->Zodiac();
+ if (zod.pszName != NULL)
+ {
+ ShowWindow(GetDlgItem(_hwndDlg, TEXT_ZODIAC), SW_SHOW);
+ SetDlgItemText(_hwndDlg, TEXT_ZODIAC, TranslateTS(zod.pszName));
+ SendDlgItemMessage(_hwndDlg, IDC_ZODIAC, STM_SETIMAGE, IMAGE_ICON, (LPARAM)zod.hIcon);
+ }
+ else
+ {
+ ShowWindow(GetDlgItem(_hwndDlg, IDC_ZODIAC), SW_HIDE);
+ ShowWindow(GetDlgItem(_hwndDlg, TEXT_ZODIAC), SW_HIDE);
+ }
+ if ((age = mt->Age()) > 0)
+ {
+ SetDlgItemInt(_hwndDlg, EDIT_AGE, age , FALSE);
+ }
+ }
+}
+
+BOOL CAnnivEditCtrl::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+ BOOL bChanged;
+ bChanged = DBGetBirthDay(hContact, pszProto);
+ bChanged |= DBGetAnniversaries(hContact);
+ SetCurSel(0);
+ SetZodiacAndAge(_pDates[0]);
+ return bChanged;
+}
+
+VOID CAnnivEditCtrl::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+ DBWriteBirthDay(hContact);
+ DBWriteAnniversaries(hContact);
+}
+
diff --git a/plugins/UserInfoEx/src/ctrl_annivedit.h b/plugins/UserInfoEx/src/ctrl_annivedit.h
new file mode 100644
index 0000000000..412afa8c0d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_annivedit.h
@@ -0,0 +1,104 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_annivedit.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_CTRLANNIVEDIT_H_
+#define _UINFOEX_CTRLANNIVEDIT_H_
+
+#include "ctrl_base.h"
+
+#define MAX_DESC 50
+
+class CAnnivEditCtrl : public CBaseCtrl
+{
+ HWND _hwndDlg; // owning dialog box
+ HWND _hwndDate; // date picker
+ HWND _hBtnMenu; // anniversary dropdown button
+ HWND _hBtnEdit; // edit anniversary button
+ HWND _hBtnAdd; // add anniversary button
+ HWND _hBtnDel; // delete anniversary button
+
+ BOOLEAN _ReminderEnabled;
+
+ MAnnivDate** _pDates;
+ WORD _numDates;
+ WORD _curDate;
+
+ BOOLEAN ItemValid(WORD wIndex) const;
+ BOOLEAN CurrentItemValid() const;
+
+ INT_PTR DBGetBirthDay(HANDLE hContact, LPCSTR pszProto);
+ INT_PTR DBWriteBirthDay(HANDLE hContact);
+
+ INT_PTR DBGetAnniversaries(HANDLE hContact);
+ INT_PTR DBWriteAnniversaries(HANDLE hContact);
+
+ CAnnivEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+ ~CAnnivEditCtrl();
+
+public:
+
+ MAnnivDate* Current() { return CurrentItemValid() ? _pDates[_curDate] : NULL; };
+ WORD CurrentIndex() const { return _curDate; };
+ WORD NumDates() const { return _numDates; };
+ BOOLEAN ReminderEnabled() const { return _ReminderEnabled; };
+
+ MAnnivDate* FindDateById(const WORD wId);
+
+ VOID EnableCurrentItem();
+ VOID EnableReminderCtrl(BOOLEAN bEnabled);
+
+ INT_PTR SetCurSel(WORD wIndex);
+
+ INT_PTR AddDate(MAnnivDate &mda);
+ INT_PTR DeleteDate(WORD wIndex);
+
+ VOID SetZodiacAndAge(MAnnivDate *mt);
+
+ // notification handlers
+ VOID OnMenuPopup();
+ VOID OnDateChanged(LPNMDATETIMECHANGE lpChange);
+ VOID OnRemindEditChanged();
+ VOID OnReminderChecked();
+
+ /**
+ * CBaseCtrl interface:
+ **/
+ static FORCEINLINE CAnnivEditCtrl* GetObj(HWND hCtrl)
+ { return (CAnnivEditCtrl*) GetUserData(hCtrl); }
+ static FORCEINLINE CAnnivEditCtrl* GetObj(HWND hDlg, WORD idCtrl)
+ { return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+ static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+ virtual VOID Release();
+ virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto);
+};
+
+#endif /* _UINFOEX_CTRLANNIVEDIT_H_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_base.cpp b/plugins/UserInfoEx/src/ctrl_base.cpp
new file mode 100644
index 0000000000..8203a5c37d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_base.cpp
@@ -0,0 +1,301 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_base.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+/***********************************************************************************************************
+ * Old methods for setting text color of dialog controls
+ ***********************************************************************************************************/
+
+COLORREF clrBoth = -1;
+COLORREF clrChanged = -1;
+COLORREF clrCustom = -1;
+COLORREF clrNormal = -1;
+COLORREF clrMeta = -1;
+
+VOID Ctrl_InitTextColours()
+{
+ clrBoth = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRBOTH, RGB(0, 160, 10));
+ clrChanged = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRCHANGED, RGB(190, 0, 0));
+ clrCustom = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRCUSTOM, RGB(0, 10, 130));
+ clrNormal = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRNORMAL, RGB(90, 90, 90));
+ clrMeta = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRMETA, RGB(120, 40, 130));
+}
+
+INT_PTR CALLBACK Ctrl_SetTextColour(HDC hdc, WORD wFlags)
+{
+ // OLD stuff
+ SetTextColor(hdc,
+ (wFlags & CTRLF_CHANGED)
+ ? clrChanged : ((wFlags & CTRLF_HASCUSTOM) && (wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)))
+ ? clrBoth : (wFlags & CTRLF_HASMETA)
+ ? clrMeta : (wFlags & CTRLF_HASCUSTOM)
+ ? clrCustom : clrNormal
+ );
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+}
+
+INT_PTR CALLBACK Ctrl_SetTextColour(HWND hCtrl, HDC hdc)
+{
+ if (hCtrl && DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, 1))
+ {
+ LPCTRL pCtrl = (LPCTRL)GetUserData(hCtrl);
+ if (PtrIsValid(pCtrl))
+ return Ctrl_SetTextColour(hdc, pCtrl->wFlags);
+ }
+ return FALSE;
+}
+
+/***********************************************************************************************************
+ * Implementation of CBaseCtrl
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl()
+{
+ ZeroMemory(this, sizeof(*this));
+ _cbSize = sizeof(CBaseCtrl);
+}
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+ ZeroMemory(this, sizeof(*this));
+ _cbSize = sizeof(CBaseCtrl);
+ _hwnd = GetDlgItem(hDlg, idCtrl);
+ if (!IsWindow(_hwnd)) throw;
+ _idCtrl = idCtrl;
+ _pszModule = USERINFO;
+ _pszSetting = pszSetting;
+ SetUserData(_hwnd, this);
+}
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ ZeroMemory(this, sizeof(*this));
+ _cbSize = sizeof(CBaseCtrl);
+ _hwnd = GetDlgItem(hDlg, idCtrl);
+ if (!IsWindow(_hwnd)) throw;
+ _idCtrl = idCtrl;
+ _pszModule = pszModule;
+ _pszSetting = pszSetting;
+ SetUserData(_hwnd, this);
+}
+
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::~CBaseCtrl()
+{
+ SetUserData(_hwnd, NULL);
+ MIR_FREE(_pszValue);
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CBaseCtrl::OnSetTextColour(HDC hdc)
+{
+ SetTextColor(hdc,
+ (_Flags.B.hasChanged)
+ ? clrChanged : ((_Flags.B.hasCustom) && (_Flags.B.hasProto || _Flags.B.hasMeta))
+ ? clrBoth : _Flags.B.hasMeta
+ ? clrMeta : _Flags.B.hasCustom
+ ? clrCustom : clrNormal
+ );
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+}
+
+/***********************************************************************************************************
+ * Implementation of CCtrlList
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+CCtrlList* CCtrlList::CreateObj(HWND hOwnerDlg)
+{
+ Ctrl_InitTextColours();
+ return new CCtrlList(hOwnerDlg);
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CCtrlList::sortFunc(CBaseCtrl *p1, CBaseCtrl *p2)
+{
+ return p1->_idCtrl - p2->_idCtrl;
+}
+
+/**
+ *
+ *
+ **/
+CCtrlList::CCtrlList(HWND hOwnerDlg)
+: LIST<CBaseCtrl>(10, (FTSortFunc) CCtrlList::sortFunc)
+{
+ _hOwnerDlg = hOwnerDlg;
+ SetUserData(_hOwnerDlg, this);
+}
+
+/**
+ *
+ *
+ **/
+CCtrlList::~CCtrlList()
+{
+ INT_PTR i;
+
+ SetUserData(_hOwnerDlg, NULL);
+ // delete data
+ for (i = 0 ; i < count; i++)
+ {
+ delete (*this)[i];
+ }
+ // delete the list
+ LIST<CBaseCtrl>::destroy();
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::Release()
+{
+ delete this;
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnReset()
+{
+ INT_PTR i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (items[i])
+ {
+ items[i]->OnReset();
+ }
+ }
+}
+
+/**
+ *
+ *
+ **/
+BOOL CCtrlList::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+ BOOL bChanged = 0;
+ INT_PTR i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (PtrIsValid(items[i]))
+ {
+ bChanged |= items[i]->OnInfoChanged(hContact, pszProto);
+ }
+ }
+ return bChanged;
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+ INT_PTR i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (PtrIsValid(items[i]))
+ {
+ items[i]->OnApply(hContact, pszProto);
+ }
+ }
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnChangedByUser(WORD idCtrl, WORD wChangedMsg)
+{
+ // prefilter messages to avoid unessesary search operations
+ switch (wChangedMsg)
+ {
+ case EN_UPDATE:
+ case EN_CHANGE:
+ case CBN_SELCHANGE:
+ {
+ CBaseCtrl *pResult = CBaseCtrl::GetObj(_hOwnerDlg, idCtrl);
+ if (PtrIsValid(pResult) && (pResult->_cbSize == sizeof(CBaseCtrl)))
+ {
+ pResult->OnChangedByUser(wChangedMsg);
+ }
+ }
+ }
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CCtrlList::OnSetTextColour(HWND hCtrl, HDC hdc)
+{
+ if (IsWindow(hCtrl) && myGlobals.ShowPropsheetColours)
+ {
+ CBaseCtrl* pCtrl = CBaseCtrl::GetObj(hCtrl);
+ if (PtrIsValid(pCtrl) && (pCtrl->_cbSize = sizeof(CBaseCtrl)))
+ {
+ return pCtrl->OnSetTextColour(hdc);
+ }
+ }
+ return FALSE;
+}
diff --git a/plugins/UserInfoEx/src/ctrl_base.h b/plugins/UserInfoEx/src/ctrl_base.h
new file mode 100644
index 0000000000..161f8aeba7
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_base.h
@@ -0,0 +1,238 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_base.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_BASE_INCLUDE_
+#define _UI_CTRL_BASE_INCLUDE_
+
+/***********************************************************************************************************
+ * old CTRL stuff (is to replace in the future
+ ***********************************************************************************************************/
+
+// control flags
+#define CTRLF_CHANGED 1
+#define CTRLF_HASPROTO 2
+#define CTRLF_HASCUSTOM 4
+#define CTRLF_HASMETA 8
+
+#define CTRLF_FIRST 16 // first free flag for derived controls
+
+// control types
+#define CTRL_ERROR 0
+#define CTRL_EDIT 1
+#define CTRL_COMBOBOX_STAT 2
+#define CTRL_DATEPICKER 3
+#define CTRL_LIST_PROFILE 4
+#define CTRL_LIST_ITEM 7
+
+typedef struct TCtrlInfo {
+ BYTE nType;
+ WORD wFlags;
+} CTRL, *LPCTRL;
+
+// for compatibility with old styled controls
+VOID Ctrl_InitTextColours();
+INT_PTR CALLBACK Ctrl_SetTextColour(HDC hdc, WORD wFlags);
+
+/***********************************************************************************************************
+ * CBaseCtrl declaration
+ ***********************************************************************************************************/
+
+union CCtrlFlags
+{
+ WORD W;
+ struct CBits
+ {
+ BYTE hasChanged : 1;
+ BYTE hasProto : 1;
+ BYTE hasCustom : 1;
+ BYTE hasMeta : 1;
+ BYTE bit_04 : 1;
+ BYTE bit_05 : 1;
+ BYTE bit_06 : 1;
+ BYTE bit_07 : 1;
+ BYTE bit_08 : 1;
+ BYTE bit_09 : 1;
+ BYTE bit_10 : 1;
+ BYTE bit_11 : 1;
+ BYTE bit_12 : 1;
+ BYTE bit_13 : 1;
+ BYTE bit_14 : 1;
+ BYTE bit_15 : 1;
+ } B;
+};
+
+/**
+ * CBaseCtrl is an abstract baseic class, which provides a common
+ * interface for all kinds of controls. It has the task to ease
+ * up programming and avoid memory leaks.
+ **/
+class CBaseCtrl
+{
+public:
+ DWORD _cbSize;
+
+ friend class CCtrlList;
+
+protected:
+
+ CCtrlFlags _Flags;
+ HWND _hwnd;
+ WORD _idCtrl;
+ LPCSTR _pszModule;
+ LPCSTR _pszSetting;
+ LPTSTR _pszValue;
+
+ /**
+ * Private constructure is to force the class used as base class only.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ CBaseCtrl();
+ CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+ CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting);
+
+ /**
+ * Private constructure is to force the class used as base class only.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ ~CBaseCtrl();
+
+public:
+
+ /**
+ *
+ *
+ **/
+ static FORCEINLINE CBaseCtrl* GetObj(HWND hCtrl)
+ { return (CBaseCtrl*) GetUserData(hCtrl); }
+
+ /**
+ *
+ *
+ **/
+ static FORCEINLINE CBaseCtrl* GetObj(HWND hDlg, WORD idCtrl)
+ { return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+ /**
+ *
+ *
+ **/
+ FORCEINLINE CCtrlFlags Flags() const { return _Flags; }
+
+ /**
+ * This is a pure virtual method, which is the common interface
+ * for deleting an instance of this class.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ virtual VOID Release() { }
+
+ /**
+ * This is a pure virtual method, which is the common interface
+ * to handle database changes. It reads the new values from
+ * database and refreshes the content of the associated control.
+ *
+ * @param hContact - the handle of the contact
+ * @param pszProto - the contact's protocol module
+ *
+ * @retval TRUE - the content was updated
+ * @retval FALSE - content not updated
+ **/
+ virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto) { return 0; }
+
+ /**
+ * This is a pure virtual method, which is the common interface
+ * for applying changed content of a control to database.
+ *
+ * @param hContact - the handle of the contact
+ * @param pszProto - the contact's protocol module
+ *
+ * @return nothing
+ **/
+ virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto) { }
+
+ /**
+ * This is a pure virtual method, which is called to set the
+ * changed bit. This is to indicate a setting has changed and
+ * therefore enable the apply button of the details dialog.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ virtual VOID OnChangedByUser(WORD wChangedMsg) { }
+
+ virtual VOID OnReset() { }
+
+ INT_PTR OnSetTextColour(HDC hdc);
+};
+
+/***********************************************************************************************************
+ * CCtrlList declaration
+ ***********************************************************************************************************/
+
+/**
+ * The CCtrlList class is a sorted list of all dialog controls, such as edit, combo, etc.
+ * with a common data structure and interface, described by the abstract class CBaseCtrl.
+ * The CCtrlList class sends notification messages to all its or to all relevant members.
+ * This reduces the risk of forgetting some message handling.
+ **/
+class CCtrlList : public LIST<CBaseCtrl>
+{
+ HWND _hOwnerDlg;
+
+ static INT_PTR sortFunc(CBaseCtrl *tz1, CBaseCtrl *tz2);
+
+ CCtrlList(HWND hOwnerDlg);
+ ~CCtrlList();
+
+public:
+
+ static CCtrlList* CreateObj(HWND hOwnerDlg);
+
+ static FORCEINLINE CCtrlList* GetObj(HWND hDlg)
+ { return (CCtrlList*)GetUserData(hDlg); }
+
+ VOID Release();
+ VOID OnReset();
+ BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+ VOID OnApply(HANDLE hContact, LPCSTR pszProto);
+ VOID OnChangedByUser(WORD idCtrl, WORD wChangedMsg);
+ INT_PTR OnSetTextColour(HWND hCtrl, HDC hdc);
+};
+
+#endif /* _UI_CTRL_BASE_INCLUDE_ */
diff --git a/plugins/UserInfoEx/src/ctrl_button.cpp b/plugins/UserInfoEx/src/ctrl_button.cpp
new file mode 100644
index 0000000000..8278b5b07d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_button.cpp
@@ -0,0 +1,708 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_button.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLID 100
+#define BUTTON_POLLDELAY 50
+
+#define MGPROC(x) GetProcAddress(themeAPIHandle,x)
+
+typedef struct TMBCtrl{
+ HWND hwnd;
+ HANDLE hThemeButton;
+ HANDLE hThemeToolbar;
+
+ HICON hIcon;
+ HICON arrow; // uses down arrow
+ HBITMAP hBitmap;
+ HFONT hFont; // font
+
+ DWORD dwStyle;
+ BOOLEAN bFocus;
+
+ INT stateId; // button state
+ INT defbutton; // default button
+ INT pbState;
+ TCHAR cHot;
+} BTNCTRL, *LPBTNCTRL;
+
+// External theme methods and properties
+static CRITICAL_SECTION csTips;
+static HWND hwndToolTips = NULL;
+static HMODULE themeAPIHandle = NULL;
+
+// theme procedures
+static HANDLE (WINAPI *OpenThemeData)(HWND,LPCWSTR);
+static HRESULT (WINAPI *CloseThemeData)(HANDLE);
+static BOOL (WINAPI *IsThemeBackgroundPartiallyTransparent)(HANDLE,INT,INT);
+static HRESULT (WINAPI *DrawThemeParentBackground)(HWND,HDC,RECT *);
+static HRESULT (WINAPI *DrawThemeBackground)(HANDLE,HDC,INT,INT,const RECT *,const RECT *);
+static HRESULT (WINAPI *DrawThemeText)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *);
+static HRESULT (WINAPI *GetThemeTextExtent)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *);
+
+/**
+ * name: ThemeSupport
+ * desc: Loads the uxtheme functions, if supported by the os
+ * param: none
+ * return: TRUE if themes are supported, FALSE if not
+ **/
+static BOOLEAN __fastcall ThemeSupport() {
+ if (IsWinVerXPPlus()) {
+ if (!themeAPIHandle) {
+ themeAPIHandle = GetModuleHandleA("uxtheme");
+ if (themeAPIHandle) {
+ OpenThemeData = (HANDLE (WINAPI *)(HWND,LPCWSTR))MGPROC("OpenThemeData");
+ CloseThemeData = (HRESULT (WINAPI *)(HANDLE))MGPROC("CloseThemeData");
+ IsThemeBackgroundPartiallyTransparent = (BOOL (WINAPI *)(HANDLE,INT,INT))MGPROC("IsThemeBackgroundPartiallyTransparent");
+ DrawThemeParentBackground = (HRESULT (WINAPI *)(HWND,HDC,RECT *))MGPROC("DrawThemeParentBackground");
+ DrawThemeBackground = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,const RECT *,const RECT *))MGPROC("DrawThemeBackground");
+ DrawThemeText = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *))MGPROC("DrawThemeText");
+ GetThemeTextExtent = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *))MGPROC("GetThemeTextExtent");
+ }
+ }
+ // Make sure all of these methods are valid (i would hope either all or none work)
+ if (OpenThemeData
+ && CloseThemeData
+ && IsThemeBackgroundPartiallyTransparent
+ && DrawThemeParentBackground
+ && DrawThemeBackground
+ && DrawThemeText
+ && GetThemeTextExtent)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * name: DestroyTheme
+ * desc: destroys theme data for buttons
+ * param: ctl - BTNCTRL structure with the information about the theme to close
+ * return: nothing
+ **/
+static VOID __fastcall DestroyTheme(BTNCTRL *ctl) {
+ if (ctl->hThemeButton) {
+ CloseThemeData(ctl->hThemeButton);
+ ctl->hThemeButton = NULL;
+ }
+ if (ctl->hThemeToolbar) {
+ CloseThemeData(ctl->hThemeToolbar);
+ ctl->hThemeToolbar = NULL;
+ }
+}
+
+/**
+ * name: LoadTheme
+ * desc: load theme data for buttons if supported by os
+ * param: ctl - BTNCTRL structure with the information about the theme to load
+ * return: nothing
+ **/
+static VOID __fastcall LoadTheme(BTNCTRL *ctl) {
+ if (ThemeSupport()) {
+ DestroyTheme(ctl);
+ ctl->hThemeButton = OpenThemeData(ctl->hwnd,L"BUTTON");
+ ctl->hThemeToolbar = OpenThemeData(ctl->hwnd,L"TOOLBAR");
+ }
+}
+
+/**
+ * name: TBStateConvert2Flat
+ * desc: convert button stateIDs
+ * param: state - state id for the normal theme button
+ * return: stateID for the flat theme button
+ **/
+static INT __fastcall 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;
+}
+
+/**
+ * name: PaintIcon
+ * desc: Draws the Icon of the button
+ * param: ctl - BTNCTRL structure for the button
+ * hdcMem - device context to draw to
+ * ccText - character count of the text of the button
+ * rcClient - rectangle of the whole button
+ * rcText - rectangle of the text to draw later on
+ * return: nothing
+ **/
+static VOID __fastcall PaintIcon(BTNCTRL *ctl, HDC hdcMem, LPWORD ccText, LPRECT rcClient, LPRECT rcText)
+{
+ RECT rcImage;
+
+ // draw icon on the left of the button
+ if (ctl->hIcon) {
+ rcImage.right = GetSystemMetrics(SM_CXSMICON);
+ rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
+ rcImage.left = (rcClient->right - rcClient->left) / 2 - ((rcImage.right + rcText->right + (*ccText > 0 ? 4 : 0) + (ctl->arrow ? rcImage.right : 0)) / 2);
+ rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
+ rcImage.right += rcImage.left;
+ rcImage.bottom += rcImage.top;
+
+ OffsetRect(rcText, rcImage.right + 4, 0);
+ if (ctl->stateId == PBS_PRESSED) OffsetRect(&rcImage, 1, 1);
+
+ DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->hIcon, 0,
+ rcImage.left, rcImage.top,
+ rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
+ IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
+ }
+
+ // draw arrow on the right of the button
+ if (ctl->arrow) {
+ rcImage.right = GetSystemMetrics(SM_CXSMICON);
+ rcImage.left = (*ccText > 0 || ctl->hIcon)
+ ? rcClient->right - GetSystemMetrics(SM_CXSMICON)
+ : (rcClient->right - rcClient->left - rcImage.right) / 2;
+ rcImage.right += rcImage.left;
+ rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
+ rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
+ if (ctl->stateId == PBS_PRESSED) OffsetRect(&rcImage, 1, 1);
+
+ DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->arrow, 0,
+ rcImage.left, rcImage.top,
+ rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
+ IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
+ }
+}
+
+/**
+ * name: PaintThemeButton
+ * desc: Draws the themed button
+ * param: ctl - BTNCTRL structure for the button
+ * hdcMem - device context to draw to
+ * rcClient - rectangle of the whole button
+ * return: nothing
+ **/
+static VOID __fastcall PaintThemeButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
+{
+ RECT rcText = { 0, 0, 0, 0 };
+ WCHAR wszText[MAX_PATH] = { 0 };
+ WORD ccText;
+
+ // Draw the flat button
+ if ((ctl->dwStyle & MBS_FLAT) && 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))) {
+ if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
+ DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
+ }
+ DrawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), rcClient, rcClient);
+ }
+ else {
+ // draw themed button background
+ 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)) {
+ if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
+ DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
+ }
+ DrawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, rcClient, rcClient);
+ }
+ }
+
+ // calculate text rect
+ {
+ RECT sizeText;
+ HFONT hOldFont;
+
+ ccText = GetWindowTextW(ctl->hwnd, wszText, sizeof(wszText) / sizeof(WCHAR));
+
+ if (ccText > 0) {
+ hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+
+ GetThemeTextExtent(
+ ctl->hThemeButton,
+ hdcMem,
+ BP_PUSHBUTTON,
+ IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
+ wszText,
+ ccText,
+ DST_PREFIXTEXT,
+ NULL,
+ &sizeText);
+
+ if (ctl->cHot) {
+ RECT rcHot;
+
+ GetThemeTextExtent(ctl->hThemeButton,
+ hdcMem,
+ BP_PUSHBUTTON,
+ IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
+ L"&",
+ 1,
+ DST_PREFIXTEXT,
+ NULL,
+ &rcHot);
+
+ sizeText.right -= (rcHot.right - rcHot.left);
+ }
+ SelectObject(hdcMem, hOldFont);
+
+ rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - (sizeText.right - sizeText.left)) / 2;
+ rcText.top = (rcClient->bottom - rcClient->top - (sizeText.bottom - sizeText.top)) / 2;
+ rcText.right = rcText.left + (sizeText.right - sizeText.left);
+ rcText.bottom = rcText.top + (sizeText.bottom - sizeText.top);
+ if (ctl->stateId == PBS_PRESSED) {
+ OffsetRect(&rcText, 1, 1);
+ }
+ }
+ }
+ PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);
+ // draw text
+ if (ccText > 0 && ctl->hThemeButton) {
+ HFONT hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+ DrawThemeText(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED, wszText, ccText, DST_PREFIXTEXT, 0, &rcText);
+ SelectObject(hdcMem, hOldFont);
+ }
+}
+
+/**
+ * name: PaintThemeButton
+ * desc: Draws the none themed button
+ * param: ctl - BTNCTRL structure for the button
+ * hdcMem - device context to draw to
+ * rcClient - rectangle of the whole button
+ * return: nothing
+ **/
+static VOID __fastcall PaintButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
+{
+ RECT rcText = { 0, 0, 0, 0 };
+ TCHAR szText[MAX_PATH] = { 0 };
+ WORD ccText;
+
+ // Draw the flat button
+ if (ctl->dwStyle & MBS_FLAT) {
+ HBRUSH hbr = NULL;
+
+ if (ctl->stateId == PBS_PRESSED || ctl->stateId == PBS_HOT)
+ hbr = GetSysColorBrush(COLOR_3DLIGHT);
+ else {
+ HDC dc;
+ HWND hwndParent;
+
+ hwndParent = GetParent(ctl->hwnd);
+ if (dc = GetDC(hwndParent)) {
+ hbr = (HBRUSH)SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM)dc, (LPARAM)hwndParent);
+ ReleaseDC(hwndParent, dc);
+ }
+ }
+ if (hbr) {
+ FillRect(hdcMem, rcClient, hbr);
+ DeleteObject(hbr);
+ }
+ if (ctl->stateId == PBS_HOT || ctl->bFocus) {
+ 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 {
+ 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->bFocus) {
+ RECT focusRect = *rcClient;
+ InflateRect(&focusRect, -3, -3);
+ DrawFocusRect(hdcMem, &focusRect);
+ }
+ }
+ // calculate text rect
+ {
+ SIZE sizeText;
+ HFONT hOldFont;
+
+ ccText = GetWindowText(ctl->hwnd, szText, SIZEOF(szText));
+
+ if (ccText > 0) {
+ hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+ GetTextExtentPoint32(hdcMem, szText, ccText, &sizeText);
+ if (ctl->cHot) {
+ SIZE sizeHot;
+
+ GetTextExtentPoint32A(hdcMem, "&", 1, &sizeHot);
+ sizeText.cx -= sizeHot.cx;
+ }
+ SelectObject(hdcMem, hOldFont);
+
+ rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - sizeText.cx) / 2;
+ rcText.top = (rcClient->bottom - rcClient->top - sizeText.cy) / 2;
+ rcText.right = rcText.left + sizeText.cx;
+ rcText.bottom = rcText.top + sizeText.cy;
+ if (ctl->stateId == PBS_PRESSED)
+ OffsetRect(&rcText, 1, 1);
+ }
+ }
+ PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);
+
+ // draw text
+ if (ccText > 0) {
+ HFONT hOldFont;
+
+ hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+
+ SetBkMode(hdcMem, TRANSPARENT);
+ SetTextColor(hdcMem,
+ IsWindowEnabled(ctl->hwnd) || !ctl->hThemeButton
+ ? ctl->stateId == PBS_HOT
+ ? GetSysColor(COLOR_HOTLIGHT)
+ : GetSysColor(COLOR_BTNTEXT)
+ : GetSysColor(COLOR_GRAYTEXT));
+
+ DrawState(hdcMem, NULL, NULL, (LPARAM)szText, 0,
+ rcText.left, rcText.top, rcText.right - rcText.left, rcText.bottom - rcText.top,
+ IsWindowEnabled(ctl->hwnd) || ctl->hThemeButton ? DST_PREFIXTEXT | DSS_NORMAL : DST_PREFIXTEXT | DSS_DISABLED);
+ SelectObject(hdcMem, hOldFont);
+ }
+}
+
+/**
+ * name: Button_WndProc
+ * desc: window procedure for the button class
+ * param: hwndBtn - window handle to the button
+ * uMsg - message to handle
+ * wParam - message specific parameter
+ * lParam - message specific parameter
+ * return: message specific
+ **/
+static LRESULT CALLBACK Button_WndProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ LPBTNCTRL bct = (LPBTNCTRL)GetWindowLongPtr(hwndBtn, 0);
+
+ switch (uMsg) {
+ case WM_NCCREATE:
+ {
+ LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
+
+ cs->style |= BS_OWNERDRAW;
+ if (!(bct = (LPBTNCTRL)mir_alloc(sizeof(BTNCTRL))))
+ return FALSE;
+ ZeroMemory(bct, sizeof(BTNCTRL));
+ bct->hwnd = hwndBtn;
+ bct->stateId = PBS_NORMAL;
+ bct->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ bct->dwStyle = cs->style;
+ if (cs->style & MBS_DOWNARROW)
+ bct->arrow = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
+ LoadTheme(bct);
+ SetWindowLongPtr(hwndBtn, 0, (LONG_PTR)bct);
+ if (cs->lpszName) SetWindowText(hwndBtn, cs->lpszName);
+ return TRUE;
+ }
+ case WM_DESTROY:
+ if (bct) {
+ EnterCriticalSection(&csTips);
+ if (hwndToolTips) {
+ TOOLINFO ti;
+
+ ZeroMemory(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
+ DestroyWindow(hwndToolTips);
+ hwndToolTips = NULL;
+ }
+ }
+ LeaveCriticalSection(&csTips);
+ DestroyTheme(bct);
+ mir_free(bct);
+ }
+ SetWindowLongPtr(hwndBtn, 0, NULL);
+ break;
+ case WM_SETTEXT:
+ bct->cHot = 0;
+ if ((LPTSTR)lParam) {
+ LPTSTR tmp = (LPTSTR)lParam;
+
+ while (*tmp) {
+ if (*tmp=='&' && *(tmp+1)) {
+ bct->cHot = _totlower(*(tmp+1));
+ break;
+ }
+ tmp++;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ case WM_SYSKEYUP:
+ if (bct->stateId != PBS_DISABLED && bct->cHot && bct->cHot == _totlower((TCHAR)wParam)) {
+ if (bct->dwStyle & MBS_PUSHBUTTON) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ else
+ SetFocus(hwndBtn);
+ SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
+ 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;
+ HDC hdcMem;
+ HBITMAP hbmMem;
+ HDC hOld;
+ RECT rcClient;
+
+ if (hdcPaint = BeginPaint(hwndBtn, &ps)) {
+ GetClientRect(bct->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 ((bct->dwStyle & MBS_PUSHBUTTON) && bct->pbState) bct->stateId = PBS_PRESSED;
+
+ if ((bct->dwStyle & MBS_FLAT) && bct->hThemeToolbar || bct->hThemeButton)
+ PaintThemeButton(bct, hdcMem, &rcClient);
+ else
+ PaintButton(bct, hdcMem, &rcClient);
+
+ BitBlt(hdcPaint, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+ EndPaint(hwndBtn, &ps);
+ }
+ return 0;
+ }
+ case BM_SETIMAGE:
+ if (wParam == IMAGE_ICON) {
+ bct->hIcon = (HICON)lParam;
+ bct->hBitmap = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ else if (wParam == IMAGE_BITMAP) {
+ bct->hIcon = NULL;
+ bct->hBitmap = (HBITMAP)lParam;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ else if (wParam == NULL && lParam == NULL) {
+ bct->hIcon = NULL;
+ bct->hBitmap = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ case BM_SETCHECK:
+ if (!(bct->dwStyle & MBS_PUSHBUTTON)) 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->dwStyle & MBS_PUSHBUTTON) return bct->pbState ? BST_CHECKED : BST_UNCHECKED;
+ return 0;
+ case BUTTONSETDEFAULT:
+ bct->defbutton = (wParam != 0);
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONADDTOOLTIP:
+ {
+ if (!wParam) break;
+ EnterCriticalSection(&csTips);
+ if (!hwndToolTips) {
+ hwndToolTips = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
+ }
+
+ if (lParam == MBF_UNICODE) {
+ TOOLINFOW ti;
+
+ ZeroMemory(&ti, sizeof(TOOLINFOW));
+ ti.cbSize = sizeof(TOOLINFOW);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFOW, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOLW, 0, (LPARAM)&ti);
+ }
+ ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ ti.lpszText=(LPWSTR)wParam;
+ SendMessage(hwndToolTips, TTM_ADDTOOLW, 0, (LPARAM)&ti);
+ }
+ else {
+ TOOLINFOA ti;
+
+ ZeroMemory(&ti, sizeof(TOOLINFOA));
+ ti.cbSize = sizeof(TOOLINFOA);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOLA, 0, (LPARAM)&ti);
+ }
+ ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ ti.lpszText=(LPSTR)wParam;
+ SendMessage(hwndToolTips, TTM_ADDTOOLA, 0, (LPARAM)&ti);
+ }
+ LeaveCriticalSection(&csTips);
+ break;
+ }
+ case BUTTONTRANSLATE:
+ {
+ TCHAR szButton[MAX_PATH];
+ GetWindowText(bct->hwnd, szButton, MAX_PATH);
+ SetWindowText(bct->hwnd, TranslateTS(szButton));
+ break;
+ }
+ case WM_SETFOCUS: // set keybord bFocus and redraw
+ bct->bFocus = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case WM_KILLFOCUS: // kill bFocus and redraw
+ bct->bFocus = 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:
+ if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+ BOOLEAN bPressed = bct->stateId == PBS_PRESSED;
+
+ if (bct->dwStyle & MBS_PUSHBUTTON) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ }
+ bct->stateId = PBS_HOT;
+
+ // Tell your daddy you got clicked, if mouse is still over the button.
+ if ((bct->dwStyle & MBS_PUSHBUTTON) || bPressed)
+ SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ 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(hwndBtn, 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(hwndBtn, &rc);
+ GetCursorPos(&pt);
+ if (!PtInRect(&rc, pt)) { // mouse must be gone, trigger mouse leave
+ PostMessage(hwndBtn, WM_MOUSELEAVE, 0, 0L);
+ KillTimer(hwndBtn, BUTTON_POLLID);
+ }
+ }
+ break;
+ case WM_ERASEBKGND:
+ return 1;
+ }
+ return DefWindowProc(hwndBtn, uMsg, wParam, lParam);
+}
+
+VOID CtrlButtonUnloadModule()
+{
+ DeleteCriticalSection(&csTips);
+ UnregisterClass(UINFOBUTTONCLASS, ghInst);
+}
+
+VOID CtrlButtonLoadModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = UINFOBUTTONCLASS;
+ wc.lpfnWndProc = Button_WndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(LPBTNCTRL);
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+ InitializeCriticalSection(&csTips);
+}
+
diff --git a/plugins/UserInfoEx/src/ctrl_button.h b/plugins/UserInfoEx/src/ctrl_button.h
new file mode 100644
index 0000000000..5dd99bb4f6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_button.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_button.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_BOTTONS_H_INCLUDED_
+#define _UINFOEX_BOTTONS_H_INCLUDED_ 1
+
+VOID CtrlButtonLoadModule();
+VOID CtrlButtonUnloadModule();
+
+#endif /* _UINFOEX_BOTTONS_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_combo.cpp b/plugins/UserInfoEx/src/ctrl_combo.cpp
new file mode 100644
index 0000000000..6cdd5bdd2c
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_combo.cpp
@@ -0,0 +1,277 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_combo.cpp $
+Revision : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+
+/**
+ * This static method creates an object for the CCombo class and returns its pointer.
+ * If combobox is filled with items from pList.
+ *
+ * @param hDlg - HWND of the owning propertysheet page
+ * @param idCtrl - the ID of the control to associate with this class's instance
+ * @param pszSetting - the database setting to be handled by this class
+ * @param bDBDataType - datatype of of the associated database setting (WORD, DWORD, ...)
+ * @param pList - pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
+ * @param nListCount - number of items in the list
+ *
+ * @return pointer of the newly created CCombo object.
+ **/
+CBaseCtrl* CCombo::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
+{
+ return new CCombo(hDlg, idCtrl, pszSetting, bDBDataType, pList, nListCount);
+}
+
+/**
+ * This is the constructor. If combobox is filled with items from pList.
+ *
+ * @param hDlg - HWND of the owning propertysheet page
+ * @param idCtrl - the ID of the control to associate with this class's instance
+ * @param pszSetting - the database setting to be handled by this class
+ * @param bDBDataType - datatype of of the associated database setting (WORD, DWORD, ...)
+ * @param pList - pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
+ * @param nListCount - number of items in the list
+ *
+ * @return nothing
+ **/
+CCombo::CCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
+: CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+ _curSel = CB_ERR;
+ _pList = pList;
+ _nList = nListCount;
+ _bDataType = bDBDataType;
+
+ // fill in data
+ if (_pList && (_nList > 0))
+ {
+ for (INT i = 0; i < _nList; i++)
+ {
+ AddItem(_pList[i].ptszTranslated, (LPARAM)&_pList[i]);
+ }
+ }
+}
+
+/**
+ * This method searches for an item which matches the given nID.
+ *
+ * @param nID - the id of the desired object.
+ *
+ * @retval CB_ERR - item not found
+ * @retval 0...n - index of the combobox item
+ **/
+INT CCombo::Find(INT nID) const
+{
+ INT i;
+ LPIDSTRLIST pd;
+
+ for (i = ComboBox_GetCount(_hwnd) - 1; i >= 0; i--)
+ {
+ pd = (LPIDSTRLIST)ComboBox_GetItemData(_hwnd, i);
+ if (PtrIsValid(pd) && (pd->nID == nID))
+ {
+ break;
+ }
+ }
+ return i;
+}
+
+/**
+ * This method searches for an item which matches the given label.
+ *
+ * @param ptszItemLabel - The translated label of the desired item.
+ *
+ * @retval CB_ERR - item not found
+ * @retval 0...n - index of the combobox item
+ **/
+INT CCombo::Find(LPTSTR ptszItemLabel) const
+{
+ return ComboBox_FindStringExact(_hwnd, 0, ptszItemLabel);
+}
+
+/**
+ * Adds a string and associated item data to a combobox.
+ *
+ * @param pszText - the text to add to the combobox
+ * @param lParam - item data to accociate with the new item
+ *
+ * @return zero-based index to new item or CB_ERR on failure
+ **/
+INT_PTR CCombo::AddItem(LPCTSTR pszText, LPARAM lParam)
+{
+ INT_PTR added = ComboBox_AddString(_hwnd, pszText);
+ if (SUCCEEDED(added))
+ {
+ if (PtrIsValid(lParam) && FAILED(ComboBox_SetItemData(_hwnd, added, lParam)))
+ {
+ ComboBox_DeleteString(_hwnd, added);
+ added = CB_ERR;
+ }
+ }
+ return added;
+}
+
+/**
+ * This functions removes the user data from a combobox.
+ *
+ * @param hCtrl - HWND of the combobox
+ *
+ * @return nothing
+ **/
+VOID CCombo::Release()
+{
+ delete this;
+}
+
+/**
+ * This method selects the combobox item which matches the contact's setting, associated with.
+ *
+ * @param hContact - HANDLE of the contact
+ * @param pszProto - the contact's protocol
+ *
+ * @return nothing
+ **/
+BOOL CCombo::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+ if (!_Flags.B.hasChanged)
+ {
+ DBVARIANT dbv;
+ LPIDSTRLIST pItem = NULL;
+ INT iVal = CB_ERR;
+
+ _Flags.B.hasCustom = _Flags.B.hasProto = _Flags.B.hasMeta = 0;
+ _Flags.W |= DB::Setting::GetTStringCtrl(hContact, USERINFO, USERINFO, pszProto, _pszSetting, &dbv);
+ EnableWindow(_hwnd, !hContact || _Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0));
+
+ if (_Flags.B.hasCustom || _Flags.B.hasProto || _Flags.B.hasMeta)
+ {
+ switch (dbv.type)
+ {
+ case DBVT_BYTE: iVal = Find((INT)dbv.bVal); break;
+ case DBVT_WORD: iVal = Find((INT)dbv.wVal); break;
+ case DBVT_DWORD: iVal = Find((INT)dbv.dVal); break;
+ case DBVT_TCHAR:
+ iVal = Find(TranslateTS(dbv.ptszVal));
+ if (iVal == CB_ERR) {
+ // other
+ iVal = Find(_pList[_nList - 1].nID);
+ }
+ }
+ }
+ if (iVal == CB_ERR)
+ {
+ // unspecified
+ iVal = Find(_pList[0].nID);
+ }
+ DB::Variant::Free(&dbv);
+ ComboBox_SetCurSel(_hwnd, iVal);
+ _curSel = ComboBox_GetCurSel(_hwnd);
+ SendMessage(GetParent(_hwnd), WM_COMMAND, MAKEWPARAM( (WORD)this->_idCtrl, (WORD)CBN_SELCHANGE), (LPARAM)_hwnd);
+ }
+ return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the combobox's item
+ *
+ * @param hContact - HANDLE of the contact
+ * @param pszProto - the contact's protocol
+ *
+ * @return nothing
+ **/
+VOID CCombo::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+ if (_Flags.B.hasChanged)
+ {
+ LPCSTR pszModule = hContact ? USERINFO : pszProto;
+
+ if ((_Flags.B.hasCustom || !hContact) && (_curSel != CB_ERR))
+ {
+ LPIDSTRLIST pd;
+
+ pd = (LPIDSTRLIST)SendMessage(_hwnd, CB_GETITEMDATA, _curSel, 0);
+ if (pd != NULL)
+ {
+ switch (_bDataType)
+ {
+ case DBVT_BYTE:
+ DB::Setting::WriteByte(hContact, pszModule, _pszSetting, pd->nID);
+ break;
+ case DBVT_WORD:
+ DB::Setting::WriteWord(hContact, pszModule, _pszSetting, pd->nID);
+ break;
+ case DBVT_DWORD:
+ DB::Setting::WriteDWord(hContact, pszModule, _pszSetting, pd->nID);
+ break;
+ case DBVT_ASCIIZ:
+ case DBVT_WCHAR:
+ DB::Setting::WriteAString(hContact, pszModule, _pszSetting, (LPSTR)pd->pszText);
+ }
+ if (!hContact)
+ {
+ _Flags.B.hasCustom = 0;
+ _Flags.B.hasProto = 1;
+ }
+ _Flags.B.hasChanged = 0;
+ }
+ }
+ if (_Flags.B.hasChanged)
+ {
+ DB::Setting::Delete(hContact, pszModule, _pszSetting);
+ _Flags.B.hasChanged = 0;
+ OnInfoChanged(hContact, pszProto);
+ }
+ InvalidateRect(_hwnd, NULL, TRUE);
+ }
+}
+
+/**
+ * The user changed combobox selection, so mark it changed.
+ *
+ * @return nothing
+ **/
+VOID CCombo::OnChangedByUser(WORD wChangedMsg)
+{
+ if (wChangedMsg == CBN_SELCHANGE)
+ {
+ INT c = ComboBox_GetCurSel(_hwnd);
+
+ if (_curSel != c)
+ {
+ if (!_Flags.B.hasChanged)
+ {
+ _Flags.B.hasChanged = 1;
+ _Flags.B.hasCustom = 1;
+ SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+ }
+ _curSel = c;
+ }
+ }
+}
+
diff --git a/plugins/UserInfoEx/src/ctrl_combo.h b/plugins/UserInfoEx/src/ctrl_combo.h
new file mode 100644
index 0000000000..84bd4e23a6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_combo.h
@@ -0,0 +1,80 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_combo.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_COMBO_INCLUDE_
+#define _UI_CTRL_COMBO_INCLUDE_
+
+#include "ctrl_base.h"
+
+/**
+ *
+ **/
+class CCombo : public CBaseCtrl
+{
+ INT _curSel;
+ LPIDSTRLIST _pList;
+ INT _nList;
+ BYTE _bDataType;
+
+ /**
+ * Private constructure is to force to use static member 'Create'
+ * as the default way of attaching a new object to the window control.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ CCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount);
+
+ INT Find(INT nIndex) const;
+ INT Find(LPTSTR ptszItemLabel) const;
+
+ INT_PTR AddItem(LPCTSTR pszText, LPARAM lParam);
+
+public:
+
+ /**
+ *
+ *
+ **/
+ static FORCEINLINE CCombo* GetObj(HWND hCtrl)
+ { return (CCombo*) GetUserData(hCtrl); }
+ static FORCEINLINE CCombo* GetObj(HWND hDlg, WORD idCtrl)
+ { return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+ static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount);
+
+ virtual VOID Release();
+ virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnChangedByUser(WORD wChangedMsg);
+};
+
+#endif /* _UI_CTRL_COMBO_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_contact.cpp b/plugins/UserInfoEx/src/ctrl_contact.cpp
new file mode 100644
index 0000000000..8b13ad9256
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_contact.cpp
@@ -0,0 +1,1539 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_contact.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+#include "ctrl_contact.h"
+
+#define MAX_CAT 64
+
+//#define BTN_MENU (WM_USER+1)
+//#define BTN_EDIT (WM_USER+2)
+#define EDIT_VALUE (WM_USER+3)
+
+#define CBEXM_MENIITEMFIRST (WM_USER+100) // caution: after this message there must be room for all menuitem commnds
+ // so it should be the last message with the highest value
+
+typedef struct TCbExItem
+{
+ WORD wMask;
+ WORD wFlags;
+ DWORD dwID;
+ TCHAR szCat[MAX_CAT];
+ LPTSTR pszVal;
+ LPCSTR pszIcon;
+ HICON hIcon;
+} CBEXITEMINTERN, *LPCBEXITEMINTERN;
+
+typedef struct TComboEx
+{
+ LPCBEXITEMINTERN pItems;
+ INT numItems;
+ INT numOther;
+ INT iSelectedItem;
+
+ BOOLEAN bIsChanged;
+ BOOLEAN bLocked;
+ BOOLEAN bIsEditChanged;
+
+ HINSTANCE hInstance;
+ HFONT hFont;
+ HWND hEdit;
+ HWND hBtnMenu;
+ HWND hBtnEdit;
+ HWND hBtnAdd;
+ HWND hBtnDel;
+ RECT rect;
+} CBEX, *LPCBEX;
+
+
+static INT compareProc(LPCVOID cbi1, LPCVOID cbi2)
+{
+ return _tcscmp(((LPCBEXITEMINTERN)cbi1)->szCat, ((LPCBEXITEMINTERN)cbi2)->szCat);
+}
+
+static INT CheckPhoneSyntax(LPTSTR pszSrc, LPTSTR szNumber, WORD cchNumber, INT& errorPos)
+{
+ INT lenNum = 0;
+ BOOLEAN hasLeftBreaket = FALSE,
+ hasRightBreaket = FALSE;
+
+ if (!szNumber || !pszSrc || !*pszSrc || !cchNumber) return 0;
+ *szNumber = 0;
+ errorPos = -1;
+
+ if (*pszSrc != '+') {
+ errorPos = 2; // set cursor after first digit
+ *(szNumber + lenNum++) = '+';
+ }
+ else
+ *(szNumber + lenNum++) = *(pszSrc++);
+
+ for (; lenNum < cchNumber - 1 && *pszSrc != 0; pszSrc++) {
+ switch (*pszSrc) {
+ case '(':
+ if (hasLeftBreaket) {
+ if (errorPos == -1) errorPos = lenNum;
+ break;
+ }
+ if (*(szNumber + lenNum - 1) != ' ') {
+ *(szNumber + lenNum++) = ' ';
+ if (errorPos == -1) errorPos = lenNum + 1;
+ }
+ *(szNumber + lenNum++) = *pszSrc;
+ hasLeftBreaket = TRUE;
+ break;
+
+ case ')':
+ if (hasRightBreaket) {
+ if (errorPos == -1) errorPos = lenNum;
+ break;
+ }
+ *(szNumber + lenNum++) = *pszSrc;
+ if (*(pszSrc + 1) != ' ') {
+ *(szNumber + lenNum++) = ' ';
+ if (errorPos == -1) errorPos = lenNum;
+ }
+ hasRightBreaket = TRUE;
+ break;
+
+ case ' ':
+ if (*(szNumber + lenNum - 1) != ' ') {
+ *(szNumber + lenNum++) = *pszSrc;
+ }
+ else
+ if (errorPos == -1) errorPos = lenNum;
+ break;
+
+ default:
+ if (*pszSrc >= '0' && *pszSrc <= '9' || *pszSrc == '-') {
+ *(szNumber + lenNum++) = *pszSrc;
+ }
+ // remember first error position
+ else if (errorPos == -1) errorPos = lenNum;
+ break;
+ }
+ }
+ *(szNumber + lenNum) = 0;
+ return lenNum;
+}
+
+/**
+ * name: DlgProc_EditEMail()
+ * desc: dialog procedure
+ *
+ * return: 0 or 1
+ **/
+static INT_PTR CALLBACK DlgProc_EMail(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ LPCBEXITEM cbi = (LPCBEXITEM)lParam;
+
+ if (!cbi) return FALSE;
+ SetUserData(hDlg, lParam);
+
+ SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_EMAIL));
+ if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+ {
+ SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+ SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+ }
+
+ if (*cbi->pszVal) SetWindowText(hDlg, LPGENT("Edit E-Mail"));
+ TranslateDialogDefault(hDlg);
+ SendDlgItemMessage(hDlg, EDIT_CATEGORY, EM_LIMITTEXT, cbi->ccCat - 1, 0);
+ SendDlgItemMessage(hDlg, EDIT_EMAIL, EM_LIMITTEXT, cbi->ccVal - 1, 0);
+ SetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat);
+ SetDlgItemText(hDlg, EDIT_EMAIL, cbi->pszVal);
+ EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), !(cbi->wFlags & CBEXIF_CATREADONLY));
+ EnableWindow(GetDlgItem(hDlg, IDOK), *cbi->pszVal);
+ // translate Userinfo buttons
+ {
+ TCHAR szButton[MAX_PATH];
+ HWND hBtn;
+
+ hBtn = GetDlgItem(hDlg, IDOK);
+ GetWindowText(hBtn, szButton, MAX_PATH);
+ SetWindowText(hBtn, TranslateTS(szButton));
+ hBtn = GetDlgItem(hDlg, IDCANCEL);
+ GetWindowText(hBtn, szButton, MAX_PATH);
+ SetWindowText(hBtn, TranslateTS(szButton));
+ }
+ return TRUE;
+ }
+
+ case WM_CTLCOLORSTATIC:
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ case IDOK:
+ {
+ LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+
+ if (cbi->pszVal && cbi->ccVal > 0)
+ GetDlgItemText(hDlg, EDIT_EMAIL, cbi->pszVal, cbi->ccVal);
+ if (cbi->pszCat && cbi->ccCat > 0)
+ GetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat, cbi->ccCat);
+ }
+ case IDCANCEL:
+ EndDialog(hDlg, LOWORD(wParam));
+ break;
+ }
+ case EDIT_EMAIL:
+ if (HIWORD(wParam) == EN_UPDATE) {
+ TCHAR szText[MAXDATASIZE];
+ LPTSTR pszAdd, pszDot;
+ LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+
+ if (PtrIsValid(cbi)) {
+ GetWindowText((HWND)lParam, szText, SIZEOF(szText));
+ EnableWindow(GetDlgItem(hDlg, IDOK),
+ ((pszAdd = _tcschr(szText, '@')) &&
+ *(pszAdd + 1) != '.' &&
+ (pszDot = _tcschr(pszAdd, '.')) &&
+ *(pszDot + 1) &&
+ _tcscmp(szText, cbi->pszVal)));
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * name: DlgProc_EditPhone()
+ * desc: dialog procedure
+ *
+ * return: 0 or 1
+ **/
+INT_PTR CALLBACK DlgProc_Phone(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ LPCBEXITEM cbi = (LPCBEXITEM)lParam;
+ UINT i, item, countryCount;
+ LPIDSTRLIST pCountries;
+ HWND hCombo = GetDlgItem(hDlg, EDIT_COUNTRY);
+
+ if (!cbi) return FALSE;
+ SetUserData(hDlg, lParam);
+
+ SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_PHONE));
+ if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+ {
+ SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+ SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+ }
+
+ // translate Userinfo buttons
+ {
+ TCHAR szButton[MAX_PATH];
+ HWND hBtn;
+
+ hBtn = GetDlgItem(hDlg, IDOK);
+ GetWindowText(hBtn, szButton, MAX_PATH);
+ SetWindowText(hBtn, TranslateTS(szButton));
+ hBtn = GetDlgItem(hDlg, IDCANCEL);
+ GetWindowText(hBtn, szButton, MAX_PATH);
+ SetWindowText(hBtn, TranslateTS(szButton));
+ }
+ if (*cbi->pszVal) SetWindowText(hDlg, LPGENT("Edit Phone Number"));
+ if (cbi->wFlags & CBEXIF_SMS) CheckDlgButton(hDlg, CHECK_SMS, BST_CHECKED);
+ TranslateDialogDefault(hDlg);
+
+ EnableWindow(GetDlgItem(hDlg, IDOK), *cbi->pszVal);
+ SendDlgItemMessage(hDlg, EDIT_AREA, EM_LIMITTEXT, 31, 0);
+ SendDlgItemMessage(hDlg, EDIT_NUMBER, EM_LIMITTEXT, 63, 0);
+ SendDlgItemMessage(hDlg, EDIT_CATEGORY, EM_LIMITTEXT, cbi->ccCat - 1, 0);
+ SendDlgItemMessage(hDlg, EDIT_PHONE, EM_LIMITTEXT, cbi->ccVal - 1, 0);
+
+ GetCountryList(&countryCount, &pCountries);
+ for (i = 0; i < countryCount; i++) {
+ if (pCountries[i].nID == 0 || pCountries[i].nID == 0xFFFF) continue;
+ item = SendMessage(hCombo, CB_ADDSTRING, NULL, (LPARAM)pCountries[i].ptszTranslated);
+ SendMessage(hCombo, CB_SETITEMDATA, item, pCountries[i].nID);
+ }
+
+ SetDlgItemText(hDlg, EDIT_PHONE, cbi->pszVal);
+ SetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat);
+ EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), !(cbi->wFlags & CBEXIF_CATREADONLY));
+ return TRUE;
+ }
+
+ case WM_CTLCOLORSTATIC:
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+ case WM_COMMAND:
+ {
+ static INT noRecursion=0;
+
+ switch (LOWORD(wParam)) {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ case IDOK:
+ {
+ LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+ TCHAR szText[MAXDATASIZE];
+ INT errorPos;
+
+ if (!GetDlgItemText(hDlg, EDIT_PHONE, szText, MAXDATASIZE) || !CheckPhoneSyntax(szText, cbi->pszVal, cbi->ccVal, errorPos) || errorPos > -1) {
+ MsgErr(hDlg, TranslateT("The phone number should start with a + and consist of\nnumbers, spaces, brackets and hyphens only."));
+ /*
+ EDITBALLOONTIP btt;
+
+ btt.cbStruct = SIZEOF(btt);
+
+ btt.pszTitle = TranslateW(L"Syntax Error");
+ btt.pszText = TranslateW(L"The phone number should start with a + and consist of\nnumbers, spaces, brackets and hyphens only.");
+
+ SendDlgItemMessage(hDlg, EDIT_PHONE, EM_SHOWBALLOONTIP, NULL, (LPARAM)&btt);
+ SetDlgItemText(hDlg, EDIT_PHONE, cbi->pszVal);
+ SendDlgItemMessage(hDlg, EDIT_PHONE, EM_SETSEL, errorPos, errorPos);
+ */
+ break;
+ }
+ // save category string
+ GetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat, cbi->ccCat);
+
+ // save SMS flag
+ if (IsDlgButtonChecked(hDlg, CHECK_SMS) != ((cbi->wFlags & CBEXIF_SMS) == CBEXIF_SMS)) {
+ cbi->wFlags ^= CBEXIF_SMS;
+ }
+ }
+ //fall through
+ case IDCANCEL:
+ EndDialog(hDlg, wParam);
+ break;
+ }
+
+ case EDIT_COUNTRY:
+ if (HIWORD(wParam) != CBN_SELCHANGE) break;
+
+ case EDIT_AREA:
+ case EDIT_NUMBER:
+ if (LOWORD(wParam) != EDIT_COUNTRY && HIWORD(wParam) != EN_CHANGE) break;
+ if (noRecursion) break;
+ EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+ {
+ TCHAR szPhone[MAXDATASIZE], szArea[32], szData[64];
+ INT nCurSel = SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETCURSEL, 0, 0);
+ UINT nCountry = (nCurSel != CB_ERR) ? SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETITEMDATA, nCurSel, 0) : 0;
+
+ GetDlgItemText(hDlg, EDIT_AREA, szArea, SIZEOF(szArea));
+ GetDlgItemText(hDlg, EDIT_NUMBER, szData, SIZEOF(szData));
+ mir_sntprintf(szPhone, MAXDATASIZE, _T("+%u (%s) %s"), nCountry, szArea, szData);
+ noRecursion = 1;
+ SetDlgItemText(hDlg, EDIT_PHONE, szPhone);
+ noRecursion = 0;
+ }
+ break;
+
+ case EDIT_PHONE:
+ if (HIWORD(wParam) != EN_UPDATE) break;
+ if (noRecursion) break;
+ noRecursion = 1;
+ {
+ TCHAR szText[MAXDATASIZE], *pText, *pArea, *pNumber;
+ INT isValid = 1;
+ GetDlgItemText(hDlg, EDIT_PHONE, szText, SIZEOF(szText));
+ if (szText[0] != '+') isValid = 0;
+ if (isValid) {
+ INT i,country = _tcstol(szText + 1, &pText, 10);
+ if (pText - szText > 4)
+ isValid = 0;
+ else {
+ for (i = SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETCOUNT, 0, 0) - 1; i >= 0; i--) {
+ if (country == SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETITEMDATA, i, 0)) {
+ SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_SETCURSEL, i, 0);
+ break;
+ }
+ }
+ }
+ if (i < 0) isValid=0;
+ }
+ if (isValid) {
+ pArea = pText + _tcscspn(pText, _T("0123456789"));
+ pText = pArea + _tcsspn(pArea, _T("0123456789"));
+ if (*pText) {
+ *pText = '\0';
+ pNumber = pText + 1 + _tcscspn(pText + 1, _T("0123456789"));
+ SetDlgItemText(hDlg, EDIT_NUMBER, pNumber);
+ }
+ SetDlgItemText(hDlg, EDIT_AREA, pArea);
+ }
+ if (!isValid) {
+ SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_SETCURSEL, -1, 0);
+ SetDlgItemText(hDlg, EDIT_AREA, _T(""));
+ SetDlgItemText(hDlg, EDIT_NUMBER, _T(""));
+ }
+ }
+ noRecursion = 0;
+ EnableWindow(GetDlgItem(hDlg, IDOK), GetWindowTextLength(GetDlgItem(hDlg, EDIT_PHONE)));
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * name: CtrlContactWndProc
+ * desc: window procedure for the extended combobox class
+ * param: hwnd - handle to a extended combobox window
+ * msg - message to handle
+ * wParam - message specific
+ * lParam - message specific
+ * return: message specific
+ **/
+static LRESULT CALLBACK CtrlContactWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LPCBEX cbex = (LPCBEX)GetWindowLongPtr(hwnd, 0);
+
+ switch (msg) {
+
+ /**
+ * name: WM_NCCREATE
+ * desc: is called to initiate the window creation
+ * param: wParam - not used
+ * lParam - pointer to a CREATESTRUCT
+ *
+ * return: FALSE on error, TRUE if initialisation was ok
+ **/
+ case WM_NCCREATE:
+ {
+ LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
+
+ if (!(cbex = (LPCBEX)mir_calloc(1*sizeof(CBEX))))
+ return FALSE;
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)cbex);
+ cbex->bLocked = 1;
+ cbex->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ cbex->hInstance = cs->hInstance;
+ cbex->iSelectedItem = -1;
+ cbex->rect.left = cs->x;
+ cbex->rect.top = cs->y;
+ cbex->rect.right = cs->x + cs->cx;
+ cbex->rect.bottom = cs->y + cs->cy;
+ return TRUE;
+ }
+
+ /**
+ * name: WM_NCCREATE
+ * desc: is called to create all subitems
+ * param: wParam - not used
+ * lParam - not used
+ *
+ * return: FALSE on error, TRUE if initialisation was ok
+ **/
+ case WM_CREATE:
+ {
+ WORD wHeight = (WORD)(cbex->rect.bottom - cbex->rect.top);
+ WORD wWidth = 130;
+ WORD x = 0;
+
+ if (!(cbex->hBtnEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+ UINFOBUTTONCLASS,
+ _T("none"),
+ WS_VISIBLE|WS_CHILD|WS_TABSTOP, 0, 0,
+ wWidth, wHeight,
+ hwnd,
+ NULL,
+ cbex->hInstance, NULL))) {
+ cbex->bLocked = 0;
+ return FALSE;
+ }
+ x += wWidth + 2;
+ wWidth = wHeight;
+ if (!(cbex->hBtnMenu = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+ UINFOBUTTONCLASS,
+ NULL,
+ WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_PUSHBUTTON|MBS_DOWNARROW,
+ x, 0,
+ wWidth, wHeight,
+ hwnd,
+ NULL,
+ cbex->hInstance, NULL))) {
+ DestroyWindow(cbex->hBtnEdit);
+ cbex->bLocked = 0;
+ return FALSE;
+ }
+ x += wWidth + 2;
+ wWidth = (WORD)(cbex->rect.right - cbex->rect.left - x - (2 * (wHeight + 2)));
+ if (!(cbex->hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
+ _T("Edit"),
+ NULL,
+ WS_VISIBLE|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL,
+ x, 1,
+ wWidth, wHeight - 2,
+ hwnd,
+ NULL,
+ cbex->hInstance, NULL))) {
+ DestroyWindow(cbex->hBtnEdit);
+ DestroyWindow(cbex->hBtnMenu);
+ cbex->bLocked = 0;
+ return FALSE;
+ }
+ x += wWidth + 2;
+ wWidth = wHeight;
+ if (!(cbex->hBtnAdd = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+ UINFOBUTTONCLASS,
+ NULL,
+ WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_FLAT,
+ x, 0,
+ wWidth, wHeight,
+ hwnd,
+ NULL,
+ cbex->hInstance, NULL))) {
+ DestroyWindow(cbex->hBtnEdit);
+ DestroyWindow(cbex->hBtnMenu);
+ DestroyWindow(cbex->hEdit);
+ cbex->bLocked = 0;
+ return FALSE;
+ }
+ x += wWidth + 2;
+ if (!(cbex->hBtnDel = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+ UINFOBUTTONCLASS,
+ NULL,
+ WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_FLAT,
+ x, 0,
+ wWidth, wHeight,
+ hwnd,
+ NULL,
+ cbex->hInstance, NULL))) {
+ DestroyWindow(cbex->hBtnEdit);
+ DestroyWindow(cbex->hBtnMenu);
+ DestroyWindow(cbex->hEdit);
+ DestroyWindow(cbex->hBtnAdd);
+ cbex->bLocked = 0;
+ return FALSE;
+ }
+
+ // set ids
+ SetWindowLongPtr(cbex->hBtnEdit, GWLP_ID, BTN_EDIT);
+ SetWindowLongPtr(cbex->hBtnMenu, GWLP_ID, BTN_MENU);
+ SetWindowLongPtr(cbex->hEdit, GWLP_ID, EDIT_VALUE);
+ SetWindowLongPtr(cbex->hBtnAdd, GWLP_ID, BTN_ADD);
+ SetWindowLongPtr(cbex->hBtnDel, GWLP_ID, BTN_DEL);
+ // set fonts & maximum edit control charachters
+ SendMessage(cbex->hEdit, WM_SETFONT, (WPARAM)cbex->hFont, NULL);
+ SendMessage(cbex->hEdit, EM_LIMITTEXT, (WPARAM)MAXDATASIZE, NULL);
+ // add tooltips
+ SendMessage(cbex->hBtnMenu, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Choose the item to display."), MBF_TCHAR);
+ SendMessage(cbex->hBtnEdit, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Edit the currently displayed item."), MBF_TCHAR);
+ SendMessage(cbex->hBtnAdd, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Add a new custom item."), MBF_TCHAR);
+ SendMessage(cbex->hBtnDel, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete the selected item."), MBF_TCHAR);
+ // reload icons
+ CtrlContactWndProc(hwnd, WM_SETICON, NULL, NULL);
+ cbex->bLocked = 0;
+ return TRUE;
+ }
+
+ /**
+ * name: WM_DESTROY
+ * desc: default destroy message, so clear up memory
+ * param: wParam - not used
+ * lParam - not used
+ * return: return value of DefWindowProc
+ **/
+ case WM_DESTROY:
+ CtrlContactWndProc(hwnd, CBEXM_DELALLITEMS, NULL, NULL);
+ DestroyWindow(cbex->hBtnEdit);
+ DestroyWindow(cbex->hBtnMenu);
+ DestroyWindow(cbex->hBtnAdd);
+ DestroyWindow(cbex->hBtnDel);
+ DestroyWindow(cbex->hEdit);
+ MIR_FREE(cbex);
+ break;
+
+ /**
+ * name: WM_CTLCOLOREDIT
+ * desc: is called on a paint message for a dialog item to determine its colour scheme
+ * param: wParam - pointer to a HDC
+ * lParam - pointer to a HWND
+ * return: a brush
+ **/
+ case WM_CTLCOLOREDIT:
+ if (!DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, 1) || (HWND)lParam != cbex->hEdit || !cbex->pItems || cbex->iSelectedItem < 0)
+ break;
+ return Ctrl_SetTextColour((HDC)wParam, cbex->pItems[cbex->iSelectedItem].wFlags);
+
+ case WM_CTLCOLORSTATIC:
+ if ((HWND)lParam == cbex->hEdit)
+ return (BOOL)GetSysColor(COLOR_WINDOW);
+ return FALSE;
+ /**
+ * name: WM_SETICON
+ * desc: updates the icons of this control
+ * param: wParam - not used
+ * lParam - not used
+ * return: always 0
+ **/
+ case WM_SETICON:
+ {
+ HICON hIcon;
+ INT i;
+
+ hIcon = IcoLib_GetIcon(ICO_BTN_ADD);
+ SendMessage(cbex->hBtnAdd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SendMessage(cbex->hBtnAdd, WM_SETTEXT, NULL, (LPARAM)(hIcon ? _T("") : _T("+")));
+
+ hIcon = IcoLib_GetIcon(ICO_BTN_DELETE);
+ SendMessage(cbex->hBtnDel, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SendMessage(cbex->hBtnDel, WM_SETTEXT, NULL, (LPARAM)(hIcon ? _T("") : _T("-")));
+
+ if (cbex->pItems && cbex->numItems > 0) {
+ for (i = 0; i < cbex->numItems; i++) {
+ cbex->pItems[i].hIcon = IcoLib_GetIcon(cbex->pItems[i].pszIcon);
+ }
+ if (cbex->iSelectedItem >= 0 && cbex->iSelectedItem < cbex->numItems)
+ SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[cbex->iSelectedItem].hIcon);
+ }
+ return 0;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ /**
+ * name: BTN_MENU
+ * desc: the button to dropdown the list to show all items is pressed
+ **/
+ case BTN_MENU:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ POINT pt = { 0, 0 };
+ RECT rc;
+ MENUITEMINFO mii;
+ INT i, nItems;
+ HMENU hMenu;
+
+ if (!(hMenu = CreatePopupMenu())) return 0;
+ SetFocus((HWND)lParam);
+
+ ZeroMemory(&mii, sizeof(MENUITEMINFO));
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_ID|MIIM_STRING|MIIM_FTYPE|MIIM_STATE;
+ mii.fType = MFT_STRING;
+
+ // insert the items
+ for (i = nItems = 0; i < cbex->numItems; i++) {
+ if ((cbex->pItems[i].wFlags & CBEXIF_DELETED) || *cbex->pItems[i].szCat == 0) continue;
+ mii.fState = (cbex->pItems[i].pszVal && *cbex->pItems[i].pszVal) ? MFS_CHECKED : MFS_UNCHECKED;
+ mii.wID = CBEXM_MENIITEMFIRST + i;
+ mii.dwTypeData = cbex->pItems[i].szCat;
+ if (!InsertMenuItem(hMenu, i, TRUE, &mii)) {
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ nItems++;
+ }
+ // add separator between default and custom items
+ if (nItems > 3) {
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ mii.wID = 0;
+ mii.dwItemData = 0;
+ InsertMenuItem(hMenu, 3, TRUE, &mii);
+ }
+ ClientToScreen((HWND)lParam, &pt);
+ GetClientRect((HWND)lParam, &rc);
+ i = TrackPopupMenuEx(hMenu, TPM_RIGHTALIGN|TPM_RETURNCMD, pt.x + rc.right, pt.y + rc.bottom, hwnd, NULL);
+ SendMessage(cbex->hBtnMenu, BM_SETCHECK, NULL, NULL);
+ if (i >= CBEXM_MENIITEMFIRST && i < CBEXM_MENIITEMFIRST + cbex->numItems) {
+ CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, (WPARAM)i - CBEXM_MENIITEMFIRST, NULL);
+ }
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ break;
+
+ /**
+ * name: BTN_ADD
+ * desc: the button to add a new entry is pressed
+ **/
+ case BTN_ADD:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ DLGPROC dlgProc;
+ WORD dlgID;
+ TCHAR szCat[MAX_CAT] = { 0 };
+ TCHAR szVal[MAXDATASIZE] = { 0 };
+ CBEXITEM cbi;
+ HWND hDlgDetails;
+
+ SetFocus((HWND)lParam);
+ if (!(hDlgDetails = GetParent(GetParent(hwnd)))) return 1;
+ if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL)) return 0;
+
+ switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+ case EDIT_PHONE:
+ dlgID = IDD_ADDPHONE;
+ dlgProc = (DLGPROC)DlgProc_Phone;
+ cbi.pszIcon = ICO_BTN_CUSTOMPHONE;
+ break;
+ case EDIT_EMAIL:
+ dlgID = IDD_ADDEMAIL;
+ dlgProc = (DLGPROC)DlgProc_EMail;
+ cbi.pszIcon = ICO_BTN_EMAIL;
+ break;
+ default:
+ return 1;
+ }
+
+ cbi.iItem = -1;
+ cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS|CBEXIM_ICONTEXT;
+ cbi.pszCat = szCat;
+ cbi.pszVal = szVal;
+ cbi.ccCat = MAX_CAT;
+ cbi.ccVal = MAXDATASIZE;
+ cbi.wFlags = 0;
+ cbi.dwID = 0;
+
+ if (DialogBoxParam(ghInst, MAKEINTRESOURCE(dlgID), GetParent(hwnd), dlgProc, (LPARAM)&cbi) == IDOK) {
+ HANDLE hContact = NULL;
+
+ SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+ if (hContact) cbi.wFlags |= CTRLF_HASCUSTOM;
+ cbi.wFlags |= CTRLF_CHANGED;
+ if (SendMessage(hwnd, CBEXM_ADDITEM, NULL, (LPARAM)&cbi) > CB_ERR) {
+ SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+ cbex->bIsChanged = TRUE;
+ SendMessage(hwnd, CBEXM_SETCURSEL, cbex->numItems - 1, NULL);
+ }
+ }
+ return 0;
+ }
+ break;
+
+ /**
+ * name: BTN_EDIT
+ * desc: the button to edit an existing entry is pressed
+ **/
+ case BTN_EDIT:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ DLGPROC dlgProc;
+ WORD dlgID;
+ TCHAR szCat[MAX_CAT] = { 0 };
+ TCHAR szVal[MAXDATASIZE] = { 0 };
+ CBEXITEM cbi;
+ HWND hDlgDetails;
+
+ SetFocus((HWND)lParam);
+ if (!(hDlgDetails = GetParent(GetParent(hwnd)))) return 1;
+ if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL)) return 0;
+ if (!cbex->pItems || cbex->iSelectedItem == -1) return 0;
+
+ switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+ case EDIT_PHONE:
+ dlgID = IDD_ADDPHONE;
+ dlgProc = (DLGPROC)DlgProc_Phone;
+ break;
+ case EDIT_EMAIL:
+ dlgID = IDD_ADDEMAIL;
+ dlgProc = (DLGPROC)DlgProc_EMail;
+ break;
+ default:
+ return 1;
+ }
+ cbi.iItem = cbex->iSelectedItem;
+ cbi.dwID = 0;
+ cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+ cbi.pszCat = szCat;
+ cbi.pszVal = szVal;
+ cbi.ccCat = MAX_CAT;
+ cbi.ccVal = MAXDATASIZE;
+ if (!CtrlContactWndProc(hwnd, CBEXM_GETITEM, NULL, (LPARAM)&cbi)) {
+ MsgErr(hwnd, LPGENT("CRITICAL: Unable to edit current entry!\nThis should not happen!"));
+ return 1;
+ }
+
+ if (DialogBoxParam(ghInst, MAKEINTRESOURCE(dlgID), GetParent(hwnd), dlgProc, (LPARAM)&cbi) == IDOK) {
+ HANDLE hContact;
+
+ SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+ if (hContact) cbi.wFlags |= CTRLF_HASCUSTOM;
+ cbi.wFlags |= CTRLF_CHANGED;
+ SendMessage(hwnd, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+ SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+ cbex->bIsChanged = TRUE;
+ }
+ return 0;
+ }
+ break;
+
+ /**
+ * name: BTN_DEL
+ * desc: the button to delete an existing entry is pressed
+ **/
+ case BTN_DEL:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ HWND hDlgDetails;
+ MSGBOX mBox;
+ TCHAR szMsg[MAXDATASIZE];
+
+ SetFocus((HWND)lParam);
+ if (!(hDlgDetails = GetParent(GetParent(hwnd))) ||
+ SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL) ||
+ !cbex->pItems ||
+ cbex->iSelectedItem < 0 ||
+ cbex->iSelectedItem >= cbex->numItems ||
+ FAILED(mir_sntprintf(szMsg, MAXDATASIZE, TranslateT("Do you really want to delete the current selected item?\n\t%s\n\t%s"),
+ cbex->pItems[cbex->iSelectedItem].szCat, cbex->pItems[cbex->iSelectedItem].pszVal))
+ )
+ {
+ return 1;
+ }
+ mBox.cbSize = sizeof(MSGBOX);
+ mBox.hParent = hDlgDetails;
+ mBox.hiLogo = IcoLib_GetIcon(ICO_DLG_PHONE);
+ mBox.uType = MB_YESNO|MB_ICON_QUESTION|MB_NOPOPUP;
+ mBox.ptszTitle = TranslateT("Delete");
+ mBox.ptszMsg = szMsg;
+ if (IDYES == MsgBoxService(NULL, (LPARAM)&mBox)) {
+ // clear value for standard entry
+ if (cbex->pItems[cbex->iSelectedItem].wFlags & CBEXIF_CATREADONLY) {
+ MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+ SetWindowText(cbex->hEdit, _T(""));
+ cbex->pItems[cbex->iSelectedItem].wFlags &= ~CBEXIF_SMS;
+ cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED;
+ }
+ // clear values for customized database entry
+ else
+ if (cbex->pItems[cbex->iSelectedItem].dwID != 0) {
+ MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+ *cbex->pItems[cbex->iSelectedItem].szCat = 0;
+ cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED|CBEXIF_DELETED;
+ CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, cbex->iSelectedItem - 1, FALSE);
+ }
+ // delete default entry
+ else
+ CtrlContactWndProc(hwnd, CBEXM_DELITEM, NULL, cbex->iSelectedItem);
+
+ SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+ cbex->bIsChanged = TRUE;
+ }
+ return 0;
+ }
+ break;
+
+ /**
+ * name: EDIT_VALUE
+ * desc: the edit control wants us to act
+ **/
+ case EDIT_VALUE:
+ switch (HIWORD(wParam)) {
+ case EN_UPDATE:
+ {
+ TCHAR szVal[MAXDATASIZE] = { 0 };
+ INT ccVal;
+ HANDLE hContact;
+ HWND hDlgDetails = GetParent(GetParent(hwnd));
+
+ EnableWindow(cbex->hBtnDel, GetWindowTextLength(cbex->hEdit) > 0);
+
+ if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL) ||
+ cbex->bLocked ||
+ !cbex->pItems ||
+ cbex->iSelectedItem < 0 ||
+ cbex->iSelectedItem >= cbex->numItems) return 1;
+
+ // get the edit control's text value and check it for syntax
+ switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+ case EDIT_PHONE:
+ {
+ INT errorPos;
+ TCHAR szEdit[MAXDATASIZE];
+
+ if (ccVal = GetWindowText(cbex->hEdit, szEdit, MAXDATASIZE)) {
+ if (!(ccVal = CheckPhoneSyntax(szEdit, szVal, MAXDATASIZE, errorPos)) || errorPos > -1) {
+ SetWindowText(cbex->hEdit, szVal);
+ SendMessage(cbex->hEdit, EM_SETSEL, errorPos, errorPos);
+ }
+ }
+ break;
+ }
+ case EDIT_EMAIL:
+ ccVal = GetWindowText(cbex->hEdit, szVal, MAXDATASIZE);
+ break;
+ default:
+ ccVal = GetWindowText(cbex->hEdit, szVal, MAXDATASIZE);
+ break;
+ }
+
+ SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+ if ((cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_CHANGED) && !(hContact && (cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_HASCUSTOM))) return 0;
+
+ if (*szVal == 0 || !cbex->pItems[cbex->iSelectedItem].pszVal || _tcscmp(szVal, cbex->pItems[cbex->iSelectedItem].pszVal)) {
+ cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED;
+ cbex->pItems[cbex->iSelectedItem].wFlags |= (hContact ? CTRLF_HASCUSTOM : CTRLF_HASPROTO);
+ cbex->bIsChanged = TRUE;
+ InvalidateRect((HWND)lParam, NULL, TRUE);
+ SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+ }
+ return 0;
+ }
+ case EN_KILLFOCUS:
+ {
+ INT ccText;
+
+ if (!cbex->pItems || cbex->iSelectedItem < 0 || cbex->iSelectedItem >= cbex->numItems) return 1;
+ if (!(cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_CHANGED)) return 0;
+
+ if ((ccText = GetWindowTextLength(cbex->hEdit)) <= 0) {
+ if (cbex->pItems[cbex->iSelectedItem].wFlags & CBEXIF_CATREADONLY) {
+ MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+ SetWindowText(cbex->hEdit, _T(""));
+ cbex->pItems[cbex->iSelectedItem].wFlags &= ~CBEXIF_SMS;
+ }
+ else
+ CtrlContactWndProc(hwnd, CBEXM_DELITEM, NULL, cbex->iSelectedItem);
+ SendMessage(GetParent(GetParent(hwnd)), PSM_CHANGED, NULL, NULL);
+ cbex->bIsChanged = TRUE;
+ }
+ else
+ if (cbex->pItems[cbex->iSelectedItem].pszVal = (LPTSTR)mir_realloc(cbex->pItems[cbex->iSelectedItem].pszVal, (ccText + 2) * sizeof(TCHAR))) {
+ cbex->pItems[cbex->iSelectedItem].pszVal[ccText + 1] = 0;
+ GetWindowText(cbex->hEdit, cbex->pItems[cbex->iSelectedItem].pszVal, ccText + 1);
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ break;
+
+ /**
+ * name: CBEXM_ADDITEM
+ * desc: add a item to the control
+ * param: wParam - not used
+ * lParam - (LPCBEXITEM)&item
+ * return: CB_ERR on failure, new item index if successful
+ **/
+ case CBEXM_ADDITEM:
+ {
+ LPCBEXITEM pItem = (LPCBEXITEM)lParam;
+
+ if (!pItem) return FALSE;
+
+ // if an item with the id of pItem exists, change it instead of adding a new one
+ // but only if it has not been changed by the user yet.
+ if ((pItem->wMask & CBEXIM_ID) && cbex->pItems && pItem->dwID != 0) {
+ INT iIndex;
+
+ for (iIndex = 0; iIndex < cbex->numItems; iIndex++) {
+ if (cbex->pItems[iIndex].dwID == pItem->dwID) {
+ pItem->iItem = iIndex;
+ if (cbex->pItems[iIndex].wFlags & CTRLF_CHANGED)
+ pItem->wFlags |= CTRLF_CHANGED;
+ else
+ CtrlContactWndProc(hwnd, CBEXM_SETITEM, 0, lParam);
+ return iIndex;
+ }
+ }
+ }
+
+ // add a new item to the combobox
+ if (!(cbex->pItems = (LPCBEXITEMINTERN)mir_realloc(cbex->pItems, (cbex->numItems + 1) * sizeof(CBEXITEMINTERN)))) {
+ cbex->numItems = 0;
+ return CB_ERR;
+ }
+
+ // set the ID
+ cbex->pItems[cbex->numItems].dwID = (pItem->wMask & CBEXIM_ID) ? pItem->dwID : 0;
+
+ // set category string
+ if (!pItem->pszCat || !pItem->pszCat[0] || !mir_tcsncpy(cbex->pItems[cbex->numItems].szCat, pItem->pszCat, MAX_CAT)) {
+ mir_sntprintf(cbex->pItems[cbex->numItems].szCat, MAX_CAT, _T("%s %d"), TranslateT("Other"), ++cbex->numOther);
+ }
+
+ // set value string
+ if ((pItem->wMask & CBEXIM_VAL) && pItem->pszVal && pItem->pszVal[0])
+ cbex->pItems[cbex->numItems].pszVal = mir_tstrdup(pItem->pszVal);
+ else
+ cbex->pItems[cbex->numItems].pszVal = NULL;
+ // set icon
+ if ((pItem->wMask & CBEXIM_ICONTEXT) && pItem->pszIcon) {
+ cbex->pItems[cbex->numItems].pszIcon = pItem->pszIcon;
+ cbex->pItems[cbex->numItems].hIcon = IcoLib_GetIcon(pItem->pszIcon);
+ }
+ // set flags
+ cbex->pItems[cbex->numItems].wFlags = (pItem->wMask & CBEXIM_CAT) ? pItem->wFlags : 0;
+
+ cbex->numItems++;
+ return cbex->numItems;
+ }
+
+ /**
+ * name: CBEXM_SETITEM
+ * desc: Set an item's information of the control.
+ * If iItem member of CBEXITEM is -1, the currently selected item is changed.
+ * param: wParam - not used
+ * lParam - (LPCBEXITEM)&item
+ * return: CB_ERR on failure, new item index if successful
+ **/
+ case CBEXM_SETITEM:
+ {
+ LPCBEXITEM pItem = (LPCBEXITEM)lParam;
+
+ if (!PtrIsValid(pItem) || !pItem->wMask || !PtrIsValid(cbex->pItems)) return FALSE;
+ if (pItem->iItem == -1) pItem->iItem = cbex->iSelectedItem;
+ if (pItem->iItem < 0 || pItem->iItem >= cbex->numItems) return FALSE;
+
+ // set new category string
+ if (pItem->wMask & CBEXIM_CAT) {
+ // set category string
+ if (!pItem->pszCat || !pItem->pszCat[0] || !mir_tcsncpy(cbex->pItems[pItem->iItem].szCat, pItem->pszCat, SIZEOF(cbex->pItems[pItem->iItem].szCat)))
+ mir_sntprintf(cbex->pItems[pItem->iItem].szCat, MAX_CAT, _T("%s %d\0"), TranslateT("Other"), ++cbex->numOther);
+ if (pItem->iItem == cbex->iSelectedItem)
+ SetWindowText(cbex->hBtnEdit, cbex->pItems[pItem->iItem].szCat);
+ }
+ // set new value
+ if (pItem->wMask & CBEXIM_VAL) {
+ MIR_FREE(cbex->pItems[pItem->iItem].pszVal);
+ if (pItem->pszVal && pItem->pszVal[0])
+ cbex->pItems[pItem->iItem].pszVal = mir_tstrdup(pItem->pszVal);
+ if (pItem->iItem == cbex->iSelectedItem)
+ SetWindowText(cbex->hEdit, cbex->pItems[pItem->iItem].pszVal ? cbex->pItems[pItem->iItem].pszVal : _T(""));
+ }
+
+ // set icon
+ if ((pItem->wMask & CBEXIM_ICONTEXT) && pItem->pszIcon) {
+ cbex->pItems[pItem->iItem].pszIcon = pItem->pszIcon;
+ cbex->pItems[pItem->iItem].hIcon = IcoLib_GetIcon(pItem->pszIcon);
+ if (pItem->iItem == cbex->iSelectedItem)
+ SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[pItem->iItem].hIcon);
+ }
+ if (pItem->wMask & CBEXIM_FLAGS) {
+ cbex->pItems[pItem->iItem].wFlags = pItem->wFlags;
+ CtrlContactWndProc(hwnd, CBEXM_ENABLEITEM, NULL, NULL);
+ }
+ return TRUE;
+ }
+
+ /**
+ * name: CBEXM_GETITEM
+ * desc: Get an item from the control.
+ * If iItem member of CBEXITEM is -1, the currently selected item is returned.
+ * param: wParam - not used
+ * lParam - (LPCBEXITEM)&item
+ * return: CB_ERR on failure, new item index if successful
+ **/
+ case CBEXM_GETITEM:
+ {
+ LPCBEXITEM pItem = (LPCBEXITEM)lParam;
+
+ if (!pItem || !cbex->pItems) return FALSE;
+
+ // try to find item by id
+ if ((pItem->wMask & CBEXIM_ID) && pItem->dwID != 0) {
+ INT i;
+
+ for (i = 0; i < cbex->numItems; i++) {
+ if (cbex->pItems[i].dwID == pItem->dwID)
+ break;
+ }
+ pItem->iItem = i;
+ }
+ else
+ if (pItem->iItem == -1) pItem->iItem = cbex->iSelectedItem;
+ if (pItem->iItem < 0 || pItem->iItem >= cbex->numItems) return FALSE;
+
+ // return only currently selected itemindex
+ if (!pItem->wMask) return TRUE;
+ // return the unique id
+ if (pItem->wMask & CBEXIM_ID)
+ pItem->dwID = cbex->pItems[pItem->iItem].dwID;
+ // return category string
+ if ((pItem->wMask & CBEXIM_CAT) && pItem->pszCat) {
+ if (*cbex->pItems[pItem->iItem].szCat != 0)
+ mir_tcsncpy(pItem->pszCat, cbex->pItems[pItem->iItem].szCat, pItem->ccCat - 1);
+ else
+ *pItem->pszCat = 0;
+ }
+ // return value string
+ if ((pItem->wMask & CBEXIM_VAL) && pItem->pszVal) {
+ if (cbex->pItems[pItem->iItem].pszVal)
+ mir_tcsncpy(pItem->pszVal, cbex->pItems[pItem->iItem].pszVal, pItem->ccVal - 1);
+ else
+ *pItem->pszVal = 0;
+ }
+ // return the icon
+ if (pItem->wMask & CBEXIM_ICONTEXT)
+ pItem->pszIcon = cbex->pItems[pItem->iItem].pszIcon;
+ // return the flags
+ if (pItem->wMask & CBEXIM_FLAGS)
+ pItem->wFlags = cbex->pItems[pItem->iItem].wFlags;
+ return TRUE;
+ }
+
+ /**
+ * name: CBEXM_DELITEM
+ * desc: delete an item from the control
+ * param: wParam - not used
+ * lParam - item index
+ * return: CB_ERR on failure, new item index if successful
+ **/
+ case CBEXM_DELITEM:
+ {
+ if (!cbex->pItems || (INT)lParam < 0 || (INT)lParam >= cbex->numItems || (cbex->pItems[lParam].wFlags & CBEXIF_CATREADONLY))
+ return FALSE;
+ MIR_FREE(cbex->pItems[(INT)lParam].pszVal);
+ memmove(cbex->pItems + (INT)lParam,
+ cbex->pItems + (INT)lParam + 1,
+ (cbex->numItems - (INT)lParam - 1) * sizeof(CBEXITEMINTERN));
+ cbex->numItems--;
+ ZeroMemory(cbex->pItems + cbex->numItems, sizeof(CBEXITEMINTERN));
+ CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, lParam - 1, FALSE);
+ return TRUE;
+ }
+
+ /**
+ * name: CBEXM_DELITEM
+ * desc: delete an item from the control
+ * param: wParam - not used
+ * lParam - item index
+ * return: CB_ERR on failure, new item index if successful
+ **/
+ case CBEXM_DELALLITEMS:
+ {
+ INT i;
+
+ if (PtrIsValid(cbex)) {
+ if (PtrIsValid(cbex->pItems)) {
+ for (i = 0; i < cbex->numItems; i++) {
+ MIR_FREE(cbex->pItems[i].pszVal);
+ }
+ MIR_FREE(cbex->pItems);
+ cbex->pItems = NULL;
+ }
+ cbex->numItems = 0;
+ cbex->iSelectedItem = -1;
+ SetWindowText(cbex->hEdit, _T(""));
+ SetWindowText(cbex->hBtnEdit, _T(""));
+ SendMessage(cbex->hBtnEdit, WM_SETICON, NULL, NULL);
+ }
+ return TRUE;
+ }
+
+ /**
+ * name: CBEXM_ENABLEITEM
+ * desc: enables or disables the current item
+ * param: wParam - not used
+ * lParam - not used
+ * return: always 0
+ **/
+ case CBEXM_ENABLEITEM:
+ if (cbex->iSelectedItem >= 0 && cbex->iSelectedItem < cbex->numItems) {
+ HANDLE hContact;
+ BOOLEAN bEnabled;
+
+ PSGetContact(GetParent(hwnd), hContact);
+
+ bEnabled = !hContact ||
+ (cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_HASCUSTOM) ||
+ !(cbex->pItems[cbex->iSelectedItem].wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
+ !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+
+ EnableWindow(cbex->hBtnEdit, bEnabled);
+ EnableWindow(cbex->hBtnDel, bEnabled && GetWindowTextLength(cbex->hEdit) > 0);
+ EnableWindow(cbex->hEdit, bEnabled);
+ }
+ break;
+
+ /**
+ * name: CBEXM_ISCHANGED
+ * desc: returns whether the control contains changed values or not
+ * param: wParam - not used
+ * lParam - not used
+ * return: TRUE if control was changed, FALSE if nothing was edited
+ **/
+ case CBEXM_ISCHANGED:
+ return cbex->bIsChanged;
+
+ /**
+ * name: CBEXM_RESETCHANGED
+ * desc: resets changed flag to FALSE
+ * param: wParam - not used
+ * lParam - not used
+ * return: always FALSE
+ **/
+ case CBEXM_RESETCHANGED:
+ cbex->bIsChanged = 0;
+ return 0;
+
+ /**
+ * name: CBEXM_SETCURSEL
+ * desc: selects a certain item
+ * param: wParam - index of the item to select
+ * lParam - (BOOLEAN)bValid - if TRUE, the next item with a value is selected
+ * return: always FALSE
+ **/
+ case CBEXM_SETCURSEL:
+ {
+ INT i;
+
+ if (!cbex->pItems) return 1;
+ if ((INT)wParam < 0 || (INT)wParam >= cbex->numItems) wParam = max(cbex->iSelectedItem, 0);
+ cbex->bLocked = 1;
+
+ if ((BOOLEAN)lParam == TRUE) {
+ INT i = (INT)wParam;
+
+ cbex->iSelectedItem = (INT)wParam;
+ while (i < cbex->numItems) {
+ if (cbex->pItems[i].pszVal && *cbex->pItems[i].pszVal) {
+ cbex->iSelectedItem = i;
+ break;
+ }
+ i++;
+ }
+ }
+ else {
+ // search for the next none deleted item
+ for (i = (INT)wParam; i < cbex->numItems && *cbex->pItems[i].szCat == 0; i++);
+ if (i == cbex->numItems && (INT)wParam > 0) {
+ for (i = 0; i < (INT)wParam && *cbex->pItems[i].szCat == 0; i++);
+ cbex->iSelectedItem = i == (INT)wParam ? 0 : i;
+ }
+ else
+ cbex->iSelectedItem = i;
+
+ }
+ SetWindowText(cbex->hBtnEdit, cbex->pItems[cbex->iSelectedItem].szCat);
+ SetWindowText(cbex->hEdit, cbex->pItems[cbex->iSelectedItem].pszVal ? cbex->pItems[cbex->iSelectedItem].pszVal : _T(""));
+ SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[cbex->iSelectedItem].hIcon);
+ CtrlContactWndProc(hwnd, CBEXM_ENABLEITEM, NULL, NULL);
+ cbex->bLocked = 0;
+ return 0;
+ }
+ case CBEXM_SORT:
+ if (cbex->numItems > 4) {
+ qsort(cbex->pItems + 3, cbex->numItems - 3, sizeof(CBEXITEMINTERN), compareProc);
+ }
+ return 0;
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_SETFOCUS:
+ SetFocus(cbex->hEdit);
+ SendMessage(cbex->hEdit, EM_SETSEL, 0, (LPARAM)-1);
+ return 0;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name: CtrlContactUnLoadModule
+ * desc: calls required operations to clean up used memory and objects
+ * param: wParam - not used
+ * lParam - not used
+ * return: always 0
+ **/
+INT CtrlContactUnLoadModule()
+{
+ UnregisterClass(UINFOCOMBOEXCLASS, ghInst);
+ return 0;
+}
+
+/**
+ * name: CtrlContactLoadModule
+ * desc: registers window class and does some other initializations
+ * param: none
+ * return: always 0
+ **/
+INT CtrlContactLoadModule()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = UINFOCOMBOEXCLASS;
+ wc.lpfnWndProc = CtrlContactWndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(LPCBEX);
+ wc.hbrBackground = (HBRUSH)GetStockObject(COLOR_WINDOW);
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+ return 0;
+}
+
+
+/**
+ * name: CtrlContactAddItemFromDB
+ * desc: add a item read from db to the combobox
+ * param: hCtrl - windowhandle to extended combobox control
+ * hIcon - icon to use for custom items
+ * szItem - category text for the item
+ * wForcedFlags - force flag for each new entry
+ * hContact - handle to contact whose settings to add
+ * pszModule - primary module to search the setting in
+ * pszProto - contacts protocol module
+ * szSettingVal - value holding setting
+ * return: TRUE - if the item is not updated, because its changed flag is set
+ * FALSE - if item is added or updated successfully
+ **/
+INT CtrlContactAddItemFromDB(
+ HWND hCtrl,
+ LPCSTR szIcon,
+ LPTSTR szItem,
+ HANDLE hContact,
+ LPCSTR pszModule,
+ LPCSTR pszProto,
+ LPCSTR szSettingVal)
+{
+ DBVARIANT dbv;
+ CBEXITEM cbi;
+ LPTSTR sms;
+
+ cbi.pszVal = NULL;
+ cbi.dwID = hashSetting(szSettingVal);
+ cbi.wFlags = CBEXIF_CATREADONLY|DB::Setting::GetTStringCtrl(hContact, pszModule, pszModule, pszProto, szSettingVal, &dbv);
+ if (dbv.type >= DBVT_WCHAR) {
+ // no value read from database
+ if (cbi.wFlags == CBEXIF_CATREADONLY) {
+ cbi.pszVal = NULL;
+ }
+ // check the database value
+ else {
+ cbi.pszVal = dbv.ptszVal;
+ if (sms = _tcsstr(cbi.pszVal, _T(" SMS\0"))) {
+ cbi.wFlags |= CBEXIF_SMS;
+ *sms = 0;
+ }
+ }
+ }
+ cbi.pszCat = szItem;
+ cbi.iItem = -1;
+ cbi.wMask = CBEXIM_ALL;
+ cbi.pszIcon = szIcon;
+ SendMessage(hCtrl, CBEXM_ADDITEM, NULL, (LPARAM)&cbi);
+ DB::Variant::Free(&dbv);
+ return (cbi.wFlags & CTRLF_CHANGED) == CTRLF_CHANGED;
+}
+
+/**
+ * name: CtrlContactAddMyItemsFromDB
+ * desc: add a item read from db to the combobox
+ * param: hCtrl - windowhandle to extended combobox control
+ * hIcon - icon to use for custom items
+ * wForcedFlags - force flag for each new entry
+ * hContact - handle to contact whose settings to add
+ * pszModule - primary module to search the setting in
+ * pszProto - contacts protocol module
+ * szFormatCat - format for the category holding setting
+ * szFormatVal - format for the value holding setting
+ * return: TRUE - if one of the items was not updated, because its changed flag is set
+ * FALSE - if all items were added or updated successfully
+ **/
+INT CtrlContactAddMyItemsFromDB(
+ HWND hCtrl,
+ LPCSTR szIcon,
+ WORD wForcedFlags,
+ HANDLE hContact,
+ LPCSTR pszModule,
+ LPCSTR pszProto,
+ LPCSTR szFormatCat,
+ LPCSTR szFormatVal)
+{
+ CBEXITEM cbi;
+ DBVARIANT dbv;
+ CHAR pszSetting[MAXSETTING];
+ WORD i;
+ LPTSTR sms;
+ INT bAnyItemIsChanged = 0;
+
+ ZeroMemory(&cbi, sizeof(cbi));
+ cbi.iItem = -1;
+ cbi.wMask = CBEXIM_ALL;
+ cbi.pszIcon = szIcon;
+
+ for (i = 0;
+ SUCCEEDED(mir_snprintf(pszSetting, MAXSETTING, szFormatVal, i)) &&
+ (cbi.wFlags = DB::Setting::GetTStringCtrl(hContact, pszModule, pszModule, pszProto, pszSetting, &dbv));
+ i++)
+ {
+ // read value
+ cbi.dwID = hashSetting(pszSetting);
+ cbi.pszVal = dbv.ptszVal;
+ dbv.type = DBVT_DELETED;
+ dbv.ptszVal = NULL;
+
+ // read category
+ if (SUCCEEDED(mir_snprintf(pszSetting, MAXSETTING, szFormatCat, i))) {
+ if (cbi.wFlags & CTRLF_HASCUSTOM) {
+ if (DB::Setting::GetTString(hContact, pszModule, pszSetting, &dbv))
+ dbv.type = DBVT_DELETED;
+ }
+ else
+ if (cbi.wFlags & CTRLF_HASPROTO) {
+ if (DB::Setting::GetTString(hContact, pszProto, pszSetting, &dbv))
+ dbv.type = DBVT_DELETED;
+ }
+
+ if (dbv.type > DBVT_DELETED && dbv.ptszVal && *dbv.ptszVal) {
+ cbi.pszCat = dbv.ptszVal;
+ dbv.type = DBVT_DELETED;
+ dbv.ptszVal = NULL;
+ }
+ }
+ if (sms = _tcsstr(cbi.pszVal, _T(" SMS"))) {
+ cbi.wFlags |= CBEXIF_SMS;
+ *sms = 0;
+ }
+ cbi.wFlags |= wForcedFlags;
+ if (CB_ERR == SendMessage(hCtrl, CBEXM_ADDITEM, NULL, (LPARAM)&cbi))
+ break;
+ bAnyItemIsChanged |= (cbi.wFlags & CTRLF_CHANGED) == CTRLF_CHANGED;
+ if (cbi.pszCat) {
+ mir_free(cbi.pszCat);
+ cbi.pszCat = NULL;
+ }
+ if (cbi.pszVal) {
+ mir_free(cbi.pszVal);
+ cbi.pszVal = NULL;
+ }
+ }
+ SendMessage(hCtrl, CBEXM_SORT, NULL, NULL);
+ return bAnyItemIsChanged;
+}
+
+/**
+ * name: CtrlContactWriteItemToDB
+ * desc: write a item from combobox to database
+ * param: none
+ * return: always 0
+ **/
+INT CtrlContactWriteItemToDB(
+ HWND hCtrl,
+ HANDLE hContact,
+ LPCSTR pszModule,
+ LPCSTR pszProto,
+ LPCSTR pszSetting)
+{
+ TCHAR szVal[MAXDATASIZE];
+ CBEXITEM cbi;
+
+ if (!CtrlContactWndProc(hCtrl, CBEXM_ISCHANGED, NULL, NULL)) return 1;
+
+ cbi.wMask = CBEXIM_ID|CBEXIM_VAL|CBEXIM_FLAGS;
+ cbi.pszVal = szVal;
+ cbi.ccVal = MAXDATASIZE - 4;
+ cbi.iItem = 0;
+ cbi.dwID = hashSetting(pszSetting);
+ if (!CtrlContactWndProc(hCtrl, CBEXM_GETITEM, NULL, (LPARAM)&cbi)) return 1;
+ if (!(cbi.wFlags & CTRLF_CHANGED)) return 0;
+ if (!hContact && !(pszModule = pszProto)) return 1;
+ if (!*szVal)
+ DB::Setting::Delete(hContact, pszModule, pszSetting);
+ else {
+ if (cbi.wFlags & CBEXIF_SMS) {
+ mir_tcsncat(szVal, _T(" SMS"), SIZEOF(szVal));
+ }
+ if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szVal)) return 1;
+ }
+ cbi.wFlags &= ~CTRLF_CHANGED;
+ cbi.wMask = CBEXIM_FLAGS;
+ CtrlContactWndProc(hCtrl, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+ InvalidateRect(GetDlgItem(hCtrl, EDIT_VALUE), NULL, TRUE);
+ return 0;
+}
+
+/**
+ * name: CtrlContactWriteMyItemsToDB
+ * desc: write a list of custom items from combobox to database
+ * param: none
+ * return: always 0
+ **/
+INT CtrlContactWriteMyItemsToDB(
+ HWND hCtrl,
+ INT iFirstItem,
+ HANDLE hContact,
+ LPCSTR pszModule,
+ LPCSTR pszProto,
+ LPCSTR szFormatCat,
+ LPCSTR szFormatVal)
+{
+ CHAR pszSetting[MAXSETTING];
+ TCHAR szCat[MAX_CAT];
+ TCHAR szVal[MAXDATASIZE];
+ LPTSTR pszOther;
+ CBEXITEM cbi;
+ INT_PTR ccOther;
+ INT i = 0;
+
+ if (!CtrlContactWndProc(hCtrl, CBEXM_ISCHANGED, NULL, NULL)) return 1;
+ if (!hContact && !(pszModule = pszProto)) return 1;
+
+ pszOther = TranslateT("Other");
+ ccOther = _tcslen(pszOther);
+ cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+ cbi.pszCat = szCat;
+ cbi.ccCat = MAX_CAT;
+ cbi.pszVal = szVal;
+ cbi.ccVal = MAXDATASIZE - 4;
+ cbi.iItem = iFirstItem;
+ cbi.dwID = 0;
+
+ while (CtrlContactWndProc(hCtrl, CBEXM_GETITEM, NULL, (LPARAM)&cbi) && cbi.iItem < 50) {
+ if (!(cbi.wFlags & CBEXIF_DELETED) && *szVal) {
+ if (cbi.wFlags & CBEXIF_SMS) {
+ mir_tcsncat(szVal, _T(" SMS"), SIZEOF(szVal));
+ }
+ mir_snprintf(pszSetting, MAXSETTING, szFormatCat, i);
+ if (*szCat && _tcsncmp(szCat, pszOther, ccOther)) {
+ if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szCat)) return 1;
+ }
+ else
+ DB::Setting::Delete(hContact, pszModule, pszSetting);
+ mir_snprintf(pszSetting, MAXSETTING, szFormatVal, i);
+ if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szVal)) return 1;
+ cbi.wFlags &= ~CTRLF_CHANGED;
+ cbi.wMask = CBEXIM_FLAGS;
+ CtrlContactWndProc(hCtrl, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+ cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+ i++;
+ }
+
+ cbi.iItem++;
+ }
+ DB::Setting::DeleteArray(hContact, pszModule, szFormatCat, i);
+ DB::Setting::DeleteArray(hContact, pszModule, szFormatVal, i);
+ InvalidateRect(GetDlgItem(hCtrl, EDIT_VALUE), NULL, TRUE);
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_contact.h b/plugins/UserInfoEx/src/ctrl_contact.h
new file mode 100644
index 0000000000..3a3536e523
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_contact.h
@@ -0,0 +1,86 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_contact.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_INCLUDE_
+#define _UI_CTRL_INCLUDE_
+
+/**
+ * ctrlContact.cpp
+ **/
+
+/* contact control v0.1.0.6+
+*/
+#define UINFOCOMBOEXCLASS _T("UInfoComboExWndClass")
+
+// messages
+#define CBEXM_ADDITEM (WM_USER+4)
+#define CBEXM_GETITEM (WM_USER+5)
+#define CBEXM_SETITEM (WM_USER+6)
+#define CBEXM_DELITEM (WM_USER+7)
+#define CBEXM_DELALLITEMS (WM_USER+8)
+#define CBEXM_ISCHANGED (WM_USER+9)
+#define CBEXM_RESETCHANGED (WM_USER+10)
+#define CBEXM_SETCURSEL (WM_USER+11)
+#define CBEXM_SORT (WM_USER+12)
+#define CBEXM_ENABLEITEM (WM_USER+13)
+
+// item masks
+#define CBEXIM_FLAGS 0x0001
+#define CBEXIM_CAT 0x0002
+#define CBEXIM_VAL 0x0004
+#define CBEXIM_ICONTEXT 0x0008
+#define CBEXIM_ID 0x0010
+#define CBEXIM_ALL (CBEXIM_ID|CBEXIM_ICONTEXT|CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS)
+
+#define CBEXIF_CATREADONLY (CTRLF_FIRST)
+#define CBEXIF_SMS (CTRLF_FIRST * 2)
+#define CBEXIF_DELETED (CTRLF_FIRST * 4)
+
+typedef struct TComboExItem
+{
+ WORD wMask; // determines which element of this structure is valid
+ WORD wFlags; // standard control flags
+ INT iItem; // position of the item in the data array
+ DWORD dwID; // unique number for each setting read from db to identify it, new entries have dwID = 0
+ LPTSTR pszCat; // pointer to a descriptive category string to set or retrieve for the data entry
+ WORD ccCat;
+ LPTSTR pszVal;
+ WORD ccVal;
+ LPCSTR pszIcon;
+} CBEXITEM, *LPCBEXITEM;
+
+INT CtrlContactLoadModule();
+INT CtrlContactUnLoadModule();
+INT CtrlContactAddItemFromDB(HWND hCtrl, LPCSTR szIcon, LPTSTR szItem, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szSettingVal);
+INT CtrlContactAddMyItemsFromDB(HWND hCtrl, LPCSTR szIcon, WORD wForcedFlags, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szFormatCat, LPCSTR szFormatVal);
+INT CtrlContactWriteItemToDB(HWND hCtrl, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR pszSetting);
+INT CtrlContactWriteMyItemsToDB(HWND hCtrl, INT iFirstItem, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szFormatCat, LPCSTR szFormatVal);
+
+#endif /* _UI_CTRL_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_edit.cpp b/plugins/UserInfoEx/src/ctrl_edit.cpp
new file mode 100644
index 0000000000..19e018f8cb
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_edit.cpp
@@ -0,0 +1,381 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_edit.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_edit.h"
+
+/**
+ * This function creates a CEditCtrl object.
+ *
+ * @param hDlg - HWND of the owning propertysheet page
+ * @param idCtrl - the ID of the dialog control to associate with this class's instance
+ * @param pszSetting - the database setting to be handled by this class
+ * @param dbType - the expected data type of the setting
+ *
+ * @return This static method returns the pointer of the created object.
+ **/
+CBaseCtrl* CEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE dbType)
+{
+ CEditCtrl *ctrl = NULL;
+
+ ctrl = new CEditCtrl(hDlg, idCtrl, USERINFO, pszSetting);
+ if (ctrl) {
+ ctrl->_dbType = dbType;
+ }
+ return (ctrl);
+}
+
+/**
+ * This function creates a CEditCtrl object.
+ *
+ * @param hDlg - HWND of the owning propertysheet page
+ * @param idCtrl - the ID of the dialog control to associate with this class's instance
+ * @param pszModule - the database module to be handled by this class
+ * @param pszSetting - the database setting to be handled by this class
+ * @param dbType - the expected data type of the setting
+ *
+ * @return This static method returns the pointer of the created object.
+ **/
+CBaseCtrl* CEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting, BYTE dbType)
+{
+ CEditCtrl *ctrl = NULL;
+
+ ctrl = new CEditCtrl(hDlg, idCtrl, pszModule, pszSetting);
+ if (ctrl) {
+ ctrl->_dbType = dbType;
+ }
+ return (ctrl);
+}
+
+/**
+ *
+ *
+ **/
+CEditCtrl::CEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting)
+ : CBaseCtrl(hDlg, idCtrl, pszModule, pszSetting)
+{
+ SendDlgItemMessage(hDlg, idCtrl, EM_LIMITTEXT, 0x7fFFffFF, 0L);
+}
+
+/**
+ * This method deletes the class object
+ * and all allocated memory of its members.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID CEditCtrl::Release()
+{
+ delete this;
+}
+
+/*
+ *
+ *
+ */
+VOID CEditCtrl::OnReset()
+{
+}
+
+
+/**
+ * This method controls the changed bit for the control.
+ *
+ * @param hCtrl - HWND of the combobox
+ * @param hContact - HANDLE of the contact whose timezone to select
+ * @param pszProto - the contact's protocol
+ *
+ * @return nothing
+ **/
+BOOL CEditCtrl::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+ if (!_Flags.B.hasChanged)
+ {
+ DBVARIANT dbv;
+ TCHAR szText[64];
+
+ _Flags.B.hasCustom = _Flags.B.hasProto = _Flags.B.hasMeta = 0;
+ _Flags.W |= DB::Setting::GetTStringCtrl(hContact, _pszModule, _pszModule, pszProto, _pszSetting, &dbv);
+
+ EnableWindow(_hwnd,
+ !hContact || _Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0));
+
+ MIR_FREE(_pszValue);
+ switch (dbv.type)
+ {
+ case DBVT_BYTE:
+ _itot_s(dbv.bVal, szText, SIZEOF(szText), 10);
+ SetWindowText(_hwnd, szText);
+ _pszValue = mir_tcsdup(szText);
+ break;
+
+ case DBVT_WORD:
+ _itot_s(dbv.wVal, szText, SIZEOF(szText), 10);
+ SetWindowText(_hwnd, szText);
+ _pszValue = mir_tcsdup(szText);
+ break;
+
+ case DBVT_DWORD:
+ _itot_s(dbv.dVal, szText, SIZEOF(szText), 10);
+ SetWindowText(_hwnd, szText);
+ _pszValue = mir_tcsdup(szText);
+ break;
+
+ case DBVT_TCHAR:
+ if (dbv.ptszVal)
+ {
+ SetWindowText(_hwnd, dbv.ptszVal);
+ _pszValue = dbv.ptszVal;
+ break;
+ }
+
+ default:
+ SetWindowText(_hwnd, _T(""));
+ DB::Variant::Free(&dbv);
+ break;
+ }
+ _Flags.B.hasChanged = 0;
+ }
+ return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the control's information to database
+ *
+ * @param hContact - HANDLE of the contact whose timezone to select
+ * @param pszProto - the contact's protocol
+ *
+ * @return nothing
+ **/
+VOID CEditCtrl::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+ if (_Flags.B.hasChanged)
+ {
+ const char* pszModule = hContact ? _pszModule : pszProto;
+
+ if (_Flags.B.hasCustom || !hContact)
+ {
+ DWORD cch = GetWindowTextLength(_hwnd);
+
+ if (cch > 0)
+ {
+ LPTSTR val = (LPTSTR)mir_alloc((cch + 1) * sizeof(TCHAR));
+
+ if (GetWindowText(_hwnd, val, cch + 1) > 0)
+ {
+ DBVARIANT dbv;
+
+ dbv.type = _dbType;
+ switch (_dbType)
+ {
+ case DBVT_BYTE:
+ dbv.bVal = (BYTE)_tcstol(val, NULL, 10);
+ break;
+
+ case DBVT_WORD:
+ dbv.wVal = (WORD)_tcstol(val, NULL, 10);
+ break;
+
+ case DBVT_DWORD:
+ dbv.dVal = (DWORD)_tcstol(val, NULL, 10);
+ break;
+
+ case DBVT_TCHAR:
+ dbv.ptszVal = val;
+ break;
+
+ default:
+ dbv.type = DBVT_DELETED;
+
+ }
+ if (dbv.type != DBVT_DELETED)
+ {
+ if (!DB::Setting::WriteVariant(hContact, pszModule, _pszSetting, &dbv))
+ {
+ if (!hContact)
+ {
+ _Flags.B.hasCustom = 0;
+ _Flags.B.hasProto = 1;
+ }
+ _Flags.B.hasChanged = 0;
+
+ // save new value
+ MIR_FREE(_pszValue);
+ _pszValue = val;
+ val = NULL;
+ }
+ }
+ }
+ MIR_FREE(val);
+ }
+ }
+ if (_Flags.B.hasChanged)
+ {
+ DB::Setting::Delete(hContact, pszModule, _pszSetting);
+
+ _Flags.B.hasChanged = 0;
+
+ OnInfoChanged(hContact, pszProto);
+ }
+ InvalidateRect(_hwnd, NULL, TRUE);
+ }
+}
+
+/**
+ * The user changed information stored in the control.
+ *
+ * @return nothing
+ **/
+VOID CEditCtrl::OnChangedByUser(WORD wChangedMsg)
+{
+ if ((wChangedMsg == EN_UPDATE) || (wChangedMsg == EN_CHANGE))
+ {
+ const int cch = GetWindowTextLength(_hwnd);
+
+ _Flags.B.hasChanged = mir_tcslen(_pszValue) != cch;
+ _Flags.B.hasCustom = (cch > 0);
+
+ if (!_Flags.B.hasChanged && _Flags.B.hasCustom) {
+ BOOLEAN need_free = 0;
+ LPTSTR szText;
+
+ __try
+ {
+ szText = (LPTSTR)alloca((cch + 1) * sizeof(TCHAR));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ szText = (LPTSTR)mir_alloc((cch + 1) * sizeof(TCHAR));
+ need_free = 1;
+ }
+
+ GetWindowText(_hwnd, szText, cch + 1);
+ _Flags.B.hasChanged = mir_tcscmp(_pszValue, szText);
+
+ if (need_free) {
+ MIR_FREE(szText);
+ }
+ }
+ InvalidateRect(_hwnd, NULL, TRUE);
+
+ if (_Flags.B.hasChanged) {
+ SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+ }
+ }
+}
+
+/**
+ * Opens the url given in a editbox in the users default browser
+ **/
+VOID CEditCtrl::OpenUrl()
+{
+ INT lenUrl = 1 + Edit_GetTextLength(_hwnd);
+ LPSTR szUrl;
+ BOOLEAN need_free = 0;
+
+ __try
+ {
+ szUrl = (LPSTR)alloca((8 + lenUrl) * sizeof(CHAR));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ szUrl = (LPSTR)mir_alloc((8 + lenUrl) * sizeof(CHAR));
+ need_free = 1;
+ }
+ if (szUrl && (GetWindowTextA(_hwnd, szUrl, lenUrl) > 0))
+ {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ }
+ if (need_free)
+ {
+ MIR_FREE(szUrl);
+ }
+}
+
+LRESULT CEditCtrl::LinkNotificationHandler(ENLINK* lnk)
+{
+ switch (lnk->msg)
+ {
+ case WM_SETCURSOR:
+ {
+ SetCursor(LoadCursor(NULL, IDC_HAND));
+ SetWindowLongPtr(GetParent(_hwnd), DWLP_MSGRESULT, TRUE);
+ }
+ return TRUE;
+
+ case WM_LBUTTONUP:
+ {
+ if (lnk)
+ {
+ TEXTRANGE tr;
+ BOOLEAN need_free = 0;
+
+ // do not call function if user selected some chars of the url string
+ SendMessage(_hwnd, EM_EXGETSEL, NULL, (LPARAM) &tr.chrg);
+ if (tr.chrg.cpMax == tr.chrg.cpMin)
+ {
+ // retrieve the url string
+ tr.chrg = lnk->chrg;
+
+ __try
+ {
+ tr.lpstrText = (LPTSTR)alloca((tr.chrg.cpMax - tr.chrg.cpMin + 8) * sizeof(TCHAR));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ tr.lpstrText = (LPTSTR)mir_alloc((tr.chrg.cpMax - tr.chrg.cpMin + 8) * sizeof(TCHAR));
+ need_free = 1;
+ }
+ if (tr.lpstrText && (SendMessage(_hwnd, EM_GETTEXTRANGE, NULL, (LPARAM)&tr) > 0))
+ {
+ if (_tcschr(tr.lpstrText, '@') != NULL && _tcschr(tr.lpstrText, ':') == NULL && _tcschr(tr.lpstrText, '/') == NULL)
+ {
+ MoveMemory(tr.lpstrText + (7*sizeof(TCHAR)), tr.lpstrText, (tr.chrg.cpMax - tr.chrg.cpMin + 1)*sizeof(TCHAR));
+ CopyMemory(tr.lpstrText, _T("mailto:"), (7*sizeof(TCHAR)));
+ }
+
+ LPSTR pszUrl = mir_t2a(tr.lpstrText);
+ if (pszUrl)
+ {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)pszUrl);
+ mir_free(pszUrl);
+ }
+ }
+ if (need_free)
+ {
+ MIR_FREE(tr.lpstrText);
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_edit.h b/plugins/UserInfoEx/src/ctrl_edit.h
new file mode 100644
index 0000000000..15a69a5160
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_edit.h
@@ -0,0 +1,80 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_edit.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_EDIT_INCLUDE_
+#define _UI_CTRL_EDIT_INCLUDE_
+
+#include "ctrl_base.h"
+
+/**
+ *
+ **/
+class CEditCtrl : public CBaseCtrl
+{
+ BYTE _dbType;
+
+ /**
+ * Private constructure is to force to use static member 'Create'
+ * as the default way of attaching a new object to the window control.
+ *
+ * @param hCtrl - the window handle of the control to
+ * associate this class's instance with
+ * @param pszSetting - the database setting to be handled by this control
+ *
+ * @return nothing
+ **/
+ CEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting);
+
+public:
+
+ /**
+ *
+ *
+ **/
+ static FORCEINLINE CEditCtrl* GetObj(HWND hCtrl)
+ { return (CEditCtrl*) GetUserData(hCtrl); }
+ static FORCEINLINE CEditCtrl* GetObj(HWND hDlg, WORD idCtrl)
+ { return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+ static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE dbType);
+ static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting, BYTE dbType);
+
+ virtual VOID Release();
+ virtual VOID OnReset();
+ virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnChangedByUser(WORD wChangedMsg);
+
+ VOID OpenUrl();
+ LRESULT LinkNotificationHandler(ENLINK* lnk);
+
+};
+
+#endif /* _UI_CTRL_EDIT_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_tzcombo.cpp b/plugins/UserInfoEx/src/ctrl_tzcombo.cpp
new file mode 100644
index 0000000000..26dfbfee32
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_tzcombo.cpp
@@ -0,0 +1,306 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_tzcombo.cpp $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "svc_timezone.h"
+#include "ctrl_tzcombo.h"
+
+
+static INT_PTR EnumNamesProc(CTimeZone *pTimeZone, INT index, LPARAM lParam)
+{
+ if (pTimeZone && pTimeZone->ptszDisplay)
+ {
+ INT added = ComboBox_AddString((HWND)lParam, pTimeZone->ptszDisplay);
+ if (SUCCEEDED(added))
+ {
+ if (FAILED(ComboBox_SetItemData((HWND)lParam, added, pTimeZone)))
+ {
+ ComboBox_DeleteString((HWND)lParam, added);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * This functions fills a combobox given by @hCtrl with
+ * all items of the global timezone manager
+ *
+ * @param hDlg - HWND of the owning propertysheet page
+ * @param idCtrl - the ID of the control to associate with this class's instance
+ * @param pszSetting - the database setting to be handled by this class
+ *
+ * @return CTzCombo*
+ **/
+CBaseCtrl* CTzCombo::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+ CTzCombo *ctrl = NULL;
+ HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+
+ ctrl = new CTzCombo(hDlg, idCtrl, pszSetting);
+ if (ctrl) {
+ //use new core tz interface
+ if(tmi.prepareList) {
+ //set the adress of our timezone handle as itemdata
+ //caller can obtain the handle htz to extract all relevant information
+ ctrl->_curSel = 0;
+ tmi.prepareList(NULL, hCtrl, TZF_PLF_CB);
+ }
+ //fallback use old UIEX method
+ else {
+ ctrl->_curSel = ComboBox_AddString(hCtrl, TranslateT("<Unspecified>"));
+ if (SUCCEEDED(ctrl->_curSel)) {
+ ComboBox_SetItemData(hCtrl, ctrl->_curSel, NULL);
+ }
+ ComboBox_SetCurSel(hCtrl, ctrl->_curSel);
+ EnumTimeZones(EnumNamesProc, (LPARAM)hCtrl);
+ }
+ }
+ return (ctrl);
+}
+
+/**
+ *
+ *
+ **/
+CTzCombo::CTzCombo() : CBaseCtrl()
+{
+ _curSel = CB_ERR;
+}
+
+/**
+ *
+ *
+ **/
+CTzCombo::CTzCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+ : CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+ _curSel = CB_ERR;
+}
+
+/**
+ * This method does a binary search in the sorted
+ * ComboBox for the item index. (old UIEX method)
+ *
+ * @param pTimeZone - CTimeZone compobox item data.
+ *
+ * @retval CB_ERR - item not found
+ * @retval 0...n - index of the combobox item
+ **/
+INT CTzCombo::Find(CTimeZone *pTimeZone) const
+{
+ INT nItemIndex;
+ INT nItemCount = ComboBox_GetCount(_hwnd);
+
+ for (nItemIndex = 0; nItemIndex < nItemCount; nItemIndex++)
+ {
+ if (pTimeZone == (CTimeZone *)ComboBox_GetItemData(_hwnd, nItemIndex))
+ return nItemIndex;
+ }
+ return CB_ERR;
+}
+
+/**
+ * This method does a binary search in the sorted
+ * ComboBox for the item index. (new core tz interface)
+ *
+ * @param pTimeZone - LPTIME_ZONE_INFORMATION to find.
+ *
+ * @retval CB_ERR - item not found
+ * @retval 0...n - index of the combobox item
+ **/
+INT CTzCombo::Find(LPTIME_ZONE_INFORMATION pTimeZone) const
+{
+ INT nItemIndex;
+ INT nItemCount = ComboBox_GetCount(_hwnd);
+
+ for (nItemIndex = 0; nItemIndex < nItemCount; nItemIndex++) {
+ if (pTimeZone == tmi.getTzi((HANDLE)ComboBox_GetItemData(_hwnd, nItemIndex)))
+ return nItemIndex;
+ }
+ return CB_ERR;
+}
+
+/**
+ * This functions removes the user data from a combobox.
+ *
+ * @param hCtrl - HWND of the combobox
+ *
+ * @return nothing
+ **/
+VOID CTzCombo::Release()
+{
+ delete this;
+}
+
+/**
+ * This functions selects the combobox item which matches the contact's timezone.
+ *
+ * @param hCtrl - HWND of the combobox
+ * @param hContact - HANDLE of the contact whose timezone to select
+ * @param pszProto - the contact's protocol (not used by new core tz interface)
+ *
+ * @return _Flags.B.hasChanged member
+ **/
+BOOL CTzCombo::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+ if (!_Flags.B.hasChanged) {
+ //use new core tz interface to change the cbbox
+ if(tmi.storeListResults) {
+ _curSel = CB_ERR;
+// _curSel = tmi.selectListItem(hContact, _hwnd, TZF_PLF_CB);
+ //dident work well, coz no fallback to proto setting.
+ //we use saver way by getTziByContact
+ LPTIME_ZONE_INFORMATION pTimeZone;
+ pTimeZone = tmi.getTziByContact(hContact);
+ ComboBox_SetCurSel(_hwnd, Find(pTimeZone));
+ _curSel = ComboBox_GetCurSel(_hwnd);
+ }
+ //fallback use old UIEX method
+ else {
+ CTimeZone *pTimeZone;
+ _curSel = CB_ERR;
+ _Flags.W = GetContactTimeZoneCtrl(hContact, pszProto, &pTimeZone);
+ if (_Flags.W) {
+ ComboBox_SetCurSel(_hwnd, Find(pTimeZone));
+ _curSel = ComboBox_GetCurSel(_hwnd);
+ }
+ if (_curSel == CB_ERR) {
+ ComboBox_SetCurSel(_hwnd, NULL);
+ _curSel = ComboBox_GetCurSel(_hwnd);
+ }
+ }
+ SendMessage(GetParent(_hwnd), WM_TIMER, 0, 0);
+ }
+ return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the combobox's item as the contact's timezone.
+ *
+ * @param hContact - HANDLE of the contact whose timezone to select
+ * @param pszProto - the contact's protocol (not used by new core tz interface)
+ *
+ * @return nothing
+ **/
+VOID CTzCombo::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+ if (_Flags.B.hasChanged)
+ {
+ const char* pszModule = hContact ? USERINFO : pszProto;
+ if (_Flags.B.hasCustom || !hContact) {
+ //use new core tz interface
+ if(tmi.storeListResults) {
+ tmi.storeListResults(hContact, _hwnd, TZF_PLF_CB);
+ if (!hContact) {
+ _Flags.B.hasCustom = 0;
+ _Flags.B.hasProto = 1;
+ }
+ _Flags.B.hasChanged = 0;
+ }
+ //fallback use old UIEX method
+ else {
+ const CTimeZone* pTimeZone = (CTimeZone*)ComboBox_GetItemData(_hwnd, _curSel);
+ if (PtrIsValid(pTimeZone)) {
+ DB::Setting::WriteTString(hContact, USERINFO, SET_CONTACT_TIMEZONENAME, pTimeZone->ptszName);
+ DB::Setting::WriteByte(hContact, pszModule, SET_CONTACT_TIMEZONE, pTimeZone->ToMirandaTimezone());
+
+ if (!hContact) {
+ _Flags.B.hasCustom = 0;
+ _Flags.B.hasProto = 1;
+ }
+ _Flags.B.hasChanged = 0;
+ }
+ }
+ }
+
+ if (_Flags.B.hasChanged)
+ {
+ DB::Setting::Delete(hContact, USERINFO, SET_CONTACT_TIMEZONENAME);
+ DB::Setting::Delete(hContact, USERINFO, SET_CONTACT_TIMEZONEINDEX);
+ DB::Setting::Delete(hContact, pszModule, SET_CONTACT_TIMEZONE);
+
+ _Flags.B.hasChanged = 0;
+ OnInfoChanged(hContact, pszProto);
+ }
+ InvalidateRect(_hwnd, NULL, TRUE);
+ }
+}
+
+/**
+ * The user changed combobox selection, so mark it changed.
+ *
+ * @return nothing
+ **/
+VOID CTzCombo::OnChangedByUser(WORD wChangedMsg)
+{
+ if (wChangedMsg == CBN_SELCHANGE) {
+ INT c = ComboBox_GetCurSel(_hwnd);
+
+ if (_curSel != c) {
+ if (!_Flags.B.hasChanged) {
+ _Flags.B.hasChanged = 1;
+ _Flags.B.hasCustom = 1;
+ SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+ }
+ _curSel = c;
+ SendMessage(GetParent(_hwnd), WM_TIMER, 0, 0);
+ }
+ }
+}
+
+/**
+ * This method fills @szTime with the current time
+ * according to the combobox's selected timezone.
+ *
+ * @param szTime - string to fill with the current time
+ * @param cchTime - number of characters the string can take
+ *
+ * @return nothing
+ **/
+VOID CTzCombo::GetTime(LPTSTR szTime, WORD cchTime)
+{
+ //use new core tz interface
+ if(tmi.printDateTime) {
+ tmi.printDateTime((HANDLE)ComboBox_GetItemData(_hwnd, _curSel), _T("t"), szTime, cchTime, 0);
+ }
+ //fallback use old UIEX method
+ else {
+ const CTimeZone *pTimeZone = (CTimeZone*)ComboBox_GetItemData(_hwnd, _curSel);
+ if (PtrIsValid(pTimeZone)) {
+ MTime now;
+ TIME_ZONE_INFORMATION tzi = *pTimeZone;
+
+ now.GetTimeUTC();
+ now.UTCToTzSpecificLocal(&tzi);
+ now.TimeFormat(szTime, cchTime);
+ }
+ else mir_tcscpy(szTime, _T("--:--"));
+ }
+}
diff --git a/plugins/UserInfoEx/src/ctrl_tzcombo.h b/plugins/UserInfoEx/src/ctrl_tzcombo.h
new file mode 100644
index 0000000000..3034f57481
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_tzcombo.h
@@ -0,0 +1,69 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_tzcombo.h $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_TZ_COMBO_INCLUDE_
+#define _UI_CTRL_TZ_COMBO_INCLUDE_
+
+#include "ctrl_base.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+
+/**
+ *
+ **/
+class CTzCombo : public CBaseCtrl
+{
+ INT _curSel; //selectet combo index
+
+ CTzCombo();
+ CTzCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+ INT Find(CTimeZone *pTimeZone) const; //old UIEX method
+ INT Find(LPTIME_ZONE_INFORMATION pTimeZone) const; //new core tz interface
+
+public:
+
+ static FORCEINLINE CTzCombo* GetObj(HWND hCtrl)
+ { return (CTzCombo*) GetUserData(hCtrl); }
+ static FORCEINLINE CTzCombo* GetObj(HWND hDlg, WORD idCtrl)
+ { return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+ static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+ virtual VOID Release();
+// virtual VOID OnReset() {};
+ virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto);
+ virtual VOID OnChangedByUser(WORD wChangedMsg);
+
+ VOID GetTime(LPTSTR szTime, WORD cchTime);
+};
+
+#endif /* _UI_CTRL_TZ_COMBO_INCLUDE_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_anniversarylist.cpp b/plugins/UserInfoEx/src/dlg_anniversarylist.cpp
new file mode 100644
index 0000000000..1b0cd689da
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_anniversarylist.cpp
@@ -0,0 +1,1120 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_anniversarylist.cpp $
+Revision : $Revision: 202 $
+Last change on : $Date: 2010-09-24 23:46:57 +0400 (Пт, 24 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_gender.h"
+#include "svc_reminder.h"
+#include "dlg_anniversarylist.h"
+
+#include "m_message.h"
+#include "m_email.h"
+
+#define IsLeap(wYear) (!(((wYear) % 4 != 0) || (((wYear) % 100 == 0) && ((wYear) % 400 != 0))))
+
+class CAnnivList;
+
+static CAnnivList *gpDlg = NULL;
+
+/***********************************************************************************************************
+ * class CAnnivList
+ ***********************************************************************************************************/
+
+class CAnnivList
+{
+ HWND _hDlg;
+ HWND _hList;
+ SIZE _sizeMin;
+ RECT _rcWin;
+ SHORT _sortOrder;
+ INT _sortHeader;
+ INT _curSel;
+ INT _numRows;
+ BYTE _bRemindEnable;
+ HANDLE _mHookExit;
+ bool _wmINIT;
+
+ typedef INT (CALLBACK* CMPPROC)(LPARAM, LPARAM LPARAM);
+
+ enum EColumn
+ {
+ COLUMN_ETA = 0,
+ COLUMN_CONTACT,
+ COLUMN_PROTO,
+ COLUMN_AGE,
+ COLUMN_DESC,
+ COLUMN_DATE,
+ };
+
+ enum EFilter
+ {
+ FILTER_ALL = 0,
+ FILTER_BIRTHDAY,
+ FILTER_ANNIV,
+ FILTER_DISABLED_REMINDER
+ };
+
+ struct CFilter
+ {
+ WORD wDaysBefore;
+ BYTE bFilterIndex;
+ LPSTR pszProto;
+ LPTSTR pszAnniv;
+
+ CFilter()
+ {
+ wDaysBefore = (WORD)-1;
+ bFilterIndex = 0;
+ pszProto = NULL;
+ pszAnniv = NULL;
+ }
+ } _filter;
+
+ struct CItemData
+ {
+ HANDLE _hContact;
+ MAnnivDate* _pDate;
+ WORD _wDaysBefore;
+ BYTE _wReminderState;
+
+ CItemData(HANDLE hContact, MAnnivDate &date)
+ {
+ _hContact = hContact;
+ _wReminderState = date.RemindOption();
+ _wDaysBefore = date.RemindOffset();
+ _pDate = new MAnnivDate(date);
+ }
+
+ ~CItemData()
+ {
+ if (_pDate)
+ {
+ // save changes
+ if (_wReminderState != _pDate->RemindOption() || _wDaysBefore != _pDate->RemindOffset())
+ {
+ _pDate->RemindOffset(_wDaysBefore);
+ _pDate->RemindOption(_wReminderState);
+ _pDate->DBWriteReminderOpts(_hContact);
+ }
+ delete _pDate;
+ _pDate = NULL;
+ }
+ }
+ };
+
+ /**
+ * This class handles the movement of child controls on size change.
+ **/
+ class CAnchor
+ {
+ public:
+ enum EAnchor
+ {
+ ANCHOR_LEFT = 1,
+ ANCHOR_RIGHT = 2,
+ ANCHOR_TOP = 4,
+ ANCHOR_BOTTOM = 8,
+ ANCHOR_ALL = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_TOP | ANCHOR_BOTTOM
+ };
+
+ private:
+ WINDOWPOS* _wndPos;
+ HDWP _hdWnds;
+ RECT _rcParent;
+
+ VOID _ScreenToClient(HWND hWnd, LPRECT rc)
+ {
+ POINT pt = { rc->left, rc->top };
+
+ ScreenToClient(hWnd, &pt);
+ rc->right += pt.x - rc->left;
+ rc->bottom += pt.y - rc->top;
+ rc->left = pt.x;
+ rc->top = pt.y;
+ }
+
+ VOID _MoveWindow(HWND hWnd, INT anchors)
+ {
+ if (!(_wndPos->flags & SWP_NOSIZE))
+ {
+ RECT rcc = _CalcPos(hWnd, anchors);
+ MoveWindow(hWnd, rcc.left, rcc.top, rcc.right - rcc.left, rcc.bottom - rcc.top, FALSE);
+ }
+ }
+
+ RECT _CalcPos(HWND hWnd, INT anchors)
+ {
+ RECT rcc;
+
+ GetWindowRect(hWnd, &rcc);
+ _ScreenToClient(_wndPos->hwnd, &rcc);
+ if (!(_wndPos->flags & SWP_NOSIZE))
+ {
+ // calculate difference between new and old size
+ const INT cx = _wndPos->cx - _rcParent.right + _rcParent.left;
+ const INT cy = _wndPos->cy - _rcParent.bottom + _rcParent.top;
+
+ if (cx != 0 || cy != 0)
+ {
+ // move client rect points to the desired new position
+ if (!(anchors & ANCHOR_LEFT) || (anchors & ANCHOR_RIGHT))
+ {
+ rcc.right += cx;
+ }
+ if (!(anchors & ANCHOR_TOP) || (anchors & ANCHOR_BOTTOM))
+ {
+ rcc.bottom += cy;
+ }
+ if ((anchors & ANCHOR_RIGHT) && (!(anchors & ANCHOR_LEFT)))
+ {
+ rcc.left += cx;
+ }
+ if ((anchors & ANCHOR_BOTTOM) && (!(anchors & ANCHOR_TOP)))
+ {
+ rcc.top += cy;
+ }
+ }
+ }
+ return rcc;
+ }
+
+ public:
+ CAnchor(WINDOWPOS* wndPos, SIZE minSize)
+ {
+ GetWindowRect(wndPos->hwnd, &_rcParent);
+ if (wndPos->cx < minSize.cx) {
+ wndPos->cx = minSize.cx;
+ }
+ if (wndPos->cy < minSize.cy) {
+ wndPos->cy = minSize.cy;
+ }
+ _wndPos = wndPos;
+ _hdWnds = BeginDeferWindowPos(2);
+ }
+
+ ~CAnchor()
+ {
+ EndDeferWindowPos(_hdWnds);
+ }
+
+ VOID MoveCtrl(WORD idCtrl, INT anchors)
+ {
+ if (!(_wndPos->flags & SWP_NOSIZE))
+ {
+ HWND hCtrl = GetDlgItem(_wndPos->hwnd, idCtrl);
+ RECT rcc = _CalcPos(hCtrl, anchors);
+ _hdWnds = DeferWindowPos(
+ _hdWnds, //HDWP hWinPosInfo
+ hCtrl, //HWND hWnd
+ HWND_NOTOPMOST, //hWndInsertAfter
+ rcc.left, //int x
+ rcc.top, //int y
+ rcc.right - rcc.left,
+ rcc.bottom - rcc.top,
+ SWP_NOZORDER //UINT uFlags
+ );
+ }
+ }
+ };
+
+ /**
+ * This compare function is used by ListView_SortItemsEx to sort the listview.
+ *
+ * @param iItem1 - index of the first item
+ * @param iItem2 - index of the second item
+ * @param pDlg - pointer to the class' object
+ *
+ * @return This function returns a number indicating comparison result.
+ **/
+ static INT CALLBACK cmpProc(INT iItem1, INT iItem2, CAnnivList* pDlg)
+ {
+ INT result;
+
+ if (pDlg) {
+ TCHAR szText1[MAX_PATH];
+ TCHAR szText2[MAX_PATH];
+
+ szText1[0] = szText2[0] = 0;
+ switch (pDlg->_sortHeader) {
+ case COLUMN_CONTACT:
+ case COLUMN_PROTO:
+ case COLUMN_DESC:
+ {
+ ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH);
+ ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH);
+ result = pDlg->_sortOrder * mir_tcscmp(szText1, szText2);
+ } break;
+
+ case COLUMN_AGE:
+ case COLUMN_ETA:
+ {
+ ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH);
+ ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH);
+ result = pDlg->_sortOrder * (_ttoi(szText1) - _ttoi(szText2));
+ } break;
+
+ case COLUMN_DATE:
+ {
+ CItemData *id1 = pDlg->ItemData(iItem1),
+ *id2 = pDlg->ItemData(iItem2);
+
+ if (PtrIsValid(id1) && PtrIsValid(id2)) {
+ result = pDlg->_sortOrder * id1->_pDate->Compare(*id2->_pDate);
+ break;
+ }
+ }
+ default:
+ {
+ result = 0;
+ }
+ }
+ }
+ else {
+ result = 0;
+ }
+ return result;
+ }
+
+ /**
+ * This static method is the window procedure for the dialog.
+ *
+ * @param hDlg - handle of the dialog window
+ * @param uMsg - message to handle
+ * @param wParam - message dependend parameter
+ * @param lParam - message dependend parameter
+ *
+ * @return depends on message
+ **/
+ static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ CAnnivList *pDlg = (CAnnivList*)GetUserData(hDlg);
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ INT i = 0;
+ HWND hCtrl;
+ HICON hIcon;
+ RECT rc;
+
+ // link the class to the window handle
+ pDlg = (CAnnivList*)lParam;
+ if (!pDlg) {
+ break;
+ }
+ SetUserData(hDlg, lParam);
+ pDlg->_hDlg = hDlg;
+
+ // init pointer listview control
+ pDlg->_hList = GetDlgItem(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (!pDlg->_hList) {
+ break;
+ }
+
+ // set icons
+ hIcon = IcoLib_GetIcon(ICO_DLG_ANNIVERSARY);
+ SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)hIcon);
+ SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+
+ // insert columns into the listboxes
+ ListView_SetExtendedListViewStyle(pDlg->_hList, LVS_EX_FULLROWSELECT);
+
+ // add columns
+ if (pDlg->AddColumn(CAnnivList::COLUMN_ETA, LPGENT("ETA"), 40) ||
+ pDlg->AddColumn(CAnnivList::COLUMN_CONTACT, LPGENT("Contact"), 160) ||
+ pDlg->AddColumn(CAnnivList::COLUMN_PROTO, LPGENT("Proto"), 50) ||
+ pDlg->AddColumn(CAnnivList::COLUMN_AGE, LPGENT("Age/Nr."), 40) ||
+ pDlg->AddColumn(CAnnivList::COLUMN_DESC, LPGENT("Anniversary"), 100) ||
+ pDlg->AddColumn(CAnnivList::COLUMN_DATE, LPGENT("Date"), 80))
+ {
+ break;
+ }
+
+ TranslateDialogDefault(hDlg);
+
+ // save minimal size
+ GetWindowRect(hDlg, &rc);
+ pDlg->_sizeMin.cx = rc.right - rc.left;
+ pDlg->_sizeMin.cy = rc.bottom - rc.top;
+
+ // restore position and size
+ Utils_RestoreWindowPosition(hDlg, NULL, MODNAME, "AnnivDlg_");
+
+ //save win pos
+ GetWindowRect(hDlg, &pDlg->_rcWin);
+
+ // add filter strings
+ if (hCtrl = GetDlgItem(hDlg, COMBO_VIEW)) {
+ ComboBox_AddString(hCtrl, TranslateT("All contacts"));
+ ComboBox_AddString(hCtrl, TranslateT("Birthdays only"));
+ ComboBox_AddString(hCtrl, TranslateT("Anniversaries only"));
+ ComboBox_AddString(hCtrl, TranslateT("Disabled reminder"));
+ ComboBox_SetCurSel(hCtrl, pDlg->_filter.bFilterIndex);
+ }
+
+ // init reminder groups
+ pDlg->_bRemindEnable = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF;
+ if (hCtrl = GetDlgItem(hDlg, CHECK_REMIND)) {
+ Button_SetCheck(hCtrl, pDlg->_bRemindEnable ? BST_INDETERMINATE : BST_UNCHECKED);
+ EnableWindow(hCtrl, pDlg->_bRemindEnable);
+ }
+
+ CheckDlgButton(hDlg, CHECK_POPUP, DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE));
+ // set number of days to show contact in advance
+ SetDlgItemInt(hDlg, EDIT_DAYS, pDlg->_filter.wDaysBefore , FALSE);
+ if (hCtrl = GetDlgItem(hDlg, CHECK_DAYS)) {
+ Button_SetCheck(hCtrl, DB::Setting::GetByte(SET_ANNIVLIST_FILTER_DAYSENABLED, FALSE));
+ DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_DAYS, BN_CLICKED), (LPARAM)hCtrl);
+ }
+
+ pDlg->_wmINIT = false;
+ }
+ return TRUE;
+
+ /**
+ * set propertysheet page's background white in aero mode
+ **/
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLORDLG:
+ if (IsAeroMode())
+ return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case EDIT_ANNIVERSARY_DATE:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ /*
+ * handle changed selection
+ */
+ case LVN_ITEMCHANGED:
+ {
+ CItemData* pid;
+ HWND hCheck;
+
+ pDlg->_curSel = ((LPNMLISTVIEW)lParam)->iItem;
+ pid = pDlg->ItemData(pDlg->_curSel);
+ if (pid && pDlg->_bRemindEnable && (hCheck = GetDlgItem(hDlg, CHECK_REMIND))) {
+ SetDlgItemInt(hDlg, EDIT_REMIND, pid->_wDaysBefore, FALSE);
+ Button_SetCheck(hCheck, pid->_wReminderState);
+ DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_REMIND, BN_CLICKED), (LPARAM)hCheck);
+ }
+ } break;
+
+ /*
+ * resort the list
+ */
+ case LVN_COLUMNCLICK:
+ {
+ LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
+
+ if (pDlg->_sortHeader == pnmv->iSubItem) {
+ pDlg->_sortOrder *= -1;
+ }
+ else {
+ pDlg->_sortOrder = 1;
+ pDlg->_sortHeader = pnmv->iSubItem;
+ }
+ ListView_SortItemsEx(pDlg->_hList, (CMPPROC)cmpProc, pDlg);
+ } break;
+
+ /*
+ * show contact menu
+ */
+ case NM_RCLICK:
+ {
+ CItemData* pid = pDlg->ItemData(pDlg->_curSel);
+ if (pid) {
+ HMENU hPopup = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) pid->_hContact, 0);
+ if (hPopup) {
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(hPopup, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hDlg, NULL);
+ DestroyMenu(hPopup);
+ }
+ }
+ } break;
+
+ /*
+ * handle double click on contact: show message dialog
+ */
+ case NM_DBLCLK:
+ {
+ CItemData* pid = pDlg->ItemData(((LPNMITEMACTIVATE)lParam)->iItem);
+ if (pid) {
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)pid->_hContact, NULL);
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ case WM_COMMAND:
+ {
+ if (PtrIsValid(pDlg)) {
+ CItemData* pid = pDlg->ItemData(pDlg->_curSel);
+
+ // process contact menu command
+ if (pid && CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)pid->_hContact)) {
+ break;
+ }
+
+ switch (LOWORD(wParam))
+ {
+
+ /*
+ * enable/disable reminder checkbox is clicked
+ */
+ case CHECK_REMIND:
+ {
+ if (pDlg->_bRemindEnable && HIWORD(wParam) == BN_CLICKED) {
+ BOOLEAN checkState = Button_GetCheck((HWND)lParam);
+
+ EnableWindow(GetDlgItem(hDlg, EDIT_REMIND), checkState == BST_CHECKED);
+ EnableWindow(GetDlgItem(hDlg, SPIN_REMIND), checkState == BST_CHECKED);
+ EnableWindow(GetDlgItem(hDlg, TXT_REMIND5), checkState == BST_CHECKED);
+ if (pid && pid->_wReminderState != checkState) {
+ pid->_wReminderState = checkState;
+ }
+ }
+ } break;
+
+ /*
+ * number of days to remind in advance is edited
+ */
+ case EDIT_REMIND:
+ {
+ if (pid && pDlg->_bRemindEnable && HIWORD(wParam) == EN_CHANGE) {
+ WORD wDaysBefore = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE);
+ if (pid->_wReminderState == BST_CHECKED && pid->_wDaysBefore != wDaysBefore) {
+ pid->_wDaysBefore = wDaysBefore;
+ }
+ }
+ } break;
+
+ /*
+ * the filter to display only contacts which have an anniversary in a certain
+ * period of time is enabled/disabled
+ */
+ case CHECK_DAYS:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ BOOLEAN isChecked = Button_GetCheck((HWND)lParam);
+ EnableWindow(GetDlgItem(hDlg, EDIT_DAYS), isChecked);
+ EnableWindow(GetDlgItem(hDlg, TXT_DAYS), isChecked);
+ pDlg->_filter.wDaysBefore = isChecked ? GetDlgItemInt(hDlg, EDIT_DAYS, NULL, FALSE) : (WORD)-1;
+ pDlg->RebuildList();
+ }
+ } break;
+
+ /*
+ * the number of days a contact must have an anniversary in advance to be displayed is edited
+ */
+ case EDIT_DAYS:
+ {
+ if (HIWORD(wParam) == EN_CHANGE) {
+ WORD wNewDays = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE);
+ if (wNewDays != pDlg->_filter.wDaysBefore) {
+ pDlg->_filter.wDaysBefore = wNewDays;
+ pDlg->RebuildList();
+ }
+ }
+ } break;
+
+ /*
+ * the filter selection of the filter combobox has changed
+ */
+ case COMBO_VIEW:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ pDlg->_filter.bFilterIndex = ComboBox_GetCurSel((HWND)lParam);
+ pDlg->RebuildList();
+ }
+ }
+ }
+ }
+ } break;
+
+ case WM_DRAWITEM:
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+ case WM_MEASUREITEM:
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+
+ case WM_WINDOWPOSCHANGING:
+ {
+ if (PtrIsValid(pDlg)) {
+ WINDOWPOS* wndPos = (WINDOWPOS*)lParam;
+ if (!pDlg->_wmINIT && (wndPos->cx != 0 || wndPos->cy != 0)) {
+ //win pos change
+ if ( (wndPos->cx == pDlg->_rcWin.right - pDlg->_rcWin.left) &&
+ (wndPos->cy == pDlg->_rcWin.bottom - pDlg->_rcWin.top)) {
+ //win pos change (store new pos)
+ GetWindowRect(hDlg, &pDlg->_rcWin);
+ }
+ //win size change
+ else {
+ // l change
+ if ( (wndPos->cx < pDlg->_sizeMin.cx) && (wndPos->x > pDlg->_rcWin.left)) {
+ wndPos->x = wndPos->x + wndPos->cx - pDlg->_sizeMin.cx;
+ wndPos->cx = pDlg->_sizeMin.cx;
+ }
+ // r change
+ else if (wndPos->cx < pDlg->_sizeMin.cx) {
+ wndPos->cx = pDlg->_sizeMin.cx;
+ }
+
+ // t change
+ if ( (wndPos->cy < pDlg->_sizeMin.cy) && (wndPos->y > pDlg->_rcWin.top)) {
+ wndPos->y = wndPos->y + wndPos->cy - pDlg->_sizeMin.cy;
+ wndPos->cy = pDlg->_sizeMin.cy;
+ }
+ // b change
+ else if (wndPos->cy < pDlg->_sizeMin.cy) {
+ wndPos->cy = pDlg->_sizeMin.cy;
+ }
+
+ pDlg->_rcWin.left = wndPos->x;
+ pDlg->_rcWin.right = wndPos->x + wndPos->cx;
+ pDlg->_rcWin.top = wndPos->y;
+ pDlg->_rcWin.bottom = wndPos->y + wndPos->cy;
+ }
+ }
+
+ CAnchor anchor(wndPos, pDlg->_sizeMin);
+ INT anchorPos = CAnchor::ANCHOR_LEFT | CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_TOP;
+
+ anchor.MoveCtrl(IDC_HEADERBAR, anchorPos);
+ anchor.MoveCtrl(GROUP_STATS, anchorPos);
+
+ // birthday list
+ anchor.MoveCtrl(EDIT_ANNIVERSARY_DATE, CAnchor::ANCHOR_ALL);
+
+ anchorPos = CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_BOTTOM;
+
+ // filter group
+ anchor.MoveCtrl(GROUP_FILTER, anchorPos);
+ anchor.MoveCtrl(COMBO_VIEW, anchorPos);
+ anchor.MoveCtrl(CHECK_DAYS, anchorPos);
+ anchor.MoveCtrl(EDIT_DAYS, anchorPos);
+ anchor.MoveCtrl(TXT_DAYS, anchorPos);
+
+ // filter group
+ anchor.MoveCtrl(GROUP_REMINDER, anchorPos);
+ anchor.MoveCtrl(CHECK_REMIND, anchorPos);
+ anchor.MoveCtrl(EDIT_REMIND, anchorPos);
+ anchor.MoveCtrl(SPIN_REMIND, anchorPos);
+ anchor.MoveCtrl(TXT_REMIND6, anchorPos);
+ anchor.MoveCtrl(CHECK_POPUP, anchorPos);
+ }
+ } break;
+
+ /**
+ * This message is sent if eighter the user clicked on the close button or
+ * Miranda fires the ME_SYSTEM_SHUTDOWN event.
+ **/
+ case WM_CLOSE:
+ {
+ DestroyWindow(hDlg);
+ } break;
+
+ /**
+ * If the anniversary list is destroyed somehow, the data class must be
+ * deleted, too.
+ **/
+ case WM_DESTROY:
+ {
+ if (PtrIsValid(pDlg)) {
+ SetUserData(hDlg, NULL);
+ delete pDlg;
+ }
+ MagneticWindows_RemoveWindow(hDlg);
+ } break;
+ }
+ return FALSE;
+ }
+
+ /**
+ * This method adds a column to the listview.
+ *
+ * @param iSubItem - desired column index
+ * @param pszText - the header text
+ * @param defaultWidth - the default witdth
+ *
+ * @retval 0 if successful
+ * @retval 1 if failed
+ **/
+ BOOLEAN AddColumn(INT iSubItem, LPCTSTR pszText, INT defaultWidth)
+ {
+ LVCOLUMN lvc;
+ CHAR pszSetting[MAXSETTING];
+
+ mir_snprintf(pszSetting, SIZEOF(pszSetting), "AnnivDlg_Col%d", iSubItem);
+ lvc.cx = DB::Setting::GetWord(pszSetting, defaultWidth);
+ lvc.mask = LVCF_WIDTH|LVCF_TEXT;
+ lvc.iSubItem = iSubItem;
+ lvc.pszText = TranslateTS(pszText);
+ return ListView_InsertColumn(_hList, lvc.iSubItem++, &lvc) == -1;
+ }
+
+ /**
+ * This method sets the subitem text for the current contact
+ *
+ * @param iItem - index of the current row
+ * @param iSubItem - column to set text for
+ * @param pszText - text to insert
+ *
+ * @retval TRUE if successful
+ * @retval FALSE if failed
+ **/
+ BOOLEAN AddSubItem(INT iItem, INT iSubItem, LPTSTR pszText)
+ {
+ LVITEM lvi;
+ if (iSubItem > 0)
+ {
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = pszText;
+ lvi.mask = LVIF_TEXT;
+ return ListView_SetItem(_hList, &lvi);
+ }
+ return FALSE;
+ }
+
+ /**
+ * This method adds a row to the listview.
+ *
+ * @param pszText - text to insert
+ * @param lParam - pointer to the rows data
+ *
+ * @retval TRUE if successful
+ * @retval FALSE if failed
+ **/
+ BOOLEAN AddItem(LPTSTR pszText, LPARAM lParam)
+ {
+ LVITEM lvi;
+
+ if (!pszText) {
+ return FALSE;
+ }
+ lvi.iItem = 0;
+ lvi.iSubItem = 0;
+ lvi.pszText = pszText;
+ lvi.mask = LVIF_TEXT|TVIF_PARAM;
+ lvi.lParam = lParam;
+ return ListView_InsertItem(_hList, &lvi);
+ }
+
+ /**
+ * This method adds a row to the listview.
+ *
+ * @param hContact - contact to add the line for
+ * @param pszProto - contact's protocol
+ * @param ad - anniversary to add
+ * @param mtNow - current time
+ * @param wDaysBefore - number of days in advance to remind the user of the anniversary
+ *
+ * @retval TRUE if successful
+ * @retval FALSE if failed
+ **/
+ BOOLEAN AddRow(HANDLE hContact, LPCSTR pszProto, MAnnivDate &ad, MTime &mtNow, WORD wDaysBefore)
+ {
+ TCHAR szText[MAX_PATH];
+ INT diff, iItem = -1;
+ CItemData *pdata;
+
+ //
+ // first column: ETA
+ //
+ diff = ad.CompareDays(mtNow);
+ if (diff < 0)
+ {
+ diff += IsLeap(mtNow.Year() + 1) ? 366 : 365;
+ }
+ // is filtered
+ if (diff <= _filter.wDaysBefore)
+ {
+ // read reminder options for the contact
+ ad.DBGetReminderOpts(hContact);
+ if ((_filter.bFilterIndex != FILTER_DISABLED_REMINDER) || (ad.RemindOption() == BST_UNCHECKED))
+ {
+ // set default offset if required
+ if (ad.RemindOffset() == (WORD)-1)
+ {
+ ad.RemindOffset(wDaysBefore);
+
+ // create data object
+ pdata = new CItemData(hContact, ad);
+ if (!pdata)
+ {
+ return FALSE;
+ }
+ // add item
+ iItem = AddItem(_itot(diff, szText, 10), (LPARAM)pdata);
+ if (iItem == -1)
+ {
+ delete pdata;
+ return FALSE;
+ }
+
+ // second column: contact name
+ AddSubItem(iItem, COLUMN_CONTACT, DB::Contact::DisplayName(hContact));
+
+ // third column: protocol
+ TCHAR* ptszProto = mir_a2t(pszProto);
+ AddSubItem(iItem, COLUMN_PROTO, ptszProto);
+ mir_free(ptszProto);
+
+ // forth line: age
+ AddSubItem(iItem, COLUMN_AGE, _itot(ad.Age(&mtNow), szText, 10));
+
+ // fifth line: anniversary
+ AddSubItem(iItem, COLUMN_DESC, (LPTSTR)ad.Description());
+
+ // sixth line: date
+ ad.DateFormat(szText, SIZEOF(szText));
+ AddSubItem(iItem, COLUMN_DATE, szText);
+
+ _numRows++;
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * This method clears the list and adds contacts again, according to the current filter settings.
+ **/
+ VOID RebuildList()
+ {
+ HANDLE hContact;
+ LPSTR pszProto;
+ MTime mtNow;
+ MAnnivDate ad;
+ INT i = 0;
+ DWORD age = 0;
+ WORD wDaysBefore = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);
+ WORD numMale = 0;
+ WORD numFemale = 0;
+ WORD numContacts = 0;
+ WORD numBirthContacts = 0;
+
+ ShowWindow(_hList, SW_HIDE);
+ DeleteAllItems();
+ mtNow.GetLocalTime();
+
+ // insert the items into the list
+ for (hContact = DB::Contact::FindFirst();
+ hContact;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ // ignore meta subcontacts here, as they are not interesting.
+ if (!DB::MetaContact::IsSub(hContact)) {
+ // filter protocol
+ pszProto = DB::Contact::Proto(hContact);
+ if (pszProto) {
+ numContacts++;
+ switch (GenderOf(hContact, pszProto))
+ {
+ case 'M': {
+ numMale++;
+ } break;
+
+ case 'F': {
+ numFemale++;
+ }
+ }
+
+ if (!ad.DBGetBirthDate(hContact, pszProto)) {
+ age += ad.Age(&mtNow);
+ numBirthContacts++;
+
+ // add birthday
+ if ((_filter.bFilterIndex != FILTER_ANNIV) && (!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto))) {
+ AddRow(hContact, pszProto, ad, mtNow, wDaysBefore);
+ }
+ }
+
+ // add anniversaries
+ if ( _filter.bFilterIndex != FILTER_BIRTHDAY &&
+ (!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto)))
+ {
+ for (i = 0; !ad.DBGetAnniversaryDate(hContact, i); i++) {
+ if (!_filter.pszAnniv || !_tcsicmp(_filter.pszAnniv, ad.Description())) {
+ AddRow(hContact, pszProto, ad, mtNow, wDaysBefore);
+ }
+ }
+ }
+ }
+ }
+ }
+ ListView_SortItemsEx(_hList, (CMPPROC)cmpProc, this);
+ ShowWindow(_hList, SW_SHOW);
+
+ // display statistics
+ SetDlgItemInt(_hDlg, TXT_NUMBIRTH, numBirthContacts, FALSE);
+ SetDlgItemInt(_hDlg, TXT_NUMCONTACT, numContacts, FALSE);
+ SetDlgItemInt(_hDlg, TXT_FEMALE, numFemale, FALSE);
+ SetDlgItemInt(_hDlg, TXT_MALE, numMale, FALSE);
+ SetDlgItemInt(_hDlg, TXT_AGE, numBirthContacts > 0 ? max(0, (age - (age % numBirthContacts)) / numBirthContacts) : 0, FALSE);
+ }
+
+ /**
+ * This method deletes all items from the listview
+ **/
+ VOID DeleteAllItems()
+ {
+ CItemData *pid;
+
+ for (INT i = 0; i < _numRows; i++)
+ {
+ pid = ItemData(i);
+ if (pid)
+ {
+ delete pid;
+ }
+ }
+ ListView_DeleteAllItems(_hList);
+ _numRows = 0;
+ }
+
+ /**
+ * This method returns the data structure accociated with a list item.
+ *
+ * @param iItem - index of the desired item
+ *
+ * @return pointer to the data strucutre on success or NULL otherwise.
+ **/
+ CItemData* ItemData(INT iItem)
+ {
+ if (_hList && iItem >= 0 && iItem < _numRows)
+ {
+ LVITEM lvi;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ if (ListView_GetItem(_hList, &lvi) && PtrIsValid(lvi.lParam))
+ {
+ return (CItemData*)lvi.lParam;
+ }
+ }
+ return NULL;
+ }
+
+ /**
+ * This method loads all filter settings from db
+ **/
+ VOID LoadFilter()
+ {
+ _filter.wDaysBefore = DB::Setting::GetWord(SET_ANNIVLIST_FILTER_DAYS, 9);
+ _filter.bFilterIndex = DB::Setting::GetByte(SET_ANNIVLIST_FILTER_INDEX, 0);
+ }
+
+ /**
+ * This method saves all filter settings to db
+ **/
+ VOID SaveFilter()
+ {
+ if (_hDlg) {
+ DB::Setting::WriteWord(SET_ANNIVLIST_FILTER_DAYS, (WORD)GetDlgItemInt(_hDlg, EDIT_DAYS, NULL, FALSE));
+ DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_DAYSENABLED, (BYTE)Button_GetCheck(GetDlgItem(_hDlg, CHECK_DAYS)));
+ DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_INDEX, (BYTE)ComboBox_GetCurSel(GetDlgItem(_hDlg, EDIT_DAYS)));
+ }
+ }
+
+public:
+
+ /**
+ * This is the default constructor.
+ **/
+ CAnnivList()
+ {
+ _hList = NULL;
+ _sortHeader = 0;
+ _sortOrder = 1;
+ _curSel = -1;
+ _numRows = 0;
+ _wmINIT = true;
+ _rcWin.left = _rcWin.right = _rcWin.top = _rcWin.bottom = 0;
+ LoadFilter();
+
+ _hDlg = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_LIST), NULL, (DLGPROC)DlgProc, (LPARAM)this);
+ if (_hDlg) {
+ MagneticWindows_AddWindow(_hDlg);
+ _mHookExit = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, _hDlg, WM_CLOSE);
+ }
+ else {
+ _mHookExit = NULL;
+ delete this;
+ }
+ }
+
+ /**
+ * This is the default destructor.
+ **/
+ ~CAnnivList()
+ {
+ // delete the shutdown hook
+ if (_mHookExit) {
+ UnhookEvent(_mHookExit);
+ _mHookExit = NULL;
+ }
+
+ // close window if required
+ if (_hDlg) {
+ // save list state
+ if (_hList) {
+ CHAR pszSetting[MAXSETTING];
+ INT c, cc = Header_GetItemCount(ListView_GetHeader(_hList));
+
+ for (c = 0; c < cc; c++) {
+ mir_snprintf(pszSetting, MAXSETTING, "AnnivDlg_Col%d", c);
+ DB::Setting::WriteWord(pszSetting, (WORD)ListView_GetColumnWidth(_hList, c));
+ }
+ DeleteAllItems();
+ }
+ // remember popup setting
+ DB::Setting::WriteByte(SET_ANNIVLIST_POPUP, (BYTE)IsDlgButtonChecked(_hDlg, CHECK_POPUP));
+ // save window position, size and column widths
+ Utils_SaveWindowPosition(_hDlg, NULL, MODNAME, "AnnivDlg_");
+ SaveFilter();
+
+ // if the window did not yet retrieve a WM_DESTROY message, do it right now.
+ if (PtrIsValid(GetUserData(_hDlg))) {
+ SetUserData(_hDlg, NULL);
+ DestroyWindow(_hDlg);
+ }
+ }
+ gpDlg = NULL;
+ }
+
+ /**
+ * name: BringToFront
+ * class: CAnnivList
+ * desc: brings anniversary list to the top
+ * param: none
+ * return: nothing
+ **/
+ VOID BringToFront()
+ {
+ ShowWindow(_hDlg, SW_RESTORE);
+ SetForegroundWindow(_hDlg);
+ SetFocus(_hDlg);
+ }
+
+}; // class CAnnivList
+
+/***********************************************************************************************************
+ * service handlers
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function that is called list all anniversaries.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return always 0
+ **/
+INT_PTR DlgAnniversaryListShow(WPARAM wParam, LPARAM lParam)
+{
+ if (!gpDlg) {
+ try
+ {
+ myGlobals.WantAeroAdaption = DB::Setting::GetByte(SET_PROPSHEET_AEROADAPTION, TRUE);
+ gpDlg = new CAnnivList();
+ }
+ catch(...)
+ {
+ delete gpDlg;
+ gpDlg = NULL;
+ }
+ }
+ else {
+ gpDlg->BringToFront();
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * loading and unloading module
+ ***********************************************************************************************************/
+
+#define TBB_IDBTN "AnnivList"
+#define TBB_ICONAME TOOLBARBUTTON_ICONIDPREFIX TBB_IDBTN TOOLBARBUTTON_ICONIDPRIMARYSUFFIX
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param wParam - none
+ *
+ * @return nothing
+ **/
+
+VOID DlgAnniversaryListOnTopToolBarLoaded()
+{
+ TTBButton ttb = {0};
+ ttb.cbSize = sizeof(ttb);
+ ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
+ ttb.pszService = MS_USERINFO_REMINDER_LIST;
+ ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_ANNIVERSARY);
+ ttb.name = ttb.pszTooltipUp = LPGEN("Anniversary list");
+ TopToolbar_AddButton(&ttb);
+}
+
+/**
+ * This function initially loads all required stuff for the anniversary list.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID DlgAnniversaryListLoadModule()
+{
+ CreateServiceFunction(MS_USERINFO_REMINDER_LIST, DlgAnniversaryListShow);
+
+ HOTKEYDESC hk = { 0 };
+ hk.cbSize = sizeof(HOTKEYDESC);
+ hk.pszSection = MODNAME;
+ hk.pszName = "AnniversaryList";
+ hk.pszDescription = LPGEN("Popup Anniversary list");
+ hk.pszService = MS_USERINFO_REMINDER_LIST;
+ Hotkey_Register(&hk);
+}
diff --git a/plugins/UserInfoEx/src/dlg_anniversarylist.h b/plugins/UserInfoEx/src/dlg_anniversarylist.h
new file mode 100644
index 0000000000..e7ff89e99c
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_anniversarylist.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_anniversarylist.h $
+Revision : $Revision: 202 $
+Last change on : $Date: 2010-09-24 23:46:57 +0400 (Пт, 24 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLGANNIVERSARYLIST_H_
+#define _DLGANNIVERSARYLIST_H_
+
+#define SET_ANNIVLIST_POPUP "AnLstPopup"
+#define SET_ANNIVLIST_FILTER_DAYSENABLED "AnLstFltDaysEnabled"
+#define SET_ANNIVLIST_FILTER_DAYS "AnLstFltDays"
+#define SET_ANNIVLIST_FILTER_INDEX "AnLstFltIndex"
+
+INT_PTR DlgAnniversaryListShow(WPARAM wParam, LPARAM lParam);
+VOID DlgAnniversaryListOnTopToolBarLoaded();
+VOID DlgAnniversaryListLoadModule();
+
+#endif /* _DLGANNIVERSARYLIST_H_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.cpp b/plugins/UserInfoEx/src/dlg_msgbox.cpp
new file mode 100644
index 0000000000..5f55802e3d
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_msgbox.cpp
@@ -0,0 +1,856 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_msgbox.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include <Commdlg.h>
+#include <m_clui.h>
+#include <m_skin.h>
+
+typedef struct _MSGPOPUPDATA
+{
+ POPUPACTION pa[3];
+ HWND hDialog;
+}
+MSGPOPUPDATA, *LPMSGPOPUPDATA;
+
+/**
+ * This helper function moves and resizes a dialog box's control element.
+ *
+ * @param hDlg - the dialog box's window handle
+ * @param idCtrl - the identication number of the control to move
+ * @param dx -number of pixels to horizontal move the control
+ * @param dy - number of pixels to vertical move the control
+ * @param dw - number of pixels to horizontal resize the control
+ * @param dh - number of pixels to vertical resize the control
+ *
+ * @return nothing
+ **/
+static FORCEINLINE VOID MoveCtrl(HWND hDlg, INT idCtrl, INT dx, INT dy, INT dw, INT dh)
+{
+ RECT ws;
+ HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+ GetWindowRect(hCtrl, &ws);
+ OffsetRect(&ws, dx, dy);
+ MoveWindow(hCtrl, ws.left, ws.top, ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE);
+}
+
+/**
+ * This function loads the icon to display for the current message.
+ *
+ * @param pMsgBox - pointer to a MSGBOX structure, with information about the message to display.
+ *
+ * @retval HICON - The function returns an icon to display with the message.
+ * @retval NULL - There is no icon for the message.
+ **/
+static HICON MsgLoadIcon(LPMSGBOX pMsgBox)
+{
+ HICON hIcon;
+
+ // load the desired status icon
+ switch (pMsgBox->uType & MB_ICONMASK)
+ {
+
+ // custom icon defined by caller function
+ case MB_ICON_OTHER:
+ {
+ hIcon = pMsgBox->hiMsg;
+ }
+ break;
+
+ // default windows icons
+ case MB_ICON_ERROR:
+ case MB_ICON_QUESTION:
+ case MB_ICON_WARNING:
+ case MB_ICON_INFO:
+ {
+ LPCTSTR ico[] = { 0, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION };
+ hIcon = LoadIcon(NULL, ico[MB_ICON_INDEX(pMsgBox->uType)]);
+ }
+ break;
+
+ // no icon
+ default:
+ {
+ hIcon = NULL;
+ }
+ }
+ return hIcon;
+}
+
+/**
+ * This function fills a given POPUPACTION structure with the data of a given message id,
+ * which is normally used by the message box. This is required to let the user interact
+ * with a popup in the same way as with a normal message dialog box.
+ *
+ * @param pa - reference to a POPUPACTION structure to fill
+ * @param id - the message id
+ * @param result - This parameter is passed to the POPUPACTION structure as is.
+ *
+ * @return nothing
+ **/
+static VOID MakePopupAction(POPUPACTION &pa, INT id)
+{
+ pa.cbSize = sizeof(POPUPACTION);
+ pa.flags = PAF_ENABLED;
+ pa.wParam = MAKEWORD(id, BN_CLICKED);
+ pa.lParam = 0;
+
+ switch (id)
+ {
+ case IDOK:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ok");
+ }
+ break;
+
+ case IDCLOSE:
+ case IDCANCEL:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Cancel");
+ }
+ break;
+
+ case IDABORT:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Abort");
+ }
+ break;
+
+ case IDRETRY:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Retry");
+ }
+ break;
+
+ case IDIGNORE:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ignore");
+ }
+ break;
+
+ case IDYES:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Yes");
+ }
+ break;
+
+ case IDNO:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/No");
+ }
+ break;
+
+ case IDHELP:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Help");
+ }
+ break;
+
+ case IDALL:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/All");
+ }
+ break;
+
+ case IDNONE:
+ {
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/None");
+ }
+ }
+}
+
+/**
+ * This is the message procedure for my nice looking message box
+ *
+ * @param hDlg - window handle
+ * @param uMsg - message to handle
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static INT retOk = IDOK;
+ static INT retAll = IDALL;
+ static INT retNon = IDNONE;
+ static INT retCancel = IDCANCEL;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ if (PtrIsValid(pMsgBox))
+ {
+ INT icoWidth = 0;
+ INT InfoBarHeight = 0;
+ HFONT hNormalFont;
+
+ hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0);
+ if (pMsgBox->uType & MB_INFOBAR)
+ {
+ LOGFONT lf;
+
+ // set bold font for name in description area
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hNormalFont = CreateFontIndirect(&lf);
+
+ // set infobar's textfont
+ SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0);
+
+ // set infobar's logo icon
+ SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)((pMsgBox->hiLogo) ? pMsgBox->hiLogo : IcoLib_GetIcon(ICO_DLG_DETAILS)));
+
+ // anable headerbar
+ ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW);
+ }
+ else
+ {
+ RECT rc;
+ GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc);
+ InfoBarHeight = rc.bottom;
+
+ if (pMsgBox->hiLogo)
+ {
+ SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo);
+ }
+ }
+
+ // draw the desired status icon
+ HICON hIcon = MsgLoadIcon(pMsgBox);
+ if (hIcon)
+ {
+ SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ }
+ else
+ {
+ RECT ws;
+ GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws);
+ icoWidth = ws.right - ws.left;
+ ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE);
+ }
+
+ // resize the messagebox and reorganize the buttons
+ if (HDC hDC = GetDC(hDlg))
+ {
+ POINT mpt = {0,0};
+ RECT ws = {0,0,0,0};
+ INT txtWidth,
+ txtHeight,
+ needX, needY;
+ RECT rcDlg;
+ SIZE ts;
+ LPTSTR h, rs;
+
+ SelectObject(hDC, hNormalFont);
+
+ for (rs = h = pMsgBox->ptszMsg, txtHeight = 0, txtWidth = 0; h; h++)
+ {
+ if (*h == '\n' || *h == '\0')
+ {
+ GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+ if (ts.cx > txtWidth)
+ {
+ txtWidth = ts.cx;
+ }
+ txtHeight += ts.cy;
+ if (*h == '\0')
+ {
+ break;
+ }
+ rs = h + 1;
+ }
+ }
+ ReleaseDC(hDlg, hDC);
+
+ // calc new dialog size
+ GetWindowRect(hDlg, &rcDlg);
+ GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws);
+ needX = txtWidth - (ws.right - ws.left) - icoWidth;
+ needY = max(0, txtHeight - (ws.bottom - ws.top) + 5);
+ rcDlg.left -= needX/2; rcDlg.right += needX/2;
+ rcDlg.top -= (needY-InfoBarHeight)/2; rcDlg.bottom += (needY-InfoBarHeight)/2;
+
+ // resize dialog window
+ MoveWindow(hDlg,
+ rcDlg.left, rcDlg.top,
+ rcDlg.right - rcDlg.left,
+ rcDlg.bottom - rcDlg.top,
+ FALSE);
+ ClientToScreen(hDlg, &mpt);
+
+ MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight);
+ MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0);
+ MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0);
+ MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0);
+ MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY);
+ MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0);
+
+ //
+ // Do pushbutton positioning
+ //
+ {
+ RECT rcOk, rcAll, rcNone, rcCancel;
+ LONG okWidth, caWidth, allWidth, noneWidth, dlgMid;
+
+ // get button rectangles
+ GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk);
+ OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll);
+ OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone);
+ OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
+ OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ okWidth = rcOk.right - rcOk.left;
+ allWidth = rcAll.right - rcAll.left;
+ noneWidth = rcNone.right - rcNone.left;
+ caWidth = rcCancel.right - rcCancel.left;
+ dlgMid = (rcDlg.right - rcDlg.left) / 2;
+
+ // load button configuration
+ switch (MB_TYPE(pMsgBox->uType))
+ {
+
+ case MB_OK:
+ {
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ }
+ break;
+
+ case MB_OKCANCEL:
+ {
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENT("OK"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ case MB_RETRYCANCEL:
+ {
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENT("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ case MB_YESNO:
+ {
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("No"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ {
+ retOk = IDABORT;
+ SetDlgItemText(hDlg, IDOK, LPGENT("Abord"));
+ retAll = IDABORT;
+ SetDlgItemText(hDlg, IDALL, LPGENT("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("Ignore"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ case MB_YESNOCANCEL:
+ {
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+ retAll = IDNO;
+ SetDlgItemText(hDlg, IDALL, LPGENT("No"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ case MB_YESALLNO:
+ {
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+ retAll = IDALL;
+ SetDlgItemText(hDlg, IDALL, LPGENT("All"));
+ //retNon = IDNONE;
+ SetDlgItemText(hDlg, IDNONE, LPGENT("None"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENT("No"));
+ rcCancel.right = rcDlg.right - rcDlg.left - 10;
+ rcCancel.left = rcCancel.right - caWidth;
+ rcNone.right = rcCancel.left - 5;
+ rcNone.left = rcNone.right - noneWidth;
+ rcAll.right = rcNone.left - 5;
+ rcAll.left = rcAll.right - allWidth;
+ rcOk.right = rcAll.left - 5;
+ rcOk.left = rcOk.right - okWidth;
+ // show buttons
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ }
+ break;
+
+ default:
+ {
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ }
+ }
+ // move ok button
+ MoveWindow(GetDlgItem(hDlg, IDOK),
+ rcOk.left, rcOk.top,
+ rcOk.right - rcOk.left, rcOk.bottom - rcOk.top,
+ FALSE);
+ // move all button
+ MoveWindow(GetDlgItem(hDlg, IDALL),
+ rcAll.left, rcAll.top,
+ rcAll.right - rcAll.left, rcAll.bottom - rcAll.top,
+ FALSE);
+ // move none button
+ MoveWindow(GetDlgItem(hDlg, IDNONE),
+ rcNone.left, rcNone.top,
+ rcNone.right - rcNone.left, rcNone.bottom - rcNone.top,
+ FALSE);
+ // move cancel button
+ MoveWindow(GetDlgItem(hDlg, IDCANCEL),
+ rcCancel.left, rcCancel.top,
+ rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top,
+ FALSE);
+ } // end* Do pushbutton positioning
+ } // end* resize the messagebox and reorganize the buttons
+
+ // set text's
+ SetWindowText(hDlg, pMsgBox->ptszTitle);
+ SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText);
+ SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg);
+
+ TranslateDialogDefault(hDlg);
+ return TRUE;
+ } // end* PtrIsValid(pMsgBox)
+ } // end* WM_INITDIALOG:
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID))
+ {
+ case STATIC_WHITERECT:
+ case ICO_DLGLOGO:
+ case ICO_MSGDLG:
+ case TXT_MESSAGE:
+ case TXT_NAME:
+ {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return GetSysColor(COLOR_WINDOW);
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ EndDialog(hDlg, retOk);
+ break;
+ case IDCANCEL:
+ EndDialog(hDlg, retCancel);
+ break;
+ case IDALL:
+ EndDialog(hDlg, retAll);
+ break;
+ case IDNONE:
+ EndDialog(hDlg, retNon);
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0));
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+/**
+ * Dummi modal MsgBox for popup,
+ * this set call function in wait stait and do not freece miranda main thread
+ * the window is outside the desktop
+ */
+static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ POPUPDATAT_V2 pd;
+ LPMSGPOPUPDATA pmpd;
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ MoveWindow(hDlg,-10,-10,0,0,FALSE);
+ pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA));
+ if (pmpd)
+ {
+ ZeroMemory(&pd, sizeof(pd));
+ pd.cbSize = sizeof(POPUPDATAT_V2);
+ pd.lchContact = NULL; //(HANDLE)wParam;
+ // icon
+ pd.lchIcon = MsgLoadIcon(pMsgBox);
+ mir_tcsncpy(pd.lptzContactName, pMsgBox->ptszTitle, SIZEOF(pd.lptzContactName));
+ mir_tcsncpy(pd.lptzText, pMsgBox->ptszMsg, SIZEOF(pd.lptzText));
+
+ // CALLBAC Proc
+ pd.PluginWindowProc = (WNDPROC)PopupProc;
+ //
+ pd.PluginData = pmpd;
+
+ pd.iSeconds = -1;
+
+ pd.hNotification = NULL;
+ pd.lpActions = pmpd->pa;
+
+ // set color of popup
+ switch (pMsgBox->uType & MB_ICONMASK)
+ {
+ case MB_ICON_ERROR:
+ {
+ pd.colorBack = RGB(200, 10, 0);
+ pd.colorText = RGB(255, 255, 255);
+ }
+ break;
+
+ case MB_ICON_WARNING:
+ {
+ pd.colorBack = RGB(200, 100, 0);
+ pd.colorText = RGB(255, 255, 255);
+ }
+ break;
+
+ default:
+ {
+ if (pMsgBox->uType & MB_CUSTOMCOLOR)
+ {
+ pd.colorBack = pMsgBox->colorBack;
+ pd.colorText = pMsgBox->colorText;
+ }
+ }
+ }
+
+ // handle for MakePopupAction
+ pmpd->hDialog = hDlg;
+
+ // active buttons
+ switch (MB_TYPE(pMsgBox->uType))
+ {
+ case MB_OK:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDOK);
+ }
+ break;
+
+ case MB_OKCANCEL:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDOK);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+ }
+ break;
+
+ case MB_RETRYCANCEL:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+ }
+ break;
+
+ case MB_YESNO:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+ }
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDABORT);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDIGNORE);
+ }
+ break;
+
+ case MB_YESNOCANCEL:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+ }
+ break;
+
+ case MB_YESALLNO:
+ {
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDALL);
+ MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+ }
+ break;
+
+ } // end* switch
+
+ // create popup
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_NEWDATA);
+ if (MB_TYPE(pMsgBox->uType) == MB_OK)
+ {
+ EndDialog(hDlg, IDOK);
+ }
+ } // end*if (pmpd)
+ break;
+ } // end* WM_INITDIALOG:
+ } // end* switch (uMsg)
+ return FALSE;
+}
+
+/**
+ * This is the message procedure for popup
+ *
+ * @param hDlg - window handle
+ * @param uMsg - message to handle
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case UM_POPUPACTION:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+
+ if (pmpd)
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ case IDABORT:
+ case IDRETRY:
+ case IDIGNORE:
+ case IDYES:
+ case IDNO:
+ case IDALL:
+ case IDNONE:
+ {
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, LOWORD(wParam));
+ }
+ break;
+
+ default:
+ {
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, IDCANCEL);
+ }
+ }
+ }
+ PUDeletePopUp(hDlg);
+ }
+ }
+ break;
+
+ case UM_FREEPLUGINDATA:
+ {
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+ if (pmpd > 0) {
+ MIR_FREE(pmpd);
+ }
+ return TRUE; //TRUE or FALSE is the same, it gets ignored.
+ }
+ break;
+
+ default:
+ break;
+ }
+ return DefWindowProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+ * This is the service function for external plugins to use the nice messagebox
+ *
+ * @param wParam - HANDLE hContact which can display an avatar for popups
+ * @param lParam - MSGBOX structure holding parameters
+ *
+ * @return The function returns the ID of the clicked button (IDOK, IDCANCEL, ...)
+ * or -1 on error.
+ **/
+INT_PTR MsgBoxService(WPARAM wParam, LPARAM lParam)
+{
+ INT rc = -1;
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ // check input
+ if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX))
+ {
+ // Shall the MessageBox displayed as popup?
+ if (!(pMsgBox->uType & (MB_INFOBAR|MB_NOPOPUP)) && // message box can be a popup?
+ ServiceExists(MS_POPUP_ADDPOPUPT) && // popups exist?
+ myGlobals.PopUpActionsExist == 1 && // popup support ext stuct?
+ (DB::Setting::GetDWord(NULL, "PopUp","Actions", 0) & 1) && // popup++ actions on?
+ DB::Setting::GetByte(SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX) // user likes popups?
+ )
+ {
+ rc = DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, (DLGPROC)MsgBoxPop, lParam);
+ }
+ else
+ {
+ rc = DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, (DLGPROC)MsgBoxProc, lParam);
+ }
+ }
+ return rc;
+}
+
+/**
+ * name: MsgBox
+ * desc: calls a messagebox
+ * param:
+ **/
+INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPTSTR pszTitle, LPTSTR pszInfo, LPTSTR pszFormat, ...)
+{
+ MSGBOX mb = {0};
+ TCHAR tszMsg[MAX_SECONDLINE];
+ va_list vl;
+
+ va_start(vl, pszFormat);
+ mir_vsntprintf(tszMsg, SIZEOF(tszMsg), TranslateTS(pszFormat), vl);
+ va_end(vl);
+
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN));
+ mb.hiMsg = NULL;
+ mb.ptszTitle = TranslateTS(pszTitle);
+ mb.ptszInfoText = TranslateTS(pszInfo);
+ mb.ptszMsg = tszMsg;
+ mb.uType = uType;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
+
+/**
+ * name: MsgErr
+ * desc: calls a messagebox
+ * param:
+ **/
+INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...)
+{
+ MSGBOX mb = {0};
+ TCHAR tszTitle[MAX_SECONDLINE];
+ TCHAR tszMsg[MAX_SECONDLINE];
+ va_list vl;
+
+ mir_sntprintf(tszTitle, SIZEOF(tszMsg),_T("%s - %s") ,_T(MODNAME), TranslateT("Error"));
+
+ va_start(vl, pszFormat);
+ mir_vsntprintf(tszMsg, SIZEOF(tszMsg), TranslateTS(pszFormat), vl);
+ va_end(vl);
+
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN));
+ mb.hiMsg = NULL;
+ mb.ptszTitle = tszTitle;
+ mb.ptszMsg = tszMsg;
+ mb.uType = MB_OK|MB_ICON_ERROR;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.h b/plugins/UserInfoEx/src/dlg_msgbox.h
new file mode 100644
index 0000000000..6b01cd55bf
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_msgbox.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_msgbox.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_DLG_INCLUDED_
+#define _SVC_DLG_INCLUDED_ 1
+
+#define SET_POPUPMSGBOX "PopupMsgBox"
+#define DEFVAL_POPUPMSGBOX FALSE
+
+/* UserInfo/MsgBox v0.1.0.3+
+Some little changed message box for nicer look of miranda's messages or questions :-)
+wParam=hContact - can be null
+lParam=(_MSGBOX*)pMsg - structure that holds information about the look of the message dialog
+uType member of _MSGBOX can be a combination of the following values, where most of them are defined in winuser.h:
+MB_OK
+MB_OKCANCEL
+MB_YESALLNO
+MB_YESNO
+For valid icon values use one of the following MB_ICON_...
+Funktion returns: IDOK, IDYES, IDALL, IDNO or IDCANCEL
+*/
+
+/*
+ Defined in winuser.h
+ ********************
+
+#define MB_OK 0x00000000L
+#define MB_OKCANCEL 0x00000001L
+#define MB_ABORTRETRYIGNORE 0x00000002L
+#define MB_YESNOCANCEL 0x00000003L
+#define MB_YESNO 0x00000004L
+#define MB_RETRYCANCEL 0x00000005L
+*/
+#define MB_YESALLNO 0x00000007L
+#define MB_TYPE(p) ((p)&MB_TYPEMASK)
+
+/*
+valid predefined icon values
+*/
+#define MB_ICON_NONE 0x00000000L // 0 - no icon
+#define MB_ICON_ERROR 0x00000010L // 16 - error icon
+#define MB_ICON_QUESTION 0x00000020L // 32 - question mark
+#define MB_ICON_WARNING 0x00000030L // 48 - warning
+#define MB_ICON_INFO 0x00000040L // 64 - info
+#define MB_ICON_OTHER 0x00000080L // 240 - use icon _MSGBOX->hiMsg
+#define MB_ICON_INDEX(p) (((p)&MB_ICONMASK)>>4)
+
+/*
+flags
+*/
+#define MB_INFOBAR 0x00000100L
+#define MB_NOPOPUP 0x00000200L
+#define MB_CUSTOMCOLOR 0x00000300L
+
+typedef struct _MSGBOX
+{
+ UINT cbSize; // size of this structure
+ UINT uType; // parameters
+ HICON hiLogo; // right upper icon of the info bar
+ HICON hiMsg; // icon left next to the message text
+ LPTSTR ptszTitle;
+ LPTSTR ptszInfoText;
+ LPTSTR ptszMsg;
+ HWND hParent; // parent window for the messagebox
+ COLORREF colorBack; // valid if MB_CUSTOMCOLOR is set
+ COLORREF colorText; // valid if MB_CUSTOMCOLOR is set
+} MSGBOX, *LPMSGBOX;
+
+
+INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPTSTR pszTitle, LPTSTR pszInfo, LPTSTR pszFormat, ...);
+INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...);
+INT_PTR MsgBoxService(WPARAM wParam, LPARAM lParam);
+
+INT_PTR CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+#endif /* _SVC_DLG_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_propsheet.cpp b/plugins/UserInfoEx/src/dlg_propsheet.cpp
new file mode 100644
index 0000000000..eee049aea1
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_propsheet.cpp
@@ -0,0 +1,1867 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_propsheet.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System & local includes:
+ **/
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "psp_base.h"
+#include "svc_ExImport.h"
+#include "svc_reminder.h"
+
+/**
+ * Miranda includes:
+ **/
+#include <m_protocols.h>
+#include <m_icq.h>
+
+#define OPTIONPAGE_OLD_SIZE (offsetof(OPTIONSDIALOGPAGE, hLangpack))
+
+#define UPDATEANIMFRAMES 20
+
+// internal dialog message handler
+#define M_CHECKONLINE (WM_USER+10)
+#define HM_PROTOACK (WM_USER+11)
+#define HM_SETTING_CHANGED (WM_USER+12)
+#define HM_RELOADICONS (WM_USER+13)
+#define HM_SETWINDOWTITLE (WM_USER+14)
+
+#define TIMERID_UPDATING 1
+#ifndef TIMERID_RENAME
+ #define TIMERID_RENAME 2
+#endif
+
+// flags for the PS structure
+#define PSF_CHANGED 0x00000100
+#define PSF_LOCKED 0x00000200
+#define PSF_INITIALIZED 0x00000400
+
+#define INIT_ICONS_NONE 0
+#define INIT_ICONS_OWNER 1
+#define INIT_ICONS_CONTACT 2
+#define INIT_ICONS_ALL (INIT_ICONS_OWNER|INIT_ICONS_CONTACT)
+
+/***********************************************************************************************************
+ * internal variables
+ ***********************************************************************************************************/
+
+static BOOLEAN bInitIcons = INIT_ICONS_NONE;
+static HANDLE ghWindowList = NULL;
+static HANDLE ghDetailsInitEvent = NULL;
+
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+CPsHdr::CPsHdr() :
+_ignore(10, (LIST<TCHAR>::FTSortFunc)_tcscmp)
+{
+ _dwSize = sizeof(*this);
+ _hContact = NULL;
+ _pszProto = NULL;
+ _pszPrefix = NULL;
+ _pPages = NULL;
+ _numPages = 0;
+ _dwFlags = 0;
+ _hImages = NULL;
+}
+
+CPsHdr::~CPsHdr()
+{
+ INT i;
+
+ // delete data
+ for (i = 0 ; i < _ignore.getCount(); i++)
+ {
+ if (_ignore[i]) mir_free(_ignore[i]);
+ }
+ // delete the list
+ _ignore.destroy();
+}
+
+VOID CPsHdr::Free_pPages()
+{
+ for ( int i = 0 ; i < _numPages; i++)
+ if (_pPages[i]) delete _pPages[i];
+ _numPages = 0;
+ MIR_FREE(_pPages);
+}
+
+/***********************************************************************************************************
+ * class CPsUpload
+ ***********************************************************************************************************/
+
+class CPsUpload {
+public:
+ enum EPsUpReturn {
+ UPLOAD_CONTINUE = 0,
+ UPLOAD_FINISH = 1,
+ UPLOAD_FINISH_CLOSE = 2
+ };
+
+private:
+ PROTOCOLDESCRIPTOR **_pPd;
+ INT _numProto;
+ BOOLEAN _bExitAfterUploading;
+ HANDLE _hUploading;
+ LPPS _pPs;
+
+ /**
+ * @class Upload
+ * @class CPsUpload
+ * @desc start upload process for the current protocol
+ * @param none
+ * @return 0 on success
+ * @return 1 otherwise
+ **/
+ INT Upload()
+ {
+ CPsTreeItem *pti;
+
+ // check if icq is online
+ if (!IsProtoOnline((*_pPd)->szName)) {
+ TCHAR szMsg[MAX_PATH];
+ LPTSTR ptszProto;
+
+ ptszProto = mir_a2t((*_pPd)->szName);
+ mir_sntprintf(szMsg, SIZEOF(szMsg), TranslateT("Protocol '%s' is offline"), ptszProto);
+ mir_free(ptszProto);
+
+ MsgBox(_pPs->hDlg, MB_ICON_WARNING, TranslateT("Upload Details"), szMsg,
+ TranslateT("You are not currently connected to the ICQ network.\nYou must be online in order to update your information on the server.\n\nYour changes will be saved to database only."));
+ }
+ // start uploading process
+ else {
+ _hUploading = (HANDLE)CallProtoService((*_pPd)->szName, PS_CHANGEINFOEX, CIXT_FULL, NULL);
+ if (_hUploading && _hUploading != (HANDLE)CALLSERVICE_NOTFOUND) {
+ EnableWindow(_pPs->pTree->Window(), FALSE);
+ if (pti = _pPs->pTree->CurrentItem()) {
+ EnableWindow(pti->Wnd(), FALSE);
+ }
+ EnableWindow(GetDlgItem(_pPs->hDlg, IDOK), FALSE);
+ EnableWindow(GetDlgItem(_pPs->hDlg, IDAPPLY), FALSE);
+ mir_snprintf(_pPs->szUpdating, SIZEOF(_pPs->szUpdating), "%s (%s)", Translate("Uploading"), (*_pPd)->szName);
+ ShowWindow(GetDlgItem(_pPs->hDlg, TXT_UPDATING), SW_SHOW);
+ SetTimer(_pPs->hDlg, TIMERID_UPDATING, 100, NULL);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+public:
+ /**
+ * @name CPsUpload
+ * @class CPsUpload
+ * @desc retrieves the list of installed protocols and initializes the class
+ * @param pPs - the owning propertysheet
+ * @param bExitAfter - whether the dialog is to close after upload or not
+ * @return nothing
+ **/
+ CPsUpload(LPPS pPs, BOOLEAN bExitAfter)
+ {
+ _pPs = pPs;
+ _pPd = NULL;
+ _numProto = 0;
+ _hUploading = NULL;
+ _bExitAfterUploading = bExitAfter;
+ }
+
+ INT UploadFirst() {
+ // create a list of all protocols which support uploading contact information
+ if (CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&_numProto, (LPARAM)&_pPd)) {
+ return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH;
+ }
+ return UploadNext();
+ }
+
+ /**
+ * @name ~CPsUpload
+ * @class CPsUpload
+ * @desc clear pointer to the upload object
+ * @param none
+ * @return nothing
+ **/
+ ~CPsUpload()
+ { _pPs->pUpload = NULL; }
+
+ /**
+ * @name Handle
+ * @class CPsUpload
+ * @desc returns the handle of the current upload process
+ * @param none
+ * @return handle of the current upload process
+ **/
+ __inline HANDLE Handle() const
+ { return _hUploading; };
+
+ /**
+ * @name UploadNext
+ * @class CPsUpload
+ * @desc Search the next protocol which supports uploading contact information
+ * and start uploading. Delete the object if ready
+ * @param none
+ * @return nothing
+ **/
+ INT UploadNext()
+ {
+ CHAR str[MAXMODULELABELLENGTH];
+ while (_pPd && *_pPd && _numProto-- > 0) {
+ if ((*_pPd)->type == PROTOTYPE_PROTOCOL) {
+ mir_strncpy(str, (*_pPd)->szName, MAXMODULELABELLENGTH);
+ mir_strncat(str, PS_CHANGEINFOEX, MAXMODULELABELLENGTH);
+ if (ServiceExists(str) && !Upload()) {
+ _pPd++;
+ return UPLOAD_CONTINUE;
+ }
+ }
+ _pPd++;
+ }
+ return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH;
+ }
+};
+
+/***********************************************************************************************************
+ * propertysheet
+ ***********************************************************************************************************/
+
+/**
+ * @name SortProc()
+ * @desc used for sorting the tab pages
+ *
+ * @return -1 or 0 or 1
+ **/
+static INT SortProc(CPsTreeItem** item1, CPsTreeItem** item2)
+{
+ if (*item1 && *item2) {
+ if ((*item2)->Pos() > (*item1)->Pos()) return -1;
+ if ((*item2)->Pos() < (*item1)->Pos()) return 1;
+ }
+ return 0;
+}
+
+/**
+ * This service routine creates the DetailsDialog
+ * @param wParam - handle to contact
+ * @param lParam - not used
+ *
+ * @retval 0 on success
+ * @retval 1 on failure
+ **/
+static INT_PTR ShowDialog(WPARAM wParam, LPARAM lParam)
+{
+ HWND hWnd;
+
+ // update some cached settings
+ myGlobals.ShowPropsheetColours = DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, TRUE);
+ myGlobals.WantAeroAdaption = DB::Setting::GetByte(SET_PROPSHEET_AEROADAPTION, TRUE);
+
+ // allow only one dialog per user
+ if (hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam)) {
+ SetForegroundWindow(hWnd);
+ SetFocus(hWnd);
+ }
+ else {
+ CPsHdr psh;
+ POINT metrics;
+ BOOLEAN bScanMetaSubContacts = FALSE;
+ HICON hDefIcon;
+
+ // init the treeview options
+ if (DB::Setting::GetByte(SET_PROPSHEET_SORTITEMS, FALSE))
+ psh._dwFlags |= PSTVF_SORTTREE;
+ if (DB::Setting::GetByte(SET_PROPSHEET_GROUPS, TRUE))
+ psh._dwFlags |= PSTVF_GROUPS;
+ // create imagelist
+ metrics.x = GetSystemMetrics(SM_CXSMICON);
+ metrics.y = GetSystemMetrics(SM_CYSMICON);
+ if ((psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) == NULL) {
+ MsgErr(NULL, LPGENT("Creating the imagelist failed!"));
+ return 1;
+ }
+
+ hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT);
+ if (!hDefIcon)
+ {
+ hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0);
+ }
+ // add the default icon to imagelist
+ ImageList_AddIcon(psh._hImages, hDefIcon);
+
+ // init contact
+ psh._hContact = (HANDLE)wParam;
+ if (psh._hContact == NULL) {
+ // mark owner icons as initiated
+ bInitIcons |= INIT_ICONS_OWNER;
+ psh._pszProto = NULL;
+ psh._pszPrefix = NULL;
+ }
+ else {
+ // get contact's protocol
+ psh._pszPrefix = psh._pszProto = DB::Contact::Proto((HANDLE)wParam);
+ if (psh._pszProto == NULL) {
+ MsgErr(NULL, LPGENT("Could not find contact's protocol. Maybe it is not active!"));
+ return 1;
+ }
+ // prepare scanning for metacontact's subcontact's pages
+ if (bScanMetaSubContacts = DB::Module::IsMetaAndScan(psh._pszProto))
+ psh._dwFlags |= PSF_PROTOPAGESONLY_INIT;
+ }
+ // add the pages
+ NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, wParam);
+ if (!psh._pPages || !psh._numPages) {
+ MsgErr(NULL, LPGENT("No pages have been added. Canceling dialog creation!"));
+ return 1;
+ }
+ // metacontacts sub pages
+ if (bScanMetaSubContacts)
+ {
+ INT numSubs = DB::MetaContact::SubCount((HANDLE)wParam);
+
+ psh._dwFlags &= ~PSF_PROTOPAGESONLY_INIT;
+ psh._dwFlags |= PSF_PROTOPAGESONLY;
+ for (INT i = 0; i < numSubs; i++)
+ {
+ psh._hContact = DB::MetaContact::Sub((HANDLE)wParam, i);
+ psh._nSubContact = i;
+ if (psh._hContact)
+ {
+ psh._pszProto = DB::Contact::Proto(psh._hContact);
+ if ((INT_PTR)psh._pszProto != CALLSERVICE_NOTFOUND)
+ NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+ }
+ }
+ psh._hContact = (HANDLE)wParam;
+ }
+
+ // sort the pages by the position read from database
+ if (!(psh._dwFlags & PSTVF_SORTTREE)) {
+ qsort(psh._pPages, psh._numPages, sizeof(CPsTreeItem*),
+ (INT (*)(const VOID*, const VOID*))SortProc);
+ }
+ // create the dialog itself
+ hWnd = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_DETAILS), NULL, DlgProc, (LPARAM)&psh);
+ if (!hWnd) {
+ MsgErr(NULL, LPGENT("Details dialog failed to be created. Returning error is %d."), GetLastError());
+ }
+ else
+ MagneticWindows_AddWindow(hWnd);
+ }
+ return 0;
+}
+
+/**
+ * @name AddPage()
+ * @desc this adds a new pages
+ * @param wParam - The List of pages we want to add the new one to
+ * @param lParam - it's the page to add
+ *
+ * @return 0
+ **/
+static INT_PTR AddPage(WPARAM wParam, LPARAM lParam)
+{
+ CPsHdr *pPsh = (CPsHdr*)wParam;
+ OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)lParam;
+
+ // check size of the handled structures
+ if (pPsh == NULL || odp == NULL || pPsh->_dwSize != sizeof(CPsHdr))
+ return 1;
+
+ if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE) && odp->cbSize != OPTIONPAGE_OLD_SIZE) {
+ MsgErr(NULL, LPGENT("The Page to add has invalid size %d bytes!"), odp->cbSize);
+ return 1;
+ }
+
+ // try to check whether the flag member is initialized or not
+ odp->flags = odp->flags > (ODPF_UNICODE|ODPF_BOLDGROUPS|ODPF_ICON|PSPF_PROTOPREPENDED) ? 0 : odp->flags;
+
+ if (pPsh->_dwFlags & (PSF_PROTOPAGESONLY|PSF_PROTOPAGESONLY_INIT)) {
+ BOOLEAN bIsUnicode = (odp->flags & ODPF_UNICODE) == ODPF_UNICODE;
+ TCHAR* ptszTitle = bIsUnicode ? mir_tstrdup(odp->ptszTitle) : mir_a2t(odp->pszTitle);
+
+ // avoid adding pages for a meta subcontact, which have been added for a metacontact.
+ if (pPsh->_dwFlags & PSF_PROTOPAGESONLY) {
+ if (pPsh->_ignore.getIndex(ptszTitle) != -1) {
+ mir_free(ptszTitle);
+ return 0;
+ }
+ }
+ // init ignore list with pages added by metacontact
+ else if (pPsh->_dwFlags & PSF_PROTOPAGESONLY_INIT)
+ pPsh->_ignore.insert(mir_tstrdup(ptszTitle));
+
+ mir_free(ptszTitle);
+ }
+
+ // create the new tree item
+ CPsTreeItem *pNew = new CPsTreeItem();
+ if (pNew) {
+ if (pNew->Create(pPsh, odp)) {
+ MIR_DELETE(pNew);
+ return 1;
+ }
+
+ // resize the array
+ pPsh->_pPages = (CPsTreeItem**)mir_realloc(pPsh->_pPages, (pPsh->_numPages + 1) * sizeof(CPsTreeItem*));
+ if (pPsh->_pPages != NULL) {
+ pPsh->_pPages[pPsh->_numPages++] = pNew;
+ return 0;
+ }
+ pPsh->_numPages = 0;
+ }
+ return 1;
+}
+
+/**
+ * @name OnDeleteContact()
+ * @desc a user was deleted, so need to close its details dialog, if one open
+ *
+ * @return 0
+ **/
+static INT OnDeleteContact(WPARAM wParam, LPARAM lParam)
+{
+ HWND hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam);
+ if (hWnd != NULL)
+ DestroyWindow(hWnd);
+ return 0;
+}
+
+/**
+ * @name OnShutdown()
+ * @desc we need to emptify the windowlist
+ *
+ * @return 0
+ **/
+static INT OnShutdown(WPARAM wParam, LPARAM lParam)
+{
+ WindowList_BroadcastAsync(ghWindowList, WM_DESTROY, 0, 0);
+ return 0;
+}
+
+/**
+ * @name AddProtocolPages()
+ * @desc is called by Miranda if user selects to display the userinfo dialog
+ * @param odp - optiondialogpage structure to use
+ * @param wParam - the propertysheet init structure to pass
+ * @param pszProto - the protocol name to prepend as item name (can be NULL)
+ *
+ * @return 0
+ **/
+static INT AddProtocolPages(OPTIONSDIALOGPAGE& odp, WPARAM wParam, LPSTR pszProto = NULL)
+{
+ TCHAR szTitle[MAX_PATH];
+ const BYTE ofs = (pszProto) ? mir_sntprintf(szTitle, SIZEOF(szTitle), _T(TCHAR_STR_PARAM) _T("\\"), pszProto) : 0;
+
+ odp.ptszTitle = szTitle;
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_GENERAL);
+ odp.position = 0x8000000;
+ odp.pfnDlgProc = PSPProcGeneral;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_GENERAL);
+ mir_tcsncpy(szTitle + ofs, LPGENT("General"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS);
+ odp.position = 0x8000001;
+ odp.pfnDlgProc = PSPProcContactHome;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADDRESS);
+ mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Contact (private)"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ORIGIN);
+ odp.position = 0x8000002;
+ odp.pfnDlgProc = PSPProcOrigin;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADVANCED);
+ mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Origin"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ANNIVERSARY);
+ odp.position = 0x8000003;
+ odp.pfnDlgProc = PSPProcAnniversary;
+ odp.hIcon = (HICON)ICONINDEX(IDI_BIRTHDAY);
+ mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Anniversaries"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_COMPANY);
+ odp.position = 0x8000004;
+ odp.pfnDlgProc = PSPProcCompany;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_COMPANY);
+ mir_tcsncpy(szTitle + ofs, LPGENT("Work"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS);
+ odp.position = 0x8000005;
+ odp.pfnDlgProc = PSPProcContactWork;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADDRESS);
+ mir_tcsncpy(szTitle + ofs, LPGENT("Work") _T("\\") LPGENT("Contact (Work)"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ABOUT);
+ odp.position = 0x8000006;
+ odp.pfnDlgProc = PSPProcAbout;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ABOUT);
+ mir_tcsncpy(szTitle + ofs, LPGENT("About"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_PROFILE);
+ odp.position = 0x8000007;
+ odp.pfnDlgProc = PSPProcContactProfile;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_PROFILE);
+ mir_tcsncpy(szTitle + ofs, LPGENT("About") _T("\\") LPGENT("Profile"), SIZEOF(szTitle) - ofs);
+ AddPage(wParam, (LPARAM)&odp);
+ return 0;
+}
+
+/**
+ * @name InitDetails
+ * @desc is called by Miranda if user selects to display the userinfo dialog
+ * @param wParam - the propertysheet init structure to pass
+ * @param lParam - handle to contact whose information are read
+ *
+ * @return 0
+ **/
+static INT InitDetails(WPARAM wParam, LPARAM lParam)
+{
+ CPsHdr* pPsh = (CPsHdr*)wParam;
+
+ if (!(pPsh->_dwFlags & PSF_PROTOPAGESONLY)) {
+
+ OPTIONSDIALOGPAGE odp;
+ BOOLEAN bChangeDetailsEnabled = myGlobals.CanChangeDetails && DB::Setting::GetByte(SET_PROPSHEET_CHANGEMYDETAILS, FALSE);
+
+ // important to avoid craches!!
+ ZeroMemory(&odp, sizeof(odp));
+
+ if (lParam || bChangeDetailsEnabled) {
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = ghInst;
+ odp.flags = ODPF_ICON | ODPF_TCHAR;
+ odp.ptszGroup = IcoLib_GetDefaultIconFileName();
+
+ if (lParam) {
+
+ // ignore common pages for weather contacts
+ if (!pPsh->_pszProto || stricmp(pPsh->_pszProto, "weather"))
+ {
+ AddProtocolPages(odp, wParam);
+ odp.ptszTitle = LPGENT("About") _T("\\") LPGENT("Notes");
+ }
+ else {
+ odp.ptszTitle = LPGENT("Notes");
+ }
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ABOUT);
+ odp.position = 0x8000008;
+ odp.pfnDlgProc = PSPProcMyNotes;
+ odp.hIcon = (HICON)ICONINDEX(IDI_TREE_NOTES);
+ AddPage(wParam, (LPARAM)&odp);
+ }
+ /* Editing owner details no longer supported due to leak of common interface for all protocols.
+ else
+ if (!(pPsh->_dwFlags & PSTVF_INITICONS))
+ {
+ PROTOCOLDESCRIPTOR **pd;
+ INT ProtoCount, i;
+ CHAR str[MAXMODULELABELLENGTH];
+
+ odp.flags |= PSPF_PROTOPREPENDED;
+
+ // create a list of all protocols which support uploading contact information
+ if (!CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&pd)) {
+ for (i = 0; i < ProtoCount; i++) {
+ if (pd[i]->type == PROTOTYPE_PROTOCOL) {
+ pPsh->_pszProto = pd[i]->szName;
+ mir_snprintf(str, MAXMODULELABELLENGTH, "%s"PS_CHANGEINFOEX, pd[i]->szName);
+ if (ServiceExists(str)) AddProtocolPages(odp, wParam, pd[i]->szName);
+ }
+ }
+ }
+ }
+ */
+ }
+ }
+ return 0;
+}
+
+/**
+ * @name InitTreeIcons()
+ * @desc initalize all treeview icons
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID DlgContactInfoInitTreeIcons()
+{
+ // make sure this is run only once
+ if (!(bInitIcons & INIT_ICONS_ALL)) {
+ CPsHdr psh;
+ POINT metrics = {0};
+ INT i = 0;
+
+ psh._dwFlags = PSTVF_INITICONS;
+
+ metrics.x = GetSystemMetrics(SM_CXSMICON);
+ metrics.y = GetSystemMetrics(SM_CYSMICON);
+ if (psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) {
+ HICON hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT);
+ if (!hDefIcon)
+ {
+ hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0);
+ }
+ // add the default icon to imagelist
+ ImageList_AddIcon(psh._hImages, hDefIcon);
+ }
+
+ // avoid pages from loading doubled
+ if (!(bInitIcons & INIT_ICONS_CONTACT)) {
+ LPCSTR pszContactProto = NULL;
+ PROTOCOLDESCRIPTOR **pd;
+ INT ProtoCount = 0;
+
+ psh._dwFlags |= PSF_PROTOPAGESONLY_INIT;
+
+ // enumerate all protocols
+ if (!CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&pd)) {
+ for (i = 0; i < ProtoCount; i++) {
+ if (pd[i]->type == PROTOTYPE_PROTOCOL) {
+ // enumerate all contacts
+ for (psh._hContact = DB::Contact::FindFirst();
+ psh._hContact != NULL;
+ psh._hContact = DB::Contact::FindNext(psh._hContact))
+ {
+ // compare contact's protocol to the current one, to add
+ pszContactProto = DB::Contact::Proto(psh._hContact);
+ if ((INT_PTR)pszContactProto != CALLSERVICE_NOTFOUND && !mir_strcmp(pd[i]->szName, pszContactProto)) {
+ // call a notification for the contact to retrieve all protocol specific tree items
+ NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+ if (psh._pPages) {
+ psh.Free_pPages();
+ psh._dwFlags = PSTVF_INITICONS|PSF_PROTOPAGESONLY;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ bInitIcons |= INIT_ICONS_CONTACT;
+ }
+ // load all treeitems for owner contact
+ if (!(bInitIcons & INIT_ICONS_OWNER)) {
+ psh._hContact = NULL;
+ psh._pszProto = NULL;
+ NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+ if (psh._pPages) {
+ psh.Free_pPages();
+ }
+ bInitIcons |= INIT_ICONS_OWNER;
+ }
+ ImageList_Destroy(psh._hImages);
+ }
+}
+
+/**
+ * @name UnLoadModule()
+ * @desc unload the UserInfo Module
+ *
+ * @return nothing
+ **/
+VOID DlgContactInfoUnLoadModule()
+{
+ DestroyHookableEvent(ghDetailsInitEvent);
+}
+
+/**
+ * @name LoadModule()
+ * @desc load the UserInfo Module
+ *
+ * @return nothing
+ **/
+VOID DlgContactInfoLoadModule()
+{
+ ghDetailsInitEvent = CreateHookableEvent(ME_USERINFO_INITIALISE);
+
+ CreateServiceFunction(MS_USERINFO_SHOWDIALOG, ShowDialog);
+ CreateServiceFunction("UserInfo/AddPage", AddPage);
+
+ HookEvent(ME_DB_CONTACT_DELETED, OnDeleteContact);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown);
+ HookEvent(ME_USERINFO_INITIALISE, InitDetails);
+ ghWindowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+
+ // check whether changing my details via UserInfoEx is basically possible
+ {
+ PROTOACCOUNT **pAcc;
+ INT i, nAccCount;
+
+ myGlobals.CanChangeDetails = FALSE;
+ if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+ {
+ for (i = 0; (i < nAccCount) && !myGlobals.CanChangeDetails; i++)
+ {
+ if (IsProtoAccountEnabled(pAcc[i]))
+ {
+ // update my contact information on icq server
+ myGlobals.CanChangeDetails = MIREXISTS(CallProtoService(pAcc[i]->szModuleName, PS_CHANGEINFOEX, NULL, NULL));
+ }
+ }
+ }
+ }
+}
+
+static void ResetUpdateInfo(LPPS pPs)
+{
+ INT i;
+
+ // free the array of accomblished acks
+ for (i = 0; i < (INT)pPs->nSubContacts; i++)
+ MIR_FREE(pPs->infosUpdated[i].acks);
+ MIR_FREE(pPs->infosUpdated);
+ pPs->nSubContacts = 0;
+}
+
+/*
+============================================================================================
+ PropertySheet's Dialog Procedures
+============================================================================================
+*/
+
+/**
+ * @name DlgProc()
+ * @desc dialog procedure for the main propertysheet dialog box
+ *
+ * @return 0 or 1
+ **/
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LPPS pPs = (LPPS)GetUserData(hDlg);
+
+ // do not process any message if pPs is no longer existent
+ if (!PtrIsValid(pPs) && uMsg != WM_INITDIALOG)
+ return FALSE;
+
+ switch (uMsg)
+ {
+ /**
+ * @class WM_INITDIALOG
+ * @desc initiates all dialog controls
+ * @param wParam - not used
+ * lParam - pointer to a PSHDR structure, which contains all information to create the dialog
+ *
+ * @return TRUE if everything is ok, FALSE if dialog creation should fail
+ **/
+ case WM_INITDIALOG:
+ {
+ CPsHdr* pPsh = (CPsHdr*)lParam;
+ WORD needWidth = 0;
+ RECT rc;
+
+ if (!pPsh || pPsh->_dwSize != sizeof(CPsHdr))
+ return FALSE;
+
+ TranslateDialogDefault(hDlg);
+
+ //
+ // create data structures
+ //
+ if (!(pPs = (LPPS)mir_alloc(sizeof(PS))))
+ return FALSE;
+ ZeroMemory(pPs, sizeof(PS));
+
+ if (!(pPs->pTree = new CPsTree(pPs)))
+ return FALSE;
+ if (!(pPs->pTree->Create(GetDlgItem(hDlg, STATIC_TREE), pPsh))) {
+ return FALSE;
+ }
+ SetUserData(hDlg, pPs);
+ pPs->hDlg = hDlg;
+ pPs->dwFlags |= PSF_LOCKED;
+ pPs->hContact = pPsh->_hContact;
+ pPs->hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hDlg, HM_PROTOACK);
+ pPs->hSettingChanged = HookEventMessage(ME_DB_CONTACT_SETTINGCHANGED, hDlg, HM_SETTING_CHANGED);
+ pPs->hIconsChanged = HookEventMessage(ME_SKIN2_ICONSCHANGED, hDlg, HM_RELOADICONS);
+
+ ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode());
+ ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG2),!IsAeroMode());
+
+ //
+ // set icons
+ //
+ SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN)));
+ DlgProc(hDlg, HM_RELOADICONS, NULL, NULL);
+
+ //
+ // load basic protocol for current contact (for faster load later on and better handling for owner protocol)
+ //
+ if (pPs->hContact) mir_strncpy(pPs->pszProto, pPsh->_pszPrefix, MAXMODULELABELLENGTH);
+
+ // set the windowtitle
+ DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, NULL);
+
+ // translate Userinfo buttons
+ {
+ SendDlgItemMessage(hDlg, BTN_UPDATE, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDAPPLY, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, BTN_EXPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Export to file"), MBF_TCHAR);
+ SendDlgItemMessage(hDlg, BTN_IMPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Import from file"), MBF_TCHAR);
+ }
+
+ //
+ // set bold font for name in description area
+ //
+ {
+ LOGFONT lf;
+ HFONT hNormalFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
+
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfHeight = 22;
+ mir_tcscpy(lf.lfFaceName, _T("Segoe UI"));
+ pPs->hCaptionFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)pPs->hCaptionFont, 0);
+
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ pPs->hBoldFont = CreateFontIndirect(&lf);
+ }
+
+ //
+ // initialize the optionpages and tree control
+ //
+ if (!pPs->pTree->InitTreeItems((LPWORD)&needWidth))
+ return FALSE;
+
+ //
+ // move and resize dialog and its controls
+ //
+ {
+ HWND hCtrl;
+ RECT rcTree;
+ POINT pt = { 0, 0 };
+ INT addWidth = 0;
+
+ // at least add width of scrollbar
+ needWidth += 8 + GetSystemMetrics(SM_CXVSCROLL);
+
+ // get tree rectangle
+ GetWindowRect(hDlg, &pPs->rcDisplay);
+ GetWindowRect(pPs->pTree->Window(), &rcTree);
+ ClientToScreen(hDlg, &pt);
+ OffsetRect(&rcTree, -pt.x, -pt.y);
+
+ // calculate the amout of pixels to resize the dialog by?
+ if (needWidth > rcTree.right - rcTree.left) {
+ RECT rcMax = { 0, 0, 0, 0 };
+
+ rcMax.right = 280;
+ MapDialogRect(hDlg, &rcMax);
+ addWidth = min(needWidth, rcMax.right) - rcTree.right + rcTree.left;
+ rcTree.right += addWidth;
+ // resize tree
+ MoveWindow(pPs->pTree->Window(), rcTree.left, rcTree.top, rcTree.right - rcTree.left, rcTree.bottom - rcTree.top, FALSE);
+
+ pPs->rcDisplay.right += addWidth;
+ MoveWindow(hDlg, pPs->rcDisplay.left, pPs->rcDisplay.top,
+ pPs->rcDisplay.right - pPs->rcDisplay.left,
+ pPs->rcDisplay.bottom - pPs->rcDisplay.top, FALSE);
+
+ }
+ GetClientRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc);
+ // calculate dislpay area for pages
+ OffsetRect(&pPs->rcDisplay, -pt.x, -pt.y);
+ pPs->rcDisplay.bottom = rcTree.bottom;
+ pPs->rcDisplay.left = rcTree.right + 2;
+ pPs->rcDisplay.top = rcTree.top+rc.bottom;
+ pPs->rcDisplay.right -= 2;
+
+ // move and resize the rest of the controls
+ if (addWidth > 0) {
+ const WORD idResize[] = { IDC_HEADERBAR, STATIC_LINE2 };
+ const WORD idMove[] = { IDC_PAGETITLE, IDC_PAGETITLEBG, IDC_PAGETITLEBG2, IDOK, IDCANCEL, IDAPPLY };
+ WORD i;
+
+ for (i = 0; i < SIZEOF(idResize); i++) {
+ if (hCtrl = GetDlgItem(hDlg, idResize[i])) {
+ GetWindowRect(hCtrl, &rc);
+ OffsetRect(&rc, -pt.x, -pt.y);
+ MoveWindow(hCtrl, rc.left, rc.top, rc.right - rc.left + addWidth, rc.bottom - rc.top, FALSE);
+ }
+ }
+ for (i = 0; i < SIZEOF(idMove); i++) {
+ if (hCtrl = GetDlgItem(hDlg, idMove[i])) {
+ GetWindowRect(hCtrl, &rc);
+ OffsetRect(&rc, -pt.x, -pt.y);
+ MoveWindow(hCtrl, rc.left + addWidth, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+ }
+ }
+ }
+ // restore window position and add required size
+ Utils_RestoreWindowPositionNoSize(hDlg, NULL, MODNAME, "DetailsDlg");
+ }
+
+ //
+ // show the first propsheetpage
+ //
+ // finally add the dialog to the window list
+ WindowList_Add(ghWindowList, hDlg, pPs->hContact);
+
+ // show the dialog
+ pPs->dwFlags &= ~PSF_LOCKED;
+ pPs->dwFlags |= PSF_INITIALIZED;
+
+
+ //
+ // initialize the "updating" button and statustext and check for online status
+ //
+ pPs->updateAnimFrame = 0;
+ if (pPs->hContact && *pPs->pszProto)
+ {
+ GetDlgItemTextA(hDlg, TXT_UPDATING, pPs->szUpdating, SIZEOF(pPs->szUpdating));
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+
+ if (DlgProc(hDlg, M_CHECKONLINE, NULL, NULL))
+ {
+ DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(BTN_UPDATE, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, BTN_UPDATE));
+ }
+ }
+
+
+ {
+ INT nPage = pPs->pTree->CurrentItemIndex();
+ if (!pPs->pTree->IsIndexValid(nPage))
+ nPage = 0;
+ TreeView_Select(pPs->pTree->Window(), NULL, TVGN_CARET);
+ TreeView_Select(pPs->pTree->Window(), pPs->pTree->TreeItemHandle(nPage), TVGN_CARET);
+ ShowWindow(hDlg, SW_SHOW);
+ }
+ }
+ return TRUE;
+
+ /**
+ * @class WM_TIMER
+ * @desc is called to display the "updating" text in the status area
+ * @param wParam - not used
+ * lParam - not used
+ *
+ * @return always FALSE
+ **/
+ case WM_TIMER:
+ switch (wParam)
+ {
+ case TIMERID_UPDATING:
+ {
+ CHAR str[84];
+
+ mir_snprintf(str, SIZEOF(str), "%.*s%s%.*s", pPs->updateAnimFrame%10, ".........", pPs->szUpdating, pPs->updateAnimFrame%10, ".........");
+ SetDlgItemTextA(hDlg, TXT_UPDATING, str);
+ if (++pPs->updateAnimFrame == UPDATEANIMFRAMES)
+ pPs->updateAnimFrame = 0;
+ return FALSE;
+ }
+ }
+ break;
+
+ case 0x031E: /*WM_DWMCOMPOSITIONCHANGED:*/
+ {
+ ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode());
+ InvalidateRect(hDlg, NULL, TRUE);
+ }
+ break;
+
+ /**
+ * @class WM_CTLCOLORSTATIC
+ * @desc sets the colour of some of the dialog's static controls
+ * @param wParam - HWND of the contrls
+ * lParam - HDC for drawing
+ *
+ * @return StockObject
+ **/
+ case WM_CTLCOLORSTATIC:
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID))
+ {
+ case TXT_UPDATING:
+ {
+ COLORREF textCol, bgCol, newCol;
+ INT ratio;
+
+ textCol = GetSysColor(COLOR_BTNTEXT);
+ bgCol = GetSysColor(COLOR_3DFACE);
+ ratio = abs(UPDATEANIMFRAMES/2 - pPs->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);
+ case IDC_PAGETITLE:
+ case IDC_PAGETITLEBG:
+ {
+ if (IsAeroMode())
+ {
+ SetTextColor((HDC)wParam, RGB(0,90,180));
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+ else
+ {
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
+ return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
+ }
+ }
+ }
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetStockObject(NULL_BRUSH);
+
+ /**
+ * @class PSM_CHANGED
+ * @desc indicates the propertysheet and the current selected page as changed
+ * @param wParam - not used
+ * lParam - not used
+ *
+ * @return TRUE if successful FALSE if the dialog is locked at the moment
+ **/
+ case PSM_CHANGED:
+ if (!(pPs->dwFlags & PSF_LOCKED))
+ {
+ pPs->dwFlags |= PSF_CHANGED;
+ pPs->pTree->CurrentItem()->AddFlags(PSPF_CHANGED);
+ EnableWindow(GetDlgItem(hDlg, IDAPPLY), TRUE);
+ return TRUE;
+ }
+ break;
+
+ /**
+ * @class PSM_GETBOLDFONT
+ * @desc returns the bold font
+ * @param wParam - not used
+ * lParam - pointer to a HFONT, which takes the boldfont
+ *
+ * @return TRUE if successful, FALSE otherwise
+ **/
+ case PSM_GETBOLDFONT:
+ if (pPs->hBoldFont && lParam)
+ {
+ *(HFONT*)lParam = pPs->hBoldFont;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hBoldFont);
+ return TRUE;
+ }
+ *(HFONT*)lParam = NULL;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l);
+ break;
+
+ /**
+ * @class PSM_GETCONTACT
+ * @desc returns the handle to the contact, associated with this propertysheet
+ * @param wParam - index or -1 for current item
+ * lParam - pointer to a HANDLE, which takes the contact handle
+ *
+ * @return TRUE if successful, FALSE otherwise
+ **/
+ case PSM_GETCONTACT:
+ if (lParam)
+ {
+ CPsTreeItem *pti = ((INT)wParam != -1)
+ ? pPs->pTree->TreeItem((INT)wParam)
+ : pPs->pTree->CurrentItem();
+
+ // prefer to return the contact accociated with the current page
+ if (pti && pti->hContact() != INVALID_HANDLE_VALUE) {
+ *(HANDLE*)lParam = pti->hContact();
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->hContact());
+ return TRUE;
+ }
+
+ // return contact who owns the details dialog
+ if (pPs->hContact != INVALID_HANDLE_VALUE) {
+ *(HANDLE*)lParam = pPs->hContact;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hContact);
+ return TRUE;
+ }
+ *(HANDLE*)lParam = NULL;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, NULL);
+ }
+ break;
+
+ /**
+ * @class PSM_GETBASEPROTO
+ * @desc returns the basic protocol module for the associated contact
+ * @param wParam - index or -1 for current item
+ * lParam - pointer to a LPCSTR which takes the protocol string pointer
+ *
+ * @return TRUE if successful, FALSE otherwise
+ **/
+ case PSM_GETBASEPROTO:
+ if (lParam) {
+ CPsTreeItem *pti = ((INT)wParam != -1)
+ ? pPs->pTree->TreeItem((INT)wParam)
+ : pPs->pTree->CurrentItem();
+
+ if (pti && pti->Proto()) {
+ // return custom protocol for the current page
+ *(LPCSTR*)lParam = pti->Proto();
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Proto());
+ return TRUE;
+ }
+
+ if (*pPs->pszProto) {
+ // return global protocol
+ *(LPSTR*)lParam = pPs->pszProto;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->pszProto);
+ return TRUE;
+ }
+ }
+ *(LPCSTR*)lParam = NULL;
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l);
+ break;
+
+ /**
+ * @class PSM_ISLOCKED
+ * @desc returns the lock state of the propertysheetpage
+ * @param wParam - not used
+ * lParam - not used
+ *
+ * @return TRUE if propertysheet is locked, FALSE if not
+ **/
+ case PSM_ISLOCKED:
+ {
+ BOOLEAN bLocked = (pPs->dwFlags & PSF_LOCKED) == PSF_LOCKED;
+
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bLocked);
+ return bLocked;
+ }
+
+ /**
+ * @class PSM_FORCECHANGED
+ * @desc force all propertysheetpages to update their controls with new values from the database
+ * @param wParam - whether to replace changed settings too or not
+ * lParam - not used
+ *
+ * @return always FALSE
+ **/
+ case PSM_FORCECHANGED:
+ if (!(pPs->dwFlags & PSF_LOCKED)) {
+ BOOLEAN bChanged;
+
+ pPs->dwFlags |= PSF_LOCKED;
+ if (bChanged = pPs->pTree->OnInfoChanged())
+ pPs->dwFlags |= PSF_CHANGED;
+ else
+ pPs->dwFlags &= ~PSF_CHANGED;
+ pPs->dwFlags &= ~PSF_LOCKED;
+ EnableWindow(GetDlgItem(hDlg, IDAPPLY), bChanged);
+ }
+ break;
+
+ /**
+ * @class PSM_DLGMESSAGE
+ * @desc Sends a message to a specified propertysheetpage
+ * @param wParam - not used
+ * lParam - LPDLGCOMMAND structure, which contains information about the message to forward
+ *
+ * @return E_FAIL if the page was not found
+ **/
+ case PSM_DLGMESSAGE:
+ {
+ LPDLGCOMMAND pCmd = (LPDLGCOMMAND)lParam;
+ CPsTreeItem *pti;
+
+ if (pCmd && (pti = pPs->pTree->FindItemByResource(pCmd->hInst, pCmd->idDlg)) && pti->Wnd()) {
+ if (!pCmd->idDlgItem)
+ return SendMessage(pti->Wnd(), pCmd->uMsg, pCmd->wParam, pCmd->lParam);
+ else
+ return SendDlgItemMessage(pti->Wnd(), pCmd->idDlgItem, pCmd->uMsg, pCmd->wParam, pCmd->lParam);
+ }
+ return E_FAIL;
+ }
+
+ /**
+ * @class PSM_GETPAGEHWND
+ * @desc get the window handle for a specified propertysheetpage
+ * @param wParam - recource id of the dialog recource
+ * lParam - hinstance of the plugin, which created the dialog box
+ *
+ * @return TRUE if handle was found and dialog was created before, false otherwise
+ **/
+ case PSM_GETPAGEHWND:
+ {
+ CPsTreeItem *pti;
+ if (pti = pPs->pTree->FindItemByResource((HINSTANCE)lParam, wParam)) {
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Wnd());
+ return (pti->Wnd() != NULL);
+ }
+ }
+ return FALSE;
+
+ case PSM_ISAEROMODE:
+ {
+ BOOLEAN bIsAeroMode = IsAeroMode();
+ if (lParam)
+ {
+ *(BOOLEAN*)lParam = bIsAeroMode;
+ }
+ return (INT_PTR)bIsAeroMode;
+ }
+
+ /**
+ * @class HM_SETWINDOWTITLE
+ * @desc set the window title and text of the infobar
+ * @param wParam - not used
+ * lParam - DBCONTACTWRITESETTING structure if called by HM_SETTING_CHANGED message handler
+ *
+ * @return FALSE
+ **/
+ case HM_SETWINDOWTITLE:
+ {
+ LPCTSTR pszName = NULL;
+ TCHAR newTitle[MAX_PATH];
+ RECT rc;
+ POINT pt = { 0, 0 };
+ HWND hName = GetDlgItem(hDlg, TXT_NAME);
+ DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam;
+
+ if (!pPs->hContact)
+ pszName = TranslateT("Owner");
+ else
+ if (pdbcws && pdbcws->value.type == DBVT_TCHAR)
+ pszName = pdbcws->value.ptszVal;
+ else
+ pszName = DB::Contact::DisplayName(pPs->hContact);
+
+ SetWindowText(hName, pszName);
+ mir_sntprintf(newTitle, MAX_PATH, _T("%s - %s"), pszName, TranslateT("Edit Contact Information"));
+ SetWindowText(hDlg, newTitle);
+ mir_sntprintf(newTitle, MAX_PATH, _T("%s\n%s"), TranslateT("Edit Contact Information"), pszName);
+ SetDlgItemText(hDlg, IDC_HEADERBAR, newTitle);
+
+ // redraw the name control
+ ScreenToClient(hDlg, &pt);
+ GetWindowRect(hName, &rc);
+ OffsetRect(&rc, pt.x, pt.y);
+ InvalidateRect(hDlg, &rc, TRUE);
+ break;
+ }
+
+ /**
+ * @class HM_RELOADICONS
+ * @desc handles the changed icon event from the icolib plugin and reloads all icons
+ * @param wParam - not used
+ * lParam - not used
+ *
+ * @return FALSE
+ **/
+ case HM_RELOADICONS:
+ {
+ HWND hCtrl;
+ HICON hIcon;
+ const ICONCTRL idIcon[] = {
+ { ICO_DLG_DETAILS, STM_SETIMAGE, ICO_DLGLOGO },
+ { ICO_BTN_UPDATE, BM_SETIMAGE, BTN_UPDATE },
+ { ICO_BTN_OK, BM_SETIMAGE, IDOK },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL },
+ { ICO_BTN_APPLY, BM_SETIMAGE, IDAPPLY }
+ };
+
+ const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 1;
+
+ IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+ if (hCtrl = GetDlgItem(hDlg, BTN_IMPORT)) {
+ hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? _T("") : _T("I"));
+ }
+ if (hCtrl = GetDlgItem(hDlg, BTN_EXPORT)) {
+ hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? _T("") : _T("E"));
+ }
+ // update page icons
+ if (PtrIsValid(pPs) && (pPs->dwFlags & PSF_INITIALIZED))
+ pPs->pTree->OnIconsChanged();
+ break;
+ }
+
+ /**
+ * @class M_CHECKONLINE
+ * @desc determines whether miranda is online or not
+ * @param wParam - not used
+ * lParam - not used
+ *
+ * @return TRUE if online, FALSE if offline
+ **/
+ case M_CHECKONLINE:
+ {
+ if (IsProtoOnline(pPs->pszProto))
+ {
+ EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), !IsWindowVisible(GetDlgItem(hDlg, TXT_UPDATING)));
+ return TRUE;
+ }
+
+ EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+ EnableWindow(GetDlgItem(hDlg, TXT_UPDATING), FALSE);
+ break;
+ }
+
+ /**
+ * @class HM_PROTOACK
+ * @desc handles all acks from the protocol plugin
+ * @param wParam - not used
+ * lParam - pointer to a ACKDATA structure
+ *
+ * @return FALSE
+ **/
+ case HM_PROTOACK:
+ {
+ ACKDATA *ack = (ACKDATA*)lParam;
+ INT i, iSubContact;
+ CPsTreeItem *pti;
+
+ if (!ack->hContact && ack->type == ACKTYPE_STATUS)
+ return DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+
+ switch (ack->type) {
+
+ case ACKTYPE_SETINFO:
+ {
+ if (ack->hContact != pPs->hContact || !pPs->pUpload || pPs->pUpload->Handle() != ack->hProcess)
+ break;
+ if (ack->result == ACKRESULT_SUCCESS) {
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+ KillTimer(hDlg, TIMERID_UPDATING);
+ // upload next protocols contact information
+ switch (pPs->pUpload->UploadNext()) {
+ case CPsUpload::UPLOAD_FINISH_CLOSE:
+ MIR_DELETE(pPs->pUpload);
+ DestroyWindow(hDlg);
+ case CPsUpload::UPLOAD_CONTINUE:
+ return FALSE;
+ case CPsUpload::UPLOAD_FINISH:
+ MIR_DELETE(pPs->pUpload);
+ break;
+ }
+ DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+ EnableWindow(pPs->pTree->Window(), TRUE);
+ if (pti = pPs->pTree->CurrentItem()) {
+ EnableWindow(pti->Wnd(), TRUE);
+ }
+ EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+ pPs->dwFlags &= ~PSF_LOCKED;
+ }
+ else
+ if (ack->result == ACKRESULT_FAILED) {
+ MsgBox(hDlg, MB_ICON_WARNING,
+ LPGENT("Upload ICQ Details"),
+ LPGENT("Upload failed"),
+ LPGENT("Your details were not uploaded successfully.\nThey were written to database only."));
+ KillTimer(hDlg, TIMERID_UPDATING);
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+ DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+
+ // upload next protocols contact information
+ switch (pPs->pUpload->UploadNext()) {
+ case CPsUpload::UPLOAD_FINISH_CLOSE:
+ MIR_DELETE(pPs->pUpload);
+ DestroyWindow(hDlg);
+ case CPsUpload::UPLOAD_CONTINUE:
+ return 0;
+ case CPsUpload::UPLOAD_FINISH:
+ MIR_DELETE(pPs->pUpload);
+ break;
+ }
+ if (pti = pPs->pTree->CurrentItem()) {
+ EnableWindow(pti->Wnd(), TRUE);
+ }
+ // activate all controls again
+ EnableWindow(pPs->pTree->Window(), TRUE);
+ EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+ pPs->dwFlags &= ~PSF_LOCKED;
+ }
+ break;
+ }
+
+ case ACKTYPE_GETINFO:
+
+ // is contact the owner of the dialog or any metasubcontact of the owner? skip handling otherwise!
+ if (ack->hContact != pPs->hContact) {
+
+ if (!myGlobals.szMetaProto)
+ break;
+
+ if (!DB::Setting::GetByte(SET_META_SCAN, TRUE))
+ break;
+
+ for (i = 0; i < pPs->nSubContacts; i++) {
+ if (pPs->infosUpdated[i].hContact == ack->hContact) {
+ iSubContact = i;
+ break;
+ }
+ }
+ if (i == pPs->nSubContacts)
+ break;
+ }
+ else {
+ iSubContact = 0;
+ }
+
+ // if they're not gonna send any more ACK's don't let that mean we should crash
+ if (!pPs->infosUpdated || (!ack->hProcess && !ack->lParam)) {
+ ResetUpdateInfo(pPs);
+
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+ KillTimer(hDlg, TIMERID_UPDATING);
+ DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+ break;
+ }
+
+ if (iSubContact < pPs->nSubContacts) {
+
+ // init the acks structure for a sub contact
+ if (pPs->infosUpdated[iSubContact].acks == NULL) {
+ pPs->infosUpdated[iSubContact].acks = (LPINT)mir_calloc(sizeof(INT) * (INT)(INT_PTR)ack->hProcess);
+ pPs->infosUpdated[iSubContact].count = (INT)(INT_PTR)ack->hProcess;
+ }
+
+ if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
+ pPs->infosUpdated[iSubContact].acks[ack->lParam] = 1;
+
+ // check for pending tasks
+ for (iSubContact = 0; iSubContact < pPs->nSubContacts; iSubContact++) {
+ for (i = 0; i < pPs->infosUpdated[iSubContact].count; i++) {
+ if (pPs->infosUpdated[iSubContact].acks[i] == 0)
+ break;
+ }
+ if (i < pPs->infosUpdated[iSubContact].count)
+ break;
+ }
+ }
+
+ // all acks are done, finish updating
+ if (iSubContact >= pPs->nSubContacts) {
+ ResetUpdateInfo(pPs);
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+ KillTimer(hDlg, TIMERID_UPDATING);
+ DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+ }
+ }
+ break;
+ }
+
+ /**
+ * @class HM_SETTING_CHANGED
+ * @desc This message is called by the ME_DB_CONTACT_SETTINGCHANGED event and forces all
+ * unedited settings in the propertysheetpages to be updated
+ * @param wParam - handle to the contact whose settings are to be changed
+ * lParam - DBCONTACTWRITESETTING structure that identifies the changed setting
+ * @return FALSE
+ **/
+ case HM_SETTING_CHANGED:
+ if (!(pPs->dwFlags & PSF_LOCKED)) {
+ HANDLE hContact = (HANDLE)wParam;
+ DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam;
+
+ if (hContact != pPs->hContact) {
+ if (!myGlobals.szMetaProto)
+ break;
+ if (pPs->hContact != (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, NULL))
+ break;
+ if (!DB::Setting::GetByte(SET_META_SCAN, TRUE))
+ break;
+ }
+
+ if (
+ !mir_stricmp(pdbcws->szSetting, SET_CONTACT_MYHANDLE) ||
+ !mir_stricmp(pdbcws->szSetting, SET_CONTACT_NICK)
+ )
+ {
+ // force the update of all propertysheetpages
+ DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL);
+ // update the windowtitle
+ DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, lParam);
+ }
+ else if (
+ !mir_stricmp(pdbcws->szModule, USERINFO) ||
+ !mir_stricmp(pdbcws->szModule, pPs->pszProto) ||
+ !mir_stricmp(pdbcws->szModule, MOD_MBIRTHDAY)
+ )
+ {
+ // force the update of all propertysheetpages
+ DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (wParam) {
+
+ //
+ // Notification Messages sent by the TreeView
+ //
+ case STATIC_TREE:
+ switch (((LPNMHDR)lParam)->code) {
+ case TVN_SELCHANGING:
+ {
+ pPs->dwFlags |= PSF_LOCKED;
+ pPs->pTree->OnSelChanging();
+ pPs->dwFlags &= ~PSF_LOCKED;
+ break;
+ }
+
+ case TVN_SELCHANGED:
+ if (pPs->dwFlags & PSF_INITIALIZED) {
+ pPs->dwFlags |= PSF_LOCKED;
+ pPs->pTree->OnSelChanged((LPNMTREEVIEW)lParam);
+ if (pPs->pTree->CurrentItem())
+ {
+ RECT rc;
+ POINT pt = { 0, 0 };
+
+ GetWindowRect(GetDlgItem(hDlg, IDC_PAGETITLE), &rc);
+ ScreenToClient(hDlg, &pt);
+ OffsetRect(&rc, pt.x, pt.y);
+ SetDlgItemText(hDlg, IDC_PAGETITLE, pPs->pTree->CurrentItem()->Label());
+ InvalidateRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc, TRUE);
+ InvalidateRect(hDlg, &rc, TRUE);
+ }
+ pPs->dwFlags &= ~PSF_LOCKED;
+ }
+ break;
+
+ case TVN_BEGINDRAG:
+ {
+ LPNMTREEVIEW nmtv = (LPNMTREEVIEW)lParam;
+
+ if (nmtv->itemNew.hItem == TreeView_GetSelection(nmtv->hdr.hwndFrom)) {
+ SetCapture(hDlg);
+ pPs->pTree->BeginDrag(nmtv->itemNew.hItem);
+ }
+ TreeView_SelectItem(nmtv->hdr.hwndFrom, nmtv->itemNew.hItem);
+ break;
+ }
+
+ case TVN_ITEMEXPANDED:
+ pPs->pTree->AddFlags(PSTVF_STATE_CHANGED);
+ break;
+
+ case NM_KILLFOCUS:
+ KillTimer(hDlg, TIMERID_RENAME);
+ break;
+
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+
+ GetCursorPos(&hti.pt);
+ ScreenToClient(pPs->pTree->Window(), &hti.pt);
+ TreeView_HitTest(pPs->pTree->Window(), &hti);
+ if ((hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) && hti.hItem == TreeView_GetSelection(pPs->pTree->Window()))
+ SetTimer(hDlg, TIMERID_RENAME, 500, NULL);
+ break;
+ }
+
+ case NM_RCLICK:
+ pPs->pTree->PopupMenu();
+ return 0;
+
+ }
+ break;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (pPs->pTree->IsDragging()) {
+ TVHITTESTINFO hti;
+
+ hti.pt.x = (SHORT)LOWORD(lParam);
+ hti.pt.y = (SHORT)HIWORD(lParam);
+ MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1);
+ TreeView_HitTest(pPs->pTree->Window(), &hti);
+
+ if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ RECT rc;
+ BYTE height;
+
+ // check where over the item, the pointer is
+ if (TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) {
+ height = (BYTE)(rc.bottom - rc.top);
+
+ if (hti.pt.y - (height / 3) < rc.top) {
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 0);
+ }
+ else
+ if (hti.pt.y + (height / 3) > rc.bottom) {
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 1);
+ }
+ else {
+ TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+ SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(CURSOR_ADDGROUP)));
+ }
+ }
+ }
+ else {
+ if (hti.flags & TVHT_ABOVE) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
+ if (hti.flags & TVHT_BELOW) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
+ TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+
+ // drop item
+ if (pPs->pTree->IsDragging()) {
+ TVHITTESTINFO hti;
+ RECT rc;
+ BYTE height;
+ BOOLEAN bAsChild = FALSE;
+
+ TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+ ReleaseCapture();
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ hti.pt.x = (SHORT)LOWORD(lParam);
+ hti.pt.y = (SHORT)HIWORD(lParam);
+ MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1);
+ TreeView_HitTest(pPs->pTree->Window(), &hti);
+
+ if (hti.hItem == pPs->pTree->DragItem()) {
+ pPs->pTree->EndDrag();
+ break;
+ }
+
+ if (hti.flags & TVHT_ABOVE) {
+ hti.hItem = TVI_FIRST;
+ }
+ else
+ if (hti.flags & (TVHT_NOWHERE|TVHT_BELOW)) {
+ hti.hItem = TVI_LAST;
+ }
+ else
+ if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+ // check where over the item, the pointer is
+ if (!TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) {
+ pPs->pTree->EndDrag();
+ break;
+ }
+ height = (BYTE)(rc.bottom - rc.top);
+
+ if (hti.pt.y - (height / 3) < rc.top) {
+ HTREEITEM hItem = hti.hItem;
+
+ if (!(hti.hItem = TreeView_GetPrevSibling(pPs->pTree->Window(), hItem))) {
+ if (!(hti.hItem = TreeView_GetParent(pPs->pTree->Window(), hItem)))
+ hti.hItem = TVI_FIRST;
+ else
+ bAsChild = TRUE;
+ }
+ }
+ else
+ if (hti.pt.y + (height / 3) <= rc.bottom) {
+ bAsChild = TRUE;
+ }
+ }
+ pPs->pTree->MoveItem(pPs->pTree->DragItem(), hti.hItem, bAsChild);
+ pPs->pTree->EndDrag();
+
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ {
+ pPs->pTree->OnCancel();
+ DestroyWindow(hDlg);
+ break;
+ }
+
+ /**
+ * name: IDOK / IDAPPLY
+ * desc: user clicked on apply or ok button in order to save changes
+ **/
+ case IDOK:
+ case IDAPPLY:
+ if (pPs->dwFlags & PSF_CHANGED) {
+ // kill focus from children to make sure all data can be saved (ComboboxEx)
+ SetFocus(hDlg);
+
+ pPs->dwFlags |= PSF_LOCKED;
+ if (pPs->pTree->OnApply()) {
+ pPs->dwFlags &= ~(PSF_LOCKED|PSF_CHANGED);
+ break;
+ }
+
+ pPs->dwFlags &= ~PSF_CHANGED;
+ EnableWindow(GetDlgItem(hDlg, IDAPPLY), FALSE);
+ CallService(MS_CLIST_INVALIDATEDISPLAYNAME, (WPARAM)pPs->hContact, NULL);
+
+ // need to upload owners settings
+ if (!pPs->hContact && myGlobals.CanChangeDetails && DB::Setting::GetByte(SET_PROPSHEET_CHANGEMYDETAILS, FALSE)) {
+ if (pPs->pUpload = new CPsUpload(pPs, LOWORD(wParam) == IDOK)) {
+ if (pPs->pUpload->UploadFirst() == CPsUpload::UPLOAD_CONTINUE)
+ break;
+ MIR_DELETE(pPs->pUpload);
+ }
+ }
+ pPs->dwFlags &= ~PSF_LOCKED;
+ }
+ if (LOWORD(wParam) == IDOK)
+ DestroyWindow(hDlg);
+ break;
+
+ case BTN_UPDATE:
+ {
+ if (pPs->hContact != NULL)
+ {
+ ResetUpdateInfo(pPs);
+
+ mir_snprintf(pPs->szUpdating, SIZEOF(pPs->szUpdating),
+ "%s (%s)", Translate("Updating"), pPs->pszProto);
+
+ // need meta contact's subcontact information
+ if (DB::Module::IsMetaAndScan(pPs->pszProto))
+ {
+ HANDLE hSubContact;
+ CHAR szService[MAXSETTING];
+ INT i, numSubs;
+
+ numSubs = DB::MetaContact::SubCount(pPs->hContact);
+
+ // count valid subcontacts whose protocol supports the PSS_GETINFO service to update the information
+ for (i = 0; i < numSubs; i++)
+ {
+ hSubContact = DB::MetaContact::Sub(pPs->hContact, i);
+ if (hSubContact != NULL)
+ {
+ mir_snprintf(szService, SIZEOF(szService), "%s%s",
+ DB::Contact::Proto(hSubContact), PSS_GETINFO);
+
+ if (ServiceExists(szService))
+ {
+ pPs->infosUpdated = (TAckInfo*)mir_realloc(pPs->infosUpdated, sizeof(TAckInfo) * (pPs->nSubContacts + 1));
+ pPs->infosUpdated[pPs->nSubContacts].hContact = hSubContact;
+ pPs->infosUpdated[pPs->nSubContacts].acks = NULL;
+ pPs->infosUpdated[pPs->nSubContacts].count = 0;
+ pPs->nSubContacts++;
+ }
+ }
+ }
+
+ if (pPs->nSubContacts != 0)
+ {
+ BOOLEAN bDo = FALSE;
+
+ // call the services
+ for (i = 0; i < pPs->nSubContacts; i++)
+ {
+ if (!CallContactService(pPs->infosUpdated[pPs->nSubContacts].hContact, PSS_GETINFO, NULL, NULL))
+ {
+ bDo = TRUE;
+ }
+ }
+ if (bDo)
+ {
+ EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW);
+ SetTimer(hDlg, TIMERID_UPDATING, 100, NULL);
+ }
+ }
+ }
+ else if (!CallContactService(pPs->hContact, PSS_GETINFO, NULL, NULL))
+ {
+ pPs->infosUpdated = (TAckInfo*)mir_calloc(sizeof(TAckInfo));
+ pPs->infosUpdated[0].hContact = pPs->hContact;
+ pPs->nSubContacts = 1;
+
+ EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+ ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW);
+ SetTimer(hDlg, TIMERID_UPDATING, 100, NULL);
+ }
+ }
+ }
+ break;
+
+ case BTN_IMPORT:
+ svcExIm_ContactImport_Service((WPARAM) pPs->hContact, 0);
+ break;
+
+ case BTN_EXPORT:
+ // save changes before exporting data
+ DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDAPPLY, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDAPPLY));
+ // do the exporting stuff
+ svcExIm_ContactExport_Service((WPARAM) pPs->hContact, 0);
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDCANCEL));
+ break;
+
+ case WM_DESTROY:
+ {
+ INT i = 0;
+
+ // hide before destroy
+ ShowWindow(hDlg, SW_HIDE);
+
+ ResetUpdateInfo(pPs);
+
+ // avoid any further message processing for this dialog page
+ WindowList_Remove(ghWindowList, hDlg);
+ SetUserData(hDlg, NULL);
+
+ // unhook events and stop timers
+ KillTimer(hDlg, TIMERID_RENAME);
+ UnhookEvent(pPs->hProtoAckEvent);
+ UnhookEvent(pPs->hSettingChanged);
+ UnhookEvent(pPs->hIconsChanged);
+
+ // save my window position
+ Utils_SaveWindowPosition(hDlg, NULL, MODNAME, "DetailsDlg");
+
+ // save current tree and destroy it
+ if (pPs->pTree != NULL) {
+ // save tree's current look
+ pPs->pTree->SaveState();
+ delete pPs->pTree;
+ pPs->pTree = NULL;
+ }
+
+ DeleteObject(pPs->hCaptionFont);
+ DeleteObject(pPs->hBoldFont);
+ mir_free(pPs); pPs = NULL;
+ MagneticWindows_RemoveWindow(hDlg);
+ }
+ }
+ return FALSE;
+}
+
diff --git a/plugins/UserInfoEx/src/dlg_propsheet.h b/plugins/UserInfoEx/src/dlg_propsheet.h
new file mode 100644
index 0000000000..622eb8fa33
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_propsheet.h
@@ -0,0 +1,281 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_propsheet.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+typedef struct TPropSheet PS, *LPPS;
+
+class CPsHdr;
+class CPsTree;
+
+/***********************************************************************************************************
+ * Tree item
+ ***********************************************************************************************************/
+
+class CPsTreeItem
+{
+ INT _idDlg; // resource id of the property page
+ LPDLGTEMPLATE _pTemplate; // locked template for the property page
+ HINSTANCE _hInst; // instance handle of the owning plugin dll
+ DLGPROC _pfnDlgProc; // dialog procedure for the property page
+ HWND _hWnd; // window handle for the property page if shown jet
+ DWORD _dwFlags; // some flags
+ INT _iPosition; // initiating position if custom (used for sorting)
+ LPARAM _initParam;
+ HANDLE _hContact; // contact the page is accociated with (may be a meta subcontact if details dialog is shown for a meta contact)
+ LPCSTR _pszProto; // protocol the page is accociated with (is the contact's protocol if _hContact is not NULL)
+ LPCSTR _pszPrefix; // pointer to the dialog owning contact's protocol
+
+ HTREEITEM _hItem; // handle to the treeview item if visible (NULL if this item is hidden)
+ INT _iParent; // index of the owning tree item
+ INT _iImage; // index of treeview item's image
+ BYTE _bState; // initial state of this treeitem
+ LPSTR _pszName; // original name, given by plugin (not customized)
+ LPTSTR _ptszLabel; // string to setting in db holding information about this treeitem
+
+ LPCSTR GlobalName();
+
+ INT Name(LPTSTR pszTitle, const BOOLEAN bIsUnicode);
+ INT ItemLabel(const BOOLEAN bReadDBValue);
+
+ HICON ProtoIcon();
+ INT Icon(HIMAGELIST hIml, OPTIONSDIALOGPAGE *odp, BOOLEAN bInitIconsOnly);
+
+public:
+ CPsTreeItem();
+ ~CPsTreeItem();
+ INT Create(CPsHdr* pPsh, OPTIONSDIALOGPAGE *odp);
+
+ __inline LPSTR Name() const { return _pszName; };
+ __inline LPCSTR Proto() const { return _pszProto; };
+ __inline LPTSTR Label() const { return _ptszLabel; };
+ VOID Rename( const LPTSTR pszLabel );
+ __inline HANDLE hContact() const { return _hContact; };
+
+ __inline HWND Wnd() const { return _hWnd; };
+ __inline INT DlgId() const { return _idDlg; };
+ __inline HINSTANCE Inst() const { return _hInst; };
+
+ __inline INT Image() const { return _iImage; };
+ __inline INT Pos() const { return _iPosition; };
+ __inline BYTE State() const { return _bState; };
+ __inline HTREEITEM Hti() const { return _hItem; };
+ __inline VOID Hti(HTREEITEM hti) { _hItem = hti; };
+ __inline INT Parent() const { return _iParent; };
+ __inline VOID Parent(const INT iParent) { _iParent = iParent; };
+
+ __inline DWORD Flags() const { return _dwFlags; };
+ __inline VOID Flags(DWORD dwFlags) { _dwFlags = dwFlags; };
+ __inline VOID AddFlags(DWORD dwFlags) { _dwFlags |= dwFlags; };
+ __inline VOID RemoveFlags(DWORD dwFlags) { _dwFlags &= ~dwFlags; };
+
+ BOOLEAN HasName(const LPCSTR pszName) const;
+
+ LPCSTR PropertyKey(LPCSTR pszProperty);
+ LPCSTR GlobalPropertyKey(LPCSTR pszProperty);
+ LPCSTR IconKey();
+
+ LPSTR ParentItemName();
+ HWND CreateWnd(LPPS pPs);
+
+ WORD DBSaveItemState(LPCSTR pszGroup, INT iItemPosition, UINT iState, DWORD dwFlags);
+
+ // notification handlers
+ VOID OnInfoChanged();
+ VOID OnPageIconsChanged();
+ VOID OnIconsChanged(CPsTree *pTree);
+};
+
+
+/***********************************************************************************************************
+ * Tree control
+ ***********************************************************************************************************/
+
+#define MAX_TINAME 64
+
+// internal setting strings
+#define SET_LASTITEM "LastItem"
+#define SET_ITEM_LABEL "label"
+#define SET_ITEM_GROUP "group"
+#define SET_ITEM_STATE "state"
+#define SET_ITEM_POS "pos"
+#define TREE_ROOTITEM "<ROOT>"
+
+// treeview states
+#define DBTVIS_INVISIBLE 0
+#define DBTVIS_NORMAL 1
+#define DBTVIS_EXPANDED 2
+
+// flags for the treeview
+#define PSTVF_SORTTREE 0x00010000
+#define PSTVF_GROUPS 0x00020000
+#define PSTVF_LABEL_CHANGED 0x00040000
+#define PSTVF_POS_CHANGED 0x00080000
+#define PSTVF_STATE_CHANGED 0x00100000
+#define PSTVF_INITICONS 0x00200000 // flag that indicates the AddPage handler to load only basic stuff
+
+class CPsTree {
+ HWND _hWndTree;
+ HIMAGELIST _hImages;
+ CPsTreeItem** _pItems;
+ INT _curItem;
+ INT _numItems;
+ DWORD _dwFlags;
+ HWND _hLabelEdit;
+ HTREEITEM _hDragItem;
+ BOOLEAN _isDragging;
+ LPPS _pPs;
+
+ WORD SaveItemsState(LPCSTR pszGroup, HTREEITEM hRootItem, INT& iItem);
+
+public:
+ CPsTree(LPPS pPs);
+ ~CPsTree();
+
+ __inline VOID BeginDrag(HTREEITEM hDragItem) { _isDragging = TRUE; _hDragItem = hDragItem; };
+ __inline VOID EndDrag() { _isDragging = FALSE; _hDragItem = NULL; };
+ __inline BOOLEAN IsDragging() const { return _isDragging; };
+ __inline HTREEITEM DragItem() const { return _hDragItem; };
+
+ __inline DWORD Flags() const { return _dwFlags; };
+ __inline VOID Flags(DWORD dwFlags) { _dwFlags = dwFlags; };
+ __inline VOID AddFlags(DWORD dwFlags) { _dwFlags |= dwFlags; };
+ __inline VOID RemoveFlags(DWORD dwFlags) { _dwFlags &= ~dwFlags; };
+
+ __inline INT NumItems() const { return _numItems; };
+ __inline HWND Window() const { return _hWndTree; };
+ __inline HIMAGELIST ImageList() const { return _hImages; };
+ __inline BOOLEAN IsIndexValid(const INT index) const { return (index >= 0 && index < _numItems); };
+
+ __inline CPsTreeItem* TreeItem(INT index) const { return (IsIndexValid(index) ? _pItems[index] : NULL); };
+ __inline HTREEITEM TreeItemHandle(INT index) const { return (IsIndexValid(index) ? _pItems[index]->Hti() : NULL); };
+
+ __inline INT CurrentItemIndex() const { return _curItem; };
+ __inline CPsTreeItem* CurrentItem() const { return TreeItem(CurrentItemIndex()); };
+
+ INT AddDummyItem(LPCSTR pszGroup);
+ BOOLEAN Create(HWND hWndTree, CPsHdr* pPsh);
+ BOOLEAN InitTreeItems(LPWORD needWidth);
+
+ VOID HideItem(const INT iPageIndex);
+ HTREEITEM ShowItem(const INT iPageIndex, LPWORD needWidth);
+
+ HTREEITEM MoveItem(HTREEITEM hItem, HTREEITEM hInsertAfter, BOOLEAN bAsChild = FALSE);
+ VOID SaveState();
+ VOID DBResetState();
+
+ INT FindItemIndexByHandle(HTREEITEM hItem);
+ INT FindItemIndexByName(LPCSTR pszName);
+
+ CPsTreeItem* FindItemByName(LPCSTR pszName);
+ CPsTreeItem* FindItemByHandle(HTREEITEM hItem);
+ HTREEITEM FindItemHandleByName(LPCSTR pszName);
+ CPsTreeItem* FindItemByResource(HINSTANCE hInst, INT idDlg);
+
+ INT BeginLabelEdit( HTREEITEM hItem );
+ INT EndLabelEdit( const BOOLEAN bSave );
+ VOID PopupMenu();
+
+ VOID OnIconsChanged();
+ BOOLEAN OnInfoChanged();
+ BOOLEAN OnSelChanging();
+ VOID OnSelChanged(LPNMTREEVIEW lpnmtv);
+ VOID OnCancel();
+ INT OnApply();
+};
+
+/***********************************************************************************************************
+ * common stuff
+ ***********************************************************************************************************/
+
+// internal flags for the PSP structure
+#define PSPF_CHANGED 4096
+#define PSPF_PROTOPREPENDED 8192 // the first token of the title is the protocol
+
+#define PSF_PROTOPAGESONLY 64 // load only contact's protocol pages
+#define PSF_PROTOPAGESONLY_INIT 128
+
+class CPsUpload;
+
+class CPsHdr
+{
+public:
+ DWORD _dwSize; // size of this class in bytes
+ HANDLE _hContact; // handle to the owning contact
+ LPCSTR _pszProto; // owning contact's protocol
+ LPCSTR _pszPrefix; // name prefix for treeitem settings
+ CPsTreeItem** _pPages; // the pages
+ WORD _numPages; // number of pages
+ DWORD _dwFlags; // some option flags
+ HIMAGELIST _hImages; // the imagelist with all tree item icons
+ LIST<TCHAR> _ignore; // list of to skipp items when adding metasubcontacts pages
+ INT _nSubContact; // index of a current subcontact
+
+ CPsHdr();
+ ~CPsHdr();
+
+ VOID Free_pPages();
+
+};
+
+struct TAckInfo
+{
+ HANDLE hContact;
+ LPINT/*PINT_PTR*/ acks;
+ INT count;
+};
+
+struct TPropSheet
+{
+ // dialogs owner
+ HANDLE hContact;
+ CHAR pszProto[MAXMODULELABELLENGTH];
+
+ HANDLE hProtoAckEvent; // eventhook for protocol acks
+ HANDLE hSettingChanged; // eventhook searching for changed contact information
+ HANDLE hIconsChanged; // eventhook for changed icons in icolib
+ HFONT hCaptionFont;
+ HFONT hBoldFont;
+ RECT rcDisplay;
+ BYTE updateAnimFrame;
+ CHAR szUpdating[64];
+ DWORD dwFlags;
+
+ TAckInfo *infosUpdated;
+ INT nSubContacts;
+
+ // controls
+ HWND hDlg;
+ CPsTree *pTree;
+ CPsUpload *pUpload;
+};
+
+VOID DlgContactInfoInitTreeIcons();
+VOID DlgContactInfoLoadModule();
+VOID DlgContactInfoUnLoadModule();
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp b/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp
new file mode 100644
index 0000000000..0f75c0df43
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp
@@ -0,0 +1,581 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactBase.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "..\svc_contactinfo.h"
+#include "classExImContactBase.h"
+#include "mir_rfcCodecs.h"
+
+HANDLE CListFindGroup(LPCSTR pszGroup)
+{
+ CHAR buf[4];
+ BYTE i;
+ DBVARIANT dbv;
+
+ for (i = 0; i < 255; i++) {
+ _itoa(i, buf, 10);
+ if (DB::Setting::GetUString(NULL, "CListGroups", buf, &dbv))
+ break;
+ if (dbv.pszVal && pszGroup && !_stricmp(dbv.pszVal + 1, pszGroup)) {
+ DB::Variant::Free(&dbv);
+ return (HANDLE)(INT_PTR)(i+1);
+ }
+ DB::Variant::Free(&dbv);
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+/**
+ * name: CExImContactBase
+ * class: CExImContactBase
+ * desc: default constructor
+ * param: none
+ * return: nothing
+ **/
+CExImContactBase::CExImContactBase()
+{
+ _pszNick = NULL;
+ _pszDisp = NULL;
+ _pszGroup = NULL;
+ _pszProto = NULL;
+ _pszProtoOld = NULL;
+ _pszAMPro = NULL;
+ _pszUIDKey = NULL;
+ _dbvUIDHash = NULL;
+ ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+ _hContact = INVALID_HANDLE_VALUE;
+ _isNewContact = FALSE;
+}
+
+/**
+ * name: ~CExImContactBase
+ * class: CExImContactBase
+ * desc: default denstructor
+ * param: none
+ * return: nothing
+ **/
+CExImContactBase::~CExImContactBase()
+{
+ MIR_FREE(_pszNick);
+ MIR_FREE(_pszDisp);
+ MIR_FREE(_pszGroup);
+ MIR_FREE(_pszProtoOld);
+ MIR_FREE(_pszProto);
+ MIR_FREE(_pszAMPro);
+ MIR_FREE(_pszUIDKey);
+ DB::Variant::Free(&_dbvUID);
+}
+
+/**
+ * name: fromDB
+ * class: CExImContactBase
+ * desc: get contact information from database
+ * param: hContact - handle to contact whose information to read
+ * return: TRUE if successful or FALSE otherwise
+ **/
+BOOLEAN CExImContactBase::fromDB(HANDLE hContact)
+{
+ BOOLEAN ret = FALSE;
+ BOOLEAN isChatRoom = FALSE;
+ LPSTR pszProto;
+ LPCSTR uidSetting;
+ DBVARIANT dbv;
+
+ _hContact = hContact;
+ _dbvUIDHash = 0;
+ MIR_FREE(_pszProtoOld);
+ MIR_FREE(_pszProto);
+ MIR_FREE(_pszAMPro);
+ MIR_FREE(_pszNick);
+ MIR_FREE(_pszDisp);
+ MIR_FREE(_pszGroup);
+ MIR_FREE(_pszUIDKey);
+ DB::Variant::Free(&_dbvUID);
+ ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+
+ // OWNER
+ if (!_hContact) return TRUE;
+
+ // Proto
+ if (!(pszProto = DB::Contact::Proto(_hContact))) return FALSE;
+ _pszProto = mir_strdup(pszProto);
+
+ // AM_BaseProto
+ if (!DB::Setting::GetUString(NULL, pszProto, "AM_BaseProto", &dbv )) {
+ _pszAMPro = mir_strdup(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ }
+
+ // unique id (for ChatRoom)
+ if (isChatRoom = DB::Setting::GetByte(_hContact, pszProto, "ChatRoom", 0)) {
+ uidSetting = "ChatRoomID";
+ _pszUIDKey = mir_strdup(uidSetting);
+ if (!DB::Setting::GetAsIs(_hContact, pszProto, uidSetting, &_dbvUID)) {
+ ret = TRUE;
+ }
+ }
+ // unique id (normal)
+ else {
+ uidSetting = (LPCSTR)CallProtoService(pszProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ // valid
+ if (uidSetting != NULL && (INT_PTR)uidSetting != CALLSERVICE_NOTFOUND) {
+ _pszUIDKey = mir_strdup(uidSetting);
+ if (!DB::Setting::GetAsIs(_hContact, pszProto, uidSetting, &_dbvUID)) {
+ ret = TRUE;
+ }
+ }
+ // fails because the protocol is no longer installed
+ else {
+ // assert(ret == TRUE);
+ ret = TRUE;
+ }
+ }
+
+ // nickname
+ if (!DB::Setting::GetUString(_hContact, pszProto, SET_CONTACT_NICK, &dbv)) {
+ _pszNick = mir_strdup(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ }
+
+ if (_hContact && ret) {
+ // Clist Group
+ if (!DB::Setting::GetUString(_hContact, MOD_CLIST, "Group", &dbv)) {
+ _pszGroup = mir_strdup(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ }
+ // Clist DisplayName
+ if (!DB::Setting::GetUString(_hContact, MOD_CLIST, SET_CONTACT_MYHANDLE, &dbv)) {
+ _pszDisp = mir_strdup(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ }
+ }
+ return ret;
+}
+
+/**
+ * name: fromIni
+ * class: CExImContactBase
+ * desc: get contact information from a row of a ini file
+ * param: row - the rows data
+ * return: TRUE if successful or FALSE otherwise
+ **/
+BOOLEAN CExImContactBase::fromIni(LPSTR& row)
+{
+ LPSTR p1, p2 = NULL;
+ LPSTR pszUIDValue, pszUIDSetting, pszProto = NULL;
+ LPSTR pszBuf = &row[0];
+ size_t cchBuf = strlen(row);
+
+ MIR_FREE(_pszProtoOld);
+ MIR_FREE(_pszProto);
+ MIR_FREE(_pszAMPro);
+ MIR_FREE(_pszNick);
+ MIR_FREE(_pszDisp);
+ MIR_FREE(_pszGroup);
+ MIR_FREE(_pszUIDKey);
+ DB::Variant::Free(&_dbvUID);
+ ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+ _dbvUIDHash = 0;
+
+ // read uid value
+ if (cchBuf > 10 && (p1 = mir_strrchr(pszBuf, '*{')) && (p2 = mir_strchr(p1, '}*')) && p1 + 2 < p2) {
+ pszUIDValue = p1 + 1;
+ *p1 = *(p2 - 1) = 0;
+
+ // insulate the uid setting from buffer pointer
+ if (cchBuf > 0 && (p1 = mir_strrchr(pszBuf, '*<')) && (p2 = mir_strchr(p1, '>*')) && p1 + 2 < p2) {
+ pszUIDSetting = p1 + 1;
+ *p1 = *(p2 - 1) = 0;
+
+ // insulate the protocol name from buffer pointer
+ if (cchBuf > 0 && (p1 = mir_strrchr(pszBuf, '*(')) && (p2 = mir_strchr(p1, ')*')) && p1 + 2 < p2) {
+ pszProto = p1 + 1;
+ *(--p1) = *(p2 - 1) = 0;
+
+ // DBVT_DWORD
+ if (strspn(pszUIDValue, "0123456789") == mir_strlen(pszUIDValue)) {
+ _dbvUID.dVal = _atoi64(pszUIDValue);
+ _dbvUID.type = DBVT_DWORD;
+ }
+ else {
+ // DBVT_UTF8
+ _dbvUID.pszVal = mir_strdup(pszUIDValue);
+ _dbvUID.type = DBVT_UTF8;
+ }
+ _pszUIDKey = mir_strdup(pszUIDSetting);
+ _pszProto = mir_strdup(pszProto);
+ } //end insulate the protocol name from buffer pointer
+ } //end insulate the uid setting from buffer pointer
+ } //end read uid value
+
+ // create valid nickname
+ _pszNick = mir_strdup(pszBuf);
+ size_t i = strlen(_pszNick)-1;
+ while (i > 0 && (_pszNick[i] == ' ' || _pszNick[i] == '\t')) {
+ _pszNick[i] = 0;
+ i--;
+ }
+ // finally try to find contact in contact list
+ findHandle();
+ return FALSE;
+}
+
+/**
+ * name: toDB
+ * class: CExImContactBase
+ * desc: searches the database for a contact representing the one
+ * identified by this class or creates a new one if it was not found
+ * param: hMetaContact - a meta contact to add this contact to
+ * return: handle of the contact if successful
+ **/
+HANDLE CExImContactBase::toDB()
+{
+ DBCONTACTWRITESETTING cws;
+
+ // create new contact if none exists
+ if (_hContact == INVALID_HANDLE_VALUE && _pszProto && _pszUIDKey && _dbvUID.type != DBVT_DELETED) {
+ PROTOACCOUNT* pszAccount = 0;
+ if(NULL == (pszAccount = ProtoGetAccount( _pszProto ))) {
+ //account does not exist
+ _hContact = INVALID_HANDLE_VALUE;
+ return INVALID_HANDLE_VALUE;
+ }
+ if (!IsAccountEnabled(pszAccount)) {
+ ;
+ }
+ // create new contact
+ _hContact = DB::Contact::Add();
+ if (!_hContact) {
+ _hContact = INVALID_HANDLE_VALUE;
+ return INVALID_HANDLE_VALUE;
+ }
+ // Add the protocol to the new contact
+ if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)_hContact, (LPARAM)_pszProto)) {
+ DB::Contact::Delete(_hContact);
+ _hContact = INVALID_HANDLE_VALUE;
+ return INVALID_HANDLE_VALUE;
+ }
+ // write uid to protocol module
+ cws.szModule = _pszProto;
+ cws.szSetting = _pszUIDKey;
+ cws.value = _dbvUID;
+ if (CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)_hContact, (LPARAM)&cws)) {
+ DB::Contact::Delete(_hContact);
+ _hContact = INVALID_HANDLE_VALUE;
+ return INVALID_HANDLE_VALUE;
+ }
+ // write nick and display name
+ if (_pszNick) DB::Setting::WriteUString(_hContact, _pszProto, SET_CONTACT_NICK, _pszNick);
+ if (_pszDisp) DB::Setting::WriteUString(_hContact, MOD_CLIST, SET_CONTACT_MYHANDLE, _pszDisp);
+
+ // add group
+ if (_pszGroup) {
+ DB::Setting::WriteUString(_hContact, MOD_CLIST, "Group", _pszGroup);
+ if (CListFindGroup(_pszGroup) == INVALID_HANDLE_VALUE) {
+ WPARAM hGroup = (WPARAM)CallService(MS_CLIST_GROUPCREATE, 0, 0);
+ if (hGroup) {
+ // renaming twice is stupid but the only way to avoid error dialog telling shit like
+ // a group with that name does exist
+ CallService(MS_CLIST_GROUPRENAME, (WPARAM)hGroup, (LPARAM)_pszGroup);
+ }
+ }
+ }
+ }
+ return _hContact;
+}
+
+/**
+ * name: toIni
+ * class: CExImContactBase
+ * desc: writes the line to an opened ini file which identifies the contact
+ * whose information are stored in this class
+ * param: file - pointer to the opened file
+ * return: nothing
+ **/
+VOID CExImContactBase::toIni(FILE* file, int modCount)
+{
+ // getting dbeditor++ NickFromHContact(hContact)
+ static char name[512] = "";
+ char* ret = 0;
+
+ if (_hContact){
+ int loaded = _pszUIDKey ? 1 : 0;
+ if (_pszProto == NULL || !loaded) {
+ if (_pszProto){
+ if (_pszNick)
+ mir_snprintf(name, sizeof(name),"%s (%s)", _pszNick, _pszProto);
+ else
+ mir_snprintf(name, sizeof(name),"(UNKNOWN) (%s)", _pszProto);
+ }
+ else
+ mir_snprintf(name, sizeof(name),"(UNKNOWN)");
+ }
+ else {
+ // Proto loadet - GetContactName(hContact,pszProto,0)
+ LPSTR pszCI = NULL;
+ CONTACTINFO ci;
+ ZeroMemory(&ci, sizeof(ci));
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = _hContact;
+ ci.szProto = _pszProto;
+ ci.dwFlag = CNF_DISPLAY;
+
+ if (!GetContactInfo(NULL, (LPARAM) &ci)) {
+ // CNF_DISPLAY always returns a string type
+ pszCI = (LPSTR)ci.pszVal;
+ }
+ LPSTR pszUID = uid2String(FALSE);
+ if (_pszUIDKey && pszUID)
+ mir_snprintf(name, sizeof(name), "%s *(%s)*<%s>*{%s}*", pszCI, _pszProto, _pszUIDKey, pszUID);
+ else
+ mir_snprintf(name, sizeof(name), "%s (%s)", pszCI, _pszProto);
+
+ mir_free(pszCI);
+ mir_free(pszUID);
+ } // end else (Proto loadet)
+
+ // it is not the best solution (but still works if only basic modules export) - need rework
+ if (modCount > 3)
+ fprintf(file, "CONTACT: %s\n", name);
+ else
+ fprintf(file, "FROM CONTACT: %s\n", name);
+
+ } // end *if (_hContact)
+ else {
+ fprintf(file, "SETTINGS:\n");
+ }
+}
+
+BOOLEAN CExImContactBase::compareUID(DBVARIANT *dbv)
+{
+ DWORD hash = 0;
+ switch (dbv->type) {
+ case DBVT_BYTE:
+ if (dbv->bVal == _dbvUID.bVal) {
+ _dbvUID.type = dbv->type;
+ return TRUE;
+ }
+ break;
+ case DBVT_WORD:
+ if (dbv->wVal == _dbvUID.wVal) {
+ _dbvUID.type = dbv->type;
+ return TRUE;
+ }
+ break;
+ case DBVT_DWORD:
+ if (dbv->dVal == _dbvUID.dVal) {
+ _dbvUID.type = dbv->type;
+ return TRUE;
+ }
+ break;
+ case DBVT_ASCIIZ:
+ hash = hashSetting_M2(dbv->pszVal);
+ case DBVT_WCHAR:
+ if (!hash) hash = hashSettingW_M2((const char *)dbv->pwszVal);
+ case DBVT_UTF8:
+ if (!hash) {
+ LPWSTR tmp = mir_utf8decodeW(dbv->pszVal);
+ hash = hashSettingW_M2((const char *)tmp);
+ mir_free(tmp);
+ }
+ if(hash == _dbvUIDHash) return TRUE;
+ break;
+ case DBVT_BLOB: //'n' cpbVal and pbVal are valid
+ if (_dbvUID.type == dbv->type &&
+ _dbvUID.cpbVal == dbv->cpbVal &&
+ memcmp(_dbvUID.pbVal, dbv->pbVal, dbv->cpbVal) == 0) {
+ return TRUE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return FALSE;
+}
+
+LPSTR CExImContactBase::uid2String(BOOLEAN bPrependType)
+{
+ CHAR szUID[MAX_PATH];
+ LPSTR ptr = szUID;
+
+ switch (_dbvUID.type) {
+ case DBVT_BYTE: //'b' bVal and cVal are valid
+ if (bPrependType) *ptr++ = 'b';
+ _itoa(_dbvUID.bVal, ptr, 10);
+ break;
+ case DBVT_WORD: //'w' wVal and sVal are valid
+ if (bPrependType) *ptr++ = 'w';
+ _itoa(_dbvUID.wVal, ptr, 10);
+ break;
+ case DBVT_DWORD: //'d' dVal and lVal are valid
+ if (bPrependType) *ptr++ = 'd';
+ _itoa(_dbvUID.dVal, ptr, 10);
+ break;
+ case DBVT_WCHAR: //'u' pwszVal is valid
+ {
+ LPSTR r = mir_utf8encodeW(_dbvUID.pwszVal);
+ if (bPrependType) {
+ LPSTR t = (LPSTR)mir_alloc(strlen(r)+2);
+ t[0] = 'u';
+ strcpy(t+1, r);
+ mir_free(r);
+ r = t;
+ }
+ return r;
+ }
+ case DBVT_UTF8: //'u' pszVal is valid
+ {
+ if (bPrependType) *ptr++ = 'u';
+ mir_strncpy(ptr, _dbvUID.pszVal, SIZEOF(szUID)-1);
+ }
+ break;
+ case DBVT_ASCIIZ: {
+ LPSTR r = mir_utf8encode(_dbvUID.pszVal);
+ if (bPrependType) {
+ LPSTR t = (LPSTR)mir_alloc(strlen(r)+2);
+ t[0] = 's';
+ strcpy(t+1, r);
+ mir_free(r);
+ r = t;
+ }
+ return r;
+ }
+ case DBVT_BLOB: //'n' cpbVal and pbVal are valid
+ {
+ if (bPrependType) { //True = XML
+ INT_PTR baselen = Base64EncodeGetRequiredLength(_dbvUID.cpbVal, BASE64_FLAG_NOCRLF);
+ LPSTR t = (LPSTR)mir_alloc(baselen + 5 + bPrependType);
+ assert(t != NULL);
+ if (Base64Encode(_dbvUID.pbVal, _dbvUID.cpbVal, t + bPrependType, &baselen, BASE64_FLAG_NOCRLF)) {
+ if (baselen){
+ t[baselen + bPrependType] = 0;
+ if (bPrependType) t[0] = 'n';
+ return t;
+ }
+ }
+ mir_free(t);
+ return NULL;
+ }
+ else { //FALSE = INI
+ WORD j;
+ INT_PTR baselen = (_dbvUID.cpbVal * 3);
+ LPSTR t = (LPSTR)mir_alloc(3 + baselen);
+ ZeroMemory(t, (3 + baselen));
+ for (j = 0; j < _dbvUID.cpbVal; j++) {
+ mir_snprintf((t + bPrependType + (j * 3)), 4,"%02X ", (BYTE)_dbvUID.pbVal[j]);
+ }
+ if (t){
+ if (bPrependType) t[0] = 'n';
+ return t;
+ }
+ else {
+ mir_free(t);
+ return NULL;
+ }
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+ return mir_strdup(szUID);
+}
+
+BOOLEAN CExImContactBase::isMeta() const
+{
+ return DB::Module::IsMeta(_pszProto);
+}
+
+BOOLEAN CExImContactBase::isHandle(HANDLE hContact)
+{
+ LPCSTR pszProto;
+ DBVARIANT dbv;
+ BOOLEAN isEqual = FALSE;
+
+ // owner contact ?
+ if (!_pszProto) return hContact == NULL;
+
+ // compare protocols
+ pszProto = DB::Contact::Proto(hContact);
+ if (pszProto == NULL || (INT_PTR)pszProto == CALLSERVICE_NOTFOUND || mir_stricmp(pszProto, _pszProto))
+ return FALSE;
+
+ // compare uids
+ if (_pszUIDKey) {
+ // get uid
+ if (DB::Setting::GetAsIs(hContact, pszProto,_pszUIDKey, &dbv))
+ return FALSE;
+
+ isEqual = compareUID (&dbv);
+ DB::Variant::Free(&dbv);
+ }
+ // compare nicknames if no UID
+ else if (!DB::Setting::GetUString(hContact, _pszProto, SET_CONTACT_NICK, &dbv)) {
+ if (dbv.type == DBVT_UTF8 && dbv.pszVal && !mir_stricmp(dbv.pszVal,_pszNick)) {
+ LPTSTR ptszNick = mir_utf8decodeT(_pszNick);
+ LPTSTR ptszProto = mir_a2t(_pszProto);
+ INT ans = MsgBox(NULL, MB_ICONQUESTION|MB_YESNO, LPGENT("Question"), LPGENT("contact identificaion"),
+ LPGENT("The contact %s(%s) has no unique id in the vCard,\nbut there is a contact in your clist with the same nick and protocol.\nDo you wish to use this contact?"),
+ ptszNick, ptszProto);
+ MIR_FREE(ptszNick);
+ MIR_FREE(ptszProto);
+ isEqual = ans == IDYES;
+ }
+ DB::Variant::Free(&dbv);
+ }
+ return isEqual;
+}
+
+/**
+ * name: handle
+ * class: CExImContactBase
+ * desc: return the handle of the contact
+ * whose information are stored in this class
+ * param: none
+ * return: handle if successful, INVALID_HANDLE_VALUE otherwise
+ **/
+HANDLE CExImContactBase::findHandle()
+{
+ HANDLE hContact;
+
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ if (isHandle(hContact)) {
+ _hContact = hContact;
+ _isNewContact = FALSE;
+ return hContact;
+ }
+ }
+ _isNewContact = TRUE;
+ _hContact = INVALID_HANDLE_VALUE;
+ return INVALID_HANDLE_VALUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactBase.h b/plugins/UserInfoEx/src/ex_import/classExImContactBase.h
new file mode 100644
index 0000000000..55e5d94f67
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactBase.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactBase.h $
+Revision : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+#include "tinyxml.h"
+#include "..\svc_gender.h"
+
+HANDLE CListFindGroup(LPCSTR pszGroup);
+
+class CExImContactBase {
+ BOOLEAN compareUID(DBVARIANT *dbv);
+
+protected:
+ LPSTR _pszNick; // utf8 encoded
+ LPSTR _pszDisp; // utf8 encoded
+ LPSTR _pszGroup; // utf8 encoded
+ LPSTR _pszAMPro;
+ LPSTR _pszProto;
+ LPSTR _pszProtoOld;
+ LPSTR _pszUIDKey;
+ DWORD _dbvUIDHash;
+ DBVARIANT _dbvUID;
+ HANDLE _hContact;
+ BOOLEAN _isNewContact; // is this contact a new one?
+
+ HANDLE findHandle();
+
+public:
+ CExImContactBase();
+ ~CExImContactBase();
+
+// __inline LPCSTR disp() const { return mir_strcmp(_pszDisp,"")? _pszDisp : NULL; }
+// __inline LPCSTR group() const { return mir_strcmp(_pszGroup,"")? _pszGroup : NULL; }
+// __inline LPCSTR nick() const { return mir_strcmp(_pszNick,"")? _pszNick : NULL; }
+// __inline LPCSTR proto() const { return mir_strcmp(_pszProto,"")? _pszProto : NULL; }
+// __inline LPCSTR ampro() const { return mir_strcmp(_pszAMPro,"")? _pszAMPro : NULL; }
+// __inline LPCSTR uidk() const { return mir_strcmp(_pszUIDKey,"")? _pszUIDKey : NULL; }
+ __inline DBVARIANT& uid() { return _dbvUID; }
+ __inline HANDLE handle() const { return _hContact; }
+
+ __inline VOID disp(LPCSTR val) { _pszDisp = val ? mir_strdup(val): NULL; }
+ __inline VOID group(LPCSTR val) { _pszGroup = val ? mir_strdup(val): NULL; }
+ __inline VOID nick(LPCSTR val) { _pszNick = val ? mir_strdup(val): NULL; }
+ __inline VOID proto(LPCSTR val) { _pszProto = val ? mir_strdup(val): NULL; }
+ __inline VOID ampro(LPCSTR val) { _pszAMPro = val ? mir_strdup(val): NULL; }
+ __inline VOID uidk(LPCSTR val) { _pszUIDKey = val ? mir_strdup(val): NULL; }
+ __inline VOID uid(BYTE val) { _dbvUID.type = DBVT_BYTE; _dbvUID.bVal = val; }
+ __inline VOID uid(WORD val) { _dbvUID.type = DBVT_WORD; _dbvUID.wVal = val; }
+ __inline VOID uid(DWORD val) { _dbvUID.type = DBVT_DWORD; _dbvUID.dVal = val; }
+ __inline VOID uidn(PBYTE val, DWORD len) { _dbvUID.type = DBVT_BLOB; _dbvUID.pbVal= val; _dbvUID.cpbVal = (WORD)len; }
+ __inline VOID uida(LPCSTR val)
+ {
+ _dbvUID.type = (_dbvUID.pszVal = mir_utf8decodeA(val))? DBVT_ASCIIZ : DBVT_DELETED;
+ _dbvUIDHash = hashSetting_M2(_dbvUID.pszVal);
+ }
+ __inline VOID uidu(LPCSTR val)
+ {
+ _dbvUID.type = (_dbvUID.pszVal = mir_strdup(val))? DBVT_UTF8 : DBVT_DELETED;
+ LPWSTR temp = mir_utf8decodeW(val);
+ _dbvUIDHash = hashSettingW_M2((const char *)temp);
+ mir_free(temp);
+ }
+
+ BOOLEAN isHandle(HANDLE hContact);
+ BOOLEAN isMeta() const;
+
+ LPSTR uid2String(BOOLEAN bPrependType);
+
+ BOOLEAN fromDB(HANDLE hContact);
+ BOOLEAN fromIni(LPSTR& row);
+
+ HANDLE toDB();
+ VOID toIni(FILE* file, int modCount);
+
+ BOOLEAN operator = (HANDLE hContact) { return fromDB(hContact); }
+};
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp b/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp
new file mode 100644
index 0000000000..2264f4d278
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp
@@ -0,0 +1,1141 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactXML.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "classExImContactXML.h"
+#include "svc_ExImXML.h"
+#include "mir_rfcCodecs.h"
+
+/***********************************************************************************************************
+ * common stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: SortProc
+ * desc: used for bsearch in CExImContactXML::IsContactInfo
+ * param: item1 - item to compare
+ * item2 - item to compare
+ * return: the difference
+ **/
+static INT SortProc(const LPDWORD item1, const LPDWORD item2)
+{
+ return *item1 - *item2;
+}
+
+/**
+ * name: CExImContactXML
+ * class: CExImContactXML
+ * desc: the constructor for the contact class
+ * param: pXmlFile - the owning xml file
+ * return: nothing
+ **/
+CExImContactXML::CExImContactXML(CFileXml * pXmlFile)
+ : CExImContactBase()
+{
+ _xmlNode = NULL;
+ _pXmlFile = pXmlFile;
+ _hEvent = NULL;
+}
+
+/**
+ * name: IsContactInfo
+ * class: CExImContactXML
+ * desc: this function compares the given setting key to the list of known contact
+ * information keys
+ * param: pszKey - the settings key to check
+ * return: TRUE if pszKey is a valid contact information
+ **/
+BOOLEAN CExImContactXML::IsContactInfo(LPCSTR pszKey)
+{
+ // This is a sorted list of all hashvalues of the contact information.
+ // This is the same as the szCiKey[] array below but sorted
+ const DWORD dwCiHash[] = {
+ 0x6576F145,0x65780A70,0x6719120C,0x6776F145,0x67780A70,0x6EDB33D7,0x6F0466B5,
+ 0x739B6915,0x73B11E48,0x760D8AD5,0x786A70D0,0x8813C350,0x88641AF8,0x8ED5652D,
+ 0x96D64541,0x97768A14,0x9B786F9C,0x9B7889F9,0x9C26E6ED,0xA6675748,0xA813C350,
+ 0xA8641AF8,0xAC408FCC,0xAC40AFCC,0xAC40CFCC,0xAEC6EA4C,0xB813C350,0xB8641AF8,
+ 0xC5227954,0xCC68DE0E,0xCCD62E70,0xCCFBAAF4,0xCD715E13,0xD36182CF,0xD361C2CF,
+ 0xD361E2CF,0xD42638DE,0xD4263956,0xD426395E,0xD453466E,0xD778D233,0xDB59D87A,
+ 0xE406F60E,0xE406FA0E,0xE406FA4E,0xECF7E910,0xEF660441,0x00331041,0x0039AB3A,
+ 0x003D88A6,0x07ABA803,0x113D8227,0x113DC227,0x113DE227,0x2288784F,0x238643D6,
+ 0x2671C03E,0x275F720B,0x2EBDC0D6,0x3075C8C5,0x32674C9F,0x33EEAE73,0x40239C1C,
+ 0x44DB75D0,0x44FA69D0,0x4C76989B,0x4FF38979,0x544B2F44,0x55AFAF8C,0x567A6BC5,
+ 0x5A96C47F,0x6376F145,0x63780A70
+ };
+ if (pszKey && *pszKey) {
+ char buf[MAXSETTING];
+ // convert to hash and make bsearch as it is much faster then working with strings
+ const DWORD dwHash = hashSetting(_strlwr(mir_strncpy(buf, pszKey, SIZEOF(buf))));
+ return bsearch(&dwHash, dwCiHash, SIZEOF(dwCiHash), sizeof(dwCiHash[0]), (INT (*)(const VOID*, const VOID*))SortProc) != NULL;
+ }
+ return FALSE;
+/*
+ WORD i;
+ const LPCSTR szCiKey[] = {
+ // naming
+ SET_CONTACT_TITLE,SET_CONTACT_FIRSTNAME,SET_CONTACT_SECONDNAME,SET_CONTACT_LASTNAME,SET_CONTACT_PREFIX,
+ // private address
+ SET_CONTACT_STREET,SET_CONTACT_ZIP,SET_CONTACT_CITY,SET_CONTACT_STATE,SET_CONTACT_COUNTRY,
+ SET_CONTACT_PHONE,SET_CONTACT_FAX,SET_CONTACT_CELLULAR,
+ SET_CONTACT_EMAIL,SET_CONTACT_EMAIL0,SET_CONTACT_EMAIL1,SET_CONTACT_HOMEPAGE,
+ // origin
+ SET_CONTACT_ORIGIN_STREET,SET_CONTACT_ORIGIN_ZIP,SET_CONTACT_ORIGIN_CITY,SET_CONTACT_ORIGIN_STATE,SET_CONTACT_ORIGIN_COUNTRY,
+ // company
+ SET_CONTACT_COMPANY_POSITION,SET_CONTACT_COMPANY_OCCUPATION,SET_CONTACT_COMPANY_SUPERIOR,SET_CONTACT_COMPANY_ASSISTENT
+ SET_CONTACT_COMPANY,SET_CONTACT_COMPANY_DEPARTMENT,SET_CONTACT_COMPANY_OFFICE,
+ SET_CONTACT_COMPANY_STREET,SET_CONTACT_COMPANY_ZIP,SET_CONTACT_COMPANY_CITY,SET_CONTACT_COMPANY_STATE,SET_CONTACT_COMPANY_COUNTRY,
+ SET_CONTACT_COMPANY_PHONE,SET_CONTACT_COMPANY_FAX,SET_CONTACT_COMPANY_CELLULAR,
+ SET_CONTACT_COMPANY_EMAIL,SET_CONTACT_COMPANY_EMAIL0,SET_CONTACT_COMPANY_EMAIL1,SET_CONTACT_COMPANY_HOMEPAGE,
+ // personal information
+ SET_CONTACT_ABOUT,SET_CONTACT_MYNOTES,SET_CONTACT_MARITAL,SET_CONTACT_PARTNER,
+ SET_CONTACT_LANG1,SET_CONTACT_LANG2,SET_CONTACT_LANG3,SET_CONTACT_TIMEZONE,SET_CONTACT_TIMEZONENAME,SET_CONTACT_TIMEZONEINDEX,
+ SET_CONTACT_AGE,SET_CONTACT_GENDER,SET_CONTACT_BIRTHDAY,SET_CONTACT_BIRTHMONTH,SET_CONTACT_BIRTHYEAR,
+ "Past0", "Past0Text","Past1", "Past1Text","Past2", "Past2Text",
+ "Affiliation0", "Affiliation0Text","Affiliation1", "Affiliation1Text","Affiliation2", "Affiliation2Text",
+ "Interest0Cat", "Interest0Text","Interest1Cat", "Interest1Text","Interest2Cat", "Interest2Text"
+ };
+ DWORD *hash = new DWORD[SIZEOF(szCiKey)];
+ char buf[MAX_PATH];
+
+ for (i = 0; i < SIZEOF(szCiKey); i++) {
+ strcpy(buf, szCiKey[i]);
+ hash[i] = hashSetting(_strlwr((char*)buf));
+ }
+ qsort(hash, SIZEOF(szCiKey), sizeof(hash[0]),
+ (INT (*)(const VOID*, const VOID*))SortProc);
+
+ FILE* fil = fopen("D:\\temp\\id.txt", "wt");
+ for (i = 0; i < SIZEOF(szCiKey); i++) {
+ fprintf(fil, "0x%08X,", hash[i]);
+ }
+ fclose(fil);
+ return FALSE;
+ */
+}
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: CreateXmlNode
+ * class: CExImContactXML
+ * desc: creates a new TiXmlElement representing the contact
+ * whose information are stored in this class
+ * param: none
+ * return: pointer to the newly created TiXmlElement
+ **/
+TiXmlElement* CExImContactXML::CreateXmlElement()
+{
+ if (_hContact) {
+ if (_pszProto) {
+ _xmlNode = new TiXmlElement(XKEY_CONTACT);
+
+ if (_xmlNode) {
+ LPSTR pszUID = uid2String(TRUE);
+ _xmlNode->SetAttribute("ampro", _pszAMPro);
+ _xmlNode->SetAttribute("proto", _pszProto);
+
+ if (_pszDisp) _xmlNode->SetAttribute("disp", _pszDisp);
+ if (_pszNick) _xmlNode->SetAttribute("nick", _pszNick);
+ if (_pszGroup) _xmlNode->SetAttribute("group",_pszGroup);
+
+ if (pszUID) {
+
+ if (_pszUIDKey) {
+ _xmlNode->SetAttribute("uidk", _pszUIDKey);
+ _xmlNode->SetAttribute("uidv", pszUID);
+ }
+ else {
+ _xmlNode->SetAttribute("uidk", "#NV");
+ _xmlNode->SetAttribute("uidv", "UNLOADED");
+ }
+ mir_free(pszUID);
+ }
+ }
+ }
+ else
+ _xmlNode = NULL;
+ }
+ else {
+ _xmlNode = new TiXmlElement(XKEY_OWNER);
+ }
+ return _xmlNode;
+}
+
+/**
+ * name: ExportContact
+ * class: CExImContactXML
+ * desc: exports a contact
+ * param: none
+ * return: ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportContact(DB::CEnumList* pModules)
+{
+ if (_pXmlFile->_wExport & EXPORT_DATA)
+ {
+ if (pModules)
+ {
+ INT i;
+ LPSTR p;
+
+ for (i = 0; i < pModules->getCount(); i++)
+ {
+ p = (*pModules)[i];
+
+ /*Filter/
+ if (mir_stricmp(p, "Protocol") && !DB::Module::IsMeta(p))*/
+ {
+ ExportModule(p);
+ }
+ }
+ }
+ else
+ {
+ ExportModule(USERINFO);
+ ExportModule(MOD_MBIRTHDAY);
+ }
+ }
+
+ // export contact's events
+ if (_pXmlFile->_wExport & EXPORT_HISTORY)
+ {
+ ExportEvents();
+ }
+
+ return ERROR_OK;
+}
+
+/**
+ * name: ExportSubContact
+ * class: CExImContactXML
+ * desc: exports a meta sub contact
+ * param: none
+ * return: ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportSubContact(CExImContactXML *vMetaContact, DB::CEnumList* pModules)
+{
+ // create xmlNode
+ if (!CreateXmlElement())
+ {
+ return ERROR_INVALID_CONTACT;
+ }
+ if (ExportContact(pModules) == ERROR_OK)
+ {
+ if (!_xmlNode->NoChildren() && vMetaContact->_xmlNode->LinkEndChild(_xmlNode))
+ {
+ return ERROR_OK;
+ }
+ }
+ if (_xmlNode) delete _xmlNode;
+ return ERROR_NOT_ADDED;
+}
+
+/**
+ * name: Export
+ * class: CExImContactXML
+ * desc: exports a contact
+ * param: xmlfile - handle to the open file to write the contact to
+ * pModules - list of modules to export for each contact
+ * return: ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::Export(FILE *xmlfile, DB::CEnumList* pModules)
+{
+ if (!xmlfile)
+ {
+ return ERROR_INVALID_PARAMS;
+ }
+
+ if (_hContact == INVALID_HANDLE_VALUE)
+ {
+ return ERROR_INVALID_CONTACT;
+ }
+
+ if (!CreateXmlElement())
+ {
+ return ERROR_INVALID_CONTACT;
+ }
+
+ // export meta
+ if (isMeta())
+ {
+ CExImContactXML vContact(_pXmlFile);
+
+ const INT cnt = DB::MetaContact::SubCount(_hContact);
+ const INT def = DB::MetaContact::SubDefNum(_hContact);
+ HANDLE hSubContact = DB::MetaContact::Sub(_hContact, def);
+ INT i;
+
+ // export default subcontact
+ if (hSubContact && vContact.fromDB(hSubContact))
+ {
+ vContact.ExportSubContact(this, pModules);
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ if (i != def)
+ {
+ hSubContact = DB::MetaContact::Sub(_hContact, i);
+ if (hSubContact && vContact.fromDB(hSubContact))
+ {
+ vContact.ExportSubContact(this, pModules);
+ }
+ }
+ }
+ }
+ ExportContact(pModules);
+
+ // add xContact to document
+ if (_xmlNode->NoChildren())
+ {
+ delete _xmlNode;
+ _xmlNode = NULL;
+ return ERROR_NOT_ADDED;
+ }
+ _xmlNode->Print(xmlfile, 1);
+ fputc('\n', xmlfile);
+
+ delete _xmlNode;
+ _xmlNode = NULL;
+
+ return ERROR_OK;
+}
+
+/**
+ * name: ExportModule
+ * class: CExImContactXML
+ * desc: enumerates all settings of a database module and adds them to the xml tree
+ * params: pszModule - the module which is to export
+ * return: ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportModule(LPCSTR pszModule)
+{
+ DB::CEnumList Settings;
+ if (!pszModule || !*pszModule) {
+ return ERROR_INVALID_PARAMS;
+ }
+ if (!Settings.EnumSettings(_hContact, pszModule)) {
+ INT i;
+ TiXmlElement *xmod;
+ xmod = new TiXmlElement(XKEY_MOD);
+ if (!xmod) {
+ return ERROR_MEMORY_ALLOC;
+ }
+ xmod->SetAttribute("key", pszModule);
+ for (i = 0; i < Settings.getCount(); i++) {
+ ExportSetting(xmod, pszModule, Settings[i]);
+ }
+
+ if (!xmod->NoChildren() && _xmlNode->LinkEndChild(xmod)) {
+ return ERROR_OK;
+ }
+ delete xmod;
+ }
+ return ERROR_EMPTY_MODULE;
+}
+
+/**
+ * name: ExportSetting
+ * desc: read a setting from database and add an xmlelement to contact node
+ * params: xmlModule - xml node to add the setting to
+ * hContact - handle of the contact whose event chain is to export
+ * pszModule - the module which is to export
+ * pszSetting - the setting which is to export
+ * return: pointer to the added element
+ **/
+INT CExImContactXML::ExportSetting(TiXmlElement *xmlModule, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+ TiXmlElement *xmlEntry = NULL;
+ TiXmlText *xmlValue = NULL;
+ CHAR buf[32];
+ LPSTR str = NULL;
+
+ if (DB::Setting::GetAsIs(_hContact, pszModule, pszSetting, &dbv))
+ return ERROR_INVALID_VALUE;
+ switch (dbv.type) {
+ case DBVT_BYTE: //'b' bVal and cVal are valid
+ buf[0] = 'b';
+ _ultoa(dbv.bVal, buf + 1, 10);
+ xmlValue = new TiXmlText(buf);
+ break;
+ case DBVT_WORD: //'w' wVal and sVal are valid
+ buf[0] = 'w';
+ _ultoa(dbv.wVal, buf + 1, 10);
+ xmlValue = new TiXmlText(buf);
+ break;
+ case DBVT_DWORD: //'d' dVal and lVal are valid
+ buf[0] = 'd';
+ _ultoa(dbv.dVal, buf + 1, 10);
+ xmlValue = new TiXmlText(buf);
+ break;
+ case DBVT_ASCIIZ: //'s' pszVal is valid
+ {
+ if(mir_IsEmptyA(dbv.pszVal)) break;
+ DB::Variant::ConvertString(&dbv, DBVT_UTF8);
+ if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+ str[0] = 's';
+ mir_strcpy(&str[1], dbv.pszVal);
+ xmlValue = new TiXmlText(str);
+ mir_free(str);
+ }
+ break;
+ }
+ case DBVT_UTF8: //'u' pszVal is valid
+ {
+ if(mir_IsEmptyA(dbv.pszVal)) break;
+ if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+ str[0] = 'u';
+ mir_strcpy(&str[1], dbv.pszVal);
+ xmlValue = new TiXmlText(str);
+ mir_free(str);
+ }
+ break;
+ }
+ case DBVT_WCHAR: //'u' pwszVal is valid
+ {
+ if(mir_IsEmptyW(dbv.pwszVal)) break;
+ DB::Variant::ConvertString(&dbv, DBVT_UTF8);
+ if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+ str[0] = 'u';
+ mir_strcpy(&str[1], dbv.pszVal);
+ xmlValue = new TiXmlText(str);
+ mir_free(str);
+ }
+ break;
+ }
+ case DBVT_BLOB: //'n' cpbVal and pbVal are valid
+ {
+ // new buffer for base64 encoded data
+ INT_PTR baselen = Base64EncodeGetRequiredLength(dbv.cpbVal, BASE64_FLAG_NOCRLF);
+ str = (LPSTR)mir_alloc(baselen + 6);
+ assert(str != NULL);
+ // encode data
+ if (Base64Encode(dbv.pbVal, dbv.cpbVal, str+1, &baselen, BASE64_FLAG_NOCRLF)) {
+ if (baselen){
+ str[baselen+1] = 0;
+ str[0] = 'n';
+ xmlValue = new TiXmlText(str);
+ }
+ }
+ mir_free(str);
+ break;
+ }
+ case DBVT_DELETED: //this setting just got deleted, no other values are valid
+ #if defined(_DEBUG)
+ OutputDebugStringA("DBVT_DELETED\n");
+ #endif
+ break;
+ default:
+ #if defined(_DEBUG)
+ OutputDebugStringA("DBVT_TYPE unknown\n");
+ #endif
+ ; // nothing
+ }
+ DB::Variant::Free(&dbv);
+ if (xmlValue) {
+ xmlEntry = new TiXmlElement(XKEY_SET);
+ if (xmlEntry) {
+ xmlEntry->SetAttribute("key", pszSetting);
+ if (xmlEntry->LinkEndChild(xmlValue) && xmlModule->LinkEndChild(xmlEntry))
+ return ERROR_OK;
+ delete xmlEntry;
+ }
+ delete xmlValue;
+ }
+ return ERROR_MEMORY_ALLOC;
+}
+
+/**
+ * name: ExportEvents
+ * desc: adds the event chain for a given contact to the xml tree
+ * params: xContact - the xml node to add the events as childs to
+ * hContact - handle of the contact whose event chain is to export
+ * return: TRUE on success, FALSE otherwise
+ **/
+BOOLEAN CExImContactXML::ExportEvents()
+{
+ DBEVENTINFO dbei;
+ HANDLE hDbEvent;
+ PBYTE pbEventBuf = NULL;
+ DWORD cbEventBuf = 0,
+ dwNumEvents = 0,
+ dwNumEventsAdded = 0;
+ LPSTR pBase64Data = NULL;
+ INT_PTR cbBase64Data = 0,
+ cbNewBase64Data = 0;
+
+ TiXmlNode *xmlModule = NULL;
+ TiXmlElement *xmlEvent = NULL;
+ TiXmlText *xmlText = NULL;
+
+ dwNumEvents = CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)_hContact, NULL);
+ if(dwNumEvents == 0) return FALSE;
+
+ try {
+ ZeroMemory(&dbei, sizeof(DBEVENTINFO));
+ dbei.cbSize = sizeof(DBEVENTINFO);
+
+ // read out all events for the current contact
+ for (hDbEvent = DB::Event::FindFirst(_hContact); hDbEvent != NULL; hDbEvent = DB::Event::FindNext(hDbEvent)) {
+ if (!DB::Event::GetInfoWithData(hDbEvent, &dbei)) {
+ // new buffer for base64 encoded data
+ cbNewBase64Data = Base64EncodeGetRequiredLength(dbei.cbBlob, BASE64_FLAG_NOCRLF);
+ if (cbNewBase64Data > cbBase64Data) {
+ pBase64Data = (LPSTR)mir_realloc(pBase64Data, cbNewBase64Data + 5);
+ if (pBase64Data == NULL) {
+ MessageBoxA(NULL, "mir_realloc(cbNewBase64Data + 5) == NULL", "Error", 0);
+ break;
+ }
+ cbBase64Data = cbNewBase64Data;
+ }
+
+ // encode data
+ if (Base64Encode(dbei.pBlob, dbei.cbBlob, pBase64Data, &cbNewBase64Data, BASE64_FLAG_NOCRLF)) {
+ pBase64Data[cbNewBase64Data] = 0;
+ xmlEvent = new TiXmlElement("evt");
+ if (xmlEvent) {
+ xmlEvent->SetAttribute("type", dbei.eventType);
+ xmlEvent->SetAttribute("time", dbei.timestamp);
+ xmlEvent->SetAttribute("flag", dbei.flags);
+
+ xmlText = new TiXmlText(pBase64Data);
+ xmlEvent->LinkEndChild(xmlText);
+
+ // find module
+ for (xmlModule = _xmlNode->FirstChild(); xmlModule != NULL; xmlModule = xmlModule->NextSibling()) {
+ if (!mir_stricmp(((TiXmlElement*)xmlModule)->Attribute("key"), dbei.szModule)) break;
+ }
+ // create new module
+ if (!xmlModule) {
+ xmlModule = _xmlNode->InsertEndChild(TiXmlElement(XKEY_MOD));
+ if (!xmlModule) break;
+ ((TiXmlElement*)xmlModule)->SetAttribute("key", dbei.szModule);
+ }
+
+ xmlModule->LinkEndChild(xmlEvent);
+ dwNumEventsAdded++;
+ xmlEvent = NULL; // avoid final deleting
+ }
+ }
+ MIR_FREE(dbei.pBlob);
+ }
+ }
+ }
+ catch(...) {
+ // fuck, do nothing
+ MIR_FREE(dbei.pBlob);
+ dwNumEventsAdded = 0;
+ }
+
+ mir_free(pbEventBuf);
+ mir_free(pBase64Data);
+ if (xmlEvent) delete xmlEvent;
+
+ return dwNumEventsAdded == dwNumEvents;
+}
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: CountKeys
+ * desc: Counts the number of events and settings stored for a contact
+ * params: xmlContact - the contact, who is the owner of the keys to count
+ * return: nothing
+ **/
+VOID CExImContactXML::CountKeys(DWORD &numSettings, DWORD &numEvents)
+{
+ TiXmlNode *xmod, *xkey;
+
+ numSettings = numEvents = 0;
+ for (xmod = _xmlNode->FirstChild();
+ xmod != NULL;
+ xmod = xmod->NextSibling(XKEY_MOD)) {
+ for (xkey = xmod->FirstChild();
+ xkey != NULL;
+ xkey = xkey->NextSibling()) {
+ if (!mir_stricmp(xkey->Value(), XKEY_SET)) numSettings++;
+ else numEvents++;
+ }
+ }
+}
+
+/**
+ * name: LoadXmlElemnt
+ * class: CExImContactXML
+ * desc: get contact information from XML-file
+ * param: xContact - TiXmlElement representing a contact
+ * return: ERROR_OK if successful or any other error number otherwise
+ **/
+INT CExImContactXML::LoadXmlElemnt(TiXmlElement *xContact)
+{
+ if (xContact == NULL) return ERROR_INVALID_PARAMS;
+
+ LPSTR pszMetaProto = myGlobals.szMetaProto ? myGlobals.szMetaProto : "MetaContacts";
+
+ // delete last contact
+ DB::Variant::Free(&_dbvUID);
+ _hContact = INVALID_HANDLE_VALUE;
+
+ _xmlNode = xContact;
+ MIR_FREE(_pszAMPro); ampro(xContact->Attribute("ampro"));
+ MIR_FREE(_pszNick); nick (xContact->Attribute("nick"));
+ MIR_FREE(_pszDisp); disp (xContact->Attribute("disp"));
+ MIR_FREE(_pszGroup); group(xContact->Attribute("group"));
+ MIR_FREE(_pszProto);
+ MIR_FREE(_pszProtoOld);
+ MIR_FREE(_pszUIDKey);
+
+ // is contact a metacontact
+ if (_pszAMPro && !strcmp(_pszAMPro, pszMetaProto) /*_xmlNode->FirstChildElement(XKEY_CONTACT)*/) {
+ TiXmlElement *xSub;
+ proto(pszMetaProto);
+
+ // meta contact must be uniquelly identified by its subcontacts
+ // the metaID may change during an export or import call
+ for(xSub = xContact->FirstChildElement(XKEY_CONTACT);
+ xSub != NULL;
+ xSub = xSub->NextSiblingElement(XKEY_CONTACT)) {
+ CExImContactXML vSub(_pXmlFile);
+ if (vSub = xSub) {
+ // identify metacontact by the first valid subcontact in xmlfile
+ if (_hContact == INVALID_HANDLE_VALUE && vSub.handle() != INVALID_HANDLE_VALUE) {
+ HANDLE hMeta = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)vSub.handle(), NULL);
+ if (hMeta != NULL) {
+ _hContact = hMeta;
+ break;
+ }
+ }
+ }
+ }
+ // if no handle was found, this is a new meta contact
+ _isNewContact = _hContact == INVALID_HANDLE_VALUE;
+ }
+ // entry is a default contact
+ else {
+ proto(xContact->Attribute("proto"));
+ uidk (xContact->Attribute("uidk"));
+ if (!_pszProto) {
+ // check if this is the owner contact
+ if (mir_stricmp(xContact->Value(), XKEY_OWNER))
+ return ERROR_INVALID_PARAMS;
+ _hContact = NULL;
+ _xmlNode = xContact;
+ return ERROR_OK;
+ }
+
+ if (_pszUIDKey && mir_strcmp("#NV", _pszUIDKey) !=0) {
+ LPCSTR pUID = xContact->Attribute("uidv");
+
+ if (pUID != NULL) {
+ size_t len = 0;
+ INT_PTR baselen = NULL;
+ PBYTE pbVal = NULL;
+
+ switch (*(pUID++)) {
+ case 'b':
+ uid((BYTE)atoi(pUID));
+ break;
+ case 'w':
+ uid((WORD)atoi(pUID));
+ break;
+ case 'd':
+ uid((DWORD)_atoi64(pUID));
+ break;
+ case 's':
+ // utf8 -> asci
+ uida(pUID);
+ break;
+ case 'u':
+ uidu(pUID);
+ break;
+ case 'n':
+ len = strlen(pUID);
+ baselen = Base64DecodeGetRequiredLength(len);
+ pbVal = (PBYTE)mir_alloc(baselen /*+1*/);
+ if (pbVal != NULL){
+ if (Base64Decode(pUID, len, pbVal, &baselen)) {
+ uidn(pbVal, baselen);
+ }
+ else {
+ assert(pUID != NULL);
+ }
+ }
+ break;
+ default:
+ uidu((LPCSTR)NULL);
+ break;
+ }
+ }
+ }
+ // finally try to find contact in contact list
+ findHandle();
+ }
+ return ERROR_OK;
+}
+
+/**
+ * name: ImportContact
+ * class: CExImContactXML
+ * desc: create the contact if neccessary and copy
+ * all information from the xmlNode to database
+ * param: none
+ * return: ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::ImportContact()
+{
+ TiXmlNode *xmod;
+
+ // create the contact if not yet exists
+ if (toDB() != INVALID_HANDLE_VALUE) {
+ DWORD numSettings, numEvents;
+
+ _hEvent = NULL;
+
+ // count settings and events and init progress dialog
+ CountKeys(numSettings, numEvents);
+ _pXmlFile->_progress.SetSettingsCount(numSettings + numEvents);
+ _pXmlFile->_numSettingsTodo += numSettings;
+ _pXmlFile->_numEventsTodo += numEvents;
+
+ // import all modules
+ for(xmod = _xmlNode->FirstChild();
+ xmod != NULL;
+ xmod = xmod->NextSibling(XKEY_MOD)) {
+
+ // import module
+ if (ImportModule(xmod) == ERROR_ABORTED) {
+ // ask to delete new incomplete contact
+ if (_isNewContact && _hContact != NULL) {
+ INT result = MsgBox(NULL, MB_YESNO|MB_ICONWARNING,
+ LPGENT("Question"),
+ LPGENT("Importing a new contact was aborted!"),
+ LPGENT("You aborted import of a new contact.\nSome information may be missing for this contact.\n\nDo you want to delete the incomplete contact?"));
+ if (result == IDYES) {
+ DB::Contact::Delete(_hContact);
+ _hContact = INVALID_HANDLE_VALUE;
+ }
+ }
+ return ERROR_ABORTED;
+ }
+ }
+ return ERROR_OK;
+ }
+ return ERROR_NOT_ADDED;
+}
+
+/**
+ * name: ImportNormalContact
+ * class: CExImContactXML
+ * desc: create the contact if neccessary and copy
+ * all information from the xmlNode to database.
+ * Remove contact from a metacontact if it is a subcontact
+ * param: none
+ * return: ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::ImportNormalContact()
+{
+ INT err = ImportContact();
+
+ // remove contact from a metacontact
+ if (err == ERROR_OK && CallService(MS_MC_GETMETACONTACT, (WPARAM)_hContact, NULL)) {
+ CallService(MS_MC_REMOVEFROMMETA, NULL, (LPARAM)_hContact);
+ }
+ return err;
+}
+
+/**
+ * name: Import
+ * class: CExImContactXML
+ * desc: create the contact if neccessary and copy
+ * all information from the xmlNode to database.
+ * Remove contact from a metacontact if it is a subcontact
+ * param: TRUE = keepMetaSubContact
+ * return: ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::Import(BOOLEAN keepMetaSubContact)
+{
+ INT result;
+ TiXmlElement *xContact = _xmlNode->FirstChildElement("CONTACT");
+
+ // xml contact contains subcontacts?
+ if (xContact) {
+
+ // contact is a metacontact and metacontacts plugin is installed?
+ if (isMeta()) {
+ // create object for first sub contact
+ CExImContactXML vContact(_pXmlFile);
+ LPTSTR pszNick;
+
+ // the contact does not yet exist
+ if (_isNewContact) {
+ // import default contact as normal contact and convert to meta contact
+ if (!(vContact = xContact)) {
+ return ERROR_CONVERT_METACONTACT;
+ }
+ // import as normal contact
+ result = vContact.ImportContact();
+ if (result != ERROR_OK) return result;
+ // convert default subcontact to metacontact
+ _hContact = (HANDLE)CallService(MS_MC_CONVERTTOMETA, (WPARAM)vContact.handle(), NULL);
+ if (_hContact == NULL) {
+ _hContact = INVALID_HANDLE_VALUE;
+ return ERROR_CONVERT_METACONTACT;
+ }
+
+ _pXmlFile->_numContactsDone++;
+ // do not load first meta contact twice
+ xContact = xContact->NextSiblingElement("CONTACT");
+ }
+ // xml contact contains more than one subcontacts?
+ if (xContact) {
+ // load all subcontacts
+ do {
+ // update progressbar and abort if user clicked cancel
+ pszNick = mir_utf8decodeT(xContact->Attribute("nick"));
+ result = _pXmlFile->_progress.UpdateContact(_T("Sub Contact: %s (") _T(TCHAR_STR_PARAM) _T(")"), pszNick, xContact->Attribute("proto"));
+ if (pszNick) mir_free(pszNick);
+ // user clicked abort button
+ if (!result) break;
+ if (vContact = xContact) {
+ if (vContact.ImportMetaSubContact(this) == ERROR_ABORTED)
+ return ERROR_ABORTED;
+ _pXmlFile->_numContactsDone++;
+ }
+ }
+ while (xContact = xContact->NextSiblingElement("CONTACT"));
+ }
+ // load metacontact information (after subcontact for faster import)
+ ImportContact();
+ return ERROR_OK;
+ }
+ // import sub contacts as normal contacts
+ return _pXmlFile->ImportContacts(_xmlNode);
+ }
+
+ // load contact information
+ result = ImportContact();
+ if (result == ERROR_OK && !keepMetaSubContact)
+ {
+ CallService(MS_MC_REMOVEFROMMETA, NULL, (LPARAM)_hContact);
+ }
+
+ return result;
+}
+
+/**
+ * name: ImportMetaSubContact
+ * class: CExImContactXML
+ * desc: create the contact if neccessary and copy
+ * all information from the xmlNode to database.
+ * Add this contact to an meta contact
+ * param: pMetaContact - the meta contact to add this one to
+ * return:
+ **/
+INT CExImContactXML::ImportMetaSubContact(CExImContactXML * pMetaContact)
+{
+ INT err = ImportContact();
+
+ // abort here if contact was not imported correctly
+ if (err != ERROR_OK) return err;
+
+ // check if contact is subcontact of the desired meta contact
+ if ((HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)_hContact, NULL) != pMetaContact->handle()) {
+ // add contact to the metacontact (this service returns TRUE if successful)
+ err = CallService(MS_MC_ADDTOMETA, (WPARAM)_hContact, (LPARAM)pMetaContact->handle());
+ if (err == FALSE) {
+ // ask to delete new contact
+ if (_isNewContact && _hContact != NULL) {
+ LPTSTR ptszNick = mir_utf8decodeT(_pszNick);
+ LPTSTR ptszMetaNick = mir_utf8decodeT(pMetaContact->_pszNick);
+ INT result = MsgBox(NULL, MB_YESNO|MB_ICONWARNING,
+ LPGENT("Question"),
+ LPGENT("Importing a new meta subcontact failed!"),
+ LPGENT("The newly created MetaSubContact '%s'\ncould not be added to MetaContact '%s'!\n\nDo you want to delete this contact?"),
+ ptszNick, ptszMetaNick);
+ MIR_FREE(ptszNick);
+ MIR_FREE(ptszMetaNick);
+ if (result == IDYES) {
+ DB::Contact::Delete(_hContact);
+ _hContact = INVALID_HANDLE_VALUE;
+ }
+ }
+ return ERROR_ADDTO_METACONTACT;
+ }
+ }
+ return ERROR_OK;
+}
+
+/**
+ * name: ImportModule
+ * class: CExImContactXML
+ * desc: interprete an xmlnode as module and add the children to database.
+ * params: hContact - handle to the contact, who is the owner of the setting to import
+ * xmlModule - xmlnode representing the module
+ * stat - structure used to collect some statistics
+ * return: ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportModule(TiXmlNode* xmlModule)
+{
+ TiXmlElement *xMod;
+ TiXmlElement *xKey;
+ LPCSTR pszModule;
+ BOOLEAN isProtoModule;
+ BOOLEAN isMetaModule;
+
+ // check if parent is really a module
+ if (!xmlModule || mir_stricmp(xmlModule->Value(), XKEY_MOD))
+ return ERROR_INVALID_SIGNATURE;
+ // convert to element
+ if (!(xMod = xmlModule->ToElement()))
+ return ERROR_INVALID_PARAMS;
+ // get module name
+ pszModule = xMod->Attribute("key");
+ if (!pszModule || !*pszModule)
+ return ERROR_INVALID_PARAMS;
+ // ignore Modul 'Protocol' as it would cause trouble
+ if (!mir_stricmp(pszModule, "Protocol"))
+ return ERROR_OK;
+
+ for (xKey = xmlModule->FirstChildElement(); xKey != NULL; xKey = xKey->NextSiblingElement()) {
+ // import setting
+ if (!mir_stricmp(xKey->Value(), XKEY_SET)) {
+ // check if the module to import is the contact's protocol module
+ isProtoModule = !mir_stricmp(pszModule, _pszProto)/* || DB::Module::IsMeta(pszModule)*/;
+ isMetaModule = DB::Module::IsMeta(pszModule);
+
+ // just ignore MetaModule on normal contact to avoid errors (only keys)
+ if (!isProtoModule && isMetaModule) {
+ continue;
+ }
+ // just ignore MetaModule on Meta to avoid errors (only import spetial keys)
+ else if(isProtoModule && isMetaModule) {
+ if (!mir_stricmp(xKey->Attribute("key"),"Nick") ||
+ !mir_stricmp(xKey->Attribute("key"),"TzName") ||
+ !mir_stricmp(xKey->Attribute("key"),"Timezone")) {
+ if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+ _pXmlFile->_numSettingsDone++;
+ }
+ }
+ }
+ // just ignore some settings of protocol module to avoid errors (only keys)
+ else if (isProtoModule && !isMetaModule) {
+ if (!IsContactInfo(xKey->Attribute("key"))) {
+ if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+ _pXmlFile->_numSettingsDone++;
+ }
+ }
+ }
+ // other module
+ else if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+ _pXmlFile->_numSettingsDone++;
+ }
+ if (!_pXmlFile->_progress.UpdateSetting(LPGENT("Settings: %S"), pszModule))
+ return ERROR_ABORTED;
+ }
+ // import event
+ else if (!mir_stricmp(xKey->Value(), XKEY_EVT)) {
+ INT error = ImportEvent(pszModule, xKey->ToElement());
+ switch (error) {
+ case ERROR_OK:
+ _pXmlFile->_numEventsDone++;
+ break;
+ case ERROR_DUPLICATED:
+ _pXmlFile->_numEventsDuplicated++;
+ break;
+ }
+ if (!_pXmlFile->_progress.UpdateSetting(LPGENT("Events: %S"), pszModule))
+ return ERROR_ABORTED;
+ }
+ } //*end for
+ return ERROR_OK;
+}
+
+/**
+ * name: ImportSetting
+ * class: CExImContactXML
+ * desc: interprete an setting representing xmlnode and write the corresponding setting to database.
+ * params: xmlModule - xmlnode representing the module to write the setting to in the database
+ * xmlEntry - xmlnode representing the setting to import
+ * return: ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportSetting(LPCSTR pszModule, TiXmlElement *xmlEntry)
+{
+ DBCONTACTWRITESETTING cws = {0};
+ TiXmlText* xval;
+ LPCSTR value;
+
+ // validate parameter
+ if (!xmlEntry || !pszModule || !*pszModule)
+ return ERROR_INVALID_PARAMS;
+
+ // validate value
+ xval = (TiXmlText*)xmlEntry->FirstChild();
+ if (!xval || xval->Type() != TiXmlText::TEXT)
+ return ERROR_INVALID_VALUE;
+ value = xval->Value();
+
+ // init write structure
+ cws.szModule = (LPSTR)pszModule;
+ cws.szSetting = xmlEntry->Attribute("key");
+
+ // convert data
+ size_t len = 0;
+ INT_PTR baselen = NULL;
+
+ switch (value[0]) {
+ case 'b': //'b' bVal and cVal are valid
+ cws.value.type = DBVT_BYTE;
+ cws.value.bVal = (BYTE)atoi(value + 1);
+ break;
+ case 'w': //'w' wVal and sVal are valid
+ cws.value.type = DBVT_WORD;
+ cws.value.wVal = (WORD)atoi(value + 1);
+ break;
+ case 'd': //'d' dVal and lVal are valid
+ cws.value.type = DBVT_DWORD;
+ cws.value.dVal = (DWORD)_atoi64(value + 1);
+// cws.value.dVal = (DWORD)atoi(value + 1);
+ break;
+ case 's': //'s' pszVal is valid
+ cws.value.type = DBVT_ASCIIZ;
+ cws.value.pszVal = (LPSTR)mir_utf8decodeA((LPSTR)(value + 1));
+ break;
+ case 'u':
+ cws.value.type = DBVT_UTF8;
+ cws.value.pszVal = (LPSTR)mir_strdup((LPSTR)(value + 1));
+ break;
+ case 'n':
+ len = strlen(value + 1);
+ baselen = Base64DecodeGetRequiredLength(len);
+ cws.value.type = DBVT_BLOB;
+ cws.value.pbVal = (PBYTE)mir_alloc(baselen +1);
+ if (cws.value.pbVal != NULL){
+ if (Base64Decode((value + 1), len, cws.value.pbVal, &baselen)) {
+ cws.value.cpbVal = baselen;
+ }
+ else {
+ mir_free(cws.value.pbVal);
+ return ERROR_NOT_ADDED;
+ }
+ }
+ break;
+ default:
+ return ERROR_INVALID_TYPE;
+ }
+ // write value to db
+ if (CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)_hContact, (LPARAM)&cws)) {
+ //if (cws.value.pbVal>0)
+ mir_free(cws.value.pbVal);
+ if(cws.value.type == DBVT_ASCIIZ || cws.value.type == DBVT_UTF8) mir_free(cws.value.pszVal);
+ return ERROR_NOT_ADDED;
+ }
+ //if (cws.value.pbVal>0)
+ mir_free(cws.value.pbVal);
+ if(cws.value.type == DBVT_ASCIIZ || cws.value.type == DBVT_UTF8) mir_free(cws.value.pszVal);
+ return ERROR_OK;
+}
+
+/**
+ * name: ImportEvent
+ * class: CExImContactXML
+ * desc: interprete an xmlnode and add the corresponding event to database.
+ * params: hContact - handle to the contact, who is the owner of the setting to import
+ * xmlModule - xmlnode representing the module to write the setting to in the database
+ * xmlEvent - xmlnode representing the event to import
+ * return: ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportEvent(LPCSTR pszModule, TiXmlElement *xmlEvent)
+{
+ DBEVENTINFO dbei;
+ TiXmlText *xmlValue;
+ LPCSTR tmp;
+ size_t cbSrc;
+ INT_PTR baselen;
+
+ // dont import events from metacontact
+ if (isMeta()) {
+ return ERROR_DUPLICATED;
+ }
+
+ if (!xmlEvent || !pszModule || !*pszModule)
+ return ERROR_INVALID_PARAMS;
+
+ if (stricmp(xmlEvent->Value(), "evt"))
+ return ERROR_NOT_ADDED;
+
+ // timestamp must be valid
+ xmlEvent->Attribute("time", (LPINT)&dbei.timestamp);
+ if (dbei.timestamp == 0) return ERROR_INVALID_TIMESTAMP;
+
+ xmlValue = (TiXmlText*)xmlEvent->FirstChild();
+ if (!xmlValue || xmlValue->Type() != TiXmlText::TEXT)
+ return ERROR_INVALID_VALUE;
+ tmp = xmlValue->Value();
+ if (!tmp || tmp[0] == 0)
+ return ERROR_INVALID_VALUE;
+
+ cbSrc = strlen(tmp);
+ baselen = Base64DecodeGetRequiredLength(cbSrc);
+ dbei.cbBlob = NULL;
+ dbei.pBlob = NULL;
+ dbei.pBlob = (PBYTE)mir_alloc(baselen + 1);
+ if (dbei.pBlob != NULL) {
+ if (Base64Decode(tmp, cbSrc, dbei.pBlob, &baselen)) {
+ INT_PTR hEvent;
+
+ // event owning module
+ dbei.cbSize = sizeof(dbei);
+ dbei.szModule = (LPSTR)pszModule;
+ dbei.cbBlob = baselen;
+
+ xmlEvent->Attribute("type", (LPINT)&dbei.eventType);
+ xmlEvent->Attribute("flag", (LPINT)&dbei.flags);
+ if (dbei.flags == 0) dbei.flags = DBEF_READ;
+
+ // search in new and existing contact for existing event to avoid duplicates
+ if (/*!_isNewContact && */DB::Event::Exists(_hContact, _hEvent, &dbei)) {
+ mir_free(dbei.pBlob);
+ return ERROR_DUPLICATED;
+ }
+
+ hEvent = CallService(MS_DB_EVENT_ADD, (WPARAM)_hContact, (LPARAM)&dbei);
+ mir_free(dbei.pBlob);
+ if (hEvent) {
+ _hEvent = (HANDLE)hEvent;
+ return ERROR_OK;
+ }
+ }
+ mir_free(dbei.pBlob);
+ }
+ return ERROR_NOT_ADDED;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactXML.h b/plugins/UserInfoEx/src/ex_import/classExImContactXML.h
new file mode 100644
index 0000000000..52ad2ef087
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactXML.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactXML.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _CLASS_EXIM_CONTACT_XML_INCLUDED_
+#define _CLASS_EXIM_CONTACT_XML_INCLUDED_ 1
+
+#include "svc_ExImport.h"
+#include "classExImContactBase.h"
+#include "svc_ExImXML.h"
+#include "dlg_ExImProgress.h"
+
+#define XKEY_MOD "MOD"
+#define XKEY_SET "SET"
+#define XKEY_EVT "evt"
+#define XKEY_CONTACT "CONTACT"
+#define XKEY_OWNER "OWNER"
+
+enum EError {
+ ERROR_OK = 0,
+ ERROR_NOT_ADDED = 1,
+ ERROR_INVALID_PARAMS = 2,
+ ERROR_INVALID_VALUE = 3,
+ ERROR_INVALID_TIMESTAMP = 4,
+ ERROR_INVALID_TYPE = 5,
+ ERROR_DUPLICATED = 6,
+ ERROR_MEMORY_ALLOC = 7,
+ ERROR_INVALID_CONTACT = 8,
+ ERROR_INVALID_SIGNATURE = 9,
+ ERROR_ABORTED = 10,
+ ERROR_CONVERT_METACONTACT = 11,
+ ERROR_ADDTO_METACONTACT = 12,
+ ERROR_EMPTY_MODULE = 13
+};
+
+class CExImContactXML : public CExImContactBase {
+
+ CFileXml* _pXmlFile; // the xmlfile
+ TiXmlElement* _xmlNode; // xmlnode with contact information
+ HANDLE _hEvent;
+
+ BOOLEAN IsContactInfo(LPCSTR pszKey);
+
+ // private importing methods
+ INT ImportModule(TiXmlNode* xmlModule);
+ INT ImportSetting(LPCSTR pszModule, TiXmlElement *xmlEntry);
+ INT ImportEvent(LPCSTR pszModule, TiXmlElement *xmlEvent);
+ INT ImportContact();
+ INT ImportNormalContact();
+ INT ImportMetaSubContact(CExImContactXML * pMetaContact);
+ VOID CountKeys(DWORD &numSettings, DWORD &numEvents);
+
+ // private exporting methods
+ INT ExportModule(LPCSTR pszModule);
+ INT ExportSetting(TiXmlElement *xmlModule, LPCSTR pszModule, LPCSTR pszSetting);
+ BOOLEAN ExportEvents();
+
+ INT ExportContact(DB::CEnumList* pModules);
+ INT ExportSubContact(CExImContactXML *vMetaContact, DB::CEnumList* pModules);
+
+public:
+ CExImContactXML(CFileXml * pXmlFile);
+
+ // exporting stuff
+ TiXmlElement* CreateXmlElement();
+ INT Export(FILE *xmlfile, DB::CEnumList* pModules);
+
+ // importing stuff
+ INT LoadXmlElemnt(TiXmlElement *xContact);
+ INT Import(BOOLEAN keepMetaSubContact = FALSE);
+
+ BOOLEAN operator = (TiXmlElement* xmlContact) {
+ return LoadXmlElemnt(xmlContact) == ERROR_OK;
+ }
+};
+
+#endif /* _CLASS_EXIM_CONTACT_XML_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp
new file mode 100644
index 0000000000..9ca27d7289
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp
@@ -0,0 +1,464 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImModules.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+//#include "svc_ExImport.h"
+#include "dlg_ExImModules.h"
+
+/***********************************************************************************************************
+ * typedefs
+ ***********************************************************************************************************/
+
+typedef struct {
+ lpExImParam ExImContact;
+ DB::CEnumList* pModules;
+} EXPORTDATA, *LPEXPORTDATA;
+
+/***********************************************************************************************************
+ * modules stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: ExportTree_AppendModuleList
+ * desc: according to the checked list items create the module list for exporting
+ * param: hTree - handle to the window of the treeview
+ * hParent - parent tree item for the item to add
+ * pModules - module list to fill
+ * return: nothing
+ **/
+void ExportTree_AppendModuleList(HWND hTree, HTREEITEM hParent, DB::CEnumList* pModules)
+{
+ TVITEMA tvi;
+
+ // add all checked modules
+ if (tvi.hItem = TreeView_GetChild(hTree, hParent)) {
+ CHAR szModule[MAXSETTING];
+
+ // add optional items
+ tvi.mask = TVIF_STATE|TVIF_TEXT;
+ tvi.stateMask = TVIS_STATEIMAGEMASK;
+ tvi.pszText = szModule;
+ tvi.cchTextMax = MAXSETTING;
+
+ do {
+ if (
+ SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi) &&
+ (
+ tvi.state == INDEXTOSTATEIMAGEMASK(0) ||
+ tvi.state == INDEXTOSTATEIMAGEMASK(2)
+ )
+ )
+ {
+ pModules->Insert(tvi.pszText);
+ }
+ }
+ while (tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem));
+ }
+}
+
+/**
+ * name: ExportTree_FindItem
+ * desc: find a item by its label
+ * param: hTree - handle to the window of the treeview
+ * hParent - parent tree item for the item to add
+ * pszText - text to match the label against
+ * return: a handle to the found treeitem or NULL
+ **/
+HTREEITEM ExportTree_FindItem(HWND hTree, HTREEITEM hParent, LPSTR pszText)
+{
+ TVITEMA tvi;
+ CHAR szBuf[128];
+
+ if (!pszText || !*pszText) return NULL;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = szBuf;
+ tvi.cchTextMax = SIZEOF(szBuf);
+
+ for (tvi.hItem = TreeView_GetChild(hTree, hParent);
+ tvi.hItem != NULL;
+ tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem))
+ {
+ if (SendMessageA(hTree, TVM_GETITEMA, NULL, (LPARAM)&tvi) && !mir_stricmp(tvi.pszText, pszText))
+ return tvi.hItem;
+ }
+ return NULL;
+}
+
+/**
+ * name: ExportTree_AddItem
+ * desc: add an item to the tree view with given options
+ * param: hTree - handle to the window of the treeview
+ * hParent - parent tree item for the item to add
+ * pszDesc - item label
+ * bUseImages - icons are loaded
+ * bState - 0-hide checkbox/1-unchecked/2-checked
+ * return: return handle to added treeitem
+ **/
+HTREEITEM ExportTree_AddItem(HWND hTree, HTREEITEM hParent, LPSTR pszDesc, BOOLEAN bUseImages, BYTE bState)
+{
+ TVINSERTSTRUCTA tvii;
+ HTREEITEM hItem = NULL;
+
+ tvii.hParent = hParent;
+ tvii.hInsertAfter = TVI_SORT;
+ tvii.itemex.mask = TVIF_TEXT;
+ if (bUseImages) {
+ tvii.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvii.itemex.iImage = tvii.itemex.iSelectedImage = 1;
+ }
+ tvii.itemex.pszText = pszDesc;
+ if (hItem = (HTREEITEM)SendMessageA(hTree, TVM_INSERTITEMA, NULL, (LPARAM)&tvii))
+ TreeView_SetItemState(hTree, hItem, INDEXTOSTATEIMAGEMASK(bState), TVIS_STATEIMAGEMASK);
+ return hItem;
+}
+
+/**
+ * name: SelectModulesToExport_DlgProc
+ * desc: dialog procedure for a dialogbox, which lists modules for a specific contact
+ * param: hDlg - handle to the window of the dialogbox
+ * uMsg - message to handle
+ * wParam - message specific parameter
+ * lParam - message specific parameter
+ * return: message specific
+ **/
+INT_PTR CALLBACK SelectModulesToExport_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LPEXPORTDATA pDat = (LPEXPORTDATA)GetUserData(hDlg);
+
+ switch (uMsg) {
+
+ case WM_INITDIALOG:
+ {
+ HWND hTree;
+ BOOLEAN bImagesLoaded = 0;
+
+ // get tree handle and set treeview style
+ if (!(hTree = GetDlgItem(hDlg, IDC_TREE))) break;
+ SetWindowLongPtr(hTree, GWL_STYLE, GetWindowLongPtr(hTree, GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+ // init the datastructure
+ if (!(pDat = (LPEXPORTDATA)mir_alloc(sizeof(EXPORTDATA))))
+ return FALSE;
+ pDat->ExImContact = ((LPEXPORTDATA)lParam)->ExImContact;
+ pDat->pModules = ((LPEXPORTDATA)lParam)->pModules;
+ SetUserData(hDlg, pDat);
+
+ // set icons
+ {
+ HICON hIcon;
+ HIMAGELIST hImages;
+ OSVERSIONINFO osvi;
+ const ICONCTRL idIcon[] = {
+ { ICO_DLG_EXPORT, WM_SETICON, NULL },
+ { ICO_DLG_EXPORT, STM_SETIMAGE, ICO_DLGLOGO },
+ { ICO_BTN_EXPORT, BM_SETIMAGE, IDOK },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }
+ };
+ const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+ IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+ // create imagelist for treeview
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ if ((hImages = ImageList_Create(
+ GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON),
+ ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1) ? ILC_COLOR32 : ILC_COLOR16)|ILC_MASK,
+ 0, 1)
+ ) != NULL)
+ {
+ SendMessage(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImages);
+
+ bImagesLoaded
+ = ((((hIcon = IcoLib_GetIcon(ICO_LST_MODULES)) != NULL) && 0 == ImageList_AddIcon(hImages, hIcon))
+ && (((hIcon = IcoLib_GetIcon(ICO_LST_FOLDER)) != NULL) && 1 == ImageList_AddIcon(hImages, hIcon)));
+ }
+ }
+ // do the translation stuff
+ {
+ SendDlgItemMessage(hDlg, BTN_CHECK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, BTN_UNCHECK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ }
+ // Set the Window Title and description
+ {
+ LPCTSTR name = NULL;
+ TCHAR oldTitle[MAXDATASIZE],
+ newTitle[MAXDATASIZE];
+ switch (pDat->ExImContact->Typ) {
+ case EXIM_ALL:
+ case EXIM_GROUP:
+ name = TranslateT("All Contacts");
+ break;
+ case EXIM_CONTACT:
+ if (pDat->ExImContact->hContact == NULL) {
+ name = TranslateT("Owner");
+ }
+ else {
+ name = DB::Contact::DisplayName(pDat->ExImContact->hContact);
+ }
+ break;
+ case EXIM_SUBGROUP:
+ name = (LPCTSTR) pDat->ExImContact->ptszName;
+ break;
+ case EXIM_ACCOUNT:
+ PROTOACCOUNT* acc = ProtoGetAccount(pDat->ExImContact->pszName);
+ name = (LPCTSTR) acc->tszAccountName;
+ break;
+ }
+ TranslateDialogDefault(hDlg); //to translate oldTitle
+ GetWindowText(hDlg, oldTitle, MAXSETTING);
+ mir_sntprintf(newTitle, MAXDATASIZE - 1, _T("%s - %s"), name, oldTitle);
+ SetWindowText(hDlg, newTitle);
+ }
+
+ {
+ LPSTR pszProto;
+ TVINSERTSTRUCT tviiT;
+ DB::CEnumList Modules;
+ HTREEITEM hItemEssential, hItemOptional;
+
+ TreeView_SetIndent(hTree, 6);
+ TreeView_SetItemHeight(hTree, 18);
+
+ pszProto = (pDat->ExImContact->Typ == EXIM_CONTACT && pDat->ExImContact->hContact != NULL)
+ ? (LPSTR)DB::Contact::Proto(pDat->ExImContact->hContact)
+ : NULL;
+
+ // add items that are always exported
+ tviiT.hParent = TVI_ROOT;
+ tviiT.hInsertAfter = TVI_FIRST;
+ tviiT.itemex.mask = TVIF_TEXT|TVIF_STATE;
+ tviiT.itemex.pszText = TranslateT("Required modules");
+ tviiT.itemex.state = tviiT.itemex.stateMask = TVIS_EXPANDED;
+ if (bImagesLoaded) {
+ tviiT.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tviiT.itemex.iImage = tviiT.itemex.iSelectedImage = 0;
+ }
+ if (hItemEssential = TreeView_InsertItem(hTree, &tviiT)) {
+ // disable state images
+ TreeView_SetItemState(hTree, hItemEssential, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
+
+ // insert essential items (modul from UIEX)
+ ExportTree_AddItem(hTree, hItemEssential, USERINFO, bImagesLoaded, 0);
+ ExportTree_AddItem(hTree, hItemEssential, MOD_MBIRTHDAY, bImagesLoaded, 0);
+
+ /*Filter/ protocol module is ignored for owner contact
+ if (pDat->hContact != NULL) {
+ ExportTree_AddItem(hTree, hItemEssential, "Protocol", bImagesLoaded, 0);
+ }*/
+
+ // base protocol is only valid for single exported contact at this position
+ if (pszProto) {
+ ExportTree_AddItem(hTree, hItemEssential, pszProto, bImagesLoaded, 0);
+ }
+ }
+
+ // add items that are optional (and more essential)
+ tviiT.hInsertAfter = TVI_LAST;
+ tviiT.itemex.pszText = TranslateT("Optional modules");
+ if (hItemOptional = TreeView_InsertItem(hTree, &tviiT)) {
+ TreeView_SetItemState(hTree, hItemOptional, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
+
+ if (!Modules.EnumModules()) // init Modul list
+ {
+ INT i;
+ LPSTR p;
+
+ for (i = 0; i < Modules.getCount(); i++)
+ {
+ p = Modules[i];
+ /*Filter/
+ if (!DB::Module::IsMeta(p))/end Filter*/
+ {
+ // module must exist in at least one contact
+ if (pDat->ExImContact->Typ != EXIM_CONTACT) // TRUE = All Contacts
+ {
+ HANDLE hContact;
+
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ // ignore empty modules
+ if (!DB::Module::IsEmpty(hContact, p)) {
+ pszProto = DB::Contact::Proto(hContact);
+ // Filter by mode
+ switch (pDat->ExImContact->Typ)
+ {
+ case EXIM_ALL:
+ case EXIM_GROUP:
+ break;
+ case EXIM_SUBGROUP:
+ if (mir_tcsncmp(pDat->ExImContact->ptszName, DB::Setting::GetTString(hContact, "CList", "Group"), mir_tcslen(pDat->ExImContact->ptszName))) {
+ continue;
+ }
+ break;
+ case EXIM_ACCOUNT:
+ if (mir_strcmp(pDat->ExImContact->pszName, pszProto)) {
+ continue;
+ }
+ break;
+ }
+
+ // contact's base protocol is to be added to the treeview uniquely
+ if (!mir_stricmp(p, pszProto))
+ {
+ if (!ExportTree_FindItem(hTree, hItemEssential, p))
+ {
+ ExportTree_AddItem(hTree, hItemEssential, p, bImagesLoaded, 0);
+ }
+ break;
+ }
+
+ // add optional module, which is valid for at least one contact
+ /*/Filter/*/
+ if ( mir_stricmp(p, USERINFO) &&
+ mir_stricmp(p, MOD_MBIRTHDAY) &&
+ // Meta is only valid as base Proto at this point
+ mir_stricmp(p, myGlobals.szMetaProto) /*&&
+ mir_stricmp(p, "Protocol")*/
+ )
+ {
+ ExportTree_AddItem(hTree, hItemOptional, p, bImagesLoaded, 1);
+ break;
+ }
+ }
+ } // end for
+ } // end TRUE = All Contacts
+
+ // module must exist in the selected contact
+ else if (
+ /*Filter/*/
+ !DB::Module::IsEmpty(pDat->ExImContact->hContact, p) &&
+ (!pDat->ExImContact->hContact || mir_stricmp(p, pszProto)) &&
+ //mir_stricmp(p, "Protocol") &&
+ mir_stricmp(p, USERINFO) &&
+ mir_stricmp(p, MOD_MBIRTHDAY))
+ {
+ ExportTree_AddItem(hTree, hItemOptional, (LPSTR)p, bImagesLoaded, 1);
+ }
+ } // end
+ }
+ }
+ }
+ }
+ TranslateDialogDefault(hDlg);
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ if (GetDlgItem(hDlg, STATIC_WHITERECT) == (HWND)lParam || GetDlgItem(hDlg, ICO_DLGLOGO) == (HWND)lParam) {
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetStockObject(NULL_BRUSH);
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ {
+ HWND hTree = GetDlgItem(hDlg, IDC_TREE);
+ HTREEITEM hParent;
+
+ // search the tree item of optional items
+ for (hParent = TreeView_GetRoot(hTree);
+ hParent != NULL;
+ hParent = TreeView_GetNextSibling(hTree, hParent))
+ {
+ ExportTree_AppendModuleList(hTree, hParent, pDat->pModules);
+ }
+ return EndDialog(hDlg, IDOK);
+ }
+ case IDCANCEL:
+ return EndDialog(hDlg, IDCANCEL);
+
+ case BTN_CHECK:
+ case BTN_UNCHECK:
+ {
+ HWND hTree = GetDlgItem(hDlg, IDC_TREE);
+ LPCSTR pszRoot = Translate("Optional modules");
+ TVITEMA tvi;
+ CHAR szText[128];
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = szText;
+ tvi.cchTextMax = sizeof(szText);
+
+ // search the tree item of optional items
+ for (tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, NULL);
+ tvi.hItem != NULL && SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
+ tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
+ {
+ if (!mir_stricmp(tvi.pszText, pszRoot)) {
+ tvi.mask = TVIF_STATE;
+ tvi.state = INDEXTOSTATEIMAGEMASK(LOWORD(wParam) == BTN_UNCHECK ? 1 : 2);
+ tvi.stateMask = TVIS_STATEIMAGEMASK;
+
+ for (tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)tvi.hItem);
+ tvi.hItem != NULL;
+ tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
+ {
+ SendMessageA(hTree, TVM_SETITEMA, NULL, (LPARAM)&tvi);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ mir_free(pDat);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * name: DlgExImModules_SelectModulesToExport
+ * desc: calls a dialog box that lists all modules for a specific contact
+ * param: ExImContact - lpExImParam
+ * pModules - pointer to an ENUMLIST structure that retrieves the resulting list of modules
+ * hParent - handle to a window which should act as the parent of the created dialog
+ * return: 0 if user pressed ok, 1 on cancel
+ **/
+INT DlgExImModules_SelectModulesToExport(lpExImParam ExImContact, DB::CEnumList* pModules, HWND hParent)
+{
+ EXPORTDATA dat;
+
+ dat.ExImContact = ExImContact;
+ dat.pModules = pModules;
+ return (IDOK != DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_EXPORT), hParent, SelectModulesToExport_DlgProc, (LPARAM)&dat));
+}
+
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h
new file mode 100644
index 0000000000..69ca65ddd3
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImModules.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMMODULES_H_
+#define _DLG_EXIMMODULES_H_
+
+#pragma once
+#include "svc_ExImport.h"
+
+INT DlgExImModules_SelectModulesToExport(lpExImParam ExImContact, DB::CEnumList* pModules, HWND hParent);
+
+#endif /* _DLG_EXIMMODULES_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp
new file mode 100644
index 0000000000..d9d0efbd7d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp
@@ -0,0 +1,371 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImOpenSaveFile.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+
+ #include <dlgs.h>
+
+
+#include "m_db3xSA.h"
+#include "dlg_ExImOpenSaveFile.h"
+
+
+
+#define HKEY_MIRANDA_PLACESBAR _T("Software\\Miranda IM\\PlacesBar")
+#define HKEY_WINPOL_PLACESBAR _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\ComDlg32\\PlacesBar")
+
+static WNDPROC DefPlacesBarProc;
+
+/**
+ * This function maps the current users registry to a dummy key and
+ * changes the policy hive which is responsible for the places to be displayed,
+ * so the desired places are visible.
+ *
+ * @param nothing
+ * @return nothing
+ **/
+static VOID InitAlteredPlacesBar()
+{
+ // do not try it on a win9x Box
+ if (IsWinVer2000Plus())
+ {
+ HKEY hkMiranda;
+ LONG result;
+
+ // create or open temporary hive for miranda specific places
+ result = RegCreateKey(HKEY_CURRENT_USER, HKEY_MIRANDA_PLACESBAR, &hkMiranda);
+ if (SUCCEEDED(result))
+ {
+ HKEY hkPlacesBar;
+
+ // map the current users registry
+ RegOverridePredefKey(HKEY_CURRENT_USER, hkMiranda);
+ // open the policy key
+ result = RegCreateKey(HKEY_CURRENT_USER, HKEY_WINPOL_PLACESBAR, &hkPlacesBar);
+ // install the places bar
+ if (SUCCEEDED(result))
+ {
+ DWORD dwFolderID;
+ LPSTR p;
+ CHAR szMirandaPath[MAX_PATH];
+ CHAR szProfilePath[MAX_PATH];
+
+ // default places: Desktop, My Documents, My Computer
+ dwFolderID = 0; RegSetValueEx(hkPlacesBar, _T("Place0"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+ dwFolderID = 5; RegSetValueEx(hkPlacesBar, _T("Place1"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+ dwFolderID = 17; RegSetValueEx(hkPlacesBar, _T("Place2"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+
+ // Miranda's installation path
+ GetModuleFileNameA(GetModuleHandle(NULL), szMirandaPath, SIZEOF(szMirandaPath));
+ p = mir_strrchr(szMirandaPath, '\\');
+ if (p)
+ {
+ RegSetValueExA(hkPlacesBar, "Place3", 0, REG_SZ, (PBYTE)szMirandaPath, (p - szMirandaPath) + 1);
+ }
+
+ // Miranda's profile path
+ if (!CallService(MS_DB_GETPROFILEPATH, SIZEOF(szProfilePath), (LPARAM)szProfilePath))
+ {
+ // only add if different from profile path
+ RegSetValueExA(hkPlacesBar, "Place4", 0, REG_SZ, (PBYTE)szProfilePath, (DWORD)strlen(szProfilePath) + 1);
+ }
+
+ RegCloseKey(hkPlacesBar);
+ }
+ RegCloseKey(hkMiranda);
+ }
+ }
+}
+
+/**
+ * name: ResetAlteredPlaceBars
+ * desc: Remove the mapping of current users registry
+ * and delete the temporary registry hive
+ * params: nothing
+ * return: nothing
+ **/
+static VOID ResetAlteredPlaceBars()
+{
+ // make sure not to call the following on a Win9x Box
+ if (IsWinVer2000Plus())
+ {
+ RegOverridePredefKey(HKEY_CURRENT_USER, NULL);
+ SHDeleteKey(HKEY_CURRENT_USER, HKEY_MIRANDA_PLACESBAR);
+ }
+}
+
+/**
+ * name: PlacesBarSubclassProc
+ * params: hWnd - handle, to control's window
+ * uMsg - the message to handle
+ * wParam - message dependend parameter
+ * lParam - message dependend parameter
+ * return: depends on message
+ **/
+static LRESULT PlacesBarSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case TB_ADDBUTTONS:
+ {
+ TBBUTTON *tbb = (TBBUTTON *)lParam;
+ TCHAR szBtnText[MAX_PATH];
+ INT iString;
+ HWND hWndToolTip;
+
+ if (tbb)
+ {
+ switch (tbb->idCommand)
+ {
+ // miranda button
+ case 41063:
+ ZeroMemory(szBtnText, sizeof(szBtnText));
+
+ mir_tcsncpy(szBtnText, TranslateT("Miranda IM"), SIZEOF(szBtnText));
+ iString = SendMessage(hWnd, TB_ADDSTRING, NULL, (LPARAM)szBtnText);
+ if (iString != -1) tbb->iString = iString;
+ // set tooltip
+ hWndToolTip = (HWND)SendMessage(hWnd, TB_GETTOOLTIPS, NULL, NULL);
+ if (hWndToolTip) {
+ TOOLINFO ti;
+
+ ZeroMemory(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.hwnd = hWnd;
+ ti.lpszText = TranslateT("Shows Miranda's installation directory.");
+ ti.uId = tbb->idCommand;
+ SendMessage(hWndToolTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+ }
+ break;
+ // profile button
+ case 41064:
+ // set button text
+ iString = SendMessage(hWnd, TB_ADDSTRING, NULL, (LPARAM) TranslateT("Profile"));
+ if (iString != -1) tbb->iString = iString;
+
+ // set tooltip
+ hWndToolTip = (HWND)SendMessage(hWnd, TB_GETTOOLTIPS, NULL, NULL);
+ if (hWndToolTip) {
+ TOOLINFO ti;
+
+ ZeroMemory(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.hwnd = hWnd;
+ ti.lpszText = TranslateT("Shows the directory with all your Miranda's profiles.");
+ ti.uId = tbb->idCommand;
+ SendMessage(hWndToolTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+ }
+ // unmap registry and delete keys
+ ResetAlteredPlaceBars();
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return CallWindowProc(DefPlacesBarProc, hWnd, uMsg, wParam,lParam);
+}
+
+/**
+ * name: OpenSaveFileDialogHook
+ * desc: it subclasses the places bar to provide the own interface for adding places
+ * params: hDlg - handle, to control's window
+ * uMsg - the message to handle
+ * wParam - message dependend parameter
+ * lParam - message dependend parameter
+ * return: depends on message
+ **/
+static UINT_PTR CALLBACK OpenSaveFileDialogHook(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == CDN_INITDONE) {
+ HWND hPlacesBar = GetDlgItem(GetParent(hDlg), ctl1);
+
+ // we have a places bar?
+ if (hPlacesBar != NULL) {
+ InitAlteredPlacesBar();
+ // finally subclass the places bar
+ DefPlacesBarProc = SubclassWindow(hPlacesBar, PlacesBarSubclassProc);
+ }
+ }
+ break;
+ case WM_DESTROY:
+ // unmap registry and delete keys
+ // (is to make it sure, if somehow the last places button was not added which also calls this function)
+ ResetAlteredPlaceBars();
+ break;
+ }
+ return FALSE;
+}
+
+
+
+/**
+ * name: GetInitialDir
+ * desc: read the last vCard directory from database
+ * pszInitialDir - buffer to store the initial dir to (size must be MAX_PATH)
+ * return: nothing
+ **/
+static VOID GetInitialDir(LPSTR pszInitialDir)
+{
+ CHAR szRelative[MAX_PATH];
+
+ ZeroMemory(szRelative, SIZEOF(szRelative));
+
+ // is some standard path defined
+ if (!DB::Setting::GetStatic(0, MODNAME, "vCardPath", szRelative, SIZEOF(szRelative))) {
+ if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)szRelative, (WPARAM)pszInitialDir))
+ strcpy(pszInitialDir, szRelative);
+ }
+ else if (//try to use environment variables supported by pathpatch of db3xSA
+ !ServiceExists(MS_DB_GETPROFILEPATH_BASIC) ||
+ !CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)PROFILEPATH "\\" PROFILENAME, (WPARAM)pszInitialDir)
+ ) {
+ // use standard path to absolute
+ if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)"", (WPARAM)pszInitialDir))
+ *pszInitialDir = 0;
+ }
+ else
+ *pszInitialDir = 0;
+}
+
+/**
+ * name: SaveInitialDir
+ * desc: save the last vCard directory from database
+ * pszInitialDir - buffer to store the initial dir to (size must be MAX_PATH)
+ * return: nothing
+ **/
+static VOID SaveInitialDir(LPSTR pszInitialDir)
+{
+ CHAR szRelative[MAX_PATH];
+ LPSTR p;
+
+ if (p = mir_strrchr(pszInitialDir, '\\')) {
+ *p = 0;
+ if (CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)pszInitialDir, (LPARAM)szRelative))
+ DB::Setting::WriteAString(0, MODNAME, "vCardPath", szRelative);
+ else
+ DB::Setting::WriteAString(0, MODNAME, "vCardPath", pszInitialDir);
+ *p = '\\';
+ }
+}
+
+/**
+ * name: InitOpenFileNameStruct
+ * desc: initialize the openfilename structure
+ * params: pofn - OPENFILENAME structure to initialize
+ * hWndParent - parent window
+ * pszTitle - title for the dialog
+ * pszFilter - the filters to offer
+ * pszInitialDir - buffer to store the initial dir to (size must be MAX_PATH)
+ * pszFile - this is the buffer to store the file to (size must be MAX_PATH)
+ * return: nothing
+ **/
+static VOID InitOpenFileNameStruct(OPENFILENAMEA *pofn, HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszInitialDir, LPSTR pszFile)
+{
+ ZeroMemory(pofn, sizeof(OPENFILENAME));
+
+ pofn->Flags = OFN_NONETWORKBUTTON|OFN_ENABLESIZING;
+ pofn->hwndOwner = hWndParent;
+ pofn->lpstrTitle = pszTitle;
+ pofn->lpstrFilter = pszFilter;
+ pofn->lpstrFile = pszFile;
+ pofn->nMaxFile = MAX_PATH;
+ pofn->lpstrDefExt = "xml";
+
+ GetInitialDir(pszInitialDir);
+ pofn->lpstrInitialDir = pszInitialDir;
+
+
+ if (IsWinVer2000Plus()) {
+ pofn->lStructSize = sizeof (OPENFILENAME);
+ pofn->Flags |= OFN_ENABLEHOOK|OFN_EXPLORER;
+ pofn->lpfnHook = (LPOFNHOOKPROC)OpenSaveFileDialogHook;
+ }
+ else {
+ pofn->lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ }
+
+}
+
+
+/**
+ * name: DlgExIm_OpenFileName
+ * desc: displayes a slightly modified OpenFileName DialogBox
+ * params: hWndParent - parent window
+ * pszTitle - title for the dialog
+ * pszFilter - the filters to offer
+ * pszFile - this is the buffer to store the file to (size must be MAX_PATH)
+ * return: -1 on error/abort or filter index otherwise
+ **/
+INT DlgExIm_OpenFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile)
+{
+ OPENFILENAMEA ofn;
+ CHAR szInitialDir[MAX_PATH];
+
+ InitOpenFileNameStruct(&ofn, hWndParent, pszTitle, pszFilter, szInitialDir, pszFile);
+ ofn.Flags |= OFN_PATHMUSTEXIST;
+ if (!GetOpenFileNameA(&ofn)) {
+ DWORD dwError = CommDlgExtendedError();
+ if (dwError) MsgErr(ofn.hwndOwner, LPGENT("The OpenFileDialog returned an error: %d!"), dwError);
+ return -1;
+ }
+ SaveInitialDir(pszFile);
+ return ofn.nFilterIndex;
+}
+
+/**
+ * name: DlgExIm_SaveFileName
+ * desc: displayes a slightly modified SaveFileName DialogBox
+ * params: hWndParent - parent window
+ * pszTitle - title for the dialog
+ * pszFilter - the filters to offer
+ * pszFile - this is the buffer to store the file to (size must be MAX_PATH)
+ * return: -1 on error/abort or filter index otherwise
+ **/
+INT DlgExIm_SaveFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile)
+{
+ OPENFILENAMEA ofn;
+ CHAR szInitialDir[MAX_PATH];
+
+ InitOpenFileNameStruct(&ofn, hWndParent, pszTitle, pszFilter, szInitialDir, pszFile);
+ ofn.Flags |= OFN_OVERWRITEPROMPT;
+
+ if (!GetSaveFileNameA(&ofn)) {
+ DWORD dwError = CommDlgExtendedError();
+
+ if (dwError) MsgErr(ofn.hwndOwner, LPGENT("The SaveFileDialog returned an error: %d!"), dwError);
+ return -1;
+ }
+ SaveInitialDir(pszFile);
+ return ofn.nFilterIndex;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h
new file mode 100644
index 0000000000..eb47e9ab30
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImOpenSaveFile.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMOPENSAVEFFILE_H_
+#define _DLG_EXIMOPENSAVEFFILE_H_
+
+#pragma once
+
+INT DlgExIm_OpenFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile);
+INT DlgExIm_SaveFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile);
+
+#endif /* _DLG_EXIMOPENSAVEFFILE_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp
new file mode 100644
index 0000000000..9da4ee4373
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp
@@ -0,0 +1,244 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImProgress.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_ExImProgress.h"
+
+/***********************************************************************************************************
+ * windows procedure
+ ***********************************************************************************************************/
+
+/**
+ * name: DlgProcProgress
+ * desc: dialog procedure for the progress dialog
+ * params: none
+ * return: nothing
+ **/
+LRESULT CALLBACK DlgProcProgress(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_DLG_IMPORT, WM_SETICON, NULL },
+ { ICO_DLG_IMPORT, STM_SETIMAGE, ICO_DLGLOGO },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }
+ };
+ const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+ IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+ TranslateDialogDefault(hDlg);
+ SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0);
+ SendDlgItemMessage(hDlg, IDC_PROGRESS2, PBM_SETPOS, 0, 0);
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, 0);
+ UpdateWindow(hDlg);
+ break;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+ //case IDC_HEADERBAR
+ case STATIC_WHITERECT:
+ case TXT_SETTING:
+ case IDC_PROGRESS:
+ case TXT_CONTACT:
+ case IDC_PROGRESS2:
+ //case ICO_DLGLOGO:
+ //case IDC_INFO:
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+ return FALSE;
+ case WM_COMMAND:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ // in the progress dialog, use the user data to indicate that the user has pressed cancel
+ ShowWindow(hDlg, SW_HIDE);
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, 1);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * name: CProgress
+ * class: CProgress
+ * desc: create the progress dialog and return a handle as pointer to the datastructure
+ * params: none
+ * return: nothing
+ **/
+CProgress::CProgress()
+{
+ _dwStartTime = GetTickCount();
+ _hDlg = CreateDialog(ghInst, MAKEINTRESOURCE(IDD_COPYPROGRESS), 0, (DLGPROC)DlgProcProgress);
+}
+
+/**
+ * name: ~CProgress
+ * class: CProgress
+ * desc: destroy the progress dialog and its data structure
+ * params: none
+ * return: nothing
+ **/
+CProgress::~CProgress()
+{
+ if(IsWindow(_hDlg)) DestroyWindow(_hDlg);
+}
+
+/**
+ * name: SetContactCount
+ * class: CProgress
+ * desc: number of contacts to show 100% for
+ * params: numContacts - the number of contacts
+ * return: nothing
+ **/
+VOID CProgress::SetContactCount(DWORD numContacts)
+{
+ if (_hDlg) {
+ HWND hProgress = GetDlgItem(_hDlg, IDC_PROGRESS2);
+ SendMessage(hProgress, PBM_SETRANGE32, 0, numContacts);
+ SendMessage(hProgress, PBM_SETPOS, 0, 0);
+ }
+}
+
+/**
+ * name: SetSettingsCount
+ * class: CProgress
+ * desc: number of settings & events to show 100% for
+ * params: numSettings - the number of settings & events
+ * return: nothing
+ **/
+VOID CProgress::SetSettingsCount(DWORD numSettings)
+{
+ if (_hDlg) {
+ HWND hProgress = GetDlgItem(_hDlg, IDC_PROGRESS);
+ SendMessage(hProgress, PBM_SETRANGE32, 0, numSettings);
+ SendMessage(hProgress, PBM_SETPOS, 0, 0);
+ }
+}
+
+/**
+ * name: Hide
+ * class: CProgress
+ * desc: hides the dialog
+ * params: none
+ * return: nothing
+ **/
+VOID CProgress::Hide()
+{
+ ShowWindow(_hDlg, SW_HIDE);
+}
+
+/**
+ * name: Update
+ * class: CProgress
+ * desc: update the progress dialog
+ * params: nothing
+ * return: FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::Update()
+{
+ MSG msg;
+
+ // show dialog after one second
+ if (GetTickCount() > _dwStartTime + 1000) {
+ ShowWindow(_hDlg, SW_SHOW);
+ }
+
+ UpdateWindow(_hDlg);
+
+ while (PeekMessage(&msg, _hDlg, 0, 0, PM_REMOVE) != 0) {
+ if (!IsDialogMessage(_hDlg, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ return GetWindowLongPtr(_hDlg, GWLP_USERDATA) == 0;
+}
+
+/**
+ * name: UpdateContact
+ * class: CProgress
+ * desc: increase contact's progressbar by one and set new text
+ * params: pszFormat - the text to display for the contact
+ * return: FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::UpdateContact(LPCTSTR pszFormat, ...)
+{
+ if (_hDlg != NULL) {
+ HWND hProg = GetDlgItem(_hDlg, IDC_PROGRESS2);
+ if (pszFormat) {
+ TCHAR buf[MAX_PATH];
+ va_list vl;
+
+ va_start(vl, pszFormat);
+ mir_vsntprintf(buf, SIZEOF(buf), TranslateTS(pszFormat), vl);
+ va_end(vl);
+ SetDlgItemText(_hDlg, TXT_CONTACT, buf);
+ }
+ SendMessage(hProg, PBM_SETPOS, (INT)SendMessage(hProg, PBM_GETPOS, 0, 0) + 1, 0);
+ return Update();
+ }
+ return TRUE;
+}
+
+/**
+ * name: UpdateContact
+ * class: CProgress
+ * desc: increase setting's progressbar by one and set new text
+ * params: pszFormat - the text to display for the setting
+ * return: FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::UpdateSetting(LPCTSTR pszFormat, ...)
+{
+ if (_hDlg != NULL) {
+ HWND hProg = GetDlgItem(_hDlg, IDC_PROGRESS);
+ if (pszFormat) {
+ TCHAR buf[MAX_PATH];
+ TCHAR tmp[MAX_PATH];
+ va_list vl;
+
+ va_start(vl, pszFormat);
+ mir_vsntprintf(buf, SIZEOF(buf), TranslateTS(pszFormat), vl);
+ va_end(vl);
+ GetDlgItemText(_hDlg, TXT_SETTING, tmp, SIZEOF(tmp));
+ if(mir_tcsicmp(tmp,buf))
+ SetDlgItemText(_hDlg, TXT_SETTING, buf);
+ }
+ SendMessage(hProg, PBM_SETPOS, (INT)SendMessage(hProg, PBM_GETPOS, 0, 0) + 1, 0);
+ return Update();
+ }
+ return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h
new file mode 100644
index 0000000000..55ab614cfe
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h
@@ -0,0 +1,56 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImProgress.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMPROGRESS_H_
+#define _DLG_EXIMPROGRESS_H_
+
+#pragma once
+
+class CProgress
+{
+ HWND _hDlg;
+ DWORD _dwStartTime;
+
+ BOOLEAN Update();
+
+public:
+ CProgress();
+ ~CProgress();
+
+ VOID Hide();
+
+ VOID SetContactCount(DWORD numContacts);
+ VOID SetSettingsCount(DWORD numSettings);
+
+ BOOLEAN UpdateContact(LPCTSTR pszFormat, ...);
+ BOOLEAN UpdateSetting(LPCTSTR pszFormat, ...);
+};
+
+#endif /* _DLG_EXIMPROGRESS_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h b/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h
new file mode 100644
index 0000000000..0fe287ec93
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h
@@ -0,0 +1,371 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/mir_rfcCodecs.h $
+Revision : $Revision: 190 $
+Last change on : $Date: 2010-09-14 14:32:57 +0400 (Вт, 14 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include <stdlib.h>
+
+//Not including CRLFs
+//NOTE: For BASE64 and UUENCODE, this actually
+//represents the amount of unencoded characters
+//per line
+#define TSSMTPMAX_QP_LINE_LENGTH 76
+#define TSSMTPMAX_BASE64_LINE_LENGTH 57
+#define TSSMTPMAX_UUENCODE_LINE_LENGTH 45
+
+//=======================================================================
+// Base64Encode/Base64Decode
+// compliant with RFC 2045
+//=======================================================================
+//
+#define BASE64_FLAG_NONE 0
+#define BASE64_FLAG_NOPAD 1
+#define BASE64_FLAG_NOCRLF 2
+
+inline INT_PTR Base64EncodeGetRequiredLength(INT_PTR nSrcLen, DWORD dwFlags = BASE64_FLAG_NONE)
+{
+ INT_PTR nRet = nSrcLen*4/3;
+
+ if ((dwFlags & BASE64_FLAG_NOPAD) == 0)
+ nRet += nSrcLen % 3;
+
+ INT_PTR nCRLFs = nRet / 76 + 3;
+ INT_PTR nOnLastLine = nRet % 76;
+
+ if (nOnLastLine) {
+ if (nOnLastLine % 4)
+ nRet += 4 - (nOnLastLine % 4);
+ }
+
+ nCRLFs *= 2;
+
+ if ((dwFlags & BASE64_FLAG_NOCRLF) == 0)
+ nRet += nCRLFs;
+
+ return nRet;
+}
+
+inline INT_PTR Base64DecodeGetRequiredLength(INT_PTR nSrcLen)
+{
+ return nSrcLen;
+}
+
+inline BOOL Base64Encode(
+ const BYTE *pbSrcData,
+ INT_PTR nSrcLen,
+ LPSTR szDest,
+ INT_PTR *pnDestLen,
+ DWORD dwFlags = BASE64_FLAG_NONE)
+{
+ static const char s_chBase64EncodingTable[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
+ 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+ 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
+
+ if (!pbSrcData || !szDest || !pnDestLen)
+ return FALSE;
+
+ INT_PTR nWritten(0);
+ INT_PTR nLen1((nSrcLen / 3) * 4);
+ INT_PTR nLen2(nLen1 / 76);
+ INT_PTR nLen3(19);
+ INT_PTR i, j, k, n;
+
+ for (i = 0; i <= nLen2; i++) {
+ if (i == nLen2)
+ nLen3 = (nLen1 % 76) / 4;
+
+ for (j = 0; j < nLen3; j++) {
+ DWORD dwCurr(0);
+ for (INT_PTR n = 0; n < 3; n++) {
+ dwCurr |= *pbSrcData++;
+ dwCurr <<= 8;
+ }
+ for (k = 0; k < 4; k++) {
+ BYTE b = (BYTE)(dwCurr >> 26);
+ *szDest++ = s_chBase64EncodingTable[b];
+ dwCurr <<= 6;
+ }
+ }
+ nWritten += nLen3 * 4;
+
+ if ((dwFlags & BASE64_FLAG_NOCRLF) == 0) {
+ *szDest++ = '\r';
+ *szDest++ = '\n';
+ *szDest++ = '\t'; // as vcards have tabs in second line of binary data
+ nWritten += 3;
+ }
+ }
+
+ if (nWritten && (dwFlags & BASE64_FLAG_NOCRLF) == 0) {
+ szDest -= 2;
+ nWritten -= 2;
+ }
+
+ nLen2 = nSrcLen % 3 ? nSrcLen % 3 + 1 : 0;
+ if (nLen2) {
+ DWORD dwCurr(0);
+ for (n = 0; n < 3; n++)
+ {
+ if (n < (nSrcLen % 3))
+ dwCurr |= *pbSrcData++;
+ dwCurr <<= 8;
+ }
+ for (k = 0; k < nLen2; k++) {
+ BYTE b = (BYTE)(dwCurr >> 26);
+ *szDest++ = s_chBase64EncodingTable[b];
+ dwCurr <<= 6;
+ }
+ nWritten+= nLen2;
+ if ((dwFlags & BASE64_FLAG_NOPAD) == 0) {
+ nLen3 = nLen2 ? 4 - nLen2 : 0;
+ for (j = 0; j < nLen3; j++) {
+ *szDest++ = '=';
+ }
+ nWritten+= nLen3;
+ }
+ }
+
+ *pnDestLen = nWritten;
+ return TRUE;
+}
+
+inline INT_PTR DecodeBase64Char(UINT ch) throw()
+{
+ // returns -1 if the character is invalid
+ // or should be skipped
+ // otherwise, returns the 6-bit code for the character
+ // from the encoding table
+ if (ch >= 'A' && ch <= 'Z')
+ return ch - 'A' + 0; // 0 range starts at 'A'
+ if (ch >= 'a' && ch <= 'z')
+ return ch - 'a' + 26; // 26 range starts at 'a'
+ if (ch >= '0' && ch <= '9')
+ return ch - '0' + 52; // 52 range starts at '0'
+ if (ch == '+')
+ return 62;
+ if (ch == '/')
+ return 63;
+ return -1;
+}
+
+inline BOOL Base64Decode(LPCSTR szSrc, INT_PTR nSrcLen, BYTE *pbDest, INT_PTR *pnDestLen) throw()
+{
+ // walk the source buffer
+ // each four character sequence is converted to 3 bytes
+ // CRLFs and =, and any characters not in the encoding table
+ // are skiped
+
+ if (szSrc == NULL || pnDestLen == NULL) {
+ return FALSE;
+ }
+
+ LPCSTR szSrcEnd = szSrc + nSrcLen;
+ INT_PTR nWritten = 0;
+
+ BOOL bOverflow = (pbDest == NULL) ? TRUE : FALSE;
+
+ while (szSrc < szSrcEnd) {
+ DWORD dwCurr = 0;
+ INT_PTR i;
+ INT_PTR nBits = 0;
+ for (i=0; i<4; i++) {
+ if (szSrc >= szSrcEnd)
+ break;
+ INT_PTR nCh = DecodeBase64Char(*szSrc);
+ szSrc++;
+ if (nCh == -1) {
+ // skip this char
+ i--;
+ continue;
+ }
+ dwCurr <<= 6;
+ dwCurr |= nCh;
+ nBits += 6;
+ }
+
+ if (!bOverflow && nWritten + (nBits/8) > (*pnDestLen))
+ bOverflow = TRUE;
+
+ // dwCurr has the 3 bytes to write to the output buffer
+ // left to right
+ dwCurr <<= 24-nBits;
+ for (i=0; i<nBits/8; i++) {
+ if (!bOverflow) {
+ *pbDest = (BYTE) ((dwCurr & 0x00ff0000) >> 16);
+ pbDest++;
+ }
+ dwCurr <<= 8;
+ nWritten++;
+ }
+ }
+ *pnDestLen = nWritten;
+ return bOverflow ? FALSE:TRUE;
+}
+
+//=======================================================================
+// Quoted Printable encode/decode
+// compliant with RFC 2045
+//=======================================================================
+//
+#define TSSMTPQPENCODE_DOT 1
+#define TSSMTPQPENCODE_TRAILING_SOFT 2
+
+inline INT_PTR QPEncodeGetRequiredLength(INT_PTR nSrcLen)
+{
+ INT_PTR nRet = 3*((3*nSrcLen)/(TSSMTPMAX_QP_LINE_LENGTH-8));
+ nRet += 3*nSrcLen;
+ nRet += 3;
+ return nRet;
+}
+
+inline INT_PTR QPDecodeGetRequiredLength(INT_PTR nSrcLen)
+{
+ return nSrcLen;
+}
+
+inline BOOL QPEncode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, BOOLEAN *bEncoded, DWORD dwFlags = 0)
+{
+ //The hexadecimal character set
+ static const CHAR s_chHexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'};
+ INT_PTR nRead = 0, nWritten = 0, nLineLen = 0;
+ CHAR ch;
+ BOOLEAN bChanged = FALSE;
+
+
+ if (!pbSrcData || !szDest || !pnDestLen) {
+ return FALSE;
+ }
+
+ while (nRead < nSrcLen) {
+ ch = *pbSrcData++;
+ nRead++;
+ if (nLineLen == 0 && ch == '.' && (dwFlags & TSSMTPQPENCODE_DOT)) {
+ *szDest++ = '.';
+ nWritten++;
+ nLineLen++;
+ bChanged = TRUE;
+ }
+ if ((ch > 32 && ch < 61) || (ch > 61 && ch < 127)) {
+ *szDest++ = ch;
+ nWritten++;
+ nLineLen++;
+ }
+ else
+ if ((ch == ' ' || ch == '\t') && (nLineLen < (TSSMTPMAX_QP_LINE_LENGTH - 12))) {
+ *szDest++ = ch;
+ nWritten++;
+ nLineLen++;
+ }
+ else {
+ *szDest++ = '=';
+ *szDest++ = s_chHexChars[(ch >> 4) & 0x0F];
+ *szDest++ = s_chHexChars[ch & 0x0F];
+ nWritten += 3;
+ nLineLen += 3;
+ bChanged = TRUE;
+ }
+ if (nLineLen >= (TSSMTPMAX_QP_LINE_LENGTH - 11)) {
+ *szDest++ = '=';
+ *szDest++ = '\r';
+ *szDest++ = '\n';
+ nLineLen = 0;
+ nWritten += 3;
+ bChanged = TRUE;
+ }
+ }
+ if (dwFlags & TSSMTPQPENCODE_TRAILING_SOFT) {
+ *szDest++ = '=';
+ *szDest++ = '\r';
+ *szDest++ = '\n';
+ nWritten += 3;
+ bChanged = TRUE;
+ }
+ *pnDestLen = nWritten;
+ if (bEncoded) *bEncoded = bChanged;
+ return TRUE;
+}
+
+
+inline BOOL QPDecode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, DWORD dwFlags = 0)
+{
+ if (!pbSrcData || !szDest || !pnDestLen)
+ {
+ return FALSE;
+ }
+
+ INT_PTR nRead = 0, nWritten = 0, nLineLen = -1;
+ char ch;
+ while (nRead <= nSrcLen)
+ {
+ ch = *pbSrcData++;
+ nRead++;
+ nLineLen++;
+ if (ch == '=')
+ {
+ //if the next character is a digit or a character, convert
+ if (nRead < nSrcLen && (isdigit(*pbSrcData) || isalpha(*pbSrcData)))
+ {
+ char szBuf[5];
+ szBuf[0] = *pbSrcData++;
+ szBuf[1] = *pbSrcData++;
+ szBuf[2] = '\0';
+ char* tmp = '\0';
+ *szDest++ = (BYTE)strtoul(szBuf, &tmp, 16);
+ nWritten++;
+ nRead += 2;
+ continue;
+ }
+ //if the next character is a carriage return or line break, eat it
+ if (nRead < nSrcLen && *pbSrcData == '\r' && (nRead+1 < nSrcLen) && *(pbSrcData+1)=='\n')
+ {
+ pbSrcData++;
+ nRead++;
+ nLineLen = -1;
+ continue;
+ }
+ return FALSE;
+ }
+ if (ch == '\r' || ch == '\n')
+ {
+ nLineLen = -1;
+ continue;
+ }
+ if ((dwFlags & TSSMTPQPENCODE_DOT) && ch == '.' && nLineLen == 0)
+ {
+ continue;
+ }
+ *szDest++ = ch;
+ nWritten++;
+ }
+
+ *pnDestLen = nWritten-1;
+ return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp
new file mode 100644
index 0000000000..f3c4ce6362
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp
@@ -0,0 +1,547 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImINI.cpp $
+Revision : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes:
+ **/
+#include "commonheaders.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImModules.h"
+#include "svc_ExImport.h"
+#include "svc_ExImINI.h"
+
+/**
+ * Miranda includes
+ **/
+#include <m_protocols.h>
+#include <m_protosvc.h>
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: ExportModule
+ * desc: write all settings from a database module to file
+ * param: hContact - handle of contact the module is owned from
+ * pszModule - name of the module to save
+ * file - file to write the settings to
+ * return nothing
+ **/
+static VOID ExportModule(HANDLE hContact, LPCSTR pszModule, FILE* file)
+{
+ DB::CEnumList Settings;
+
+ if (!Settings.EnumSettings(hContact, pszModule))
+ {
+ DBVARIANT dbv;
+ LPSTR here;
+ WORD j;
+ INT i;
+ LPSTR pszSetting;
+ //char tmp[32];
+
+ // print the module header..
+ fprintf(file, "\n[%s]\n", pszModule);
+
+ for (i = 0; i < Settings.getCount(); i++)
+ {
+ pszSetting = Settings[i];
+
+ if (!DB::Setting::GetAsIs(hContact, pszModule, pszSetting, &dbv))
+ {
+ switch (dbv.type)
+ {
+ case DBVT_BYTE:
+ {
+ fprintf(file, "%s=b%u\n", pszSetting, dbv.bVal);
+ }
+ break;
+
+ case DBVT_WORD:
+ {
+ fprintf(file, "%s=w%u\n", pszSetting, dbv.wVal);
+ }
+ break;
+
+ case DBVT_DWORD:
+ {
+ fprintf(file, "%s=d%u\n", pszSetting, dbv.dVal);
+ }
+ break;
+
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ {
+ for (here = dbv.pszVal; here && *here; here++)
+ {
+ switch (*here) {
+ // convert \r to STX
+ case '\r':
+ *here = 2;
+ break;
+
+ // convert \n to ETX
+ case '\n':
+ *here = 3;
+ }
+ }
+ if (dbv.type == DBVT_UTF8)
+ fprintf(file, "%s=u%s\n", pszSetting, dbv.pszVal);
+ else
+ fprintf(file, "%s=s%s\n", pszSetting, dbv.pszVal);
+ }
+ break;
+
+ case DBVT_BLOB:
+ {
+ fprintf(file, "%s=n", pszSetting);
+ for (j = 0; j < dbv.cpbVal; j++)
+ {
+ fprintf(file, "%02X ", (BYTE)dbv.pbVal[j]);
+ }
+ fputc('\n', file);
+ }
+ break;
+ }
+ DB::Variant::Free(&dbv);
+ }
+ }
+ }
+}
+
+/**
+ * name: ExportContact
+ * desc: Exports a certain contact to an ini file.
+ * param: hContact - contact to export or -1 to export all contacts
+ * pModules - module to export, NULL to export all modules of a contact
+ * file - ini file to write the contact to
+ **/
+static BOOLEAN ExportContact(HANDLE hContact, DB::CEnumList* pModules, FILE* file)
+{
+ CExImContactBase vcc;
+
+ if (pModules)
+ {
+ if ((vcc = hContact) >= NULL)
+ {
+ INT i;
+ LPSTR p;
+
+ vcc.toIni(file, pModules->getCount()-1);
+
+ for (i = 0; i < pModules->getCount(); i++)
+ {
+ p = (*pModules)[i];
+
+ /*Filter/
+ if (mir_stricmp(p, "Protocol"))*/
+ {
+ ExportModule(hContact, p, file);
+ }
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * name: SvcExImINI_Export
+ * desc: Exports a certain contact or all contacts to an ini file.
+ * param: hContact - contact to export or -1 to export all contacts
+ * pszFileName - ini-filename to write the contact to
+ **/
+INT SvcExImINI_Export(lpExImParam ExImContact, LPCSTR pszFileName)
+{
+ FILE* file;
+ errno_t err;
+ DB::CEnumList Modules;
+ SYSTEMTIME now;
+ HANDLE hContact;
+
+ if (!DlgExImModules_SelectModulesToExport(ExImContact, &Modules, NULL))
+ {
+ if ((err = fopen_s(&file, pszFileName, "wt")) != NULL)
+ {
+ MsgErr(NULL,
+ LPGENT("The ini-file \"%s\"\nfor saving contact information could not be opened."),
+ pszFileName);
+ return 1;
+ }
+
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+ // write header
+ GetLocalTime(&now);
+ fprintf(file,
+ ";DATE = %04d-%02d-%02d %02d:%02d:%02d\n\n",
+ now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond
+ );
+
+ if (Modules.getCount() == 0)
+ {
+ Modules.EnumModules();
+ }
+
+ // hContact == -1 export entire db.
+ if (ExImContact->Typ != EXIM_CONTACT)
+ {
+ // Owner
+ ExportContact(NULL, &Modules, file);
+ fprintf(file, "\n\n");
+ // Contacts
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ ExportContact(hContact, &Modules, file);
+ fprintf(file, "\n\n");
+ }
+ }
+ // export only one contact
+ else
+ {
+ ExportContact(ExImContact->hContact, &Modules, file);
+ }
+
+ if (file)
+ fclose(file);
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+LPSTR strnrchr(LPSTR string, INT ch, DWORD len)
+{
+ LPSTR start = (LPSTR)string;
+
+ string += len; /* find end of string */
+ /* search towards front */
+ while (--string != start && *string != (CHAR)ch);
+ if (*string == (CHAR)ch) /* char found ? */
+ return ((LPSTR)string);
+ return(NULL);
+}
+
+/**
+ * name: ImportreadLine
+ * desc: read exactly one line into a buffer and return its pointer. Size of buffer is managed.
+ * param: file - pointer to a file
+ * string - the string to write the read line to
+ * return: pointer to the buffer on success or NULL on error
+ **/
+static DWORD ImportreadLine(FILE* file, LPSTR &str)
+{
+ CHAR c;
+ DWORD l = 0;
+ BOOLEAN bComment = 0;
+
+ str[0] = 0;
+ while (!feof(file)) {
+ switch (c = fgetc(file)) {
+ case EOF:
+ // reading error
+ if (ferror(file)) {
+ MIR_FREE(str);
+ return 0;
+ }
+ // end of line & file
+ return l;
+
+ case '\r':
+ case '\n':
+ // ignore empty lines
+ if (l == 0) {
+ bComment = 0;
+ continue;
+ }
+ return l;
+
+ case ';':
+ // found a comment line
+ bComment |= l == 0;
+ case '\t':
+ case ' ':
+ // ignore space and tab at the beginning of the line
+ if (l == 0) break;
+
+ default:
+ if (!bComment) {
+ str = mir_strncat_c(str, c);
+ l++;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * name: ImportFindContact
+ * desc: This function decodes the given line, which is already identified to be a contact line.
+ * The resulting information is matcht to the given hContact if it isn't NULL.
+ * Otherwise all existing contacts are matched.
+ * param: hContact - handle to contact to match or NULL to match all existing
+ * pszBuf - pointer to the buffer holding the string of the current line in the ini.-file
+ * cchBuf - character count of the buffer
+ * return: handle to the contact that matches the information or NULL if no match
+ **/
+static HANDLE ImportFindContact(HANDLE hContact, LPSTR &strBuf, BOOLEAN bCanCreate)
+{
+ CExImContactBase vcc;
+
+ vcc.fromIni(strBuf);
+ if (vcc.handle() != INVALID_HANDLE_VALUE) {
+ //if (vcc.isHandle(hContact))
+ // return hContact;
+ return vcc.handle();
+ }
+ else if (bCanCreate)
+ return vcc.toDB();
+
+ return vcc.handle();
+}
+
+/**
+ * name: ImportSetting
+ * desc: This function writes a line identified as a setting to the database
+ * param: hContact - handle to contact to match or NULL to match all existing
+ * pszModule - module to write the setting to
+ * strLine - string with the setting and its value to write to db
+ * return: 0 if writing was ok, 1 otherwise
+ **/
+INT ImportSetting(HANDLE hContact, LPCSTR pszModule, LPSTR &strLine)
+{
+ DBCONTACTWRITESETTING cws;
+ LPSTR end, value;
+ size_t numLines = 0;
+ size_t brk;
+ LPSTR pszLine = strLine;
+
+ // check Module and filter "Protocol"
+ if (!pszModule || !*pszModule || mir_strncmp(pszModule,"Protocol",8) == 0)
+ return 1;
+ if ((end = value = mir_strchr(pszLine, '=')) == NULL)
+ return 1;
+
+ // truncate setting string if it has spaces at the end
+ do {
+ if (end == pszLine)
+ return 1;
+ *(end--) = 0;
+ } while (*end == '\t' || *end == ' ' || *end < 27);
+
+ cws.szModule = pszModule;
+ cws.szSetting = pszLine;
+
+ // skip spaces from the beginning of the value
+ do {
+ value++;
+ // if the value is empty, delete it from db
+ if (*value == '\0')
+ return DB::Setting::Delete(hContact, pszModule, pszLine);
+ } while (*value == '\t' || *value == ' ');
+
+ // decode database type and value
+ switch (*(value++)) {
+ case 'b':
+ case 'B':
+ if (brk = strspn(value, "0123456789-"))
+ *(value + brk) = 0;
+ cws.value.type = DBVT_BYTE;
+ cws.value.bVal = (BYTE)atoi(value);
+ break;
+ case 'w':
+ case 'W':
+ if (brk = strspn(value, "0123456789-"))
+ *(value + brk) = 0;
+ cws.value.type = DBVT_WORD;
+ cws.value.wVal = (WORD)atoi(value);
+ break;
+ case 'd':
+ case 'D':
+ if (brk = strspn(value, "0123456789-"))
+ *(value + brk) = 0;
+ cws.value.type = DBVT_DWORD;
+ cws.value.dVal = (DWORD)_atoi64(value);
+ break;
+ case 's':
+ case 'S':
+ case 'u':
+ case 'U':
+ for (end = value; end && *end; end++) {
+ switch (*end) {
+ // convert STX back to \r
+ case 2:
+ *end = '\r';
+ break;
+ // convert ETX back to \n
+ case 3:
+ *end = '\n';
+ break;
+ }
+ }
+ switch (*(value - 1)) {
+ case 's':
+ case 'S':
+ cws.value.type = DBVT_ASCIIZ;
+ cws.value.pszVal = value;
+ break;
+ case 'u':
+ case 'U':
+ cws.value.type = DBVT_UTF8;
+ cws.value.pszVal = value;
+ break;
+ }
+ break;
+ case 'n':
+ case 'N':
+ {
+ PBYTE dest;
+ cws.value.type = DBVT_BLOB;
+ cws.value.cpbVal = (WORD)mir_strlen(value) / 3;
+ cws.value.pbVal = (PBYTE)value;
+ for ( dest = cws.value.pbVal, value = strtok(value, " ");
+ value && *value;
+ value = strtok(NULL, " "))
+ *(dest++) = (BYTE)strtol(value, NULL, 16);
+ *dest = 0;
+ break;
+ }
+ default:
+ cws.value.type = DBVT_DELETED;
+ //return 1;
+ }
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws);
+}
+
+/**
+ * name: Import
+ * desc: This function imports an ini file
+ * param: hContact - handle to contact to match or NULL to match all existing
+ * file - module to write the setting to
+ * strLine - string with the setting and its value to write to db
+ * return: 0 if writing was ok, 1 otherwise
+ **/
+INT SvcExImINI_Import(HANDLE hContact, LPCSTR pszFileName)
+{
+ FILE *file;
+ HANDLE hNewContact = INVALID_HANDLE_VALUE;
+ DWORD end,
+ numLines = 0;
+ CHAR szModule[MAXSETTING] = {0};
+ WORD numContactsInFile = 0, // number of contacts in the inifile
+ numContactsAdded = 0; // number of contacts, that were added to the database
+ CHAR *strBuf = (CHAR *) mir_alloc(1);
+ *strBuf = 0;
+
+ if (file = fopen(pszFileName, "rt")) {
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+ while (ImportreadLine(file, strBuf)) {
+ numLines++;
+
+ // contact was found and imported
+ if (hContact != INVALID_HANDLE_VALUE && hNewContact != INVALID_HANDLE_VALUE)
+ break;
+
+ // importing settings is only valid vor the main menu item
+ if (hContact == INVALID_HANDLE_VALUE) {
+ if (!strncmp(strBuf, "SETTINGS:", 9)) {
+ *szModule = 0;
+ hNewContact = NULL;
+ continue;
+ }
+ }
+
+ // there are some modules of a contact (import only if contact exist)
+ if (!strncmp(strBuf, "FROM CONTACT:", 13)) {
+ strBuf = mir_strnerase(strBuf, 0, 13);
+ while (strBuf[0] == ' ' || strBuf[0] == '\t')
+ strBuf = mir_strnerase(strBuf, 0, 1);
+
+ numContactsInFile++;
+ if ((hNewContact = ImportFindContact(hContact, strBuf, FALSE)) != INVALID_HANDLE_VALUE)
+ numContactsAdded++;
+ continue;
+ }
+
+ // there is a contact to import / add
+ if (!strncmp(strBuf, "CONTACT:", 8)) {
+ strBuf = mir_strnerase(strBuf, 0, 8);
+ while (strBuf[0] == ' ' || strBuf[0] == '\t')
+ strBuf = mir_strnerase(strBuf, 0, 1);
+
+ *szModule = 0;
+ numContactsInFile++;
+ if ((hNewContact = ImportFindContact(hContact, strBuf, TRUE)) != INVALID_HANDLE_VALUE)
+ numContactsAdded++;
+ continue;
+ }
+
+ // read modules and settings only for valid contacts
+ if (hNewContact != INVALID_HANDLE_VALUE) {
+ // found a module line
+ if (strBuf[0] == '[' && (end = (strchr(strBuf, ']') - strBuf)) > 0) {
+ mir_strncpy(szModule, &strBuf[1], end);
+ continue;
+ }
+ // try to import a setting
+ ImportSetting(hNewContact, szModule, strBuf);
+ }
+ } //end while
+ fclose(file);
+ mir_free(strBuf);
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ // the contact was not found in the file
+ if (numContactsInFile > 0 && !numContactsAdded) {
+ MsgErr(NULL,
+ LPGENT("None of the %d contacts, stored in the ini-file, match the selected contact!\nNothing will be imported"),
+ numContactsInFile);
+ }
+ // Import complete
+ else{
+ MsgBox(NULL, MB_ICON_INFO, LPGENT("Import complete"), LPGENT("Some basic statistics"),
+ LPGENT("Added %d of %d contacts stored in the ini-file."),
+ numContactsAdded, numContactsInFile);
+ }
+ return 0;
+ }
+ MsgErr(NULL,
+ LPGENT("The ini-file \"%s\"\nfor reading contact information could not be opened."),
+ pszFileName);
+ return 1;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h
new file mode 100644
index 0000000000..63ecce7cbd
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImINI.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_EXIMINI_H_
+#define _SVC_EXIMINI_H_
+
+#pragma once
+
+ INT SvcExImINI_Export(lpExImParam ExImContact, LPCSTR pszFileName);
+ INT SvcExImINI_Import(HANDLE hContact, LPCSTR pszFileName);
+
+#endif /* _SVC_EXIMINI_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp
new file mode 100644
index 0000000000..53faf60c02
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp
@@ -0,0 +1,1364 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImVCF.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes:
+ **/
+#include "commonheaders.h"
+#include "../svc_reminder.h"
+#include "svc_ExImport.h"
+#include "svc_ExImVCF.h"
+
+#include <m_protosvc.h>
+
+#define BLOCKSIZE 260
+
+#define DELIM ";"
+
+/**
+ * name: IsUSASCII
+ * desc: determine whether the pBuffer string is ascii or not
+ * param: pBuffer - string to check
+ *
+ * return TRUE or FALSE
+ **/
+BOOLEAN IsUSASCII(LPCSTR pBuffer, LPDWORD pcbBuffer)
+{
+ BYTE c;
+ PBYTE s = (PBYTE)pBuffer;
+ BOOLEAN bIsUTF = 0;
+
+ if (s == NULL) return 1;
+ while ((c = *s++) != 0) {
+ if (c < 0x80) continue;
+ if (!pcbBuffer) return 0;
+ bIsUTF = 1;
+ }
+ if (pcbBuffer) *pcbBuffer = s - (PBYTE)pBuffer;
+ return !bIsUTF;
+}
+
+/*
+=========================================================================================================================
+ class CLineBuffer
+=========================================================================================================================
+*/
+
+
+/**
+ * name: CLineBuffer::CLineBuffer
+ * desc: initializes all members on construction of the class
+ * param: none
+ *
+ * return: nothing
+ **/
+CLineBuffer::CLineBuffer()
+{
+ _pVal = NULL;
+ _pTok = NULL;
+ _cbVal = 0;
+ _cbUsed = 0;
+}
+
+/**
+ * name: CLineBuffer::~CLineBuffer
+ * desc: frees up all memory on class destruction
+ * param: none
+ *
+ * return: nothing
+ **/
+CLineBuffer::~CLineBuffer()
+{
+ if (_pVal) mir_free(_pVal);
+}
+
+/**
+ * name: CLineBuffer::_resizeBuf
+ * desc: ensure, the right size for the buffer
+ * param: cbReq - number of bytes required for the next operation
+ *
+ * return: TRUE if reallocation successful or memoryblock is large enough, FALSE otherwise
+ **/
+BOOLEAN CLineBuffer::_resizeBuf(const size_t cbReq)
+{
+ if (cbReq > _cbVal - _cbUsed) {
+ if (!(_pVal = (PBYTE)mir_realloc(_pVal, BLOCKSIZE + _cbVal + 1))) {
+ _cbVal = 0;
+ _cbUsed = 0;
+ return FALSE;
+ }
+ _cbVal += BLOCKSIZE;
+ }
+ return TRUE;
+}
+
+/**
+ * name: CLineBuffer::operator =
+ * desc: applys the specified string to the class's _pVal member
+ * param: szVal - string to apply
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator = (const CHAR *szVal)
+{
+ if (szVal) {
+ size_t cbLength = mir_strlen(szVal);
+
+ _cbUsed = 0;
+ if (_resizeBuf(cbLength)) {
+ memcpy(_pVal, szVal, cbLength);
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified string to the class's _pVal member
+ * param: szVal - string to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const CHAR *szVal)
+{
+ if (szVal) {
+ size_t cbLength = mir_strlen(szVal);
+
+ if (_resizeBuf(cbLength)) {
+ memcpy(_pVal + _cbUsed, szVal, cbLength);
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified unicode string to the class's _pVal member
+ * param: wszVal - string to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const WCHAR *wszVal)
+{
+ if (wszVal) {
+ size_t cbLength = mir_wcslen(wszVal);
+ CHAR* szVal = mir_u2a(wszVal);
+
+ if (szVal) {
+ size_t cbLength = mir_strlen(szVal);
+
+ if (_resizeBuf(cbLength)) {
+ memcpy(_pVal + _cbUsed, szVal, cbLength);
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified character's value (-127 ... 128) as a string to the class's _pVal member
+ * param: cVal - character whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const CHAR cVal)
+{
+ if (_resizeBuf(1)) {
+ *(_pVal + _cbUsed++) = cVal;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified bytes's value (0 ... 255) as a string to the class's _pVal member
+ * param: bVal - character whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const BYTE bVal)
+{
+ size_t cbLength = 3;
+
+ if (_resizeBuf(cbLength)) {
+ cbLength = mir_strlen(_itoa(bVal, (LPSTR)(_pVal + _cbUsed), 10));
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified short integer as a string to the class's _pVal member
+ * param: sVal - short integer whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const SHORT sVal)
+{
+ size_t cbLength = 6;
+
+ if (_resizeBuf(cbLength)) {
+ cbLength = mir_strlen(_itoa(sVal, (LPSTR)(_pVal + _cbUsed), 10));
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified word as a string to the class's _pVal member
+ * param: wVal - word whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const WORD wVal)
+{
+ size_t cbLength = 5;
+
+ if (_resizeBuf(cbLength)) {
+ cbLength = mir_strlen(_itoa(wVal, (LPSTR)(_pVal + _cbUsed), 10));
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified long integer as a string to the class's _pVal member
+ * param: lVal - long integer whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const LONG lVal)
+{
+ size_t cbLength = 11;
+
+ if (_resizeBuf(cbLength)) {
+ cbLength = mir_strlen(_ltoa(lVal, (LPSTR)(_pVal + _cbUsed), 10));
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::operator +
+ * desc: appends the specified double word integer as a string to the class's _pVal member
+ * param: dVal - double word integer whose value to add
+ *
+ * return: length of the string, added
+ **/
+size_t CLineBuffer::operator + (const DWORD dVal)
+{
+ size_t cbLength = 10;
+
+ if (_resizeBuf(cbLength)) {
+ cbLength = mir_strlen(_ltoa(dVal, (LPSTR)(_pVal + _cbUsed), 10));
+ _cbUsed += cbLength;
+ return cbLength;
+ }
+ return 0;
+}
+
+/**
+ * name: CLineBuffer::GetLength
+ * desc: returns the length of the _pVal string
+ * param: nothing
+ *
+ * return: length of the string
+ **/
+size_t CLineBuffer::GetLength()
+{
+ return _cbUsed;
+}
+
+/**
+ * name: CLineBuffer::GetBuffer
+ * desc: returns the pointer of the _pVal string
+ * !!Use carefully
+ * param: nothing
+ *
+ * return: pointer to _pVal
+ **/
+LPCSTR CLineBuffer::GetBuffer()
+{
+ return (LPCSTR)_pVal;
+}
+
+/**
+ * name: CLineBuffer::TruncToLength
+ * desc: resulting string has cbLength characters
+ * param: cbLength - desired length of the string member
+ *
+ * return: nothing
+ **/
+VOID CLineBuffer::TruncToLength(size_t cbLength)
+{
+ if (cbLength < _cbUsed) {
+ _cbUsed = cbLength;
+ _pVal[cbLength] = 0;
+ }
+}
+
+/**
+ * name: CLineBuffer::TruncToLength
+ * desc: resulting string is Truncated by the specified number of bytes
+ * param: count - desired count of bytes to Truncate
+ *
+ * return: nothing
+ **/
+VOID CLineBuffer::Truncate(size_t count)
+{
+ if (_cbUsed <= count) {
+ _cbUsed = 0;
+ *_pVal = 0;
+ }
+ else {
+ _cbUsed -= count;
+ _pVal[_cbUsed] = 0;
+ }
+}
+
+/**
+ * name: CLineBuffer::TruncateSMS
+ * desc: resulting string is Truncated by the " SMS" if found
+ * param: nothing
+ *
+ * return: nothing
+ **/
+VOID CLineBuffer::TruncateSMS()
+{
+ if (!strncmp((LPSTR)(_pVal + _cbUsed - 4), " SMS", 4)) {
+ _cbUsed -= 4;
+ _pVal[_cbUsed] = 0;
+ }
+}
+
+/**
+ * name: CLineBuffer::fput
+ * desc: string member is written to the specified stream and Truncated to zero afterwards
+ * param: outfile - the stream to write to
+ *
+ * return: nothing
+ **/
+VOID CLineBuffer::fput(FILE *outfile)
+{
+ if (_pVal) {
+ _pVal[_cbUsed] = 0;
+ fputs((LPCSTR)_pVal, outfile);
+ _cbUsed = 0;
+ *_pVal = 0;
+ }
+}
+
+/**
+ * name: CLineBuffer::fputEncoded
+ * desc: string member is encoded and written to the specified stream and Truncated to zero afterwards
+ * param: outfile - the stream to write to
+ *
+ * return: nothing
+ **/
+VOID CLineBuffer::fputEncoded(FILE *outFile)
+{
+ PBYTE pVal = _pVal;
+
+ if (pVal && _cbUsed > 0) {
+ _pVal[_cbUsed] = 0;
+ while (_cbUsed > 0 && *pVal) {
+ switch (*pVal) {
+ // translate special characters
+ case ':':
+ case ';':
+ case '\r':
+ case '\n':
+ case '\t':
+ fprintf(outFile, "=%02X", *pVal);
+ break;
+ // Some database texts may contain encoded escapes, that have to be translated too.
+ case '\\':
+ if (*(pVal+1) == 'r') {
+ fprintf(outFile, "=%02X", '\r');
+ pVal++;
+ break;
+ }
+ if (*(pVal+1) == 't') {
+ fprintf(outFile, "=%02X", '\t');
+ pVal++;
+ break;
+ }
+ if (*(pVal+1) == 'n') {
+ fprintf(outFile, "=%02X", '\n');
+ pVal++;
+ break;
+ }
+ // translate all characters which are not contained in the USASCII code
+ default:
+ if (*pVal > 127) fprintf(outFile, "=%02X", *pVal);
+ else fputc(*pVal, outFile);
+ break;
+ }
+ pVal++;
+ (_cbUsed)--;
+ }
+ *_pVal = 0;
+ }
+}
+
+/**
+ * name: CLineBuffer::fgetEncoded
+ * desc: string member is read from the specified stream decoded
+ * param: outfile - the stream to write to
+ *
+ * return: nothing
+ **/
+INT CLineBuffer::fgetEncoded(FILE *inFile)
+{
+ CHAR c;
+ CHAR hex[3];
+ WORD wAdd = 0;
+
+ hex[2] = 0;
+
+ _cbUsed = 0;
+
+ while (EOF != (c = fgetc(inFile))) {
+ switch (c) {
+ case '\n':
+ if (_cbUsed > 0 && _pVal[_cbUsed - 1] == '\r') {
+ _pVal[--_cbUsed] = 0;
+ wAdd--;
+ }
+ else
+ _pVal[_cbUsed] = 0;
+ return wAdd;
+ case '=':
+ if (_resizeBuf(1)) {
+ fread(hex, 2, 1, inFile);
+ *(_pVal + _cbUsed++) = (BYTE)strtol(hex, NULL, 16);
+ wAdd++;
+ }
+ break;
+ default:
+ if (_resizeBuf(1)) {
+ *(_pVal + _cbUsed++) = c;
+ wAdd++;
+ }
+ break;
+ }
+ }
+ _pVal[_cbUsed] = 0;
+ return _cbUsed > 0 ? wAdd : EOF;
+}
+
+/**
+ * name: CLineBuffer::GetTokenFirst
+ * desc: scans for the first <delim> in the _pVal member and returns all characters
+ * that come before the first <delim> in a new CLineBuffer class
+ * param: delim - the deliminer which delimins the string
+ * pBuf - pointer to a new CLineBuffer class holding the result
+ *
+ * return: length of the found string value
+ **/
+size_t CLineBuffer::GetTokenFirst(const CHAR delim, CLineBuffer * pBuf)
+{
+ PBYTE here;
+ size_t wLength;
+
+ _pTok = _pVal;
+
+ if (!_pTok || !*_pTok)
+ return 0;
+
+ for (here = _pTok;; here++) {
+ if (*here == 0 || *here == '\n' || *here == delim) {
+ wLength = here - _pTok;
+ if (pBuf) {
+ CHAR c = *here;
+ *here = 0;
+ *pBuf = (LPCSTR)_pTok;
+ *here = c;
+ }
+ _pTok = (*here == 0 || *here == '\n') ? NULL : ++here;
+ break;
+ }
+ }
+ return wLength;
+}
+
+/**
+ * name: CLineBuffer::GetTokenNext
+ * desc: scans for the next <delim> in the _pVal member and returns all characters
+ * that come before the first <delim> in a new CLineBuffer class
+ * param: delim - the deliminer which delimins the string
+ * pBuf - pointer to a new CLineBuffer class holding the result
+ *
+ * return: length of the found string value
+ **/
+size_t CLineBuffer::GetTokenNext(const CHAR delim, CLineBuffer * pBuf)
+{
+ PBYTE here;
+ size_t wLength;
+
+ if (!_pTok || !*_pTok)
+ return 0;
+
+ for (here = _pTok;; here++) {
+ if (*here == 0 || *here == '\n' || *here == delim) {
+ wLength = here - _pTok;
+
+ if (pBuf) {
+ CHAR c = *here;
+
+ *here = 0;
+ *pBuf = (LPCSTR)_pTok;
+ *here = c;
+ }
+ _pTok = (*here == 0 || *here == '\n') ? NULL : ++here;
+ break;
+ }
+ }
+ return wLength;
+}
+
+/**
+ * name: CLineBuffer::DBWriteTokenFirst
+ * desc: scans for the first <delim> in the _pVal member and writes all characters
+ * that come before the first <delim> to database
+ * param: hContact - handle to the contact to write the setting to
+ * pszModule - destination module
+ * pszSetting - destination setting for the value
+ * delim - the deliminer which delimins the string
+ *
+ * return: 0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteTokenFirst(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim)
+{
+ PBYTE here;
+ INT iRet = 1;
+ _pTok = _pVal;
+
+ if (_pTok && *_pTok) {
+ for (here = _pTok;; here++) {
+ if (*here == 0 || *here == '\n' || *here == delim) {
+
+ if (here - _pTok > 0) {
+ CHAR c = *here;
+
+ *here = 0;
+ iRet = DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pTok);
+ *here = c;
+ }
+ _pTok = (*here == 0 || *here == '\n') ? NULL : ++here;
+ break;
+ }
+ }
+ }
+ if (iRet) iRet = DB::Setting::Delete(hContact, pszModule, pszSetting);
+ return iRet;
+}
+
+/**
+ * name: CLineBuffer::GetTokenNext
+ * desc: scans for the next <delim> in the _pVal member and writes all characters
+ * that come before the first <delim> to database
+ * param: hContact - handle to the contact to write the setting to
+ * pszModule - destination module
+ * pszSetting - destination setting for the value
+ * delim - the deliminer which delimins the string
+ *
+ * return: 0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteTokenNext(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim)
+{
+ PBYTE here;
+ INT iRet = 1;
+
+ if (_pTok && *_pTok) {
+ for (here = _pTok;; here++) {
+ if (*here == 0 || *here == '\n' || *here == delim) {
+
+ if (here - _pTok > 0) {
+ CHAR c = *here;
+
+ *here = 0;
+ iRet = DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pTok);
+ *here = c;
+ }
+ _pTok = (*here == 0 || *here == '\n') ? NULL : ++here;
+ break;
+ }
+ }
+ }
+ if (iRet) iRet = DB::Setting::Delete(hContact, pszModule, pszSetting);
+ return iRet;
+}
+
+/**
+ * name: CLineBuffer::GetTokenNext
+ * desc: writes _pVal member to database
+ * param: hContact - handle to the contact to write the setting to
+ * pszModule - destination module
+ * pszSetting - destination setting for the value
+ *
+ * return: 0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteSettingString(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting)
+{
+ if (_pVal && _cbUsed > 0)
+ return DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pVal);
+ return 1;
+}
+
+/*
+=========================================================================================================================
+ class CVCardFileVCF
+=========================================================================================================================
+*/
+
+/**
+ * name: CVCardFileVCF
+ * desc: default constructor
+ * param: none
+ * return none
+ **/
+CVCardFileVCF::CVCardFileVCF()
+{
+ _pFile = NULL;
+ _hContact = INVALID_HANDLE_VALUE;
+ _pszBaseProto = NULL;
+ _hasUtf8 = 0;
+ _useUtf8 = FALSE;
+}
+
+/**
+ * name: CVCardFileVCF::CVCardFileVCF
+ * desc: searches a static stringlist for the occureance of iID and adds
+ * the translated string value to the line buffer
+ * param: pList - pointer to the list to search in
+ * nList - number of items in the list
+ * iID - the id to search for
+ * cbRew - number of characters to truncate the _clVal by before writing to file
+ *
+ * return number of the added bytes
+ **/
+size_t CVCardFileVCF::packList(LPIDSTRLIST pList, UINT nList, INT iID, size_t *cbRew)
+{
+ UINT i;
+ WORD wAdd = 0;
+
+ for (i = 0; i < nList; i++) {
+ if (pList[i].nID == iID) {
+ return (_clVal + pList[i].ptszTranslated);
+ }
+ }
+ if (cbRew) (*cbRew)++;
+ return 0;
+}
+
+/**
+ * name: CVCardFileVCF::GetSetting
+ * desc: trys to read a value from the pszModule and than from the basic protocol module of the contact
+ * where strings are converted to ansi
+ * param: pszModule - module to read the value from
+ * pszSetting - setting to read the value from
+ * dbv - pointer to the structure holding the result
+ *
+ * return value type
+ **/
+BOOLEAN CVCardFileVCF::GetSetting(const CHAR *pszModule, const CHAR *pszSetting, DBVARIANT *dbv)
+{
+ DBCONTACTGETSETTING cgs;
+
+ cgs.szModule = pszModule;
+ cgs.szSetting = pszSetting;
+ cgs.pValue = dbv;
+ dbv->type = _useUtf8 ? DBVT_UTF8 : DBVT_ASCIIZ;
+ dbv->pszVal = NULL;
+ if (!pszModule || CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)_hContact, (LPARAM)&cgs) || (dbv->type == DBVT_ASCIIZ && !dbv->pszVal && !*dbv->pszVal)) {
+ cgs.szModule = _pszBaseProto;
+ cgs.szSetting = pszSetting;
+ cgs.pValue = dbv;
+ dbv->type = DBVT_ASCIIZ;
+ if (!_pszBaseProto || CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)_hContact, (LPARAM)&cgs) || (dbv->type == DBVT_ASCIIZ && !dbv->pszVal && !*dbv->pszVal)) {
+ return DBVT_DELETED;
+ }
+ }
+ _hasUtf8 += _useUtf8 && !IsUSASCII(dbv->pszVal, NULL);
+ return dbv->type;
+}
+
+/**
+ * name: CVCardFileVCF::packDB
+ * desc: read a value from the database and add it to the line buffer
+ * param: pszModule - module to read the value from
+ * pszSetting - setting to read the value from
+ * cbRew - number of characters to truncate the _clVal by before writing to file
+ *
+ * return number of bytes, added to the linebuffer
+ **/
+size_t CVCardFileVCF::packDB(const CHAR *pszModule, const CHAR *pszSetting, size_t *cbRew)
+{
+ DBVARIANT dbv;
+
+ switch (GetSetting(pszModule, pszSetting, &dbv)) {
+ case DBVT_DELETED:
+ if (cbRew) (*cbRew)++;
+ break;
+ case DBVT_BYTE:
+ return _clVal + dbv.bVal;
+ case DBVT_WORD:
+ return _clVal + dbv.wVal;
+ case DBVT_DWORD:
+ return _clVal + dbv.dVal;
+ case DBVT_UTF8:
+ case DBVT_ASCIIZ:
+ {
+ size_t wAdd = _clVal + dbv.pszVal;
+ DB::Variant::Free(&dbv);
+ return wAdd;
+ }
+ default:
+ if (cbRew) (*cbRew)++;
+ DB::Variant::Free(&dbv);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * name: CVCardFileVCF::packDBList
+ * desc: read a value from the database and add a found list item to the line buffer
+ * param: pszModule - module to read the value from
+ * pszSetting - setting to read the value from
+ * GetList - pointer to a function retrieving the list pointer
+ * bSigned - is the read database value signed?
+ * cbRew - number of characters to truncate the _clVal by before writing to file
+ *
+ * return number of bytes, added to the linebuffer
+ **/
+size_t CVCardFileVCF::packDBList(const CHAR *pszModule, const CHAR *pszSetting, MIRANDASERVICE GetList, BOOLEAN bSigned, size_t *cbRew)
+{
+ DBVARIANT dbv;
+ UINT nList;
+ LPIDSTRLIST pList;
+ size_t wAdd = 0;
+
+ GetList((WPARAM)&nList, (LPARAM)&pList);
+ switch (GetSetting(pszModule, pszSetting, &dbv)) {
+ case DBVT_BYTE:
+ wAdd = packList(pList, nList, (INT)(bSigned ? dbv.cVal : dbv.bVal), cbRew);
+ break;
+ case DBVT_WORD:
+ wAdd = packList(pList, nList, (INT)(bSigned ? dbv.sVal : dbv.wVal), cbRew);
+ break;
+ case DBVT_DWORD:
+ wAdd = packList(pList, nList, (INT)(bSigned ? dbv.lVal : dbv.dVal), cbRew);
+ break;
+ case DBVT_UTF8:
+ case DBVT_ASCIIZ:
+ wAdd = _clVal + Translate(dbv.pszVal);
+ DB::Variant::Free(&dbv);
+ break;
+ case DBVT_DELETED:
+ wAdd = 0;
+ break;
+ default:
+ wAdd = 0;
+ DB::Variant::Free(&dbv);
+ break;
+ }
+ if (cbRew) *cbRew = wAdd ? 0 : *cbRew + 1;
+ return wAdd;
+}
+
+/**
+ * name: CVCardFileVCF::writeLine
+ * desc: write a line as clear text to the vcard file
+ * param: szSet - the string, which identifies the line
+ * cbRew - number of characters to truncate the _clVal by before writing to file
+ *
+ * return number of bytes, added to the linebuffer
+ **/
+VOID CVCardFileVCF::writeLine(const CHAR *szSet, size_t *cbRew)
+{
+ if (cbRew) {
+ _clVal.Truncate(*cbRew);
+ *cbRew = 0;
+ }
+ if (_clVal.GetLength() > 0) {
+ fputs(szSet, _pFile);
+ if (_hasUtf8) {
+ fputs(";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:", _pFile);
+ _clVal.fputEncoded(_pFile);
+ _hasUtf8 = FALSE;
+ }
+ else {
+ fputc(':', _pFile);
+ _clVal.fput(_pFile);
+ }
+ fputc('\n', _pFile);
+ }
+}
+
+/**
+ * name: CVCardFileVCF::writeLineEncoded
+ * desc: write a line as encoded text to the vcard file
+ * param: szSet - the string, which identifies the line
+ * cbRew - number of characters to truncate the _clVal by before writing to file
+ *
+ * return number of bytes, added to the linebuffer
+ **/
+VOID CVCardFileVCF::writeLineEncoded(const CHAR *szSet, size_t *cbRew)
+{
+ if (cbRew) {
+ _clVal.Truncate(*cbRew);
+ *cbRew = 0;
+ }
+ if (_clVal.GetLength() > 0) {
+ fputs(szSet, _pFile);
+ if (_hasUtf8) {
+ fputs(";CHARSET=UTF-8", _pFile);
+ _hasUtf8 = FALSE;
+ }
+ fputs(";ENCODING=QUOTED-PRINTABLE:", _pFile);
+ _clVal.fputEncoded(_pFile);
+ fputc('\n', _pFile);
+ }
+}
+
+/**
+ * name: Open
+ * desc: open a specified filename and link to the contact
+ * param: hContact - handle to the contact to link with the vCard file
+ * pszFileName - path to the file to open
+ * pszMode - the mode the file should be opened with
+ * return TRUE or FALSE
+ **/
+BOOLEAN CVCardFileVCF::Open(HANDLE hContact, LPCSTR pszFileName, LPCSTR pszMode)
+{
+ if (!(_pFile = fopen(pszFileName, pszMode)))
+ return FALSE;
+ if ((_hContact = hContact) == INVALID_HANDLE_VALUE)
+ return FALSE;
+ if (!(_pszBaseProto = DB::Contact::Proto(_hContact)))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * name: Close
+ * desc: close up the file
+ * param: hContact - handle to the contact to link with the vCard file
+ * pszFileName - path to the file to open
+ * pszMode - the mode the file should be opened with
+ * return TRUE or FALSE
+ **/
+VOID CVCardFileVCF::Close(VOID)
+{
+ if (_pFile)
+ fclose(_pFile);
+ _pFile = NULL;
+ _hContact = INVALID_HANDLE_VALUE;
+ _pszBaseProto = NULL;
+}
+
+/**
+ * name: Export
+ * desc: export the contacts information
+ * param: none
+ * return TRUE or FALSE
+ **/
+BOOLEAN CVCardFileVCF::Export(BOOLEAN bExportUtf)
+{
+ size_t cbRew = 0;
+
+ _useUtf8 = bExportUtf;
+
+ fputs("BEGIN:VCARD\nVERSION:2.1\n", _pFile);
+
+ //
+ // naming
+ //
+ packDB(USERINFO, SET_CONTACT_LASTNAME, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_FIRSTNAME, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_SECONDNAME, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_TITLE, &cbRew);
+ _clVal + DELIM;
+ packDBList(USERINFO, SET_CONTACT_PREFIX, (MIRANDASERVICE)GetNamePrefixList, FALSE, &cbRew);
+ writeLine("N", &cbRew);
+
+ if (packDB(USERINFO, SET_CONTACT_TITLE))
+ _clVal + " ";
+
+ if (packDB(USERINFO, SET_CONTACT_FIRSTNAME))
+ _clVal + " ";
+ else
+ cbRew = 1;
+
+ if (packDB(USERINFO, SET_CONTACT_SECONDNAME))
+ _clVal + " ";
+ else
+ cbRew = 1;
+
+ if (packDB(USERINFO, SET_CONTACT_LASTNAME))
+ _clVal + " ";
+ else
+ cbRew = 1;
+
+ packDBList(USERINFO, SET_CONTACT_PREFIX, (MIRANDASERVICE)GetNamePrefixList, FALSE, &cbRew);
+ writeLine("FN");
+
+ packDB(USERINFO, SET_CONTACT_NICK);
+ writeLine("NICKNAME");
+
+ //
+ // organisation
+ //
+ packDB(USERINFO, SET_CONTACT_COMPANY, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_DEPARTMENT, &cbRew);
+ writeLine("ORG", &cbRew);
+ packDB(USERINFO, SET_CONTACT_COMPANY_POSITION);
+ writeLine("TITLE");
+ packDBList(USERINFO, SET_CONTACT_COMPANY_OCCUPATION, (MIRANDASERVICE)GetOccupationList, FALSE);
+ writeLine("ROLE");
+
+ //
+ // phone numbers
+ //
+ if (packDB(USERINFO, SET_CONTACT_PHONE)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;HOME;VOICE");
+ }
+ if (packDB(USERINFO, SET_CONTACT_FAX)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;HOME;FAX");
+ }
+ if (packDB(USERINFO, SET_CONTACT_CELLULAR)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;CELL;VOICE");
+ }
+ if (packDB(USERINFO, SET_CONTACT_COMPANY_PHONE)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;WORK;VOICE");
+ }
+ if (packDB(USERINFO, SET_CONTACT_COMPANY_FAX)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;WORK;FAX");
+ }
+ if (packDB(USERINFO, SET_CONTACT_COMPANY_CELLULAR)) {
+ _clVal.TruncateSMS();
+ writeLine("TEL;PAGER;VOICE");
+ }
+
+ //
+ // private address
+ //
+ _clVal + ";;";
+ cbRew = 1;
+ packDB(USERINFO, SET_CONTACT_STREET, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_CITY, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_STATE, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_ZIP, &cbRew);
+ _clVal + DELIM;
+ packDBList(USERINFO, SET_CONTACT_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+ writeLine("ADR;HOME", &cbRew);
+
+ //
+ // company address
+ //
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_OFFICE, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_STREET, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_CITY, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_STATE, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_COMPANY_ZIP, &cbRew);
+ _clVal + DELIM;
+ packDBList(USERINFO, SET_CONTACT_COMPANY_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+ writeLine("ADR;WORK", &cbRew);
+
+ //
+ // origin address
+ //
+ _clVal + ";;";
+ cbRew = 1;
+ packDB(USERINFO, SET_CONTACT_ORIGIN_STREET, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_ORIGIN_CITY, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_ORIGIN_STATE, &cbRew);
+ _clVal + DELIM;
+ packDB(USERINFO, SET_CONTACT_ORIGIN_ZIP, &cbRew);
+ _clVal + DELIM;
+ packDBList(USERINFO, SET_CONTACT_ORIGIN_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+ writeLine("ADR;POSTAL", &cbRew);
+
+ //
+ // homepages
+ //
+ if (packDB(USERINFO, SET_CONTACT_HOMEPAGE))
+ writeLine("URL;HOME");
+ if (packDB(USERINFO, SET_CONTACT_COMPANY_HOMEPAGE))
+ writeLine("URL;WORK");
+
+ //
+ // e-mails
+ //
+ if (packDB(USERINFO, SET_CONTACT_EMAIL))
+ writeLine("EMAIL;PREF;intERNET");
+ if (packDB(USERINFO, SET_CONTACT_EMAIL0))
+ writeLine("EMAIL;intERNET");
+ if (packDB(USERINFO, SET_CONTACT_EMAIL1))
+ writeLine("EMAIL;intERNET");
+
+ //
+ // gender
+ //
+ {
+ BYTE gender = DB::Setting::GetByte(_hContact, USERINFO, SET_CONTACT_GENDER, 0);
+ if (!gender) gender = DB::Setting::GetByte(_hContact, _pszBaseProto, SET_CONTACT_GENDER, 0);
+ switch (gender) {
+ case 'F':
+ fputs("X-WAB-GENDER:1\n", _pFile);
+ break;
+ case 'M':
+ fputs("X-WAB-GENDER:2\n", _pFile);
+ break;
+ }
+ }
+
+ //
+ // birthday
+ //
+ {
+ MAnnivDate mdb;
+
+ if (!mdb.DBGetBirthDate(_hContact, NULL))
+ fprintf(_pFile, "BDAY:%d%02d%02d\n\0", mdb.Year(), mdb.Month(), mdb.Day());
+ }
+
+ //
+ // notes
+ //
+ if (packDB(USERINFO, SET_CONTACT_MYNOTES))
+ writeLineEncoded("NOTE");
+
+ //
+ // about
+ //
+ if (packDB(USERINFO, SET_CONTACT_ABOUT))
+ writeLineEncoded("ABOUT");
+
+ //
+ // contacts protocol, uin setting, uin value
+ //
+ {
+ CHAR szUID[MAXUID];
+ LPCSTR uid;
+
+ uid = (LPCSTR)CallProtoService(_pszBaseProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ if ((INT_PTR)uid != CALLSERVICE_NOTFOUND && uid) {
+ if (!DB::Setting::GetStatic(_hContact, _pszBaseProto, uid, szUID, sizeof(szUID)))
+ fprintf(_pFile, "IM;%s;%s:%s\n", _pszBaseProto, uid, szUID);
+ }
+ }
+
+ //
+ // time of creation
+ //
+ {
+ SYSTEMTIME st;
+
+ GetLocalTime(&st);
+ fprintf(_pFile, "REV:%04d%02d%02dD%02d%02d%02dT\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+ }
+
+ fputs("END:VCARD", _pFile);
+ return 0;
+}
+
+/**
+ * name: CVCardFileVCF::readLine
+ * desc: read one line from the VCF file and return the setting string
+ * to the szVCFSetting and the value to _clVal
+ * param: szVCFSetting - string holding the setting information
+ * cchSetting - number of bytes the szVCFSetting can hold
+ *
+ * return: number of characters read from the file or EOF
+ **/
+INT CVCardFileVCF::readLine(LPSTR szVCFSetting, WORD cchSetting)
+{
+ LPSTR here;
+
+ // read setting (size is never larger than MAX_SETTING, error otherwise!)
+ for (here = szVCFSetting; here - szVCFSetting < cchSetting && EOF != (*here = fgetc(_pFile)); here++) {
+ // end of the setting string
+ if (*here == ':') {
+ *here = 0;
+ break;
+ }
+ // end of line before value?
+ if (*here == '\n')
+ return 0;
+ }
+ // ignore line if setting was not read correctly
+ if (here - szVCFSetting == cchSetting)
+ return 0;
+
+ // read the value to the linebuffer, because its length may be very large
+ return _clVal.fgetEncoded(_pFile);
+}
+
+/**
+ * name: CVCardFileVCF::Import
+ * desc: imports all lines from the file and writes them to database
+ * param: nothing
+ *
+ * return: number of characters read from the file or EOF
+ **/
+BOOLEAN CVCardFileVCF::Import()
+{
+ CHAR szEnt[MAX_PATH];
+ LPSTR pszParam;
+ INT cbLine;
+ BYTE numEmails = 0;
+
+ while (EOF != (cbLine = readLine(szEnt, MAX_PATH))) {
+
+ // ignore empty lines
+ if (!cbLine) continue;
+
+ // isolate the param string
+ if (pszParam = mir_strchr(szEnt, ';')) {
+ *(pszParam++) = 0;
+ }
+ switch (*szEnt) {
+ case 'A':
+ if (!strcmp(szEnt, "ABOUT")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_ABOUT);
+ continue;
+ }
+ if (!strcmp(szEnt, "ADR")) {
+ if (!pszParam) continue;
+ if (!strcmp(pszParam, "HOME")) {
+ _clVal.GetTokenFirst(';', NULL);
+ _clVal.GetTokenNext(';', NULL);
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_STREET, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_CITY, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_STATE, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ZIP, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COUNTRY, ';');
+ continue;
+ }
+ if (!strcmp(pszParam, "WORK")) {
+ _clVal.GetTokenFirst(';', NULL);
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_OFFICE, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_STREET, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_CITY, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_STATE, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_ZIP, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_COUNTRY, ';');
+ continue;
+ }
+ if (!strcmp(pszParam, "POSTAL")) {
+ _clVal.GetTokenFirst(';', NULL);
+ _clVal.GetTokenNext(';', NULL);
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_STREET, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_CITY, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_STATE, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_ZIP, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_COUNTRY, ';');
+ }
+ }
+ continue;
+
+ case 'B':
+ if (!strcmp(szEnt, "BDAY")) {
+ if (_clVal.GetLength() == 8) {
+ CHAR buf[5];
+
+ memcpy(buf, _clVal.GetBuffer(), 4);
+ buf[4] = 0;
+ DB::Setting::WriteWord(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHYEAR, (WORD)strtol(buf, NULL, 10));
+ memcpy(buf, _clVal.GetBuffer() + 4, 2);
+ buf[2] = 0;
+ DB::Setting::WriteByte(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHMONTH, (BYTE)strtol(buf, NULL, 10));
+ memcpy(buf, _clVal.GetBuffer() + 6, 2);
+ buf[2] = 0;
+ DB::Setting::WriteByte(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, (BYTE)strtol(buf, NULL, 10));
+ }
+ }
+ continue;
+
+ case 'E':
+ if (!strcmp(szEnt, "EMAIL")) {
+ if (!pszParam || !strstr(pszParam, "intERNET"))
+ continue;
+ if (strstr(pszParam, "PREF")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL);
+ continue;
+ }
+ switch (numEmails++) {
+ case 0:
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL0);
+ break;
+ case 1:
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL1);
+ break;
+ }
+ }
+ continue;
+ /*
+ case 'I':
+ if (!strcmp(szEnt, "IM")) {
+ LPSTR pszModule, pszSetting;
+
+ if (pszParam && (pszModule = strtok(pszParam, DELIM)) && (pszSetting = strtok(NULL, DELIM)))
+ _clVal.DBWriteSettingString(_hContact, pszModule, pszSetting);
+ }
+ continue;
+ */
+ case 'N':
+ if (!strcmp(szEnt, "N")) {
+ _clVal.DBWriteTokenFirst(_hContact, USERINFO, SET_CONTACT_LASTNAME, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_FIRSTNAME, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_SECONDNAME, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_TITLE, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_PREFIX, ';');
+ continue;
+ }
+ if (!strcmp(szEnt, "NICKNAME")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_NICK);
+ continue;
+ }
+ if (!strcmp(szEnt, "NOTE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_MYNOTES);
+ }
+ continue;
+
+ case 'O':
+ if (!strcmp(szEnt, "ORG")) {
+ _clVal.DBWriteTokenFirst(_hContact, USERINFO, SET_CONTACT_COMPANY, ';');
+ _clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_DEPARTMENT, ';');
+ }
+ continue;
+
+ case 'R':
+ if (!strcmp(szEnt, "ROLE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_OCCUPATION);
+ }
+ continue;
+
+ case 'T':
+ if (!strcmp(szEnt, "TITLE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_POSITION);
+ continue;
+ }
+ if (!strcmp(szEnt, "TEL")) {
+
+ if (!pszParam) continue;
+
+ if (!strcmp(pszParam, "HOME;VOICE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_PHONE);
+ continue;
+ }
+ if (!strcmp(pszParam, "HOME;FAX")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_FAX);
+ continue;
+ }
+ if (!strcmp(pszParam, "CELL;VOICE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_CELLULAR);
+ continue;
+ }
+ if (!strcmp(pszParam, "WORK;VOICE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_PHONE);
+ continue;
+ }
+ if (!strcmp(pszParam, "WORK;FAX")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_FAX);
+ continue;
+ }
+ if (!strcmp(pszParam, "PAGER;VOICE")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_CELLULAR);
+ continue;
+ }
+ }
+ continue;
+
+ case 'U':
+ if (!strcmp(szEnt, "URL")) {
+
+ if (!pszParam) continue;
+
+ if (!strcmp(pszParam, "HOME")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_HOMEPAGE);
+ continue;
+ }
+ if (!strcmp(pszParam, "WORK")) {
+ _clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_HOMEPAGE);
+ }
+ }
+ continue;
+
+ case 'X':
+ if (!strcmp(szEnt, "X-WAB-GENDER")) {
+ if (!strcmp(_clVal.GetBuffer(), "1"))
+ DB::Setting::WriteByte(_hContact, USERINFO, SET_CONTACT_GENDER, 'F');
+ else
+ if (!strcmp(_clVal.GetBuffer(), "2"))
+ DB::Setting::WriteByte(_hContact, USERINFO, SET_CONTACT_GENDER, 'M');
+ }
+ continue;
+ }
+ }
+ return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h
new file mode 100644
index 0000000000..4465614b80
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImVCF.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#pragma once
+
+class CLineBuffer
+{
+private:
+ PBYTE _pVal;
+ PBYTE _pTok;
+ size_t _cbVal;
+ size_t _cbUsed;
+
+ BOOLEAN _resizeBuf(const size_t cbReq);
+
+public:
+ CLineBuffer();
+ ~CLineBuffer();
+
+ size_t operator = (const CHAR *szVal);
+
+ size_t operator + (const CHAR *szVal);
+ size_t operator + (const WCHAR *wszVal);
+ size_t operator + (const CHAR cVal);
+ size_t operator + (const BYTE bVal);
+ size_t operator + (const SHORT sVal);
+ size_t operator + (const WORD wVal);
+ size_t operator + (const LONG lVal);
+ size_t operator + (const DWORD dVal);
+
+ size_t GetLength();
+ LPCSTR GetBuffer();
+
+ VOID TruncToLength(size_t cbLength);
+ VOID Truncate(size_t count);
+ VOID TruncateSMS();
+
+ VOID fput(FILE *outfile);
+ VOID fputEncoded(FILE *outFile);
+ INT fgetEncoded(FILE *inFile);
+
+ size_t GetTokenFirst(const CHAR delim, CLineBuffer * pBuf);
+ size_t GetTokenNext(const CHAR delim, CLineBuffer * pBuf);
+ INT DBWriteTokenFirst(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim);
+ INT DBWriteTokenNext(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim);
+ INT DBWriteSettingString(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting);
+};
+
+class CVCardFileVCF
+{
+private:
+ CLineBuffer _clVal;
+ FILE *_pFile;
+ HANDLE _hContact;
+ const CHAR *_pszBaseProto;
+ WORD _cbRew;
+ BOOLEAN _useUtf8;
+ WORD _hasUtf8;
+
+ size_t packList(LPIDSTRLIST pList, UINT nList, INT iID, size_t *cbRew = NULL);
+ BOOLEAN GetSetting(const CHAR *pszModule, const CHAR *pszSetting, DBVARIANT *dbv);
+ size_t packDB(const CHAR *pszModule, const CHAR *pszSetting, size_t *cbRew = NULL);
+ size_t packDBList(const CHAR *pszModule, const CHAR *pszSetting, MIRANDASERVICE GetList, BOOLEAN bSigned = FALSE, size_t *cbRew = NULL);
+
+ VOID writeLine(const CHAR *szSet, size_t *cbRew = NULL);
+ VOID writeLineEncoded(const CHAR *szSet, size_t *cbRew = NULL);
+ INT readLine(LPSTR szVCFSetting, WORD cchSetting);
+
+public:
+ CVCardFileVCF();
+
+ BOOLEAN Open(HANDLE hContact, LPCSTR pszFileName, LPCSTR pszMode);
+ VOID Close(VOID);
+ BOOLEAN Export(BOOLEAN bExportUtf);
+ BOOLEAN Import();
+};
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp
new file mode 100644
index 0000000000..dca52f1685
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp
@@ -0,0 +1,453 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImXML.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+#define XMLCARD_VERSION "1.1"
+
+/**
+ * system & local includes:
+ **/
+#include "dlg_ExImModules.h"
+#include "classExImContactXML.h"
+#include "svc_ExImport.h"
+
+LRESULT CALLBACK DlgProc_DataHistory(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_DLG_EXPORT, WM_SETICON, NULL },
+ { ICO_DLG_EXPORT, STM_SETIMAGE, ICO_DLGLOGO },
+ { ICO_BTN_EXPORT, BM_SETIMAGE, IDOK },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }
+ };
+ const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+ IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+ TranslateDialogDefault(hDlg);
+ SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ break;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+ case STATIC_WHITERECT:
+ case ICO_DLGLOGO:
+ case IDC_INFO:
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+ return FALSE;
+ case WM_COMMAND:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ break;
+ case IDOK: {
+ WORD hiWord = 0;
+
+ if (IsDlgButtonChecked(hDlg, IDC_CHECK1))
+ hiWord |= EXPORT_DATA;
+ if (IsDlgButtonChecked(hDlg, IDC_CHECK2))
+ hiWord |= EXPORT_HISTORY;
+ EndDialog(hDlg, (INT_PTR)MAKELONG(IDOK, hiWord));
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name: Export
+ * desc: globally accessible function which does the whole export stuff.
+ * params: hContact - handle to the contact who is to export
+ * pszFileName - full qualified path to the xml file which is destination for the export process
+ * return: 0 on success, 1 otherwise
+ **/
+INT CFileXml::Export(lpExImParam ExImContact, LPCSTR pszFileName)
+{
+ FILE *xmlfile;
+ DB::CEnumList Modules;
+ LONG cbHeader;
+ SYSTEMTIME now;
+ DWORD result;
+ HANDLE hContact;
+
+ result = (DWORD)DialogBox(ghInst,
+ MAKEINTRESOURCE(IDD_EXPORT_DATAHISTORY),
+ NULL, (DLGPROC)DlgProc_DataHistory);
+ if (LOWORD(result) != IDOK)
+ {
+ return 0;
+ }
+ _wExport = HIWORD(result);
+
+ // show dialog to enable user to select modules for export
+ if (!(_wExport & EXPORT_DATA) ||
+ !DlgExImModules_SelectModulesToExport(ExImContact, &Modules, NULL))
+ {
+
+ xmlfile = fopen(pszFileName, "wt");
+ if (!xmlfile)
+ {
+ MsgErr(NULL, LPGENT("Can't create xml file!\n%S"), pszFileName);
+ return 1;
+ }
+
+ GetLocalTime(&now);
+
+ // write xml header raw as it is without using the tinyxml api
+ fprintf(xmlfile,
+ "%c%c%c<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+ "<XMLCard ver=\""XMLCARD_VERSION"\" ref=\"%04d-%02d-%02d %02d:%02d:%02d\">\n",
+ 0xefU, 0xbbU, 0xbfU, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond
+ );
+ // remember the header's size
+ cbHeader = ftell(xmlfile);
+
+ CExImContactXML vContact(this);
+
+ // write data
+ if ( ExImContact->Typ == EXIM_CONTACT) {
+ // export single contact
+ _hContactToWorkOn = ExImContact->hContact;
+ if (vContact.fromDB(ExImContact->hContact)) {
+ vContact.Export(xmlfile, &Modules);
+ }
+ }
+ else {
+ // other export mode
+ _hContactToWorkOn = INVALID_HANDLE_VALUE;
+#ifdef _DEBUG
+ LARGE_INTEGER freq, t1, t2;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&t1);
+#endif
+ // export owner contact
+ if (ExImContact->Typ == EXIM_ALL && vContact.fromDB(NULL)) {
+ vContact.Export(xmlfile, &Modules);
+ }
+ // loop for all other contact
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ switch (ExImContact->Typ)
+ {
+ case EXIM_ALL:
+ case EXIM_GROUP:
+ // dont export meta subcontacts by default
+ if (!DB::MetaContact::IsSub(hContact)) {
+ if (vContact.fromDB(hContact)) {
+ vContact.Export(xmlfile, &Modules);
+ }
+ }
+ break;
+ case EXIM_SUBGROUP:
+ // dont export meta subcontacts by default and
+ // export only contact with selectet group name
+ if (!DB::MetaContact::IsSub(hContact) &&
+ mir_tcsncmp(ExImContact->ptszName, DB::Setting::GetTString(hContact, "CList", "Group"), mir_tcslen(ExImContact->ptszName))== 0)
+ {
+ if (vContact.fromDB(hContact)) {
+ vContact.Export(xmlfile, &Modules);
+ }
+ }
+ break;
+ case EXIM_ACCOUNT:
+ // export only contact with selectet account name
+ if (!mir_strncmp(ExImContact->pszName, DB::Contact::Proto(hContact), mir_strlen(ExImContact->pszName))) {
+ if (vContact.fromDB(hContact)) {
+ vContact.Export(xmlfile, &Modules);
+ }
+ }
+ break;
+ }
+ } // *end for
+#ifdef _DEBUG
+ QueryPerformanceCounter(&t2);
+ MsgErr(NULL, LPGENT("Export took %f msec"),
+ (long double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.);
+#endif
+ }// *end other export mode
+
+ // nothing exported?
+ if (cbHeader == ftell(xmlfile)) {
+ fclose(xmlfile);
+ DeleteFileA(pszFileName);
+ return 1;
+ }
+ fputs("</XMLCard>\n", xmlfile);
+ fclose(xmlfile);
+ }
+ return 0;
+}
+
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+CFileXml::CFileXml()
+{
+ _numContactsTodo = 0;
+ _numContactsDone = 0;
+ _numSettingsTodo = 0;
+ _numSettingsDone = 0;
+ _numEventsTodo = 0;
+ _numEventsDone = 0;
+ _numEventsDuplicated = 0;
+}
+
+/**
+ * name: ImportOwner
+ * desc: Interpretes an xmlnode as owner contact, finds a corresponding contact in database
+ * or adds a new one including all xml childnodes.
+ * params: xContact - xmlnode representing the contact
+ * stat - structure used to collect some statistics
+ * return: ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CFileXml::ImportOwner(TiXmlElement* xContact)
+{
+ CExImContactXML vContact(this);
+
+ if (vContact = xContact) {
+ vContact.Import();
+ return ERROR_OK;
+ }
+ return ERROR_NOT_ADDED;
+}
+
+/**
+ * name: ImportContacts
+ * desc: Parse all child nodes of an given parent node and try to import all found contacts
+ * params: xmlParent - xmlnode representing the parent of the list of contacts
+ * hContact - handle to the contact, who is the owner of the setting to import
+ * stat - structure used to collect some statistics
+ * return: ERROR_OK if at least one contact was successfully imported
+ **/
+INT CFileXml::ImportContacts(TiXmlElement* xmlParent)
+{
+ TiXmlElement *xContact;
+ CExImContactXML vContact(this);
+ INT result;
+ LPTSTR pszNick;
+
+ // import contacts
+ for (xContact = xmlParent->FirstChildElement(); xContact != NULL; xContact = xContact->NextSiblingElement()) {
+ if (!mir_stricmp(xContact->Value(), XKEY_CONTACT)) {
+ // update progressbar and abort if user clicked cancel
+ pszNick = mir_utf8decodeT(xContact->Attribute("nick"));
+ // user clicked abort button
+ if (_progress.UpdateContact(LPGENT("Contact: %s (%S)"), pszNick, xContact->Attribute("proto"))) {
+ result = vContact.LoadXmlElemnt(xContact);
+ switch (result) {
+ case ERROR_OK:
+ // init contact class and import if matches the user desires
+ if (_hContactToWorkOn == INVALID_HANDLE_VALUE || vContact.handle() == _hContactToWorkOn) {
+ result = vContact.Import(_hContactToWorkOn != INVALID_HANDLE_VALUE);
+ switch (result) {
+ case ERROR_OK:
+ _numContactsDone++;
+ break;
+ case ERROR_ABORTED:
+ if (pszNick) mir_free(pszNick);
+ return ERROR_ABORTED;
+#ifdef _DEBUG
+ default:
+ MsgErr(NULL, LPGENT("Importing %s caused error %d"), pszNick, result);
+ break;
+#endif
+ }
+ }
+ break;
+ case ERROR_ABORTED:
+ if (pszNick) mir_free(pszNick);
+ return ERROR_ABORTED;
+#ifdef _DEBUG
+ default:
+ MsgErr(NULL, LPGENT("Loading contact %s from xml failed with error %d"), pszNick, result);
+ break;
+#endif
+ }
+ }
+ if (pszNick) mir_free(pszNick);
+ }
+ // import owner contact
+ else if (_hContactToWorkOn == INVALID_HANDLE_VALUE && !mir_stricmp(xContact->Value(), XKEY_OWNER) && (vContact = xContact)) {
+ result = vContact.Import();
+ switch (result) {
+ case ERROR_OK:
+ _numContactsDone++;
+ break;
+ case ERROR_ABORTED:
+ return ERROR_ABORTED;
+#ifdef _DEBUG
+ default:
+ MsgErr(NULL, LPGENT("Importing Owner caused error %d"), result);
+#endif
+ }
+ }
+ }
+ return ERROR_OK;
+}
+
+/**
+ * name: CountContacts
+ * desc: Counts the number of contacts stored in the file
+ * params: xContact - the contact, who is the owner of the keys to count
+ * return: nothing
+ **/
+DWORD CFileXml::CountContacts(TiXmlElement* xmlParent)
+{
+ DWORD dwCount = 0;
+ TiXmlNode *xContact;
+
+ try {
+ // count contacts in file for progress bar
+ for (xContact = xmlParent->FirstChild(); xContact != NULL; xContact = xContact->NextSibling()) {
+ if (!mir_stricmp(xContact->Value(), XKEY_CONTACT) || !mir_stricmp(xContact->Value(), XKEY_OWNER)) {
+ dwCount += CountContacts(xContact->ToElement()) + 1;
+ }
+ }
+ }
+ catch(...) {
+ return 0;
+ }
+ return dwCount;
+}
+
+/**
+ * name: Import
+ * desc: Interpretes an xmlnode as owner contact, finds a corresponding contact in database
+ * or adds a new one including all xml childnodes.
+ * params: hContact - handle to the contact, who is the owner of the setting to import
+ * pszFileName - full qualified path to the xml file which is to import
+ * return: ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CFileXml::Import(HANDLE hContact, LPCSTR pszFileName)
+{
+ TiXmlDocument doc;
+ TiXmlElement *xmlCard = NULL;
+
+ try {
+ _hContactToWorkOn = hContact;
+ // load xml file
+ if (!doc.LoadFile(pszFileName)) {
+ MsgErr(NULL, LPGENT("Parser is unable to load XMLCard \"%s\"\nError: %d\nDescription: %s"),
+ pszFileName, doc.ErrorId(), doc.ErrorDesc());
+ return 1;
+ }
+ // is xmlfile a XMLCard ?
+ if ((xmlCard = doc.FirstChildElement("XMLCard")) == NULL) {
+ MsgErr(NULL, LPGENT("The selected file is no valid XMLCard"));
+ return 1;
+ }
+ // check version
+ if (mir_strcmp(xmlCard->Attribute("ver"), XMLCARD_VERSION)) {
+ MsgErr(NULL, LPGENT("The version of the XMLCard is not supported by UserInfoEx"));
+ return 1;
+ }
+
+ // is owner contact to import ?
+ if (_hContactToWorkOn == NULL) {
+ INT ret;
+
+ // disable database safty mode to speed up the operation
+ CallService(MS_DB_SETSAFETYMODE, 0, 0);
+ // import owner contact
+ ret = ImportOwner(xmlCard->FirstChildElement(XKEY_OWNER));
+ // as soon as possible enable safty mode again!
+ CallService(MS_DB_SETSAFETYMODE, 1, 0);
+
+ if (!ret) {
+ MsgBox(NULL, MB_ICONINFORMATION,
+ LPGENT("Complete"),
+ LPGENT("Import complete"),
+ LPGENT("Owner contact successfully imported."));
+ return 0;
+ } else {
+ MsgErr(NULL, LPGENT("Selected XMLCard does not contain an owner contact!"));
+ return 1;
+ }
+ }
+ else {
+#ifdef _DEBUG
+ LARGE_INTEGER freq, t1, t2;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&t1);
+#endif
+ // count contacts in file for progress bar
+ _numContactsTodo = CountContacts(xmlCard);
+ if (_numContactsTodo > 0) {
+ _progress.SetContactCount(_numContactsTodo);
+ // disable database safty mode to speed up the operation
+ CallService(MS_DB_SETSAFETYMODE, 0, 0);
+ // import the contacts
+ ImportContacts(xmlCard);
+ // as soon as possible enable safty mode again!
+ CallService(MS_DB_SETSAFETYMODE, 1, 0);
+ }
+ // finally hide the progress dialog
+ _progress.Hide();
+
+#ifdef _DEBUG
+ QueryPerformanceCounter(&t2);
+ MsgErr(NULL, LPGENT("Import took %f msec"),
+ (long double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.);
+#endif
+ // show results
+ MsgBox(NULL, MB_ICONINFORMATION, LPGENT("Import complete"), LPGENT("Some basic statistics"),
+ LPGENT("added contacts: %u / %u\nadded settings: %u / %u\nadded events %u / %u\nduplicated events: %u"),
+ _numContactsDone, _numContactsTodo,
+ _numSettingsDone, _numSettingsTodo,
+ _numEventsDone, _numEventsTodo,
+ _numEventsDuplicated);
+
+ }
+ }
+ catch(...) {
+ MsgErr(NULL, LPGENT("FATAL: An exception was thrown while importing contacts from xmlCard!"));
+ return 1;
+ }
+ return 0;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h
new file mode 100644
index 0000000000..a2524a7dd6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h
@@ -0,0 +1,75 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImXML.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_FILEXML_INCLUDED_
+#define _SVC_FILEXML_INCLUDED_ 1
+
+#include "svc_ExImport.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImProgress.h"
+
+#define EXPORT_DATA 1
+#define EXPORT_HISTORY 2
+#define EXPORT_ALL (EXPORT_DATA|EXPORT_HISTORY)
+
+class CFileXml {
+ friend class CExImContactXML;
+
+ DWORD _numContactsTodo;
+ DWORD _numContactsDone;
+ DWORD _numSettingsTodo;
+ DWORD _numSettingsDone;
+ DWORD _numEventsTodo;
+ DWORD _numEventsDone;
+ DWORD _numEventsDuplicated;
+
+ HANDLE _hContactToWorkOn; // contact to ex/import (NULL=owner|INVALID_HANDLE_VALUE=all|HADNLE=one user)
+
+ WORD _wExport;
+
+ CProgress _progress;
+
+ INT ImportOwner(TiXmlElement* xmlContact);
+ INT ImportContacts(TiXmlElement* xmlParent);
+
+ DWORD CountContacts(TiXmlElement* xmlParent);
+
+ /*
+ INT ExportOwner(FILE *xmlfile, BOOLEAN bExportEvents);
+ INT ExportContact(FILE *xmlfile, HANDLE hContact, BOOLEAN bExportEvents, LPENUMLIST pModules);
+ INT ExportSubContact(TiXmlElement *xContact, HANDLE hContact, BOOLEAN bExportEvents);
+ */
+
+public:
+ CFileXml();
+ INT Import(HANDLE hContact, LPCSTR pszFileName);
+ INT Export(lpExImParam ExImContact, LPCSTR pszFileName);
+};
+
+#endif /* _SVC_FILEXML_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp
new file mode 100644
index 0000000000..35bb59c3b9
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp
@@ -0,0 +1,382 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImport.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImModules.h"
+#include "dlg_ExImOpenSaveFile.h"
+#include "svc_ExImport.h"
+#include "svc_ExImINI.h"
+#include "svc_ExImVCF.h"
+#include "svc_ExImXML.h"
+
+#include <m_clui.h>
+#include <m_clc.h>
+
+/***********************************************************************************************************
+ * internal functions
+ ***********************************************************************************************************/
+
+/**
+ * name: DisplayNameToFileName
+ * desc: convert contact's display name to valid filename
+ * param: hContact - handle of contact to create the filename for
+ * pszFileName - buffer, retrieving the converted filename
+ * cchFileName - number of maximum characters the filename can be
+ * return: nothing
+ **/
+static VOID DisplayNameToFileName(lpExImParam ExImContact, LPSTR pszFileName, WORD cchFileName)
+{
+ LPCSTR disp = 0;
+ LPSTR temp = 0;
+
+ cchFileName--;
+
+ ZeroMemory(pszFileName, cchFileName);
+
+ switch (ExImContact->Typ) {
+ case EXIM_ALL:
+ case EXIM_GROUP:
+ mir_strncpy(pszFileName, Translate("all Contacts"), cchFileName);
+ return;
+ case EXIM_CONTACT:
+ if (ExImContact->hContact == NULL) {
+ mir_strncpy(pszFileName, Translate("Owner"), cchFileName);
+ return;
+ }
+ else {
+ disp = (LPCSTR)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)ExImContact->hContact, NULL);
+ }
+ break;
+ case EXIM_SUBGROUP:
+ temp = mir_t2a(ExImContact->ptszName);
+ disp = temp;
+ break;
+ case EXIM_ACCOUNT:
+ PROTOACCOUNT* acc = ProtoGetAccount(ExImContact->pszName);
+ temp = mir_t2a(acc->tszAccountName);
+ disp = temp;
+ break;
+ }
+
+ // replace unwanted characters
+ while (*disp != 0 && cchFileName > 1) {
+ switch (*disp) {
+ case '?': case '*': case ':':
+ case '\\': case '|': case '/':
+ case '<': case '>': case '"':
+ *(pszFileName++) = '_';
+ break;
+ default:
+ *(pszFileName++) = *disp;
+ break;
+ }
+ disp++;
+ cchFileName--;
+ }
+ mir_free(temp);
+}
+
+LPCSTR FilterString(lpExImParam ExImContact)
+{
+ LPCSTR pszFilter = 0;
+ switch (ExImContact->Typ) {
+ case EXIM_SUBGROUP:
+ case EXIM_ACCOUNT:
+ pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0");
+ break;
+ case EXIM_ALL:
+ case EXIM_GROUP:
+ pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0DBEditor++ File (*.ini)\0*.ini\0");
+ break;
+ case EXIM_CONTACT:
+ pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0DBEditor++ File (*.ini)\0*.ini\0Standard vCard 2.1 (*.vcf)\0*.vcf\0");
+ break;
+ }
+ return pszFilter;
+}
+
+/**
+ * name: SvcExImport_Export
+ * desc: service function to export contact information
+ * param: wParam - handle to contact or NULL
+ * lParam - parent window
+ * return: 0 always
+ **/
+INT_PTR SvcExImport_Export(lpExImParam ExImContact, HWND hwndParent)
+{
+ CHAR szFileName[MAX_PATH] = { 0 };
+ // create the filename to suggest the user for the to export contact
+ DisplayNameToFileName(ExImContact, szFileName, SIZEOF(szFileName));
+ INT nIndex = DlgExIm_SaveFileName(hwndParent,
+ Translate("Select a destination file..."),
+ FilterString(ExImContact),
+ szFileName);
+
+ switch (nIndex) {
+ case 1: // .xml
+ {
+ CFileXml xmlFile;
+ return xmlFile.Export(ExImContact, szFileName);
+ }
+ case 2: // .ini
+ {
+ return SvcExImINI_Export(ExImContact, szFileName);
+ }
+ case 3: // .vcf
+ {
+ CVCardFileVCF vcfFile;
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+ if (vcfFile.Open(ExImContact->hContact, szFileName, "wt")) {
+ vcfFile.Export(FALSE);
+ vcfFile.Close();
+ }
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * name: SvcExImport_Import
+ * desc: service function to export contact information
+ * param: wParam - handle to contact or NULL
+ * lParam - parent window
+ * return: 0 always
+ **/
+INT_PTR SvcExImport_Import(lpExImParam ExImContact, HWND hwndParent)
+{
+ CHAR szFileName[MAX_PATH] = { 0 };
+
+ // create the filename to suggest the user for the to export contact
+ DisplayNameToFileName(ExImContact, szFileName, SIZEOF(szFileName));
+
+ INT nIndex = DlgExIm_OpenFileName(hwndParent,
+ Translate("Import User Details from VCard"),
+ FilterString(ExImContact),
+ szFileName);
+
+// Stop during develop
+if (ExImContact->Typ == EXIM_ACCOUNT ||
+ ExImContact->Typ == EXIM_GROUP) return 1;
+
+ switch (nIndex) {
+ case 1:
+ {
+ CFileXml xmlFile;
+ CallService(MS_CLIST_SETHIDEOFFLINE, -1, 0); //workarround to refresh the clist....
+ xmlFile.Import(ExImContact->hContact, szFileName);
+ CallService(MS_CLIST_SETHIDEOFFLINE, -1, 0); //...after import.
+ //pcli->pfnClcBroadcast(CLM_AUTOREBUILD, 0, 0); //does not work
+ return 0;
+ }
+ // .ini
+ case 2:
+ return SvcExImINI_Import(ExImContact->hContact, szFileName);
+
+ // .vcf
+ case 3:
+ {
+ CVCardFileVCF vcfFile;
+
+ if (vcfFile.Open(ExImContact->hContact, szFileName, "rt")) {
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+ vcfFile.Import();
+ vcfFile.Close();
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/***********************************************************************************************************
+ * service functions
+ ***********************************************************************************************************/
+
+/*********************************
+ * Ex/import All (MainMenu)
+ *********************************/
+INT_PTR svcExIm_MainExport_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ ExIm.hContact = INVALID_HANDLE_VALUE;
+ ExIm.Typ = EXIM_ALL;
+ return SvcExImport_Export(&ExIm, (HWND)lParam);
+}
+
+INT_PTR svcExIm_MainImport_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ ExIm.hContact = INVALID_HANDLE_VALUE;
+ ExIm.Typ = EXIM_ALL;
+ return SvcExImport_Import(&ExIm, (HWND)lParam);
+}
+
+
+/*********************************
+ * Ex/import Contact (ContactMenu)
+ *********************************/
+INT_PTR svcExIm_ContactExport_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ ExIm.hContact = (HANDLE)wParam;
+ ExIm.Typ = EXIM_CONTACT;
+ return SvcExImport_Export(&ExIm, (HWND)lParam);
+}
+
+INT_PTR svcExIm_ContactImport_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ ExIm.hContact = (HANDLE)wParam;
+ ExIm.Typ = EXIM_CONTACT;
+ return SvcExImport_Import(&ExIm, (HWND)lParam);
+}
+
+
+/*********************************
+ *Ex/import (Sub)Group (GroupMenu)
+ *********************************/
+
+/**
+ * This service is call by (Sub)Group MenuItem Export and MenuItem Import
+ *
+ * @param wParam - gmp.wParam = 0 ->Import
+ * @param wParam - gmp.wParam != 0 ->Export
+ * @param lParam - gmp.lParam not used
+ *
+ * @return always 0
+ **/
+INT_PTR svcExIm_Group_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ INT_PTR hItem = 0, hRoot = 0, hParent = 0;
+ TCHAR tszGroup[120], tszItem[120];
+ ZeroMemory(&tszGroup, sizeof(tszGroup));
+ ZeroMemory(&tszItem, sizeof(tszItem));
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ LPTSTR ptszGroup = tszGroup;
+ LPTSTR ptszItem = tszItem;
+
+ HWND hClist = (HWND)CallService(MS_CLUI_GETHWNDTREE,0,0);
+ // get clist selection
+ hItem = SendMessage(hClist,CLM_GETSELECTION,0,0);
+ hRoot = SendMessage(hClist,CLM_GETNEXTITEM, (WPARAM)CLGN_ROOT, (LPARAM)hItem);
+ while (hItem) {
+ if (SendMessage(hClist,CLM_GETITEMTYPE, (WPARAM)hItem, 0) == CLCIT_GROUP) {
+ SendMessage(hClist,CLM_GETITEMTEXT, (WPARAM)hItem, (LPARAM)ptszItem);
+ LPTSTR temp = mir_tstrdup(ptszGroup);
+ mir_sntprintf(tszGroup, SIZEOF(tszGroup),_T("%s%s%s"), ptszItem, _tcslen(temp)? _T("\\"):_T(""), temp);
+ mir_free (temp);
+ }
+ hParent = SendMessage(hClist,CLM_GETNEXTITEM, (WPARAM)CLGN_PARENT, (LPARAM)hItem);
+ hItem = (hParent != hRoot) ? hParent : 0;
+ }
+ ExIm.ptszName = ptszGroup;
+ ExIm.Typ = EXIM_SUBGROUP;
+
+ if (wParam) {
+ //Export "/ExportGroup"
+ SvcExImport_Export(&ExIm, hClist);
+ }
+ else {
+ //Import "/ImportGroup"
+ SvcExImport_Import(&ExIm, hClist);
+ }
+
+ return 0;
+};
+
+
+/*********************************
+ *Ex/Import Account (AccountMenu)
+ *********************************/
+typedef struct
+// neeed for MO_MENUITEMGETOWNERDATA
+// taken from core clistmenus.ccp
+{
+ char *proto; //This is unique protoname
+ int protoindex;
+ int status;
+
+ BOOL custom;
+ char *svc;
+ HANDLE hMenuItem;
+} StatusMenuExecParam,*lpStatusMenuExecParam;
+
+/**
+ * This service is call by Account MenuItem Export and MenuItem Import
+ *
+ * @param wParam - not used
+ * @param lParam - MenuItem from MS_CLIST_ADDSTATUSMENUITEM
+ *
+ * @return always 0
+ **/
+INT_PTR svcExIm_Account_Service(WPARAM wParam, LPARAM lParam)
+{
+ ExImParam ExIm;
+ ZeroMemory(&ExIm, sizeof(ExIm));
+ HWND hClist = (HWND)CallService(MS_CLUI_GETHWNDTREE,0,0);
+ lpStatusMenuExecParam smep = (lpStatusMenuExecParam) CallService(MO_MENUITEMGETOWNERDATA, (WPARAM) lParam, NULL);
+ ExIm.pszName = mir_strdup(smep->proto);
+ ExIm.Typ = EXIM_ACCOUNT;
+
+ if (strstr( smep->svc, "/ExportAccount" )) {
+ //Export "/ExportAccount"
+ SvcExImport_Export(&ExIm, hClist);
+ }
+ else {
+ //Import "/ImportAccount"
+ SvcExImport_Import(&ExIm, hClist);
+ }
+ mir_free(ExIm.pszName);
+ return 0;
+};
+
+/**
+ * name: SvcExImport_LoadModule()
+ * desc: initializes the Ex/Import Services
+ *
+ * return: 0 or 1
+ **/
+VOID SvcExImport_LoadModule()
+{
+ CreateServiceFunction(MS_USERINFO_VCARD_EXPORTALL, svcExIm_MainExport_Service);
+ CreateServiceFunction(MS_USERINFO_VCARD_IMPORTALL, svcExIm_MainImport_Service);
+ CreateServiceFunction(MS_USERINFO_VCARD_EXPORT, svcExIm_ContactExport_Service);
+ CreateServiceFunction(MS_USERINFO_VCARD_IMPORT, svcExIm_ContactImport_Service);
+ return;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImport.h b/plugins/UserInfoEx/src/ex_import/svc_ExImport.h
new file mode 100644
index 0000000000..3539ad72cd
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImport.h
@@ -0,0 +1,62 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImport.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_EXIMPORT_INCLUDED_
+#define _SVC_EXIMPORT_INCLUDED_ 1
+
+typedef struct
+{
+ BYTE Typ;
+ union {
+ HANDLE hContact;
+ LPSTR pszName;
+ LPTSTR ptszName;
+ };
+}
+ ExImParam,*lpExImParam;
+
+enum ExImType {
+ EXIM_ALL = 1,
+ EXIM_CONTACT = 2,
+ EXIM_GROUP = 4,
+ EXIM_SUBGROUP = 8,
+ EXIM_ACCOUNT = 16
+};
+
+INT_PTR svcExIm_MainExport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_MainImport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_ContactExport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_ContactImport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_Group_Service(WPARAM wParam,LPARAM lParam);
+INT_PTR svcExIm_Account_Service(WPARAM wParam,LPARAM lParam);
+
+VOID SvcExImport_LoadModule();
+
+#endif /* _SVC_EXIMPORT_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ex_import/tinystr.cpp b/plugins/UserInfoEx/src/ex_import/tinystr.cpp
new file mode 100644
index 0000000000..3ec0fe4676
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinystr.cpp
@@ -0,0 +1,143 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+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.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinystr.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifdef USE_MMGR
+#include <assert.h>
+#include <string.h>
+
+#include "mmgr.h"
+#endif
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< size_type >(-1);
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, '\0' };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+ if (cap > capacity())
+ {
+ TiXmlString tmp;
+ tmp.init(length(), cap);
+ memcpy(tmp.start(), data(), length());
+ swap(tmp);
+ }
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+ size_type cap = capacity();
+ if (len > cap || cap > 3*(len + 8))
+ {
+ TiXmlString tmp;
+ tmp.init(len);
+ memcpy(tmp.start(), str, len);
+ swap(tmp);
+ }
+ else
+ {
+ memmove(start(), str, len);
+ set_size(len);
+ }
+ return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+ size_type newsize = length() + len;
+ if (newsize > capacity())
+ {
+ reserve (newsize + capacity());
+ }
+ memmove(finish(), str, len);
+ set_size(newsize);
+ return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ tmp.reserve(a.length() + b.length());
+ tmp += a;
+ tmp += b;
+ return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
+ tmp.reserve(a.length() + b_len);
+ tmp += a;
+ tmp.append(b, b_len);
+ return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
+ tmp.reserve(a_len + b.length());
+ tmp.append(a, a_len);
+ tmp += b;
+ return tmp;
+}
+
+
+#endif // TIXML_USE_STL
diff --git a/plugins/UserInfoEx/src/ex_import/tinystr.h b/plugins/UserInfoEx/src/ex_import/tinystr.h
new file mode 100644
index 0000000000..1061e795d8
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinystr.h
@@ -0,0 +1,335 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+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.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinystr.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#ifndef USE_MMGR
+#include <assert.h>
+#include <string.h>
+#endif
+
+/* The support for explicit isn't that universal, and it isn't really
+ required - it is used to check that the TiXmlString class isn't incorrectly
+ used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+ // Microsoft visual studio, version 6 and higher.
+ #define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
+ // GCC version 3 and higher.s
+ #define TIXML_EXPLICIT explicit
+#else
+ #define TIXML_EXPLICIT
+#endif
+
+
+/*
+ TiXmlString is an emulation of a subset of the std::string template.
+ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+ Only the member functions relevant to the TinyXML project have been implemented.
+ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+ a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+ public :
+ // The size type used
+ typedef size_t size_type;
+
+ // Error value for find primitive
+ static const size_type npos; // = -1;
+
+
+ // TiXmlString empty constructor
+ TiXmlString () : rep_(&nullrep_)
+ {
+ }
+
+ // TiXmlString copy constructor
+ TiXmlString (const TiXmlString & copy)
+ {
+ init(copy.length());
+ memcpy(start(), copy.data(), length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString (const char * copy)
+ {
+ init(static_cast<size_type>(strlen(copy)));
+ memcpy(start(), copy, length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString (const char * str, size_type len)
+ {
+ init(len);
+ memcpy(start(), str, len);
+ }
+
+ // TiXmlString destructor
+ ~TiXmlString ()
+ {
+ quit();
+ }
+
+ // = operator
+ TiXmlString& operator = (const char * copy)
+ {
+ return assign(copy, (size_type)strlen(copy));
+ }
+
+ // = operator
+ TiXmlString& operator = (const TiXmlString & copy)
+ {
+ return assign(copy.start(), copy.length());
+ }
+
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const char * suffix)
+ {
+ return append(suffix, static_cast<size_type>(strlen(suffix)));
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (char single)
+ {
+ return append(&single, 1);
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const TiXmlString & suffix)
+ {
+ return append(suffix.data(), suffix.length());
+ }
+
+
+ // Convert a TiXmlString into a null-terminated char *
+ const char * c_str () const { return rep_->str; }
+
+ // Convert a TiXmlString into a char * (need not be null terminated).
+ const char * data () const { return rep_->str; }
+
+ // Return the length of a TiXmlString
+ size_type length () const { return rep_->size; }
+
+ // Alias for length()
+ size_type size () const { return rep_->size; }
+
+ // Checks if a TiXmlString is empty
+ bool empty () const { return rep_->size == 0; }
+
+ // Return capacity of string
+ size_type capacity () const { return rep_->capacity; }
+
+
+ // single char extraction
+ const char& at (size_type index) const
+ {
+ assert(index < length());
+ return rep_->str[ index ];
+ }
+
+ // [] operator
+ char& operator [] (size_type index) const
+ {
+ assert(index < length());
+ return rep_->str[ index ];
+ }
+
+ // find a char in a string. Return TiXmlString::npos if not found
+ size_type find (char lookup) const
+ {
+ return find(lookup, 0);
+ }
+
+ // find a char in a string from an offset. Return TiXmlString::npos if not found
+ size_type find (char tofind, size_type offset) const
+ {
+ if (offset >= length()) return npos;
+
+ for (const char* p = c_str() + offset; *p != '\0'; ++p)
+ {
+ if (*p == tofind) return static_cast< size_type >(p - c_str());
+ }
+ return npos;
+ }
+
+ void clear ()
+ {
+ //Lee:
+ //The original was just too strange, though correct:
+ // TiXmlString().swap(*this);
+ //Instead use the quit & re-init:
+ quit();
+ init(0,0);
+ }
+
+ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
+ function DOES NOT clear the content of the TiXmlString if any exists.
+ */
+ void reserve (size_type cap);
+
+ TiXmlString& assign (const char* str, size_type len);
+
+ TiXmlString& append (const char* str, size_type len);
+
+ void swap (TiXmlString& other)
+ {
+ Rep* r = rep_;
+ rep_ = other.rep_;
+ other.rep_ = r;
+ }
+
+ private:
+
+ void init(size_type sz) { init(sz, sz); }
+ void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+ char* start() const { return rep_->str; }
+ char* finish() const { return rep_->str + rep_->size; }
+
+ struct Rep
+ {
+ size_type size, capacity;
+ char str[1];
+ };
+
+ void init(size_type sz, size_type cap)
+ {
+ if (cap)
+ {
+ // Lee: the original form:
+ // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+ // doesn't work in some cases of new being overloaded. Switching
+ // to the normal allocation, although use an 'int' for systems
+ // that are overly picky about structure alignment.
+ const size_type bytesNeeded = sizeof(Rep) + cap;
+ const size_type intsNeeded = (bytesNeeded + sizeof(int) - 1) / sizeof(int);
+ rep_ = reinterpret_cast<Rep*>(new int[ intsNeeded ]);
+
+ rep_->str[ rep_->size = sz ] = '\0';
+ rep_->capacity = cap;
+ }
+ else
+ {
+ rep_ = &nullrep_;
+ }
+ }
+
+ void quit()
+ {
+ if (rep_ != &nullrep_)
+ {
+ // The rep_ is really an array of ints. (see the allocator, above).
+ // Cast it back before delete, so the compiler won't incorrectly call destructors.
+ delete [] (reinterpret_cast<int*>(rep_));
+ }
+ }
+
+ Rep * rep_;
+ static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+ return (a.length() == b.length()) // optimization on some platforms
+ && (strcmp(a.c_str(), b.c_str()) == 0); // actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+ return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+ Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const TiXmlString & in)
+ {
+ *this += in;
+ return *this;
+ }
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const char * in)
+ {
+ *this += in;
+ return *this;
+ }
+
+} ;
+
+#endif // TIXML_STRING_INCLUDED
+#endif // TIXML_USE_STL
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxml.cpp b/plugins/UserInfoEx/src/ex_import/tinyxml.cpp
new file mode 100644
index 0000000000..2eba13a7ea
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxml.cpp
@@ -0,0 +1,1978 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+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.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxml.cpp $
+Revision : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+#include "tinyxml.h"
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#ifdef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mmgr.h"
+#endif
+
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+void TiXmlBase::StreamDepth(TIXML_OSTREAM* stream, int depth) const
+{
+ for (int i = 0; i < depth; ++i)
+ {
+ (*stream) << " ";
+ }
+}
+
+void TiXmlBase::PutString(const TIXML_STRING& str, TIXML_OSTREAM* stream)
+{
+ TIXML_STRING buffer;
+ PutString(str, &buffer);
+ (*stream) << buffer;
+}
+
+void TiXmlBase::PutString(const TIXML_STRING& str, TIXML_STRING* outString)
+{
+ int i=0;
+
+ while (i<(int)str.length())
+ {
+ unsigned char c = (unsigned char) str[i];
+
+ if ( c == '&'
+ && i < ((int)str.length() - 2)
+ && str[i+1] == '#'
+ && str[i+2] == 'x')
+ {
+ // Hexadecimal character reference.
+ // Pass through unchanged.
+ // &#xA9; -- copyright symbol, for example.
+ //
+ // The -1 is a bug fix from Rob Laveaux. It keeps
+ // an overflow from happening if there is no ';'.
+ // There are actually 2 ways to exit this loop -
+ // while fails (error case) and break (semicolon found).
+ // However, there is no mechanism (currently) for
+ // this function to return an error.
+ while (i<(int)str.length()-1)
+ {
+ outString->append(str.c_str() + i, 1);
+ ++i;
+ if (str[i] == ';')
+ break;
+ }
+ }
+ else if (c == '&')
+ {
+ outString->append(entity[0].str, entity[0].strLength);
+ ++i;
+ }
+ else if (c == '<')
+ {
+ outString->append(entity[1].str, entity[1].strLength);
+ ++i;
+ }
+ else if (c == '>')
+ {
+ outString->append(entity[2].str, entity[2].strLength);
+ ++i;
+ }
+ else if (c == '\"')
+ {
+ outString->append(entity[3].str, entity[3].strLength);
+ ++i;
+ }
+ else if (c == '\'')
+ {
+ outString->append(entity[4].str, entity[4].strLength);
+ ++i;
+ }
+ else if (c < 32)
+ {
+ // Easy pass at non-alpha/numeric/symbol
+ // Below 32 is symbolic.
+ char buf[ 32 ];
+
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "&#x%02X;", (unsigned) (c & 0xff));
+ #else
+ sprintf(buf, "&#x%02X;", (unsigned) (c & 0xff));
+ #endif
+
+ //*ME: warning C4267: convert 'size_t' to 'int'
+ //*ME: Int-Cast to make compiler happy ...
+ outString->append(buf, (int)strlen(buf));
+ ++i;
+ }
+ else
+ {
+ //char realc = (char) c;
+ //outString->append(&realc, 1);
+ *outString += (char) c; // somewhat more efficient function call.
+ ++i;
+ }
+ }
+}
+
+
+// <-- Strange class for a bug fix. Search for STL_STRING_BUG
+TiXmlBase::StringToBuffer::StringToBuffer(const TIXML_STRING& str)
+{
+ buffer = new char[ str.length()+1 ];
+ if (buffer)
+ {
+ strcpy(buffer, str.c_str());
+ }
+}
+
+
+TiXmlBase::StringToBuffer::~StringToBuffer()
+{
+ delete [] buffer;
+}
+// End strange bug fix. -->
+
+
+TiXmlNode::TiXmlNode(NodeType _type) : TiXmlBase()
+{
+ parent = 0;
+ type = _type;
+ firstChild = 0;
+ lastChild = 0;
+ prev = 0;
+ next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while (node)
+ {
+ temp = node;
+ node = node->next;
+
+ delete temp;
+ }
+}
+
+
+void TiXmlNode::CopyTo(TiXmlNode* target) const
+{
+ target->SetValue (value.c_str());
+ target->userData = userData;
+}
+
+
+void TiXmlNode::Clear()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while (node)
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+
+ firstChild = 0;
+ lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild(TiXmlNode* node)
+{
+ assert(node->parent == 0 || node->parent == this);
+ assert(node->GetDocument() == 0 || node->GetDocument() == this->GetDocument());
+
+ if (node->Type() == TiXmlNode::DOCUMENT)
+ {
+ delete node;
+ if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+
+ node->parent = this;
+
+ node->prev = lastChild;
+ node->next = 0;
+
+ if (lastChild)
+ lastChild->next = node;
+ else
+ firstChild = node; // it was an empty list.
+
+ lastChild = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild(const TiXmlNode& addThis)
+{
+ if (addThis.Type() == TiXmlNode::DOCUMENT)
+ {
+ if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+ TiXmlNode* node = addThis.Clone();
+ if (!node)
+ return 0;
+
+ return LinkEndChild(node);
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild(TiXmlNode* beforeThis, const TiXmlNode& addThis)
+{
+ if (!beforeThis || beforeThis->parent != this) {
+ return 0;
+ }
+ if (addThis.Type() == TiXmlNode::DOCUMENT)
+ {
+ if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if (!node)
+ return 0;
+ node->parent = this;
+
+ node->next = beforeThis;
+ node->prev = beforeThis->prev;
+ if (beforeThis->prev)
+ {
+ beforeThis->prev->next = node;
+ }
+ else
+ {
+ assert(firstChild == beforeThis);
+ firstChild = node;
+ }
+ beforeThis->prev = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild(TiXmlNode* afterThis, const TiXmlNode& addThis)
+{
+ if (!afterThis || afterThis->parent != this) {
+ return 0;
+ }
+ if (addThis.Type() == TiXmlNode::DOCUMENT)
+ {
+ if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if (!node)
+ return 0;
+ node->parent = this;
+
+ node->prev = afterThis;
+ node->next = afterThis->next;
+ if (afterThis->next)
+ {
+ afterThis->next->prev = node;
+ }
+ else
+ {
+ assert(lastChild == afterThis);
+ lastChild = node;
+ }
+ afterThis->next = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild(TiXmlNode* replaceThis, const TiXmlNode& withThis)
+{
+ if (replaceThis->parent != this)
+ return 0;
+
+ TiXmlNode* node = withThis.Clone();
+ if (!node)
+ return 0;
+
+ node->next = replaceThis->next;
+ node->prev = replaceThis->prev;
+
+ if (replaceThis->next)
+ replaceThis->next->prev = node;
+ else
+ lastChild = node;
+
+ if (replaceThis->prev)
+ replaceThis->prev->next = node;
+ else
+ firstChild = node;
+
+ delete replaceThis;
+ node->parent = this;
+ return node;
+}
+
+
+bool TiXmlNode::RemoveChild(TiXmlNode* removeThis)
+{
+ if (removeThis->parent != this)
+ {
+ assert(0);
+ return false;
+ }
+
+ if (removeThis->next)
+ removeThis->next->prev = removeThis->prev;
+ else
+ lastChild = removeThis->prev;
+
+ if (removeThis->prev)
+ removeThis->prev->next = removeThis->next;
+ else
+ firstChild = removeThis->next;
+
+ delete removeThis;
+ return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild(const char * _value) const
+{
+ const TiXmlNode* node;
+ for (node = firstChild; node; node = node->next)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+
+TiXmlNode* TiXmlNode::FirstChild(const char * _value)
+{
+ TiXmlNode* node;
+ for (node = firstChild; node; node = node->next)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild(const char * _value) const
+{
+ const TiXmlNode* node;
+ for (node = lastChild; node; node = node->prev)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+TiXmlNode* TiXmlNode::LastChild(const char * _value)
+{
+ TiXmlNode* node;
+ for (node = lastChild; node; node = node->prev)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+const TiXmlNode* TiXmlNode::IterateChildren(const TiXmlNode* previous) const
+{
+ if (!previous)
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert(previous->parent == this);
+ return previous->NextSibling();
+ }
+}
+
+TiXmlNode* TiXmlNode::IterateChildren(TiXmlNode* previous)
+{
+ if (!previous)
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert(previous->parent == this);
+ return previous->NextSibling();
+ }
+}
+
+const TiXmlNode* TiXmlNode::IterateChildren(const char * val, const TiXmlNode* previous) const
+{
+ if (!previous)
+ {
+ return FirstChild(val);
+ }
+ else
+ {
+ assert(previous->parent == this);
+ return previous->NextSibling(val);
+ }
+}
+
+TiXmlNode* TiXmlNode::IterateChildren(const char * val, TiXmlNode* previous)
+{
+ if (!previous)
+ {
+ return FirstChild(val);
+ }
+ else
+ {
+ assert(previous->parent == this);
+ return previous->NextSibling(val);
+ }
+}
+
+const TiXmlNode* TiXmlNode::NextSibling(const char * _value) const
+{
+ const TiXmlNode* node;
+ for (node = next; node; node = node->next)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+TiXmlNode* TiXmlNode::NextSibling(const char * _value)
+{
+ TiXmlNode* node;
+ for (node = next; node; node = node->next)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+const TiXmlNode* TiXmlNode::PreviousSibling(const char * _value) const
+{
+ const TiXmlNode* node;
+ for (node = prev; node; node = node->prev)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+TiXmlNode* TiXmlNode::PreviousSibling(const char * _value)
+{
+ TiXmlNode* node;
+ for (node = prev; node; node = node->prev)
+ {
+ if (strcmp(node->Value(), _value) == 0)
+ return node;
+ }
+ return 0;
+}
+
+void TiXmlElement::RemoveAttribute(const char * name)
+{
+ TIXML_STRING str(name);
+ TiXmlAttribute* node = attributeSet.Find(str);
+ if (node)
+ {
+ attributeSet.Remove(node);
+ delete node;
+ }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling())
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+TiXmlElement* TiXmlNode::FirstChildElement()
+{
+ TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling())
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement(const char * _value) const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild(_value);
+ node;
+ node = node->NextSibling(_value))
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+TiXmlElement* TiXmlNode::FirstChildElement(const char * _value)
+{
+ TiXmlNode* node;
+
+ for ( node = FirstChild(_value);
+ node;
+ node = node->NextSibling(_value))
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling())
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+TiXmlElement* TiXmlNode::NextSiblingElement()
+{
+ TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling())
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+const TiXmlElement* TiXmlNode::NextSiblingElement(const char * _value) const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling(_value);
+ node;
+ node = node->NextSibling(_value))
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+TiXmlElement* TiXmlNode::NextSiblingElement(const char * _value)
+{
+ TiXmlNode* node;
+
+ for ( node = NextSibling(_value);
+ node;
+ node = node->NextSibling(_value))
+ {
+ if (node->ToElement())
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+ const TiXmlNode* node;
+
+ for (node = this; node; node = node->parent)
+ {
+ if (node->ToDocument())
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+TiXmlDocument* TiXmlNode::GetDocument()
+{
+ TiXmlNode* node;
+
+ for (node = this; node; node = node->parent)
+ {
+ if (node->ToDocument())
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+TiXmlElement::TiXmlElement (const char * _value)
+ : TiXmlNode(TiXmlNode::ELEMENT)
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement(const std::string& _value)
+ : TiXmlNode(TiXmlNode::ELEMENT)
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement(const TiXmlElement& copy)
+ : TiXmlNode(TiXmlNode::ELEMENT)
+{
+ firstChild = lastChild = 0;
+ copy.CopyTo(this);
+}
+
+
+void TiXmlElement::operator=(const TiXmlElement& base)
+{
+ ClearThis();
+ base.CopyTo(this);
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+ ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+ Clear();
+ while (attributeSet.First())
+ {
+ TiXmlAttribute* node = attributeSet.First();
+ attributeSet.Remove(node);
+ delete node;
+ }
+}
+
+
+const char * TiXmlElement::Attribute(const char * name) const
+{
+ TIXML_STRING str(name);
+ const TiXmlAttribute* node = attributeSet.Find(str);
+
+ if (node)
+ return node->Value();
+
+ return 0;
+}
+
+
+const char * TiXmlElement::Attribute(const char * name, int* i) const
+{
+ const char * s = Attribute(name);
+ if (i)
+ {
+ if (s)
+ *i = (int)_atoi64(s);
+ else
+ *i = 0;
+ }
+ return s;
+}
+
+
+const char * TiXmlElement::Attribute(const char * name, double* d) const
+{
+ const char * s = Attribute(name);
+ if (d)
+ {
+ if (s)
+ *d = atof(s);
+ else
+ *d = 0;
+ }
+ return s;
+}
+
+
+int TiXmlElement::QueryIntAttribute(const char* name, int* ival) const
+{
+ TIXML_STRING str(name);
+ const TiXmlAttribute* node = attributeSet.Find(str);
+ if (!node)
+ return TIXML_NO_ATTRIBUTE;
+
+ return node->QueryIntValue(ival);
+}
+
+
+int TiXmlElement::QueryDoubleAttribute(const char* name, double* dval) const
+{
+ TIXML_STRING str(name);
+ const TiXmlAttribute* node = attributeSet.Find(str);
+ if (!node)
+ return TIXML_NO_ATTRIBUTE;
+
+ return node->QueryDoubleValue(dval);
+}
+
+
+void TiXmlElement::SetAttribute(const char * name, int val)
+{
+ char buf[64];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", val);
+ #else
+ sprintf(buf, "%d", val);
+ #endif
+ SetAttribute(name, buf);
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute(const std::string& name, int val)
+{
+ std::ostringstream oss;
+ oss << val;
+ SetAttribute(name, oss.str());
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute(const char * name, double val)
+{
+ char buf[256];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%f", val);
+ #else
+ sprintf(buf, "%f", val);
+ #endif
+ SetAttribute(name, buf);
+}
+
+
+void TiXmlElement::SetAttribute(const char * cname, const char * cvalue)
+{
+ TIXML_STRING _name(cname);
+ TIXML_STRING _value(cvalue);
+
+ TiXmlAttribute* node = attributeSet.Find(_name);
+ if (node)
+ {
+ node->SetValue(cvalue);
+ return;
+ }
+
+ TiXmlAttribute* attrib = new TiXmlAttribute(cname, cvalue);
+ if (attrib)
+ {
+ attributeSet.Add(attrib);
+ }
+ else
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute(const std::string& name, const std::string& _value)
+{
+ TiXmlAttribute* node = attributeSet.Find(name);
+ if (node)
+ {
+ node->SetValue(_value);
+ return;
+ }
+
+ TiXmlAttribute* attrib = new TiXmlAttribute(name, _value);
+ if (attrib)
+ {
+ attributeSet.Add(attrib);
+ }
+ else
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ }
+}
+#endif
+
+
+void TiXmlElement::Print(FILE* cfile, int depth) const
+{
+ int i;
+ for (i=0; i<depth; i++)
+ {
+ fprintf(cfile, " ");
+ }
+
+ fprintf(cfile, "<%s", value.c_str());
+
+ const TiXmlAttribute* attrib;
+ for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+ {
+ fprintf(cfile, " ");
+ attrib->Print(cfile, depth);
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a <foo /> node
+ // 2) An element with only a text child is printed as <foo> text </foo>
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if (!firstChild)
+ {
+ fprintf(cfile, " />");
+ }
+ else if (firstChild == lastChild && firstChild->ToText())
+ {
+ fprintf(cfile, ">");
+ firstChild->Print(cfile, depth + 1);
+ fprintf(cfile, "</%s>", value.c_str());
+ }
+ else
+ {
+ fprintf(cfile, ">");
+
+ for (node = firstChild; node; node=node->NextSibling())
+ {
+ if (!node->ToText())
+ {
+ fprintf(cfile, "\n");
+ }
+ node->Print(cfile, depth+1);
+ }
+ fprintf(cfile, "\n");
+ for (i=0; i<depth; ++i)
+ fprintf(cfile, " ");
+ fprintf(cfile, "</%s>", value.c_str());
+ }
+}
+
+void TiXmlElement::StreamOut(TIXML_OSTREAM * stream) const
+{
+ (*stream) << "<" << value;
+
+ const TiXmlAttribute* attrib;
+ for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+ {
+ (*stream) << " ";
+ attrib->StreamOut(stream);
+ }
+
+ // If this node has children, give it a closing tag. Else
+ // make it an empty tag.
+ TiXmlNode* node;
+ if (firstChild)
+ {
+ (*stream) << ">";
+
+ for (node = firstChild; node; node=node->NextSibling())
+ {
+ node->StreamOut(stream);
+ }
+ (*stream) << "</" << value << ">";
+ }
+ else
+ {
+ (*stream) << " />";
+ }
+}
+
+void TiXmlElement::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+ // Adding tabs to get the proper tree format
+ int oldDepth = depth;
+ StreamDepth(stream, depth);
+
+ // Element name
+ (*stream) << "<" << value;
+
+ // Attributes
+ const TiXmlAttribute* attrib;
+ for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+ {
+ (*stream) << " ";
+ attrib->StreamOut(stream);
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a <foo /> node
+ // 2) An element with only a text child is printed as <foo> text </foo>
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if (!firstChild)
+ {
+ (*stream) << " />" << TIXML_ENDL;
+ }
+ else if (firstChild == lastChild && firstChild->ToText())
+ {
+ (*stream) << ">";
+ firstChild->FormattedStreamOut(stream, depth + 1);
+ (*stream) << "</" << value << ">" << TIXML_ENDL;
+ }
+ else
+ {
+ (*stream) << ">" << TIXML_ENDL;
+
+ // Children
+ depth++;
+ for (node = firstChild; node; node=node->NextSibling())
+ {
+ node->FormattedStreamOut(stream, depth);
+ }
+ StreamDepth(stream, oldDepth);
+ (*stream) << "</" << value << ">" << TIXML_ENDL;
+ }
+}
+
+void TiXmlElement::CopyTo(TiXmlElement* target) const
+{
+ // superclass:
+ TiXmlNode::CopyTo(target);
+
+ // Element class:
+ // Clone the attributes, then clone the children.
+ const TiXmlAttribute* attribute = 0;
+ for ( attribute = attributeSet.First();
+ attribute;
+ attribute = attribute->Next())
+ {
+ target->SetAttribute(attribute->Name(), attribute->Value());
+ }
+
+ TiXmlNode* node = 0;
+ for (node = firstChild; node; node = node->NextSibling())
+ {
+ target->LinkEndChild(node->Clone());
+ }
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+ TiXmlElement* clone = new TiXmlElement(Value());
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+ const TiXmlNode* child = this->FirstChild();
+ if (child) {
+ const TiXmlText* childText = child->ToText();
+ if (childText) {
+ return childText->Value();
+ }
+ }
+ return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ ClearError();
+}
+
+TiXmlDocument::TiXmlDocument(const char * documentName) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument(const std::string& documentName) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument(const TiXmlDocument& copy) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+ copy.CopyTo(this);
+}
+
+
+void TiXmlDocument::operator=(const TiXmlDocument& copy)
+{
+ Clear();
+ copy.CopyTo(this);
+}
+
+
+bool TiXmlDocument::LoadFile(TiXmlEncoding encoding)
+{
+ // See STL_STRING_BUG below.
+ StringToBuffer buf(value);
+
+ if (buf.buffer && LoadFile(buf.buffer, encoding))
+ return true;
+
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+ // See STL_STRING_BUG below.
+ StringToBuffer buf(value);
+
+ if (buf.buffer && SaveFile(buf.buffer))
+ return true;
+
+ return false;
+}
+
+#ifdef TIXML_USE_STL
+std::string TiXmlDocument::GetAsString()
+{
+ std::stringstream out;
+ FormattedStreamOut(&out, 0);
+ return out.str();
+}
+#endif
+
+bool TiXmlDocument::GetAsCharBuffer(char* buffer, size_t bufferSize)
+{
+ #ifdef TIXML_USE_STL
+ std::string data = GetAsString();
+ #else
+ TIXML_OSTREAM data;
+ FormattedStreamOut(&data, 0);
+ #endif
+
+ if (bufferSize < data.length())
+ {
+ return false;
+ }
+ else
+ {
+ strcpy(buffer, data.c_str());
+ return true;
+ }
+}
+
+bool TiXmlDocument::LoadFile(const char* filename, TiXmlEncoding encoding)
+{
+ // There was a really terrifying little bug here. The code:
+ // value = filename
+ // in the STL case, cause the assignment method of the std::string to
+ // be called. What is strange, is that the std::string had the same
+ // address as it's c_str() method, and so bad things happen. Looks
+ // like a bug in the Microsoft STL implementation.
+ // See STL_STRING_BUG above.
+ // Fixed with the StringToBuffer class.
+ value = filename;
+
+ // reading in binary mode so that tinyxml can normalize the EOL
+ FILE* file = fopen(value.c_str (), "rb");
+
+ if (file)
+ {
+ bool result = LoadFile(file, encoding);
+ fclose(file);
+ return result;
+ }
+ else
+ {
+ SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return false;
+ }
+}
+
+bool TiXmlDocument::LoadFile(FILE* file, TiXmlEncoding encoding)
+{
+ if (!file)
+ {
+ SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+ long length = 0;
+ fseek(file, 0, SEEK_END);
+ length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ // Strange case, but good to handle up front.
+ if (length == 0)
+ {
+ SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return false;
+ }
+
+ // If we have a file, assume it is all one big XML file, and read it in.
+ // The document parser may decide the document ends sooner than the entire file, however.
+ TIXML_STRING data;
+ data.reserve(length);
+
+ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+ // 2.11 End-of-Line Handling
+ // <snip>
+ // <quote>
+ // ...the XML processor MUST behave as if it normalized all line breaks in external
+ // parsed entities (including the document entity) on input, before parsing, by translating
+ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+ // a single #xA character.
+ // </quote>
+ //
+ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
+ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+ // convention, and not work generally.
+
+ /*
+ while (fgets(buf, sizeof(buf), file))
+ {
+ data += buf;
+ }
+ */
+
+ char* buf = new char[ length+1 ];
+ buf[0] = 0;
+
+ if (fread(buf, length, 1, file) != 1) {
+ delete [] buf;
+ SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return false;
+ }
+
+ const char* lastPos = buf;
+ const char* p = buf;
+
+ buf[length] = 0;
+ while (*p) {
+ assert(p < (buf+length));
+ if (*p == 0xa) {
+ // Newline character. No special rules for this. Append all the characters
+ // since the last string, and include the newline.
+ data.append(lastPos, (p-lastPos+1)); // append, include the newline
+ ++p; // move past the newline
+ lastPos = p; // and point to the new buffer (may be 0)
+ assert(p <= (buf+length));
+ }
+ else if (*p == 0xd) {
+ // Carriage return. Append what we have so far, then
+ // handle moving forward in the buffer.
+ if ((p-lastPos) > 0) {
+ data.append(lastPos, p-lastPos); // do not add the CR
+ }
+ data += (char)0xa; // a proper newline
+
+ if (*(p+1) == 0xa) {
+ // Carriage return - new line sequence
+ p += 2;
+ lastPos = p;
+ assert(p <= (buf+length));
+ }
+ else {
+ // it was followed by something else...that is presumably characters again.
+ ++p;
+ lastPos = p;
+ assert(p <= (buf+length));
+ }
+ }
+ else {
+ ++p;
+ }
+ }
+ // Handle any left over characters.
+ if (p-lastPos) {
+ data.append(lastPos, p-lastPos);
+ }
+ delete [] buf;
+ buf = 0;
+
+ Parse(data.c_str(), 0, encoding);
+
+ if ( Error())
+ return false;
+ else
+ return true;
+}
+
+
+bool TiXmlDocument::SaveFile(const char * filename) const
+{
+ // The old c stuff lives on...
+ FILE* fp = fopen(filename, "w");
+ if (fp)
+ {
+ bool result = SaveFile(fp);
+ fclose(fp);
+ return result;
+ }
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile(FILE* fp) const
+{
+ if (useMicrosoftBOM)
+ {
+ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+ fputc(TIXML_UTF_LEAD_0, fp);
+ fputc(TIXML_UTF_LEAD_1, fp);
+ fputc(TIXML_UTF_LEAD_2, fp);
+ }
+ Print(fp, 0);
+ return true;
+}
+
+
+void TiXmlDocument::CopyTo(TiXmlDocument* target) const
+{
+ TiXmlNode::CopyTo(target);
+
+ target->error = error;
+ target->errorDesc = errorDesc.c_str ();
+
+ TiXmlNode* node = 0;
+ for (node = firstChild; node; node = node->NextSibling())
+ {
+ target->LinkEndChild(node->Clone());
+ }
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+ TiXmlDocument* clone = new TiXmlDocument();
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+void TiXmlDocument::Print(FILE* cfile, int depth) const
+{
+ const TiXmlNode* node;
+ for (node=FirstChild(); node; node=node->NextSibling())
+ {
+ node->Print(cfile, depth);
+ fprintf(cfile, "\n");
+ }
+}
+
+void TiXmlDocument::StreamOut(TIXML_OSTREAM * out) const
+{
+ const TiXmlNode* node;
+ for (node=FirstChild(); node; node=node->NextSibling())
+ {
+ node->StreamOut(out);
+
+ // Special rule for streams: stop after the root element.
+ // The stream in code will only read one element, so don't
+ // write more than one.
+ if (node->ToElement())
+ break;
+ }
+}
+
+void TiXmlDocument::FormattedStreamOut(TIXML_OSTREAM * out, int depth) const
+{
+ const TiXmlNode* node;
+ for (node=FirstChild(); node; node=node->NextSibling())
+ {
+ node->FormattedStreamOut(out, depth);
+
+ // Special rule for streams: stop after the root element.
+ // The stream in code will only read one element, so don't
+ // write more than one.
+ if (node->ToElement())
+ break;
+ }
+}
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if (next->value.empty() && next->name.empty())
+ return 0;
+ return next;
+}
+
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if (next->value.empty() && next->name.empty())
+ return 0;
+ return next;
+}
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if (prev->value.empty() && prev->name.empty())
+ return 0;
+ return prev;
+}
+
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if (prev->value.empty() && prev->name.empty())
+ return 0;
+ return prev;
+}
+
+void TiXmlAttribute::Print(FILE* cfile, int /*depth*/) const
+{
+ TIXML_STRING n, v;
+
+ PutString(name, &n);
+ PutString(value, &v);
+
+ if (value.find ('\"') == TIXML_STRING::npos)
+ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str());
+ else
+ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str());
+}
+
+
+void TiXmlAttribute::StreamOut(TIXML_OSTREAM * stream) const
+{
+ if (value.find('\"') != TIXML_STRING::npos)
+ {
+ PutString(name, stream);
+ (*stream) << "=" << "'";
+ PutString(value, stream);
+ (*stream) << "'";
+ }
+ else
+ {
+ PutString(name, stream);
+ (*stream) << "=" << "\"";
+ PutString(value, stream);
+ (*stream) << "\"";
+ }
+}
+
+int TiXmlAttribute::QueryIntValue(int* ival) const
+{
+ if (sscanf(value.c_str(), "%d", ival) == 1)
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue(double* dval) const
+{
+ if (sscanf(value.c_str(), "%lf", dval) == 1)
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue(int _value)
+{
+ char buf [64];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+ #else
+ sprintf (buf, "%d", _value);
+ #endif
+ SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue(double _value)
+{
+ char buf [256];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%lf", _value);
+ #else
+ sprintf (buf, "%lf", _value);
+ #endif
+ SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+ return (int)_atoi64(value.c_str ());
+}
+
+double TiXmlAttribute::DoubleValue() const
+{
+ return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment(const TiXmlComment& copy) : TiXmlNode(TiXmlNode::COMMENT)
+{
+ copy.CopyTo(this);
+}
+
+
+void TiXmlComment::operator=(const TiXmlComment& base)
+{
+ Clear();
+ base.CopyTo(this);
+}
+
+
+void TiXmlComment::Print(FILE* cfile, int depth) const
+{
+ for (int i=0; i<depth; i++)
+ {
+ fputs(" ", cfile);
+ }
+ fprintf(cfile, "<!--%s-->", value.c_str());
+}
+
+void TiXmlComment::StreamOut(TIXML_OSTREAM * stream) const
+{
+ (*stream) << "<!--";
+ //PutString(value, stream);
+ (*stream) << value;
+ (*stream) << "-->";
+}
+
+void TiXmlComment::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+ StreamDepth(stream, depth);
+
+ StreamOut(stream);
+
+ (*stream) << TIXML_ENDL;
+}
+
+void TiXmlComment::CopyTo(TiXmlComment* target) const
+{
+ TiXmlNode::CopyTo(target);
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+ TiXmlComment* clone = new TiXmlComment();
+
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+void TiXmlText::Print(FILE* cfile, int depth) const
+{
+ if (cdata)
+ {
+ int i;
+ fprintf(cfile, "\n");
+ for (i=0; i<depth; i++) {
+ fprintf(cfile, " ");
+ }
+ fprintf(cfile, "<![CDATA[");
+ fprintf(cfile, "%s", value.c_str()); // unformatted output
+ fprintf(cfile, "]]>\n");
+ }
+ else
+ {
+ TIXML_STRING buffer;
+ PutString(value, &buffer);
+ fprintf(cfile, "%s", buffer.c_str());
+ }
+}
+
+
+void TiXmlText::StreamOut(TIXML_OSTREAM * stream) const
+{
+ if (cdata)
+ {
+ (*stream) << "<![CDATA[" << value << "]]>";
+ }
+ else
+ {
+ PutString(value, stream);
+ }
+}
+
+void TiXmlText::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+ if (cdata)
+ {
+ (*stream) << TIXML_ENDL;
+ StreamDepth(stream, depth);
+ (*stream) << "<![CDATA[" << value << "]]>" << TIXML_ENDL;
+ }
+ else
+ {
+ PutString(value, stream);
+ }
+}
+
+void TiXmlText::CopyTo(TiXmlText* target) const
+{
+ TiXmlNode::CopyTo(target);
+ target->cdata = cdata;
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+ TiXmlText* clone = 0;
+ clone = new TiXmlText("");
+
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration(const char * _version,
+ const char * _encoding,
+ const char * _standalone)
+ : TiXmlNode(TiXmlNode::DECLARATION)
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone)
+ : TiXmlNode(TiXmlNode::DECLARATION)
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration(const TiXmlDeclaration& copy)
+ : TiXmlNode(TiXmlNode::DECLARATION)
+{
+ copy.CopyTo(this);
+}
+
+
+void TiXmlDeclaration::operator=(const TiXmlDeclaration& copy)
+{
+ Clear();
+ copy.CopyTo(this);
+}
+
+
+void TiXmlDeclaration::Print(FILE* cfile, int /*depth*/) const
+{
+ fprintf (cfile, "<?xml ");
+
+ if (!version.empty())
+ fprintf (cfile, "version=\"%s\" ", version.c_str ());
+ if (!encoding.empty())
+ fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+ if (!standalone.empty())
+ fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+ fprintf (cfile, "?>");
+}
+
+void TiXmlDeclaration::StreamOut(TIXML_OSTREAM * stream) const
+{
+ (*stream) << "<?xml ";
+
+ if (!version.empty())
+ {
+ (*stream) << "version=\"";
+ PutString(version, stream);
+ (*stream) << "\" ";
+ }
+ if (!encoding.empty())
+ {
+ (*stream) << "encoding=\"";
+ PutString(encoding, stream);
+ (*stream) << "\" ";
+ }
+ if (!standalone.empty())
+ {
+ (*stream) << "standalone=\"";
+ PutString(standalone, stream);
+ (*stream) << "\" ";
+ }
+ (*stream) << "?>";
+}
+
+void TiXmlDeclaration::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+ StreamDepth(stream, depth);
+ StreamOut(stream);
+ (*stream) << TIXML_ENDL;
+}
+
+void TiXmlDeclaration::CopyTo(TiXmlDeclaration* target) const
+{
+ TiXmlNode::CopyTo(target);
+
+ target->version = version;
+ target->encoding = encoding;
+ target->standalone = standalone;
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+ TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+void TiXmlUnknown::Print(FILE* cfile, int depth) const
+{
+ for (int i=0; i<depth; i++)
+ fprintf(cfile, " ");
+ fprintf(cfile, "<%s>", value.c_str());
+}
+
+
+void TiXmlUnknown::StreamOut(TIXML_OSTREAM * stream) const
+{
+ (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown.
+}
+
+void TiXmlUnknown::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+ StreamDepth(stream, depth);
+ (*stream) << "<" << value << ">" << TIXML_ENDL; // Don't use entities here! It is unknown.
+}
+
+void TiXmlUnknown::CopyTo(TiXmlUnknown* target) const
+{
+ TiXmlNode::CopyTo(target);
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+ TiXmlUnknown* clone = new TiXmlUnknown();
+
+ if (!clone)
+ return 0;
+
+ CopyTo(clone);
+ return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+ sentinel.next = &sentinel;
+ sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+ assert(sentinel.next == &sentinel);
+ assert(sentinel.prev == &sentinel);
+}
+
+
+void TiXmlAttributeSet::Add(TiXmlAttribute* addMe)
+{
+ assert(!Find(TIXML_STRING(addMe->Name()))); // Shouldn't be multiply adding to the set.
+
+ addMe->next = &sentinel;
+ addMe->prev = sentinel.prev;
+
+ sentinel.prev->next = addMe;
+ sentinel.prev = addMe;
+}
+
+void TiXmlAttributeSet::Remove(TiXmlAttribute* removeMe)
+{
+ TiXmlAttribute* node;
+
+ for (node = sentinel.next; node != &sentinel; node = node->next)
+ {
+ if (node == removeMe)
+ {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+ return;
+ }
+ }
+ assert(0); // we tried to remove a non-linked attribute.
+}
+
+const TiXmlAttribute* TiXmlAttributeSet::Find(const TIXML_STRING& name) const
+{
+ const TiXmlAttribute* node;
+
+ for (node = sentinel.next; node != &sentinel; node = node->next)
+ {
+ if (node->name == name)
+ return node;
+ }
+ return 0;
+}
+
+TiXmlAttribute* TiXmlAttributeSet::Find(const TIXML_STRING& name)
+{
+ TiXmlAttribute* node;
+
+ for (node = sentinel.next; node != &sentinel; node = node->next)
+ {
+ if (node->name == name)
+ return node;
+ }
+ return 0;
+}
+
+#ifdef TIXML_USE_STL
+TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base)
+{
+ TIXML_STRING tag;
+ tag.reserve(8 * 1000);
+ base.StreamIn(&in, &tag);
+
+ base.Parse(tag.c_str(), 0, TIXML_DEFAULT_ENCODING);
+ return in;
+}
+#endif
+
+
+TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base)
+{
+ base.StreamOut (& out);
+ return out;
+}
+
+
+#ifdef TIXML_USE_STL
+std::string & operator<< (std::string& out, const TiXmlNode& base)
+{
+ std::ostringstream os_stream(std::ostringstream::out);
+ base.StreamOut(&os_stream);
+
+ out.append(os_stream.str());
+ return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+ if (node)
+ {
+ TiXmlNode* child = node->FirstChild();
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild(const char * value) const
+{
+ if (node)
+ {
+ TiXmlNode* child = node->FirstChild(value);
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+ if (node)
+ {
+ TiXmlElement* child = node->FirstChildElement();
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement(const char * value) const
+{
+ if (node)
+ {
+ TiXmlElement* child = node->FirstChildElement(value);
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::Child(int count) const
+{
+ if (node)
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling(), ++i)
+ {
+ // nothing
+ }
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::Child(const char* value, int count) const
+{
+ if (node)
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild(value);
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling(value), ++i)
+ {
+ // nothing
+ }
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement(int count) const
+{
+ if (node)
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement(), ++i)
+ {
+ // nothing
+ }
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement(const char* value, int count) const
+{
+ if (node)
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement(value);
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement(value), ++i)
+ {
+ // nothing
+ }
+ if (child)
+ return TiXmlHandle(child);
+ }
+ return TiXmlHandle(0);
+}
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxml.h b/plugins/UserInfoEx/src/ex_import/tinyxml.h
new file mode 100644
index 0000000000..0bc947422f
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxml.h
@@ -0,0 +1,1599 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+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.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxml.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4530)
+#pragma warning(disable : 4786)
+#endif
+
+#ifndef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#endif
+
+// Help out windows:
+#if defined(_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_TICPP
+ #ifndef TIXML_USE_STL
+ #define TIXML_USE_STL
+ #endif
+#endif
+
+#ifdef TIXML_USE_STL
+ #include <string>
+ #include <iostream>
+ #include <sstream>
+ #define TIXML_STRING std::string
+ #define TIXML_ISTREAM std::istream
+ #define TIXML_OSTREAM std::ostream
+ #define TIXML_ENDL std::endl
+#else
+ #include "tinystr.h"
+ #define TIXML_STRING TiXmlString
+ #define TIXML_OSTREAM TiXmlOutStream
+ #define TIXML_ENDL "\n"
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+
+#define TIXML_SAFE // TinyXml isn't fully buffer overrun protected, safe code. This is work in progress.
+#ifdef TIXML_SAFE
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400)
+ // Microsoft visual studio, version 2005 and higher.
+ #define TIXML_SNPRINTF _snprintf_s
+ #define TIXML_SNSCANF _snscanf_s
+ #elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+ // Microsoft visual studio, version 6 and higher.
+ //#pragma message("Using _sn* functions.")
+ #define TIXML_SNPRINTF _snprintf
+ #define TIXML_SNSCANF _snscanf
+ #elif defined(__GNUC__) && (__GNUC__ >= 3)
+ // GCC version 3 and higher.s
+ //#warning("Using sn* functions.")
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_SNSCANF snscanf
+ #endif
+#endif
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 4;
+const int TIXML_PATCH_VERSION = 3;
+
+/* Internal structure for tracking location of items
+ in the XML file.
+*/
+struct TiXmlCursor
+{
+ TiXmlCursor() { Clear(); }
+ void Clear() { row = col = -1; }
+
+ int row; // 0 based.
+ int col; // 0 based.
+};
+
+
+// Only used by Attribute::Query functions
+enum
+{
+ TIXML_SUCCESS,
+ TIXML_NO_ATTRIBUTE,
+ TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+ TIXML_ENCODING_UNKNOWN,
+ TIXML_ENCODING_UTF8,
+ TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+ It does little except to establish that TinyXml classes
+ can be printed and provide some utility functions.
+
+ In XML, the document and elements can contain
+ other elements and other types of nodes.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration(leaf)
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ A Decleration contains: Attributes (not on tree)
+ @endverbatim
+*/
+#ifdef TIXML_USE_TICPP
+#include "ticpprc.h"
+class TiXmlBase : public TiCppRC
+#else
+class TiXmlBase
+#endif
+{
+ friend class TiXmlNode;
+ friend class TiXmlElement;
+ friend class TiXmlDocument;
+
+public:
+ TiXmlBase() : userData(0) {}
+ virtual ~TiXmlBase() {}
+
+ /** All TinyXml classes can print themselves to a filestream.
+ This is a formatted print, and will insert tabs and newlines.
+
+ (For an unformatted stream, use the << operator.)
+ */
+ virtual void Print(FILE* cfile, int depth) const = 0;
+
+ /** The world does not agree on whether white space should be kept or
+ not. In order to make everyone happy, these global, static functions
+ are provided to set whether or not TinyXml will condense all white space
+ into a single space or not. The default is to condense. Note changing this
+ values is not thread safe.
+ */
+ static void SetCondenseWhiteSpace(bool condense) { condenseWhiteSpace = condense; }
+
+ /// Return the current white space setting.
+ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
+
+ /** Return the position, in the original source file, of this node or attribute.
+ The row and column are 1-based. (That is the first row and first column is
+ 1,1). If the returns values are 0 or less, then the parser does not have
+ a row and column value.
+
+ Generally, the row and column value will be set when the TiXmlDocument::Load(void),
+ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+ when the DOM was created from operator>>.
+
+ The values reflect the initial load. Once the DOM is modified programmatically
+ (by adding or changing nodes and attributes) the new values will NOT update to
+ reflect changes in the document.
+
+ There is a minor performance cost to computing the row and column. Computation
+ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+ @sa TiXmlDocument::SetTabSize()
+ */
+ int Row() const { return location.row + 1; }
+ int Column() const { return location.col + 1; } ///< See Row()
+
+ void _SetUserData(void* user) { userData = user; }
+ void* _GetUserData() { return userData; }
+
+ // Table that returs, for a given lead byte, the total number of bytes
+ // in the UTF-8 sequence.
+ static const int utf8ByteTable[256];
+
+ virtual const char* Parse( const char* p,
+ TiXmlParsingData* data,
+ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */) = 0;
+
+ enum
+ {
+ TIXML_NO_ERROR = 0,
+ TIXML_ERROR,
+ TIXML_ERROR_OPENING_FILE,
+ TIXML_ERROR_OUT_OF_MEMORY,
+ TIXML_ERROR_PARSING_ELEMENT,
+ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+ TIXML_ERROR_READING_ELEMENT_VALUE,
+ TIXML_ERROR_READING_ATTRIBUTES,
+ TIXML_ERROR_PARSING_EMPTY,
+ TIXML_ERROR_READING_END_TAG,
+ TIXML_ERROR_PARSING_UNKNOWN,
+ TIXML_ERROR_PARSING_COMMENT,
+ TIXML_ERROR_PARSING_DECLARATION,
+ TIXML_ERROR_DOCUMENT_EMPTY,
+ TIXML_ERROR_EMBEDDED_NULL,
+ TIXML_ERROR_PARSING_CDATA,
+ TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+ TIXML_ERROR_STRING_COUNT
+ };
+
+protected:
+
+ void StreamDepth(TIXML_OSTREAM* stream, int depth) const;
+
+ // See STL_STRING_BUG
+ // Utility class to overcome a bug.
+ class StringToBuffer
+ {
+ public:
+ StringToBuffer(const TIXML_STRING& str);
+ ~StringToBuffer();
+ char* buffer;
+ };
+
+ static const char* SkipWhiteSpace(const char*, TiXmlEncoding encoding);
+ inline static bool IsWhiteSpace(char c)
+ {
+ return (isspace((unsigned char) c) || c == '\n' || c == '\r');
+ }
+ inline static bool IsWhiteSpace(int c)
+ {
+ if (c < 256)
+ return IsWhiteSpace((char) c);
+ return false; // Again, only truly correct for English/Latin...but usually works.
+ }
+
+ virtual void StreamOut (TIXML_OSTREAM *) const = 0;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const = 0;
+
+ #ifdef TIXML_USE_STL
+ static bool StreamWhiteSpace(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ static bool StreamTo(TIXML_ISTREAM * in, int character, TIXML_STRING * tag);
+ #endif
+
+ /* Reads an XML name into the string provided. Returns
+ a pointer just past the last character of the name,
+ or 0 if the function has an error.
+ */
+ static const char* ReadName(const char* p, TIXML_STRING* name, TiXmlEncoding encoding);
+
+ /* Reads text. Returns a pointer past the given end tag.
+ Wickedly complex options, but it keeps the (sensitive) code in one place.
+ */
+ static const char* ReadText( const char* in, // where to start
+ TIXML_STRING* text, // the string read
+ bool ignoreWhiteSpace, // whether to keep the white space
+ const char* endTag, // what ends this text
+ bool ignoreCase, // whether to ignore case in the end tag
+ TiXmlEncoding encoding); // the current encoding
+
+ // If an entity has been found, transform it into a character.
+ static const char* GetEntity(const char* in, char* value, int* length, TiXmlEncoding encoding);
+
+ // Get a character, while interpreting entities.
+ // The length can be from 0 to 4 bytes.
+ inline static const char* GetChar(const char* p, char* _value, int* length, TiXmlEncoding encoding)
+ {
+ assert(p);
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ *length = utf8ByteTable[ *((unsigned char*)p) ];
+ assert(*length >= 0 && *length < 5);
+ }
+ else
+ {
+ *length = 1;
+ }
+
+ if (*length == 1)
+ {
+ if (*p == '&')
+ return GetEntity(p, _value, length, encoding);
+ *_value = *p;
+ return p+1;
+ }
+ else if (*length)
+ {
+ //strncpy(_value, p, *length); // lots of compilers don't like this function (unsafe),
+ // and the null terminator isn't needed
+ for (int i=0; p[i] && i<*length; ++i) {
+ _value[i] = p[i];
+ }
+ return p + (*length);
+ }
+ else
+ {
+ // Not valid text.
+ return 0;
+ }
+ }
+
+ // Puts a string to a stream, expanding entities as it goes.
+ // Note this should not contian the '<', '>', etc, or they will be transformed into entities!
+ static void PutString(const TIXML_STRING& str, TIXML_OSTREAM* out);
+
+ static void PutString(const TIXML_STRING& str, TIXML_STRING* out);
+
+ // Return true if the next characters in the stream are any of the endTag sequences.
+ // Ignore case only works for english, and should only be relied on when comparing
+ // to English words: StringEqual(p, "version", true) is fine.
+ static bool StringEqual( const char* p,
+ const char* endTag,
+ bool ignoreCase,
+ TiXmlEncoding encoding);
+
+ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+ TiXmlCursor location;
+
+ /// Field containing a generic user pointer
+ void* userData;
+
+ // None of these methods are reliable for any language except English.
+ // Good for approximation, not great for accuracy.
+ static int IsAlpha(unsigned char anyByte, TiXmlEncoding encoding);
+ static int IsAlphaNum(unsigned char anyByte, TiXmlEncoding encoding);
+ inline static int ToLower(int v, TiXmlEncoding encoding)
+ {
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ if (v < 128) return tolower(v);
+ return v;
+ }
+ else
+ {
+ return tolower(v);
+ }
+ }
+ static void ConvertUTF32ToUTF8(unsigned long input, char* output, int* length);
+
+private:
+ TiXmlBase(const TiXmlBase&); // not implemented.
+ void operator=(const TiXmlBase& base); // not allowed.
+
+ struct Entity
+ {
+ const char* str;
+ unsigned int strLength;
+ char chr;
+ };
+ enum
+ {
+ NUM_ENTITY = 5,
+ MAX_ENTITY_LENGTH = 6
+
+ };
+ static Entity entity[ NUM_ENTITY ];
+ static bool condenseWhiteSpace;
+};
+
+
+/** The parent class for everything in the Document Object Model.
+ (Except for attributes).
+ Nodes have siblings, a parent, and children. A node can be
+ in a document, or stand on its own. The type of a TiXmlNode
+ can be queried, and it can be cast to its more defined type.
+*/
+class TiXmlNode : public TiXmlBase
+{
+ friend class TiXmlDocument;
+ friend class TiXmlElement;
+
+public:
+ #ifdef TIXML_USE_STL
+
+ /** An input stream operator, for every class. Tolerant of newlines and
+ formatting, but doesn't expect them.
+ */
+ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+ /** An output stream operator, for every class. Note that this outputs
+ without any newlines or formatting, as opposed to Print(), which
+ includes tabs and new lines.
+
+ The operator<< and operator>> are not completely symmetric. Writing
+ a node to a stream is very well defined. You'll get a nice stream
+ of output, without any extra whitespace or newlines.
+
+ But reading is not as well defined. (As it always is.) If you create
+ a TiXmlElement (for example) and read that from an input stream,
+ the text needs to define an element or junk will result. This is
+ true of all input streams, but it's worth keeping in mind.
+
+ A TiXmlDocument will read nodes until it reads a root element, and
+ all the children of that root element.
+ */
+ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+ /// Appends the XML node or attribute to a std::string.
+ friend std::string& operator<< (std::string& out, const TiXmlNode& base);
+
+ #else
+ // Used internally, not part of the public API.
+ friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base);
+ #endif
+
+ /** The types of XML nodes supported by TinyXml. (All the
+ unsupported types are picked up by UNKNOWN.)
+ */
+ enum NodeType
+ {
+ DOCUMENT,
+ ELEMENT,
+ COMMENT,
+ UNKNOWN,
+ TEXT,
+ DECLARATION,
+ TYPECOUNT
+ };
+
+ virtual ~TiXmlNode();
+
+ /** The meaning of 'value' changes for the specific type of
+ TiXmlNode.
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+
+ The subclasses will wrap this function.
+ */
+ const char *Value() const { return value.c_str (); }
+
+ #ifdef TIXML_USE_STL
+ /** Return Value() as a std::string. If you only use STL,
+ this is more efficient than calling Value().
+ Only available in STL mode.
+ */
+ const std::string& ValueStr() const { return value; }
+ #endif
+
+ /** Changes the value of the node. Defined as:
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
+ void SetValue(const char * _value) { value = _value;}
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetValue(const std::string& _value) { value = _value; }
+ #endif
+
+ /// Delete all the children of this node. Does not affect 'this'.
+ void Clear();
+
+ /// One step up the DOM.
+ TiXmlNode* Parent() { return parent; }
+ const TiXmlNode* Parent() const { return parent; }
+
+ const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
+ TiXmlNode* FirstChild() { return firstChild; }
+ const TiXmlNode* FirstChild(const char * value) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
+ TiXmlNode* FirstChild(const char * value); ///< The first child of this node with the matching 'value'. Will be null if none found.
+
+ const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
+ TiXmlNode* LastChild() { return lastChild; }
+ const TiXmlNode* LastChild(const char * value) const; /// The last child of this node matching 'value'. Will be null if there are no children.
+ TiXmlNode* LastChild(const char * value);
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* FirstChild(const std::string& _value) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* FirstChild(const std::string& _value) { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* LastChild(const std::string& _value) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* LastChild(const std::string& _value) { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** An alternate way to walk the children of a node.
+ One way to iterate over nodes is:
+ @verbatim
+ for (child = parent->FirstChild(); child; child = child->NextSibling())
+ @endverbatim
+
+ IterateChildren does the same thing with the syntax:
+ @verbatim
+ child = 0;
+ while (child = parent->IterateChildren(child))
+ @endverbatim
+
+ IterateChildren takes the previous child as input and finds
+ the next one. If the previous child is null, it returns the
+ first. IterateChildren will return null when done.
+ */
+ const TiXmlNode* IterateChildren(const TiXmlNode* previous) const;
+ TiXmlNode* IterateChildren(TiXmlNode* previous);
+
+ /// This flavor of IterateChildren searches for children with a particular 'value'
+ const TiXmlNode* IterateChildren(const char * value, const TiXmlNode* previous) const;
+ TiXmlNode* IterateChildren(const char * value, TiXmlNode* previous);
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* IterateChildren(const std::string& _value, const TiXmlNode* previous) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ TiXmlNode* IterateChildren(const std::string& _value, TiXmlNode* previous) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ #endif
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertEndChild(const TiXmlNode& addThis);
+
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+
+ NOTE: the node to be added is passed by pointer, and will be
+ henceforth owned (and deleted) by tinyXml. This method is efficient
+ and avoids an extra copy, but should be used with care as it
+ uses a different memory model than the other insert functions.
+
+ @sa InsertEndChild
+ */
+ TiXmlNode* LinkEndChild(TiXmlNode* addThis);
+
+ /** Add a new node related to this. Adds a child before the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertBeforeChild(TiXmlNode* beforeThis, const TiXmlNode& addThis);
+
+ /** Add a new node related to this. Adds a child after the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis);
+
+ /** Replace a child of this node.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* ReplaceChild(TiXmlNode* replaceThis, const TiXmlNode& withThis);
+
+ /// Delete a child of this node.
+ bool RemoveChild(TiXmlNode* removeThis);
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling() const { return prev; }
+ TiXmlNode* PreviousSibling() { return prev; }
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling(const char *) const;
+ TiXmlNode* PreviousSibling(const char *);
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* PreviousSibling(const std::string& _value) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* PreviousSibling(const std::string& _value) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* NextSibling(const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* NextSibling(const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* NextSibling() const { return next; }
+ TiXmlNode* NextSibling() { return next; }
+
+ /// Navigate to a sibling node with the given 'value'.
+ const TiXmlNode* NextSibling(const char *) const;
+ TiXmlNode* NextSibling(const char *);
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement() const;
+ TiXmlElement* NextSiblingElement();
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement(const char *) const;
+ TiXmlElement* NextSiblingElement(const char *);
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* NextSiblingElement(const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* NextSiblingElement(const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement() const;
+ TiXmlElement* FirstChildElement();
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement(const char * value) const;
+ TiXmlElement* FirstChildElement(const char * value);
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* FirstChildElement(const std::string& _value) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* FirstChildElement(const std::string& _value) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** Query the type (as an enumerated value, above) of this node.
+ The possible types are: DOCUMENT, ELEMENT, COMMENT,
+ UNKNOWN, TEXT, and DECLARATION.
+ */
+ int Type() const { return type; }
+
+ /** Return a pointer to the Document this node lives in.
+ Returns null if not in a document.
+ */
+ const TiXmlDocument* GetDocument() const;
+ TiXmlDocument* GetDocument();
+
+ /// Returns true if this node has no children.
+ bool NoChildren() const { return !firstChild; }
+
+ virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ /** Create an exact duplicate of this node and return it. The memory must be deleted
+ by the caller.
+ */
+ virtual TiXmlNode* Clone() const = 0;
+
+protected:
+ TiXmlNode(NodeType _type);
+
+ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+ // and the assignment operator.
+ void CopyTo(TiXmlNode* target) const;
+
+ #ifdef TIXML_USE_STL
+ // The real work of the input operator.
+ virtual void StreamIn(TIXML_ISTREAM* in, TIXML_STRING* tag) = 0;
+ #endif
+
+ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+ TiXmlNode* Identify(const char* start, TiXmlEncoding encoding);
+
+ TiXmlNode* parent;
+ NodeType type;
+
+ TiXmlNode* firstChild;
+ TiXmlNode* lastChild;
+
+ TIXML_STRING value;
+
+ TiXmlNode* prev;
+ TiXmlNode* next;
+
+private:
+ TiXmlNode(const TiXmlNode&); // not implemented.
+ void operator=(const TiXmlNode& base); // not allowed.
+};
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not TiXmlNodes, since they are not
+ part of the tinyXML document object model. There are other
+ suggested ways to look at this problem.
+*/
+class TiXmlAttribute : public TiXmlBase
+{
+ friend class TiXmlAttributeSet;
+
+public:
+ /// Construct an empty attribute.
+ TiXmlAttribute() : TiXmlBase()
+ {
+ document = 0;
+ prev = next = 0;
+ }
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlAttribute(const std::string& _name, const std::string& _value)
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+ #endif
+
+ /// Construct an attribute with a name and value.
+ TiXmlAttribute(const char * _name, const char * _value)
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+
+ const char* Name() const { return name.c_str(); } ///< Return the name of this attribute.
+ const char* Value() const { return value.c_str(); } ///< Return the value of this attribute.
+ #ifdef TIXML_USE_STL
+ const std::string& ValueStr() const { return value; } ///< Return the value of this attribute.
+ #endif
+ int IntValue() const; ///< Return the value of this attribute, converted to an integer.
+ double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
+
+ // Get the tinyxml string representation
+ const TIXML_STRING& NameTStr() const { return name; }
+
+ /** QueryIntValue examines the value string. It is an alternative to the
+ IntValue() method with richer error checking.
+ If the value is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE.
+
+ A specialized but useful call. Note that for success it returns 0,
+ which is the opposite of almost all other TinyXml calls.
+ */
+ int QueryIntValue(int* _value) const;
+ /// QueryDoubleValue examines the value string. See QueryIntValue().
+ int QueryDoubleValue(double* _value) const;
+
+ void SetName(const char* _name) { name = _name; } ///< Set the name of this attribute.
+ void SetValue(const char* _value) { value = _value; } ///< Set the value.
+
+ void SetIntValue(int _value); ///< Set the value from an integer.
+ void SetDoubleValue(double _value); ///< Set the value from a double.
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetName(const std::string& _name) { name = _name; }
+ /// STL std::string form.
+ void SetValue(const std::string& _value) { value = _value; }
+ #endif
+
+ /// Get the next sibling attribute in the DOM. Returns null at end.
+ const TiXmlAttribute* Next() const;
+ TiXmlAttribute* Next();
+ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
+ const TiXmlAttribute* Previous() const;
+ TiXmlAttribute* Previous();
+
+ bool operator==(const TiXmlAttribute& rhs) const { return rhs.name == name; }
+ bool operator<(const TiXmlAttribute& rhs) const { return name < rhs.name; }
+ bool operator>(const TiXmlAttribute& rhs) const { return name > rhs.name; }
+
+ /* Attribute parsing starts: first letter of the name
+ returns: the next char after the value end quote
+ */
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ // Prints this Attribute to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ virtual void StreamOut(TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const {}
+
+ // [internal use]
+ // Set the document pointer so the attribute can report errors.
+ void SetDocument(TiXmlDocument* doc) { document = doc; }
+
+private:
+ TiXmlAttribute(const TiXmlAttribute&); // not implemented.
+ void operator=(const TiXmlAttribute& base); // not allowed.
+
+ TiXmlDocument* document; // A pointer back to a document, for error reporting.
+ TIXML_STRING name;
+ TIXML_STRING value;
+ TiXmlAttribute* prev;
+ TiXmlAttribute* next;
+};
+
+
+/* A class used to manage a group of attributes.
+ It is only used internally, both by the ELEMENT and the DECLARATION.
+
+ The set can be changed transparent to the Element and Declaration
+ classes that use it, but NOT transparent to the Attribute
+ which has to implement a next() and previous() method. Which makes
+ it a bit problematic and prevents the use of STL.
+
+ This version is implemented with circular lists because:
+ - I like circular lists
+ - it demonstrates some independence from the (typical) doubly linked list.
+*/
+class TiXmlAttributeSet
+{
+public:
+ TiXmlAttributeSet();
+ ~TiXmlAttributeSet();
+
+ void Add(TiXmlAttribute* attribute);
+ void Remove(TiXmlAttribute* attribute);
+
+ const TiXmlAttribute* First() const { return (sentinel.next == &sentinel) ? 0 : sentinel.next; }
+ TiXmlAttribute* First() { return (sentinel.next == &sentinel) ? 0 : sentinel.next; }
+ const TiXmlAttribute* Last() const { return (sentinel.prev == &sentinel) ? 0 : sentinel.prev; }
+ TiXmlAttribute* Last() { return (sentinel.prev == &sentinel) ? 0 : sentinel.prev; }
+
+ const TiXmlAttribute* Find(const TIXML_STRING& name) const;
+ TiXmlAttribute* Find(const TIXML_STRING& name);
+
+private:
+ //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+ //*ME: this class must be also use a hidden/disabled copy-constructor !!!
+ TiXmlAttributeSet(const TiXmlAttributeSet&); // not allowed
+ void operator=(const TiXmlAttributeSet&); // not allowed (as TiXmlAttribute)
+
+ TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+*/
+class TiXmlElement : public TiXmlNode
+{
+public:
+ /// Construct an element.
+ TiXmlElement (const char * in_value);
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlElement(const std::string& _value);
+ #endif
+
+ TiXmlElement(const TiXmlElement&);
+
+ void operator=(const TiXmlElement& base);
+
+ virtual ~TiXmlElement();
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ */
+ const char* Attribute(const char* name) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an integer,
+ the integer value will be put in the return 'i', if 'i'
+ is non-null.
+ */
+ const char* Attribute(const char* name, int* i) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an double,
+ the double value will be put in the return 'd', if 'd'
+ is non-null.
+ */
+ const char* Attribute(const char* name, double* d) const;
+
+ /** QueryIntAttribute examines the attribute - it is an alternative to the
+ Attribute() method with richer error checking.
+ If the attribute is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE. If the attribute
+ does not exist, then TIXML_NO_ATTRIBUTE is returned.
+ */
+ int QueryIntAttribute(const char* name, int* _value) const;
+ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+ int QueryDoubleAttribute(const char* name, double* _value) const;
+ /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+ int QueryFloatAttribute(const char* name, float* _value) const {
+ double d;
+ int result = QueryDoubleAttribute(name, &d);
+ if (result == TIXML_SUCCESS) {
+ *_value = (float)d;
+ }
+ return result;
+ }
+ #ifdef TIXML_USE_STL
+ /** Template form of the attribute query which will try to read the
+ attribute into the specified type. Very easy, very powerful, but
+ be careful to make sure to call this with the correct type.
+
+ @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+ */
+ template< typename T > int QueryValueAttribute(const std::string& name, T* outValue) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find(name);
+ if (!node)
+ return TIXML_NO_ATTRIBUTE;
+
+ std::stringstream sstream(node->ValueStr());
+ sstream >> *outValue;
+ if (!sstream.fail())
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+ }
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute(const char* name, const char * _value);
+
+ #ifdef TIXML_USE_STL
+ const char* Attribute(const std::string& name) const { return Attribute(name.c_str()); }
+ const char* Attribute(const std::string& name, int* i) const { return Attribute(name.c_str(), i); }
+ const char* Attribute(const std::string& name, double* d) const { return Attribute(name.c_str(), d); }
+ int QueryIntAttribute(const std::string& name, int* _value) const { return QueryIntAttribute(name.c_str(), _value); }
+ int QueryDoubleAttribute(const std::string& name, double* _value) const { return QueryDoubleAttribute(name.c_str(), _value); }
+
+ /// STL std::string form.
+ void SetAttribute(const std::string& name, const std::string& _value);
+ ///< STL std::string form.
+ void SetAttribute(const std::string& name, int _value);
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute(const char * name, int value);
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetDoubleAttribute(const char * name, double value);
+
+ /** Deletes an attribute with the given name.
+ */
+ void RemoveAttribute(const char * name);
+ #ifdef TIXML_USE_STL
+ void RemoveAttribute(const std::string& name) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
+ #endif
+
+ const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
+ TiXmlAttribute* FirstAttribute() { return attributeSet.First(); }
+ const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
+ TiXmlAttribute* LastAttribute() { return attributeSet.Last(); }
+
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ <foo>This is text</foo>
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ <foo><b>This is text</b></foo>
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ <foo>This is <b>text</b></foo>
+ @endverbatim
+ GetText() will return "This is ".
+
+ WARNING: GetText() accesses a child node - don't become confused with the
+ similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
+ safe type casts on the referenced node.
+ */
+ const char* GetText() const;
+
+ /// Creates a new Element and returns it - the returned element is a copy.
+ virtual TiXmlNode* Clone() const;
+ // Print the Element to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ /* Attribtue parsing starts: next char past '<'
+ returns: next char past '>'
+ */
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+
+ void CopyTo(TiXmlElement* target) const;
+ void ClearThis(); // like clear, but initializes 'this' object as well
+
+ // Used to be public [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+ virtual void StreamOut(TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+ /* [internal use]
+ Reads the "value" of the element -- another element, or text.
+ This should terminate with the current end tag.
+ */
+ const char* ReadValue(const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding);
+
+private:
+
+ TiXmlAttributeSet attributeSet;
+};
+
+
+/** An XML comment.
+*/
+class TiXmlComment : public TiXmlNode
+{
+public:
+ /// Constructs an empty comment.
+ TiXmlComment() : TiXmlNode(TiXmlNode::COMMENT) {}
+ TiXmlComment(const TiXmlComment&);
+ void operator=(const TiXmlComment& base);
+
+ virtual ~TiXmlComment() {}
+
+ /// Returns a copy of this Comment.
+ virtual TiXmlNode* Clone() const;
+ /// Write this Comment to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ /* Attribtue parsing starts: at the ! of the !--
+ returns: next char past '>'
+ */
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+ void CopyTo(TiXmlComment* target) const;
+
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+ virtual void StreamOut(TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+*/
+class TiXmlText : public TiXmlNode
+{
+ friend class TiXmlElement;
+public:
+ /** Constructor for text element. By default, it is treated as
+ normal, encoded text. If you want it be output as a CDATA text
+ element, set the parameter _cdata to 'true'
+ */
+ TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT)
+ {
+ SetValue(initValue);
+ cdata = false;
+ }
+ virtual ~TiXmlText() {}
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlText(const std::string& initValue) : TiXmlNode (TiXmlNode::TEXT)
+ {
+ SetValue(initValue);
+ cdata = false;
+ }
+ #endif
+
+ TiXmlText(const TiXmlText& copy) : TiXmlNode(TiXmlNode::TEXT) { copy.CopyTo(this); }
+ void operator=(const TiXmlText& base) { base.CopyTo(this); }
+
+ /// Write this text object to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ /// Queries whether this represents text using a CDATA section.
+ bool CDATA() { return cdata; }
+ /// Turns on or off a CDATA representation of text.
+ void SetCDATA(bool _cdata) { cdata = _cdata; }
+
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected :
+ /// [internal use] Creates a new Element and returns it.
+ virtual TiXmlNode* Clone() const;
+ void CopyTo(TiXmlText* target) const;
+
+ virtual void StreamOut (TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+ bool Blank() const; // returns true if all white space and new lines
+ // [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+
+private:
+ bool cdata; // true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+ <?xml version="1.0" standalone="yes"?>
+ @endverbatim
+
+ TinyXml will happily read or write files without a declaration,
+ however. There are 3 possible attributes to the declaration:
+ version, encoding, and standalone.
+
+ Note: In this version of the code, the attributes are
+ handled as special cases, not generic attributes, simply
+ because there can only be at most 3 and they are always the same.
+*/
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+ /// Construct an empty declaration.
+ TiXmlDeclaration() : TiXmlNode(TiXmlNode::DECLARATION) {}
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone);
+#endif
+
+ /// Construct.
+ TiXmlDeclaration( const char* _version,
+ const char* _encoding,
+ const char* _standalone);
+
+ TiXmlDeclaration(const TiXmlDeclaration& copy);
+ void operator=(const TiXmlDeclaration& copy);
+
+ virtual ~TiXmlDeclaration() {}
+
+ /// Version. Will return an empty string if none was found.
+ const char *Version() const { return version.c_str (); }
+ /// Encoding. Will return an empty string if none was found.
+ const char *Encoding() const { return encoding.c_str (); }
+ /// Is this a standalone document?
+ const char *Standalone() const { return standalone.c_str (); }
+
+ /// Creates a copy of this Declaration and returns it.
+ virtual TiXmlNode* Clone() const;
+ /// Print this declaration to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+ void CopyTo(TiXmlDeclaration* target) const;
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+ virtual void StreamOut (TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+ TIXML_STRING version;
+ TIXML_STRING encoding;
+ TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+*/
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+ TiXmlUnknown() : TiXmlNode(TiXmlNode::UNKNOWN) {}
+ virtual ~TiXmlUnknown() {}
+
+ TiXmlUnknown(const TiXmlUnknown& copy) : TiXmlNode(TiXmlNode::UNKNOWN) { copy.CopyTo(this); }
+ void operator=(const TiXmlUnknown& copy) { copy.CopyTo(this); }
+
+ /// Creates a copy of this Unknown and returns it.
+ virtual TiXmlNode* Clone() const;
+ /// Print this Unknown to a FILE stream.
+ virtual void Print(FILE* cfile, int depth) const;
+
+ virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+ virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+ void CopyTo(TiXmlUnknown* target) const;
+
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+ virtual void StreamOut (TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+ XML pieces. It can be saved, loaded, and printed to the screen.
+ The 'value' of a document node is the xml file name.
+*/
+class TiXmlDocument : public TiXmlNode
+{
+public:
+ /// Create an empty document, that has no name.
+ TiXmlDocument();
+ /// Create a document with a name. The name of the document is also the filename of the xml.
+ TiXmlDocument(const char * documentName);
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDocument(const std::string& documentName);
+ #endif
+
+ TiXmlDocument(const TiXmlDocument& copy);
+ void operator=(const TiXmlDocument& copy);
+
+ virtual ~TiXmlDocument() {}
+
+ /** Load a file using the current document value.
+ Returns true if successful. Will delete any existing
+ document data before loading.
+ */
+ bool LoadFile(TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ /// Save a file using the current document value. Returns true if successful.
+ bool SaveFile() const;
+ /// Load a file using the given filename. Returns true if successful.
+ bool LoadFile(const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ /// Save a file using the given filename. Returns true if successful.
+ bool SaveFile(const char * filename) const;
+ /** Load a file using the given FILE*. Returns true if successful. Note that this method
+ doesn't stream - the entire object pointed at by the FILE*
+ will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+ file location. Streaming may be added in the future.
+ */
+ bool LoadFile(FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ /// Save a file using the given FILE*. Returns true if successful.
+ bool SaveFile(FILE*) const;
+
+ #ifdef TIXML_USE_STL
+ bool LoadFile(const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING) ///< STL std::string version.
+ {
+ StringToBuffer f(filename);
+ return (f.buffer && LoadFile(f.buffer, encoding));
+ }
+ bool SaveFile(const std::string& filename) const ///< STL std::string version.
+ {
+ StringToBuffer f(filename);
+ return (f.buffer && SaveFile(f.buffer));
+ }
+ /** Write the document to a string using formatted printing ("pretty print").
+ @return the document as a formatted standard string.
+ */
+ std::string GetAsString();
+ #endif
+
+ /** Write the document to a string using formatted printing ("pretty print").
+ @param buffer A character buffer that should be big enough to hold your document.
+ @param bufferSize The size of @p buffer.
+
+ @return True if the document could be copied into @p buffer, else false.
+ */
+ bool GetAsCharBuffer(char* buffer, size_t bufferSize);
+
+ /** Parse the given null terminated block of xml data. Passing in an encoding to this
+ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+ to use that encoding, regardless of what TinyXml might otherwise try to detect.
+ */
+ virtual const char* Parse(const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+
+ /** Get the root element -- the only top level element -- of the document.
+ In well formed XML, there should only be one. TinyXml is tolerant of
+ multiple elements at the document level.
+ */
+ const TiXmlElement* RootElement() const { return FirstChildElement(); }
+ TiXmlElement* RootElement() { return FirstChildElement(); }
+
+ /** If an error occurs, Error will be set to true. Also,
+ - The ErrorId() will contain the integer identifier of the error (not generally useful)
+ - The ErrorDesc() method will return the name of the error. (very useful)
+ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
+ */
+ bool Error() const { return error; }
+
+ /// Contains a textual (english) description of the error if one occurs.
+ const char * ErrorDesc() const { return errorDesc.c_str (); }
+
+ /** Generally, you probably want the error string (ErrorDesc()). But if you
+ prefer the ErrorId, this function will fetch it.
+ */
+ int ErrorId() const { return errorId; }
+
+ /** Returns the location (if known) of the error. The first column is column 1,
+ and the first row is row 1. A value of 0 means the row and column wasn't applicable
+ (memory errors, for example, have no row/column) or the parser lost the error. (An
+ error in the error reporting, in that case.)
+
+ @sa SetTabSize, Row, Column
+ */
+ int ErrorRow() { return errorLocation.row+1; }
+ int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
+
+ /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+ to report the correct values for row and column. It does not change the output
+ or input in any way.
+
+ By calling this method, with a tab size
+ greater than 0, the row and column of each node and attribute is stored
+ when the file is loaded. Very useful for tracking the DOM back in to
+ the source file.
+
+ The tab size is required for calculating the location of nodes. If not
+ set, the default of 4 is used. The tabsize is set per document. Setting
+ the tabsize to 0 disables row/column tracking.
+
+ Note that row and column tracking is not supported when using operator>>.
+
+ The tab size needs to be enabled before the parse or load. Correct usage:
+ @verbatim
+ TiXmlDocument doc;
+ doc.SetTabSize(8);
+ doc.Load("myfile.xml");
+ @endverbatim
+
+ @sa Row, Column
+ */
+ void SetTabSize(int _tabsize) { tabsize = _tabsize; }
+
+ int TabSize() const { return tabsize; }
+
+ /** If you have handled the error, it can be reset with this call. The error
+ state is automatically cleared if you Parse a new XML block.
+ */
+ void ClearError() { error = false;
+ errorId = 0;
+ errorDesc = "";
+ errorLocation.row = errorLocation.col = 0;
+ //errorLocation.last = 0;
+ }
+
+ /** Dump the document to standard out. */
+ void Print() const { Print(stdout, 0); }
+
+ /// Print this Document to a FILE stream.
+ virtual void Print(FILE* cfile, int depth = 0) const;
+ // [internal use]
+ void SetError(int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding);
+
+ virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected :
+ virtual void StreamOut (TIXML_OSTREAM * out) const;
+ virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+ // [internal use]
+ virtual TiXmlNode* Clone() const;
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+ #endif
+
+private:
+ void CopyTo(TiXmlDocument* target) const;
+
+ bool error;
+ int errorId;
+ TIXML_STRING errorDesc;
+ int tabsize;
+ TiXmlCursor errorLocation;
+ bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+ DOM structure. It is a separate utility class.
+
+ Take an example:
+ @verbatim
+ <Document>
+ <Element attributeA = "valueA">
+ <Child attributeB = "value1" />
+ <Child attributeB = "value2" />
+ </Element>
+ <Document>
+ @endverbatim
+
+ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+ easy to write a *lot* of code that looks like:
+
+ @verbatim
+ TiXmlElement* root = document.FirstChildElement("Document");
+ if (root)
+ {
+ TiXmlElement* element = root->FirstChildElement("Element");
+ if (element)
+ {
+ TiXmlElement* child = element->FirstChildElement("Child");
+ if (child)
+ {
+ TiXmlElement* child2 = child->NextSiblingElement("Child");
+ if (child2)
+ {
+ // Finally do something useful.
+ @endverbatim
+
+ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
+ and correct to use:
+
+ @verbatim
+ TiXmlHandle docHandle(&document);
+ TiXmlElement* child2 = docHandle.FirstChild("Document").FirstChild("Element").Child("Child", 1).Element();
+ if (child2)
+ {
+ // do something useful
+ @endverbatim
+
+ Which is MUCH more concise and useful.
+
+ It is also safe to copy handles - internally they are nothing more than node pointers.
+ @verbatim
+ TiXmlHandle handleCopy = handle;
+ @endverbatim
+
+ What they should not be used for is iteration:
+
+ @verbatim
+ int i=0;
+ while (true)
+ {
+ TiXmlElement* child = docHandle.FirstChild("Document").FirstChild("Element").Child("Child", i).Element();
+ if (!child)
+ break;
+ // do something
+ ++i;
+ }
+ @endverbatim
+
+ It seems reasonable, but it is in fact two embedded while loops. The Child method is
+ a linear walk to find the element, so this code would iterate much more than it needs
+ to. Instead, prefer:
+
+ @verbatim
+ TiXmlElement* child = docHandle.FirstChild("Document").FirstChild("Element").FirstChild("Child").Element();
+
+ for (child; child; child=child->NextSiblingElement())
+ {
+ // do something
+ }
+ @endverbatim
+*/
+class TiXmlHandle
+{
+public:
+ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+ TiXmlHandle(TiXmlNode* _node) { this->node = _node; }
+ /// Copy constructor
+ TiXmlHandle(const TiXmlHandle& ref) { this->node = ref.node; }
+ TiXmlHandle operator=(const TiXmlHandle& ref) { this->node = ref.node; return *this; }
+
+ /// Return a handle to the first child node.
+ TiXmlHandle FirstChild() const;
+ /// Return a handle to the first child node with the given name.
+ TiXmlHandle FirstChild(const char * value) const;
+ /// Return a handle to the first child element.
+ TiXmlHandle FirstChildElement() const;
+ /// Return a handle to the first child element with the given name.
+ TiXmlHandle FirstChildElement(const char * value) const;
+
+ /** Return a handle to the "index" child with the given name.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child(const char* value, int index) const;
+ /** Return a handle to the "index" child.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child(int index) const;
+ /** Return a handle to the "index" child element with the given name.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement(const char* value, int index) const;
+ /** Return a handle to the "index" child element.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement(int index) const;
+
+ #ifdef TIXML_USE_STL
+ TiXmlHandle FirstChild(const std::string& _value) const { return FirstChild(_value.c_str()); }
+ TiXmlHandle FirstChildElement(const std::string& _value) const { return FirstChildElement(_value.c_str()); }
+
+ TiXmlHandle Child(const std::string& _value, int index) const { return Child(_value.c_str(), index); }
+ TiXmlHandle ChildElement(const std::string& _value, int index) const { return ChildElement(_value.c_str(), index); }
+ #endif
+
+ /// Return the handle as a TiXmlNode. This may return null.
+ TiXmlNode* Node() const { return node; }
+ /// Return the handle as a TiXmlElement. This may return null.
+ TiXmlElement* Element() const { return ((node && node->ToElement()) ? node->ToElement() : 0); }
+ /// Return the handle as a TiXmlText. This may return null.
+ TiXmlText* Text() const { return ((node && node->ToText()) ? node->ToText() : 0); }
+ /// Return the handle as a TiXmlUnknown. This may return null;
+ TiXmlUnknown* Unknown() const { return ((node && node->ToUnknown()) ? node->ToUnknown() : 0); }
+
+private:
+ TiXmlNode* node;
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
+
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp b/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp
new file mode 100644
index 0000000000..3c42354d11
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp
@@ -0,0 +1,76 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+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.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxmlerror.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifdef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "mmgr.h"
+#endif
+
+#include "tinyxml.h"
+
+// The goal of the seperate error file is to make the first
+// step towards localization. tinyxml (currently) only supports
+// english error messages, but the could now be translated.
+//
+// It also cleans up the code a bit.
+//
+
+const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
+{
+ "No error",
+ "Error",
+ "Failed to open file",
+ "Memory allocation failed.",
+ "Error parsing Element.",
+ "Failed to read Element name",
+ "Error reading Element value.",
+ "Error reading Attributes.",
+ "Error: empty tag.",
+ "Error reading end tag.",
+ "Error parsing Unknown.",
+ "Error parsing Comment.",
+ "Error parsing Declaration.",
+ "Error document empty.",
+ "Error null (0) or unexpected EOF found in input stream.",
+ "Error parsing CDATA.",
+ "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
+};
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp b/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp
new file mode 100644
index 0000000000..73f2c18679
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp
@@ -0,0 +1,1613 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+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.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxmlparser.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include <ctype.h>
+#include <stddef.h>
+
+#ifdef USE_MMGR
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include "mmgr.h"
+#endif
+
+#include "tinyxml.h"
+
+//#define DEBUG_PARSER
+#if defined(DEBUG_PARSER)
+# if defined(DEBUG) && defined(_MSC_VER)
+# include <windows.h>
+# define TIXML_LOG OutputDebugString
+# else
+# define TIXML_LOG printf
+# endif
+#endif
+
+// Note tha "PutString" hardcodes the same list. This
+// is less flexible than it appears. Changing the entries
+// or order will break putstring.
+TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] =
+{
+ { "&amp;", 5, '&' },
+ { "&lt;", 4, '<' },
+ { "&gt;", 4, '>' },
+ { "&quot;", 6, '\"' },
+ { "&apos;", 6, '\'' }
+};
+
+// Bunch of unicode info at:
+// http://www.unicode.org/faq/utf_bom.html
+// Including the basic of this table, which determines the #bytes in the
+// sequence from the lead byte. 1 placed for invalid sequences --
+// although the result will be junk, pass it through as much as possible.
+// Beware of the non-characters in UTF-8:
+// ef bb bf (Microsoft "lead bytes")
+// ef bf be
+// ef bf bf
+
+const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+const int TiXmlBase::utf8ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 1, 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
+};
+
+
+void TiXmlBase::ConvertUTF32ToUTF8(unsigned long input, char* output, int* length)
+{
+ const unsigned long BYTE_MASK = 0xBF;
+ const unsigned long BYTE_MARK = 0x80;
+ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ if (input < 0x80)
+ *length = 1;
+ else if (input < 0x800)
+ *length = 2;
+ else if (input < 0x10000)
+ *length = 3;
+ else if (input < 0x200000)
+ *length = 4;
+ else
+ { *length = 0; return; } // This code won't covert this correctly anyway.
+
+ output += *length;
+
+ // Scary scary fall throughs.
+ switch (*length)
+ {
+ case 4:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 3:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 2:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 1:
+ --output;
+ *output = (char)(input | FIRST_BYTE_MARK[*length]);
+ }
+}
+
+
+/*static*/ int TiXmlBase::IsAlpha(unsigned char anyByte, TiXmlEncoding /*encoding*/)
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if (encoding == TIXML_ENCODING_UTF8)
+// {
+ if (anyByte < 127)
+ return isalpha(anyByte);
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalpha(anyByte);
+// }
+}
+
+
+/*static*/ int TiXmlBase::IsAlphaNum(unsigned char anyByte, TiXmlEncoding /*encoding*/)
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if (encoding == TIXML_ENCODING_UTF8)
+// {
+ if (anyByte < 127)
+ return isalnum(anyByte);
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalnum(anyByte);
+// }
+}
+
+
+class TiXmlParsingData
+{
+ friend class TiXmlDocument;
+ public:
+ void Stamp(const char* now, TiXmlEncoding encoding);
+
+ const TiXmlCursor& Cursor() { return cursor; }
+
+ private:
+ // Only used by the document!
+ TiXmlParsingData(const char* start, int _tabsize, int row, int col)
+ {
+ assert(start);
+ stamp = start;
+ tabsize = _tabsize;
+ cursor.row = row;
+ cursor.col = col;
+ }
+
+ TiXmlCursor cursor;
+ const char* stamp;
+ int tabsize;
+};
+
+
+void TiXmlParsingData::Stamp(const char* now, TiXmlEncoding encoding)
+{
+ assert(now);
+
+ // Do nothing if the tabsize is 0.
+ if (tabsize < 1)
+ {
+ return;
+ }
+
+ // Get the current row, column.
+ int row = cursor.row;
+ int col = cursor.col;
+ const char* p = stamp;
+ assert(p);
+
+ while (p < now)
+ {
+ // Treat p as unsigned, so we have a happy compiler.
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Code contributed by Fletcher Dunn: (modified by lee)
+ switch (*pU) {
+ case 0:
+ // We *should* never get here, but in case we do, don't
+ // advance past the terminating null character, ever
+ return;
+
+ case '\r':
+ // bump down to the next line
+ ++row;
+ col = 0;
+ // Eat the character
+ ++p;
+
+ // Check for \r\n sequence, and treat this as a single character
+ if (*p == '\n') {
+ ++p;
+ }
+ break;
+
+ case '\n':
+ // bump down to the next line
+ ++row;
+ col = 0;
+
+ // Eat the character
+ ++p;
+
+ // Check for \n\r sequence, and treat this as a single
+ // character. (Yes, this bizarre thing does occur still
+ // on some arcane platforms...)
+ if (*p == '\r') {
+ ++p;
+ }
+ break;
+
+ case '\t':
+ // Eat the character
+ ++p;
+
+ // Skip to next tab stop
+ col = (col / tabsize + 1) * tabsize;
+ break;
+
+ case TIXML_UTF_LEAD_0:
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ if (*(p+1) && *(p+2))
+ {
+ // In these cases, don't advance the column. These are
+ // 0-width spaces.
+ if (*(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2)
+ p += 3;
+ else if (*(pU+1)==0xbfU && *(pU+2)==0xbeU)
+ p += 3;
+ else if (*(pU+1)==0xbfU && *(pU+2)==0xbfU)
+ p += 3;
+ else
+ { p +=3; ++col; } // A normal character.
+ }
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+
+ default:
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ // Eat the 1 to 4 byte utf8 character.
+ int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)];
+ if (step == 0)
+ step = 1; // Error case from bad encoding, but handle gracefully.
+ p += step;
+
+ // Just advance one column, of course.
+ ++col;
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+ }
+ }
+ cursor.row = row;
+ cursor.col = col;
+ assert(cursor.row >= -1);
+ assert(cursor.col >= -1);
+ stamp = p;
+ assert(stamp);
+}
+
+
+const char* TiXmlBase::SkipWhiteSpace(const char* p, TiXmlEncoding encoding)
+{
+ if (!p || !*p)
+ {
+ return 0;
+ }
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ while (*p)
+ {
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Skip the stupid Microsoft UTF-8 Byte order marks
+ if ( *(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==TIXML_UTF_LEAD_1
+ && *(pU+2)==TIXML_UTF_LEAD_2)
+ {
+ p += 3;
+ continue;
+ }
+ else if (*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbeU)
+ {
+ p += 3;
+ continue;
+ }
+ else if (*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbfU)
+ {
+ p += 3;
+ continue;
+ }
+
+ if (IsWhiteSpace(*p) || *p == '\n' || *p =='\r') // Still using old rules for white space.
+ ++p;
+ else
+ break;
+ }
+ }
+ else
+ {
+ while (*p && IsWhiteSpace(*p) || *p == '\n' || *p =='\r')
+ ++p;
+ }
+
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+/*static*/ bool TiXmlBase::StreamWhiteSpace(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ for (;;)
+ {
+ if (!in->good()) return false;
+
+ int c = in->peek();
+ // At this scope, we can't get to a document. So fail silently.
+ if (!IsWhiteSpace(c) || c <= 0)
+ return true;
+
+ *tag += (char) in->get();
+ }
+}
+
+/*static*/ bool TiXmlBase::StreamTo(TIXML_ISTREAM * in, int character, TIXML_STRING * tag)
+{
+ //assert(character > 0 && character < 128); // else it won't work in utf-8
+ while (in->good())
+ {
+ int c = in->peek();
+ if (c == character)
+ return true;
+ if (c <= 0) // Silent failure: can't get document at this scope
+ return false;
+
+ in->get();
+ *tag += (char) c;
+ }
+ return false;
+}
+#endif
+
+const char* TiXmlBase::ReadName(const char* p, TIXML_STRING * name, TiXmlEncoding encoding)
+{
+ *name = "";
+ assert(p);
+
+ // Names start with letters or underscores.
+ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
+ // algorithm is generous.
+ //
+ // After that, they can be letters, underscores, numbers,
+ // hyphens, or colons. (Colons are valid ony for namespaces,
+ // but tinyxml can't tell namespaces from names.)
+ if ( p && *p
+ && (IsAlpha((unsigned char) *p, encoding) || *p == '_'))
+ {
+ while ( p && *p
+ && ( IsAlphaNum((unsigned char) *p, encoding)
+ || *p == '_'
+ || *p == '-'
+ || *p == '.'
+ || *p == ':'))
+ {
+ (*name) += *p;
+ ++p;
+ }
+ return p;
+ }
+ return 0;
+}
+
+const char* TiXmlBase::GetEntity(const char* p, char* value, int* length, TiXmlEncoding encoding)
+{
+ // Presume an entity, and pull it out.
+ TIXML_STRING ent;
+ int i;
+ *length = 0;
+
+ if (*(p+1) && *(p+1) == '#' && *(p+2))
+ {
+ unsigned long ucs = 0;
+ ptrdiff_t delta = 0;
+ unsigned mult = 1;
+
+ if (*(p+2) == 'x')
+ {
+ // Hexadecimal.
+ if (!*(p+3)) return 0;
+
+ const char* q = p+3;
+ q = strchr(q, ';');
+
+ if (!q || !*q) return 0;
+
+ delta = q-p;
+ --q;
+
+ while (*q != 'x')
+ {
+ if (*q >= '0' && *q <= '9')
+ ucs += mult * (*q - '0');
+ else if (*q >= 'a' && *q <= 'f')
+ ucs += mult * (*q - 'a' + 10);
+ else if (*q >= 'A' && *q <= 'F')
+ ucs += mult * (*q - 'A' + 10);
+ else
+ return 0;
+ mult *= 16;
+ --q;
+ }
+ }
+ else
+ {
+ // Decimal.
+ if (!*(p+2)) return 0;
+
+ const char* q = p+2;
+ q = strchr(q, ';');
+
+ if (!q || !*q) return 0;
+
+ delta = q-p;
+ --q;
+
+ while (*q != '#')
+ {
+ if (*q >= '0' && *q <= '9')
+ ucs += mult * (*q - '0');
+ else
+ return 0;
+ mult *= 10;
+ --q;
+ }
+ }
+ if (encoding == TIXML_ENCODING_UTF8)
+ {
+ // convert the UCS to UTF-8
+ ConvertUTF32ToUTF8(ucs, value, length);
+ }
+ else
+ {
+ *value = (char)ucs;
+ *length = 1;
+ }
+ return p + delta + 1;
+ }
+
+ // Now try to match it.
+ for (i=0; i<NUM_ENTITY; ++i)
+ {
+ if (strncmp(entity[i].str, p, entity[i].strLength) == 0)
+ {
+ assert(strlen(entity[i].str) == entity[i].strLength);
+ *value = entity[i].chr;
+ *length = 1;
+ return (p + entity[i].strLength);
+ }
+ }
+
+ // So it wasn't an entity, its unrecognized, or something like that.
+ *value = *p; // Don't put back the last one, since we return it!
+ //*length = 1; // Leave unrecognized entities - this doesn't really work.
+ // Just writes strange XML.
+ return p+1;
+}
+
+
+bool TiXmlBase::StringEqual(const char* p,
+ const char* tag,
+ bool ignoreCase,
+ TiXmlEncoding encoding)
+{
+ assert(p);
+ assert(tag);
+ if (!p || !*p)
+ {
+ assert(0);
+ return false;
+ }
+
+ const char* q = p;
+
+ if (ignoreCase)
+ {
+ while (*q && *tag && ToLower(*q, encoding) == ToLower(*tag, encoding))
+ {
+ ++q;
+ ++tag;
+ }
+
+ if (*tag == 0)
+ return true;
+ }
+ else
+ {
+ while (*q && *tag && *q == *tag)
+ {
+ ++q;
+ ++tag;
+ }
+
+ if (*tag == 0) // Have we found the end of the tag, and everything equal?
+ return true;
+ }
+ return false;
+}
+
+const char* TiXmlBase::ReadText( const char* p,
+ TIXML_STRING * text,
+ bool trimWhiteSpace,
+ const char* endTag,
+ bool caseInsensitive,
+ TiXmlEncoding encoding)
+{
+ *text = "";
+ if ( !trimWhiteSpace // certain tags always keep whitespace
+ || !condenseWhiteSpace) // if true, whitespace is always kept
+ {
+ // Keep all the white space.
+ while ( p && *p
+ && !StringEqual(p, endTag, caseInsensitive, encoding)
+ )
+ {
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar(p, cArr, &len, encoding);
+ text->append(cArr, len);
+ }
+ }
+ else
+ {
+ bool whitespace = false;
+
+ // Remove leading white space:
+ p = SkipWhiteSpace(p, encoding);
+ while ( p && *p
+ && !StringEqual(p, endTag, caseInsensitive, encoding))
+ {
+ if (*p == '\r' || *p == '\n')
+ {
+ whitespace = true;
+ ++p;
+ }
+ else if (IsWhiteSpace(*p))
+ {
+ whitespace = true;
+ ++p;
+ }
+ else
+ {
+ // If we've found whitespace, add it before the
+ // new character. Any whitespace just becomes a space.
+ if (whitespace)
+ {
+ (*text) += ' ';
+ whitespace = false;
+ }
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar(p, cArr, &len, encoding);
+ if (len == 1)
+ (*text) += cArr[0]; // more efficient
+ else
+ text->append(cArr, len);
+ }
+ }
+ }
+ return p + strlen(endTag);
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlDocument::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ // The basic issue with a document is that we don't know what we're
+ // streaming. Read something presumed to be a tag (and hope), then
+ // identify it, and call the appropriate stream method on the tag.
+ //
+ // This "pre-streaming" will never read the closing ">" so the
+ // sub-tag can orient itself.
+
+ if (!StreamTo(in, '<', tag))
+ {
+ SetError(TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+
+ while (in->good())
+ {
+ int tagIndex = (int) tag->length();
+ while (in->good() && in->peek() != '>')
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ break;
+ }
+ (*tag) += (char) c;
+ }
+
+ if (in->good())
+ {
+ // We now have something we presume to be a node of
+ // some sort. Identify it, and call the node to
+ // continue streaming.
+ TiXmlNode* node = Identify(tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING);
+
+ if (node)
+ {
+ node->StreamIn(in, tag);
+ bool isElement = node->ToElement() != 0;
+ delete node;
+ node = 0;
+
+ // If this is the root element, we're done. Parsing will be
+ // done by the >> operator.
+ if (isElement)
+ {
+ return;
+ }
+ }
+ else
+ {
+ SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+ }
+ }
+ // We should have returned sooner.
+ SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);
+}
+
+#endif
+
+const char* TiXmlDocument::Parse(const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding)
+{
+ ClearError();
+
+ // Parse away, at the document level. Since a document
+ // contains nothing but other tags, most of what happens
+ // here is skipping white space.
+ if (!p || !*p)
+ {
+ SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+
+ // Note that, for a document, this needs to come
+ // before the while space skip, so that parsing
+ // starts from the pointer we are given.
+ location.Clear();
+ if (prevData)
+ {
+ location.row = prevData->cursor.row;
+ location.col = prevData->cursor.col;
+ }
+ else
+ {
+ location.row = 0;
+ location.col = 0;
+ }
+ TiXmlParsingData data(p, TabSize(), location.row, location.col);
+ location = data.Cursor();
+
+ if (encoding == TIXML_ENCODING_UNKNOWN)
+ {
+ // Check for the Microsoft UTF-8 lead bytes.
+ const unsigned char* pU = (const unsigned char*)p;
+ if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
+ && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
+ && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2)
+ {
+ encoding = TIXML_ENCODING_UTF8;
+ useMicrosoftBOM = true;
+ }
+ }
+
+ p = SkipWhiteSpace(p, encoding);
+ if (!p)
+ {
+ SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return 0;
+ }
+
+ while (p && *p)
+ {
+ TiXmlNode* node = Identify(p, encoding);
+ if (node)
+ {
+ p = node->Parse(p, &data, encoding);
+ LinkEndChild(node);
+ }
+ else
+ {
+ break;
+ }
+
+ // Did we get encoding info?
+ if ( encoding == TIXML_ENCODING_UNKNOWN
+ && node->ToDeclaration())
+ {
+ TiXmlDeclaration* dec = node->ToDeclaration();
+ const char* enc = dec->Encoding();
+ assert(enc);
+
+ if (*enc == 0)
+ encoding = TIXML_ENCODING_UTF8;
+ else if (StringEqual(enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN))
+ encoding = TIXML_ENCODING_UTF8;
+ else if (StringEqual(enc, "UTF8", true, TIXML_ENCODING_UNKNOWN))
+ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
+ else
+ encoding = TIXML_ENCODING_LEGACY;
+ }
+
+ p = SkipWhiteSpace(p, encoding);
+ }
+
+ // Was this empty?
+ if (!firstChild) {
+ SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding);
+ return 0;
+ }
+
+ // All is well.
+ return p;
+}
+
+void TiXmlDocument::SetError(int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ // The first error in a chain is more accurate - don't set again!
+ if (error)
+ return;
+
+ assert(err > 0 && err < TIXML_ERROR_STRING_COUNT);
+ error = true;
+ errorId = err;
+ errorDesc = errorString[ errorId ];
+
+ errorLocation.Clear();
+ if (pError && data)
+ {
+ data->Stamp(pError, encoding);
+ errorLocation = data->Cursor();
+ }
+}
+
+
+TiXmlNode* TiXmlNode::Identify(const char* p, TiXmlEncoding encoding)
+{
+ TiXmlNode* returnNode = 0;
+
+ p = SkipWhiteSpace(p, encoding);
+ if (!p || !*p || *p != '<')
+ {
+ return 0;
+ }
+
+ TiXmlDocument* doc = GetDocument();
+ p = SkipWhiteSpace(p, encoding);
+
+ if (!p || !*p)
+ {
+ return 0;
+ }
+
+ // What is this thing?
+ // - Elements start with a letter or underscore, but xml is reserved.
+ // - Comments: <!--
+ // - Decleration: <?xml
+ // - Everthing else is unknown to tinyxml.
+ //
+
+ const char* xmlHeader = { "<?xml" };
+ const char* commentHeader = { "<!--" };
+ const char* dtdHeader = { "<!" };
+ const char* cdataHeader = { "<![CDATA[" };
+
+ if (StringEqual(p, xmlHeader, true, encoding))
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing Declaration\n");
+ #endif
+ returnNode = new TiXmlDeclaration();
+ }
+ else if (StringEqual(p, commentHeader, false, encoding))
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing Comment\n");
+ #endif
+ returnNode = new TiXmlComment();
+ }
+ else if (StringEqual(p, cdataHeader, false, encoding))
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing CDATA\n");
+ #endif
+ TiXmlText* text = new TiXmlText("");
+ text->SetCDATA(true);
+ returnNode = text;
+ }
+ else if (StringEqual(p, dtdHeader, false, encoding))
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing Unknown(1)\n");
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+ else if ( IsAlpha(*(p+1), encoding)
+ || *(p+1) == '_')
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing Element\n");
+ #endif
+ returnNode = new TiXmlElement("");
+ }
+ else
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG("XML parsing Unknown(2)\n");
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+
+ if (returnNode)
+ {
+ // Set the parent, so it can report errors
+ returnNode->parent = this;
+ }
+ else
+ {
+ if (doc)
+ doc->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+ }
+ return returnNode;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ // We're called with some amount of pre-parsing. That is, some of "this"
+ // element is in "tag". Go ahead and stream to the closing ">"
+ while (in->good())
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+ (*tag) += (char) c ;
+
+ if (c == '>')
+ break;
+ }
+
+ if (tag->length() < 3) return;
+
+ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
+ // If not, identify and stream.
+
+ if ( tag->at(tag->length() - 1) == '>'
+ && tag->at(tag->length() - 2) == '/')
+ {
+ // All good!
+ return;
+ }
+ else if (tag->at(tag->length() - 1) == '>')
+ {
+ // There is more. Could be:
+ // text
+ // closing tag
+ // another node.
+ for (;;)
+ {
+ StreamWhiteSpace(in, tag);
+
+ // Do we have text?
+ if (in->good() && in->peek() != '<')
+ {
+ // Yep, text.
+ TiXmlText text("");
+ text.StreamIn(in, tag);
+
+ // What follows text is a closing tag or another node.
+ // Go around again and figure it out.
+ continue;
+ }
+
+ // We now have either a closing tag...or another node.
+ // We should be at a "<", regardless.
+ if (!in->good()) return;
+ assert(in->peek() == '<');
+ int tagIndex = (int) tag->length();
+
+ bool closingTag = false;
+ bool firstCharFound = false;
+
+ for (;;)
+ {
+ if (!in->good())
+ return;
+
+ int c = in->peek();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+
+ if (c == '>')
+ break;
+
+ *tag += (char) c;
+ in->get();
+
+ if (!firstCharFound && c != '<' && !IsWhiteSpace(c))
+ {
+ firstCharFound = true;
+ if (c == '/')
+ closingTag = true;
+ }
+ }
+ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
+ // If it was not, the streaming will be done by the tag.
+ if (closingTag)
+ {
+ if (!in->good())
+ return;
+
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+ assert(c == '>');
+ *tag += (char) c;
+
+ // We are done, once we've found our closing tag.
+ return;
+ }
+ else
+ {
+ // If not a closing tag, id it, and stream.
+ const char* tagloc = tag->c_str() + tagIndex;
+ TiXmlNode* node = Identify(tagloc, TIXML_DEFAULT_ENCODING);
+ if (!node)
+ return;
+ node->StreamIn(in, tag);
+ delete node;
+ node = 0;
+
+ // No return: go around from the beginning: text, closing tag, or node.
+ }
+ }
+ }
+}
+#endif
+
+const char* TiXmlElement::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ p = SkipWhiteSpace(p, encoding);
+ TiXmlDocument* document = GetDocument();
+
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding);
+ return 0;
+ }
+
+ if (data)
+ {
+ data->Stamp(p, encoding);
+ location = data->Cursor();
+ }
+
+ if (*p != '<')
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, p, data, encoding);
+ return 0;
+ }
+
+ p = SkipWhiteSpace(p+1, encoding);
+
+ // Read the name.
+ const char* pErr = p;
+
+ p = ReadName(p, &value, encoding);
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding);
+ return 0;
+ }
+
+ TIXML_STRING endTag ("</");
+ endTag += value;
+ endTag += ">";
+
+ // Check for and read attributes. Also look for an empty
+ // tag or an end tag.
+ while (p && *p)
+ {
+ pErr = p;
+ p = SkipWhiteSpace(p, encoding);
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);
+ return 0;
+ }
+ if (*p == '/')
+ {
+ ++p;
+ // Empty tag.
+ if (*p != '>')
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_EMPTY, p, data, encoding);
+ return 0;
+ }
+ return (p+1);
+ }
+ else if (*p == '>')
+ {
+ // Done with attributes (if there were any.)
+ // Read the value -- which can include other
+ // elements -- read the end tag, and return.
+ ++p;
+ p = ReadValue(p, data, encoding); // Note this is an Element method, and will set the error if one happens.
+ if (!p || !*p)
+ return 0;
+
+ // We should find the end tag now
+ if (StringEqual(p, endTag.c_str(), false, encoding))
+ {
+ p += endTag.length();
+ return p;
+ }
+ else
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_END_TAG, p, data, encoding);
+ return 0;
+ }
+ }
+ else
+ {
+ // Try to read an attribute:
+ TiXmlAttribute* attrib = new TiXmlAttribute();
+ if (!attrib)
+ {
+ if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding);
+ return 0;
+ }
+
+ attrib->SetDocument(document);
+ const char* pErr = p;
+ p = attrib->Parse(p, data, encoding);
+
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding);
+ delete attrib;
+ return 0;
+ }
+
+ // Handle the strange case of double attributes:
+ TiXmlAttribute* node = attributeSet.Find(attrib->NameTStr());
+ if (node)
+ {
+ node->SetValue(attrib->Value());
+ delete attrib;
+ return 0;
+ }
+
+ attributeSet.Add(attrib);
+ }
+ }
+ return p;
+}
+
+
+const char* TiXmlElement::ReadValue(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ TiXmlDocument* document = GetDocument();
+
+ // Read in text and elements in any order.
+ const char* pWithWhiteSpace = p;
+ p = SkipWhiteSpace(p, encoding);
+
+ while (p && *p)
+ {
+ if (*p != '<')
+ {
+ // Take what we have, make a text element.
+ TiXmlText* textNode = new TiXmlText("");
+
+ if (!textNode)
+ {
+ if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding);
+ return 0;
+ }
+
+ if (TiXmlBase::IsWhiteSpaceCondensed())
+ {
+ p = textNode->Parse(p, data, encoding);
+ }
+ else
+ {
+ // Special case: we want to keep the white space
+ // so that leading spaces aren't removed.
+ p = textNode->Parse(pWithWhiteSpace, data, encoding);
+ }
+
+ if (!textNode->Blank())
+ LinkEndChild(textNode);
+ else
+ delete textNode;
+ }
+ else
+ {
+ // We hit a '<'
+ // Have we hit a new element or an end tag? This could also be
+ // a TiXmlText in the "CDATA" style.
+ if (StringEqual(p, "</", false, encoding))
+ {
+ return p;
+ }
+ else
+ {
+ TiXmlNode* node = Identify(p, encoding);
+ if (node)
+ {
+ p = node->Parse(p, data, encoding);
+ LinkEndChild(node);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ pWithWhiteSpace = p;
+ p = SkipWhiteSpace(p, encoding);
+ }
+
+ if (!p)
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding);
+ }
+ return p;
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlUnknown::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ while (in->good())
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+ (*tag) += (char) c;
+
+ if (c == '>')
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlUnknown::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ TiXmlDocument* document = GetDocument();
+ p = SkipWhiteSpace(p, encoding);
+
+ if (data)
+ {
+ data->Stamp(p, encoding);
+ location = data->Cursor();
+ }
+ if (!p || !*p || *p != '<')
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding);
+ return 0;
+ }
+ ++p;
+ value = "";
+
+ while (p && *p && *p != '>')
+ {
+ value += *p;
+ ++p;
+ }
+
+ if (!p)
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding);
+ }
+ if (*p == '>')
+ return p+1;
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlComment::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ while (in->good())
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+
+ (*tag) += (char) c;
+
+ if (c == '>'
+ && tag->at(tag->length() - 2) == '-'
+ && tag->at(tag->length() - 3) == '-')
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlComment::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ TiXmlDocument* document = GetDocument();
+ value = "";
+
+ p = SkipWhiteSpace(p, encoding);
+
+ if (data)
+ {
+ data->Stamp(p, encoding);
+ location = data->Cursor();
+ }
+ const char* startTag = "<!--";
+ const char* endTag = "-->";
+
+ if (!StringEqual(p, startTag, false, encoding))
+ {
+ document->SetError(TIXML_ERROR_PARSING_COMMENT, p, data, encoding);
+ return 0;
+ }
+ p += strlen(startTag);
+ p = ReadText(p, &value, false, endTag, false, encoding);
+ return p;
+}
+
+
+const char* TiXmlAttribute::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ p = SkipWhiteSpace(p, encoding);
+ if (!p || !*p) return 0;
+
+// int tabsize = 4;
+// if (document)
+// tabsize = document->TabSize();
+
+ if (data)
+ {
+ data->Stamp(p, encoding);
+ location = data->Cursor();
+ }
+ // Read the name, the '=' and the value.
+ const char* pErr = p;
+ p = ReadName(p, &name, encoding);
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);
+ return 0;
+ }
+ p = SkipWhiteSpace(p, encoding);
+ if (!p || !*p || *p != '=')
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+ return 0;
+ }
+
+ ++p; // skip '='
+ p = SkipWhiteSpace(p, encoding);
+ if (!p || !*p)
+ {
+ if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+ return 0;
+ }
+
+ const char* end;
+ const char SINGLE_QUOTE = '\'';
+ const char DOUBLE_QUOTE = '\"';
+
+ if (*p == SINGLE_QUOTE)
+ {
+ ++p;
+ end = "\'"; // single quote in string
+ p = ReadText(p, &value, false, end, false, encoding);
+ }
+ else if (*p == DOUBLE_QUOTE)
+ {
+ ++p;
+ end = "\""; // double quote in string
+ p = ReadText(p, &value, false, end, false, encoding);
+ }
+ else
+ {
+ // All attribute values should be in single or double quotes.
+ // But this is such a common error that the parser will try
+ // its best, even without them.
+ value = "";
+ while ( p && *p // existence
+ && !IsWhiteSpace(*p) && *p != '\n' && *p != '\r' // whitespace
+ && *p != '/' && *p != '>') // tag end
+ {
+ if (*p == SINGLE_QUOTE || *p == DOUBLE_QUOTE) {
+ // [ 1451649 ] Attribute values with trailing quotes not handled correctly
+ // We did not have an opening quote but seem to have a
+ // closing one. Give up and throw an error.
+ if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+ return 0;
+ }
+ value += *p;
+ ++p;
+ }
+ }
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlText::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ if (cdata)
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+
+ (*tag) += (char) c;
+
+ if (c == '>'
+ && tag->at(tag->length() - 2) == ']'
+ && tag->at(tag->length() - 3) == ']')
+ {
+ // All is well.
+ return;
+ }
+ }
+ else
+ {
+ while (in->good())
+ {
+ int c = in->peek();
+ if (c == '<')
+ return;
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+
+ (*tag) += (char) c;
+ in->get();
+ }
+ }
+}
+#endif
+
+const char* TiXmlText::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+ value = "";
+ TiXmlDocument* document = GetDocument();
+
+ if (data)
+ {
+ data->Stamp(p, encoding);
+ location = data->Cursor();
+ }
+
+ const char* const startTag = "<![CDATA[";
+ const char* const endTag = "]]>";
+
+ if (cdata || StringEqual(p, startTag, false, encoding))
+ {
+ cdata = true;
+
+ if (!StringEqual(p, startTag, false, encoding))
+ {
+ document->SetError(TIXML_ERROR_PARSING_CDATA, p, data, encoding);
+ return 0;
+ }
+ p += strlen(startTag);
+
+ // Keep all the white space, ignore the encoding, etc.
+ while ( p && *p
+ && !StringEqual(p, endTag, false, encoding)
+ )
+ {
+ value += *p;
+ ++p;
+ }
+
+ TIXML_STRING dummy;
+ p = ReadText(p, &dummy, false, endTag, false, encoding);
+ return p;
+ }
+ else
+ {
+ bool ignoreWhite = true;
+
+ const char* end = "<";
+ p = ReadText(p, &value, ignoreWhite, end, false, encoding);
+ if (p)
+ return p-1; // don't truncate the '<'
+ return 0;
+ }
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlDeclaration::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+ while (in->good())
+ {
+ int c = in->get();
+ if (c <= 0)
+ {
+ TiXmlDocument* document = GetDocument();
+ if (document)
+ document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+ return;
+ }
+ (*tag) += (char) c;
+
+ if (c == '>')
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+const char* TiXmlDeclaration::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding)
+{
+ p = SkipWhiteSpace(p, _encoding);
+ // Find the beginning, find the end, and look for
+ // the stuff in-between.
+ TiXmlDocument* document = GetDocument();
+ if (!p || !*p || !StringEqual(p, "<?xml", true, _encoding))
+ {
+ if (document) document->SetError(TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding);
+ return 0;
+ }
+ if (data)
+ {
+ data->Stamp(p, _encoding);
+ location = data->Cursor();
+ }
+ p += 5;
+
+ version = "";
+ encoding = "";
+ standalone = "";
+
+ while (p && *p)
+ {
+ if (*p == '>')
+ {
+ ++p;
+ return p;
+ }
+
+ p = SkipWhiteSpace(p, _encoding);
+ if (StringEqual(p, "version", true, _encoding))
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse(p, data, _encoding);
+ version = attrib.Value();
+ }
+ else if (StringEqual(p, "encoding", true, _encoding))
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse(p, data, _encoding);
+ encoding = attrib.Value();
+ }
+ else if (StringEqual(p, "standalone", true, _encoding))
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse(p, data, _encoding);
+ standalone = attrib.Value();
+ }
+ else
+ {
+ // Read over whatever it is.
+ while (p && *p && *p != '>' && !IsWhiteSpace(*p))
+ ++p;
+ }
+ }
+ return 0;
+}
+
+bool TiXmlText::Blank() const
+{
+ for (unsigned i=0; i<value.length(); i++)
+ if (!IsWhiteSpace(value[i]))
+ return false;
+ return true;
+}
+
diff --git a/plugins/UserInfoEx/src/init.cpp b/plugins/UserInfoEx/src/init.cpp
new file mode 100644
index 0000000000..3b7b0b8249
--- /dev/null
+++ b/plugins/UserInfoEx/src/init.cpp
@@ -0,0 +1,315 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/init.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "version.h"
+
+#include "mir_menuitems.h"
+#include "ctrl_base.h"
+#include "ctrl_button.h"
+#include "ctrl_contact.h"
+#include "dlg_propsheet.h"
+#include "dlg_anniversarylist.h"
+#include "psp_options.h"
+#include "ex_import/svc_ExImport.h"
+//#include "ex_import/svc_ExImVCF.h"
+#include "svc_avatar.h"
+#include "svc_contactinfo.h"
+#include "svc_email.h"
+#include "svc_gender.h"
+#include "svc_homepage.h"
+#include "svc_phone.h"
+#include "svc_refreshci.h"
+#include "svc_reminder.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+#include "flags/svc_flags.h"
+
+static PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_DISPLAY_NAME,
+ __VERSION_DWORD,
+ __DESC,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ MIID_UIUSERINFOEX
+};
+
+static HANDLE ghModulesLoadedHook = NULL;
+static HANDLE ghTopToolBarLoaded = NULL;
+static HANDLE ghModernToolBarLoaded = NULL;
+static HANDLE ghShutdownHook = NULL;
+static HANDLE ghPrebuildStatusMenu = NULL;
+int hLangpack;
+
+/*
+============================================================================================
+ event hooks
+============================================================================================
+*/
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return always 0
+ **/
+static INT OnTopToolBarLoaded(WPARAM wParam, LPARAM lParam)
+{
+ DlgAnniversaryListOnTopToolBarLoaded();
+ SvcReminderOnTopToolBarLoaded();
+ return 0;
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return always 0
+ **/
+static INT OnModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ myGlobals.HaveCListExtraIcons = ServiceExists(MS_CLIST_EXTRA_SET_ICON);
+ myGlobals.ExtraIconsServiceExist = ServiceExists(MS_EXTRAICON_REGISTER);
+ myGlobals.PopUpActionsExist = ServiceExists(MS_POPUP_REGISTERACTIONS);
+ myGlobals.MsgAddIconExist = ServiceExists(MS_MSG_ADDICON);
+
+ // init meta contacts
+ INT_PTR ptr = CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+ myGlobals.szMetaProto = (ptr != CALLSERVICE_NOTFOUND) ? (LPCSTR)ptr : NULL;
+
+ // options
+ OptionsLoadModule();
+ // create services to receive string lists of languages and timezones
+ SvcConstantsLoadModule();
+ // load module to remind user about birthday and a anniversary
+ SvcReminderOnModulesLoaded();
+ // load extended intagration services
+ SvcEMailOnModulesLoaded();
+ SvcHomepageLoadModule();
+ SvcPhoneLoadModule();
+ SvcGenderLoadModule();
+ SvcFlagsOnModulesLoaded();
+
+#ifdef _DEBUG // new feature, not in release jet
+ NServices::NAvatar::OnModulesLoaded();
+#endif
+
+ // build contact's menuitems
+ RebuildMenu();
+ ghPrebuildStatusMenu = HookEvent( ME_CLIST_PREBUILDSTATUSMENU, (MIRANDAHOOK)RebuildAccount);
+
+ // install known modules strings to database
+ DB::Setting::WriteAString(NULL, "KnownModules", MODULELONGNAME, USERINFO","MODNAME","MOD_MBIRTHDAY","MODNAMEFLAGS);
+
+ return 0;
+}
+
+static INT OnShutdown(WPARAM wParam, LPARAM lParam)
+{
+ UnhookEvent(ghShutdownHook);
+ DlgContactInfoUnLoadModule();
+ SvcReminderUnloadModule();
+
+ // uninitialize classes
+ CtrlContactUnLoadModule();
+ CtrlButtonUnloadModule();
+
+ SvcConstantsUnloadModule();
+ UnhookEvent(ghPrebuildStatusMenu);
+ SvcEMailUnloadModule();
+ SvcFlagsUnloadModule();
+ SvcGenderUnloadModule();
+ SvcHomepageUnloadModule();
+ SvcPhoneUnloadModule();
+
+ mir_free(hMenuItemAccount);
+ return 0;
+}
+
+static BOOL CoreCheck()
+{
+ BOOL bOk = TRUE;
+ CHAR szVer[260];
+ TCHAR tszExePath[1024];
+
+ GetModuleFileName(GetModuleHandle(NULL), tszExePath, SIZEOF(tszExePath));
+ CallService(MS_SYSTEM_GETVERSIONTEXT, SIZEOF(szVer), (LPARAM)szVer);
+
+ strlwr(szVer);
+ _tcslwr(tszExePath);
+
+
+ bOk *= (GetVersion() & 0x80000000) == 0;
+ bOk *= strstr(szVer, "unicode") != 0;
+
+
+ bOk *= _tcsstr(_tcsrchr(tszExePath, '\\'), _T("miranda")) != 0;
+ bOk *= !strstr(szVer, "coffee") && strncmp(szVer, "1.", 2) && !strstr(szVer, " 1.");
+ bOk *= myGlobals.mirandaVersion < PLUGIN_MAKE_VERSION(1,0,0,0);
+ return bOk;
+}
+
+/*
+============================================================================================
+ plugin interface & DllEntrypoint
+============================================================================================
+*/
+
+/**
+ * This function is called by Miranda to get some information about this plugin.
+ *
+ * @return pointer to pluginInfo struct
+ **/
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ myGlobals.mirandaVersion = mirandaVersion;
+ return &pluginInfo;
+}
+
+/**
+ * This function returns the provided interfaces.
+ *
+ * @return array of interfaces
+ **/
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {
+ MIID_UIUSERINFOEX, // this is just me
+ MIID_UIUSERINFO, // replace the default userinfo module
+ MIID_CONTACTINFO, // indicate, that MS_CONTACT_GETCONTACTINFO service is provided
+ MIID_REMINDER, // indicate an Reminder of being provided
+ MIID_SREMAIL, // Send/Receive E-Mail service is provided
+ MIID_LAST
+};
+
+/**
+ * This function is called by Miranda just to make it possible to unload some memory, ...
+ *
+ * @return 0
+ **/
+extern "C" INT __declspec(dllexport) Unload(VOID)
+{
+ return 0;
+}
+
+/**
+ * This function is called by Miranda to initialize the plugin.
+ *
+ * @return 0
+ **/
+extern "C" INT __declspec(dllexport) Load(void)
+{
+ mir_getLP(&pluginInfo);
+ if ( !CoreCheck())
+ return 1;
+
+ // init common controls
+ INITCOMMONCONTROLSEX ccEx;
+ ccEx.dwSize = sizeof(ccEx);
+ ccEx.dwICC = ICC_WIN95_CLASSES|ICC_DATE_CLASSES;
+ InitCommonControlsEx(&ccEx);
+
+ ZeroMemory(&myGlobals, sizeof(MGLOBAL));
+
+ // init clist interface
+ pcli = (CLIST_INTERFACE*)CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)0);
+
+ // init new miranda timezone interface
+ mir_getTMI(&tmi);
+
+ // init freeimage interface
+ INT_PTR result = CALLSERVICE_NOTFOUND;
+ if(ServiceExists(MS_IMG_GETINTERFACE))
+ result = CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&FIP);
+
+ if(FIP == NULL || result != S_OK) {
+ MessageBoxEx(NULL, TranslateT("Fatal error, image services not found. Flags Module will be disabled."), _T("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL, 0);
+ return 1;
+ }
+
+ if (IsWinVerVistaPlus())
+ {
+ HMODULE hDwmApi = LoadLibraryA("dwmapi.dll");
+ if (hDwmApi)
+ dwmIsCompositionEnabled = (pfnDwmIsCompositionEnabled)GetProcAddress(hDwmApi,"DwmIsCompositionEnabled");
+ }
+
+ // check for dbx_tree
+ myGlobals.UseDbxTree = ServiceExists("DBT/Entity/GetRoot");
+
+ // load icon library
+ IcoLib_LoadModule();
+
+ SvcFlagsLoadModule();
+ tmi.getTimeZoneTime ? SvcTimezoneLoadModule() : SvcTimezoneLoadModule_old();
+ SvcContactInfoLoadModule();
+ SvcEMailLoadModule();
+ SvcRefreshContactInfoLoadModule();
+
+ CtrlContactLoadModule();
+ // load my button class
+ CtrlButtonLoadModule();
+ // initializes the Ex/Import Services
+ SvcExImport_LoadModule();
+ // load the UserInfoPropertySheet module
+ DlgContactInfoLoadModule();
+
+ // Anniversary stuff
+ DlgAnniversaryListLoadModule();
+ SvcReminderLoadModule();
+
+ // Now the module is loaded! Start initializing certain things
+ ghModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ ghTopToolBarLoaded = HookEvent(ME_TTB_MODULELOADED, OnTopToolBarLoaded);
+ ghShutdownHook = HookEvent(ME_SYSTEM_SHUTDOWN, OnShutdown);
+ return 0;
+}
+
+/**
+ * Windows needs it for loading.
+ *
+ * @return TRUE
+ **/
+BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ ghInst = hinst;
+ break;
+ }
+ return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/mir_contactqueue.cpp b/plugins/UserInfoEx/src/mir_contactqueue.cpp
new file mode 100644
index 0000000000..93e3bbd976
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_contactqueue.cpp
@@ -0,0 +1,425 @@
+/*
+Copyright 2006 Ricardo Pescuma Domenecci
+
+Modified 2008-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_contactqueue.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "mir_contactqueue.h"
+#include <process.h>
+
+/**
+ * This static helper function is used to sort the queue items by time
+ * beginning with the next upcoming item to call the Callback for.
+ *
+ * @param i1 - the first queue item
+ * @param i2 - the second queue item
+ *
+ * @return The function returns the time slack between the two items.
+ **/
+static INT QueueSortItems(const CQueueItem *i1, const CQueueItem *i2)
+{
+ INT rc = i1->check_time - i2->check_time;
+ if (!rc)
+ {
+ rc = i1->hContact != i2->hContact;
+ }
+ return rc;
+}
+
+/**
+ *
+ *
+ **/
+CContactQueue::CContactQueue(INT initialSize)
+ : _queue(initialSize, QueueSortItems)
+{
+ _hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ _status = RUNNING;
+
+ InitializeCriticalSection(&_cs);
+
+ mir_forkthread((pThreadFunc)CContactQueue::ThreadProc, this);
+}
+
+/**
+ *
+ *
+ **/
+CContactQueue::~CContactQueue()
+{
+ if (_status == RUNNING)
+ {
+ _status = STOPPING;
+ }
+ SetEvent(_hEvent);
+
+ for (INT count = 0; _status != STOPPED && ++count < 50;)
+ {
+ Sleep(10);
+ }
+
+ for (INT i = 0; i < _queue.getCount(); i++)
+ {
+ mir_free(_queue[i]);
+ }
+ _queue.destroy();
+
+ CloseHandle(_hEvent);
+ DeleteCriticalSection(&_cs);
+}
+
+/**
+ *
+ *
+ **/
+VOID CContactQueue::Lock()
+{
+ EnterCriticalSection(&_cs);
+}
+
+/**
+ *
+ *
+ **/
+VOID CContactQueue::Release()
+{
+ LeaveCriticalSection(&_cs);
+}
+
+/**
+ * This function removes all queue items.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::RemoveAll()
+{
+ Lock();
+
+ for (INT i = _queue.getCount() - 1; i >= 0; --i)
+ {
+ mir_free(_queue[i]);
+ }
+ _queue.destroy();
+
+ Release();
+}
+
+/**
+ * This function removes all queue items for the hContact.
+ *
+ * @param hContact - the contact whose queue items to delete
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::RemoveAll(HANDLE hContact)
+{
+ Lock();
+
+ for (INT i = _queue.getCount() - 1; i >= 0; --i)
+ {
+ CQueueItem *qi = _queue[i];
+
+ if (qi->hContact == hContact)
+ {
+ _queue.remove(i);
+ mir_free(qi);
+ }
+ }
+
+ Release();
+}
+
+/**
+ * This function removes all queue items for the hContact considering the correct parameter.
+ *
+ * @param hContact - the contact whose queue items to delete
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::RemoveAllConsiderParam(HANDLE hContact, PVOID param)
+{
+ Lock();
+
+ for (INT i = _queue.getCount() - 1; i >= 0; --i)
+ {
+ CQueueItem *qi = _queue[i];
+
+ if (qi->hContact == hContact && qi->param == param)
+ {
+ _queue.remove(i);
+ mir_free(qi);
+ }
+ }
+
+ Release();
+}
+
+/**
+ * This method adds the desired new item.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+BOOL CContactQueue::Add(INT waitTime, HANDLE hContact, PVOID param)
+{
+ BOOL rc;
+
+ Lock();
+
+ rc = InternalAdd(waitTime, hContact, param);
+
+ Release();
+
+ return rc;
+}
+
+/**
+ * This method adds the desired new item only, if the queue does not yet contain
+ * an item for the contact.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddIfDontHave(INT waitTime, HANDLE hContact, PVOID param)
+{
+ INT i;
+ BOOL rc;
+
+ Lock();
+
+ for (i = _queue.getCount() - 1; i >= 0; --i)
+ {
+ if (_queue[i]->hContact == hContact)
+ {
+ break;
+ }
+ }
+
+ rc = (i == -1) ? InternalAdd(waitTime, hContact, param) : 0;
+
+ Release();
+
+ return rc;
+}
+
+/**
+ * This method removes all existing queue items for the contact and adds a new queue item
+ * for the given contact. This method might be used to move an existing entry,
+ * whose check_time has changed.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddUnique(INT waitTime, HANDLE hContact, PVOID param)
+{
+ BOOL rc;
+
+ Lock();
+
+ RemoveAll(hContact);
+ rc = InternalAdd(waitTime, hContact, param);
+
+ Release();
+
+ return rc;
+}
+
+/**
+ * This method removes all existing queue items for the contact with the same parameter as @e param
+ * and adds a new queue item for the given contact. This method might be used to move an existing
+ * entry, whose check_time has changed.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddUniqueConsiderParam(INT waitTime, HANDLE hContact, PVOID param)
+{
+ BOOL rc;
+
+ Lock();
+
+ RemoveAllConsiderParam(hContact, param);
+ rc = InternalAdd(waitTime, hContact, param);
+
+ Release();
+
+ return rc;
+}
+
+/**
+ * This member function really adds an item into the time sorted queue list.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+BOOL CContactQueue::InternalAdd(INT waitTime, HANDLE hContact, PVOID param)
+{
+ BOOL rc;
+ CQueueItem *qi = (CQueueItem *) mir_alloc(sizeof(CQueueItem));
+
+ qi->hContact = hContact;
+ qi->check_time = GetTickCount() + waitTime;
+ qi->param = param;
+
+ rc = _queue.insert(qi);
+ if (!rc)
+ {
+ mir_free(qi);
+ }
+
+ SetEvent(_hEvent);
+
+ return rc;
+}
+
+/**
+ * This is the real thread callback function. As long as _status
+ * is set to RUNNING it looks for items in the queue to perform
+ * the _pfnCallback function on them. If the queue is empty or the
+ * next upcoming item is located in the future, the thread is suspended
+ * in the meanwhile.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::Thread()
+{
+ while (_status == RUNNING)
+ {
+ ResetEvent(_hEvent);
+
+ Lock();
+
+ if (_queue.getCount() <= 0)
+ {
+ // can be used by a derivant
+ OnEmpty();
+
+ // No items, so supend thread
+ Release();
+
+ Suspend(INFINITE);
+ }
+ else
+ {
+ // Take a look at first queue item
+ CQueueItem *qi = _queue[0];
+
+ INT dt = qi->check_time - GetTickCount();
+ if (dt > 0)
+ {
+ // Not time to request yet, wait...
+ Release();
+
+ Suspend(dt);
+ }
+ else
+ {
+ // Will request this queue item
+ _queue.remove(0);
+
+ Release();
+
+ Callback(qi->hContact, qi->param);
+
+ mir_free(qi);
+ }
+ }
+ }
+ _status = STOPPED;
+}
+
+/**
+ * This method suspends the worker thread for the given ammount of time.
+ *
+ * @param time - milliseconds to suspend the thread for
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::Suspend(INT time) const
+{
+ if (_status == RUNNING)
+ {
+ WaitForSingleObject(_hEvent, time);
+ }
+}
+
+/**
+ * This method resumes the worker thread and immitiatly goes on with the next entry.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID CContactQueue::ContinueWithNext()
+{
+ if (_status == RUNNING)
+ {
+ INT i, c, dt;
+
+ Lock();
+
+ c = _queue.getCount();
+ if (c > 0)
+ {
+ dt = _queue[0]->check_time - GetTickCount() - 3000;
+ if (dt > 0)
+ {
+ for (i = 0; i < c; i++)
+ {
+ _queue[i]->check_time -= dt;
+ }
+ }
+ }
+ SetEvent(_hEvent);
+ Release();
+ }
+}
diff --git a/plugins/UserInfoEx/src/mir_contactqueue.h b/plugins/UserInfoEx/src/mir_contactqueue.h
new file mode 100644
index 0000000000..b8f77530b7
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_contactqueue.h
@@ -0,0 +1,221 @@
+/*
+Copyright 2006 Ricardo Pescuma Domenecci
+
+Modified 2008-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_contactqueue.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+
+#ifndef __CONTACTASYNCQUEUE_H__
+#define __CONTACTASYNCQUEUE_H__
+
+#ifndef MIRANDA_VER
+#define MIRANDA_VER 0x0A00
+#endif
+
+#include <windows.h>
+#include <newpluginapi.h>
+#include <m_system_cpp.h>
+
+/**
+ *
+ *
+ **/
+struct CQueueItem
+{
+ DWORD check_time;
+ HANDLE hContact;
+ PVOID param;
+};
+
+/**
+ *
+ *
+ **/
+class CContactQueue
+{
+public:
+
+ enum EQueueStatus
+ {
+ RUNNING = 0,
+ STOPPING = 1,
+ STOPPED = 2
+ };
+
+ CContactQueue (INT initialSize = 10);
+ ~CContactQueue ();
+
+ inline INT Size () const { return _queue.getCount();}
+ inline INT Remove (INT idx) { mir_free(_queue[idx]); return _queue.remove(idx);}
+ inline CQueueItem* Get (INT idx) const { return _queue[idx];}
+
+
+ VOID RemoveAll();
+
+ /**
+ * This function removes all queue items for the hContact.
+ *
+ * @param hContact - the contact whose queue items to delete
+ *
+ * @return nothing
+ **/
+ VOID RemoveAll(HANDLE hContact);
+
+ /**
+ * This function removes all queue items for the hContact considering the correct parameter.
+ *
+ * @param hContact - the contact whose queue items to delete
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @return nothing
+ **/
+ VOID RemoveAllConsiderParam(HANDLE hContact, PVOID param);
+
+ /**
+ * This method adds the desired new item.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+ BOOL Add(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+ /**
+ * This method adds the desired new item only, if the queue does not yet contain
+ * an item for the contact.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+ BOOL AddIfDontHave(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+ /**
+ * This method removes all existing queue items for the contact and adds a new queue item
+ * for the given contact. This method might be used to move an existing entry,
+ * whose check_time has changed.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @return nothing
+ **/
+ BOOL AddUnique(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+ /**
+ * This method removes all existing queue items for the contact with the same parameter as @e param
+ * and adds a new queue item for the given contact. This method might be used to move an existing
+ * entry, whose check_time has changed.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @return nothing
+ **/
+ BOOL AddUniqueConsiderParam (INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+ /**
+ * This method resumes the worker thread and immitiatly goes on with the next entry.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ VOID ContinueWithNext();
+
+protected:
+
+ virtual VOID OnEmpty () {};
+ virtual VOID Callback (HANDLE hContact, PVOID param) = 0;
+
+ /**
+ * This is the real thread callback function. As long as _status
+ * is set to RUNNING it looks for items in the queue to perform
+ * the _pfnCallback function on them. If the queue is empty or the
+ * next upcoming item is located in the future, the thread is suspended
+ * in the meanwhile.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ VOID Thread();
+
+ /**
+ * This is a static method to redirect the thread's callback function
+ * to the desired class object.
+ *
+ * @param obj - pointer to the object (instance) of CContactQueue
+ *
+ * @return nothing
+ **/
+ static VOID ThreadProc(CContactQueue* obj)
+ {
+ obj->Thread();
+ }
+
+ /**
+ * This method suspends the worker thread for the given ammount of time.
+ *
+ * @param time - milliseconds to suspend the thread for
+ *
+ * @return nothing
+ **/
+ VOID Suspend(INT time) const;
+
+private:
+
+ LIST<CQueueItem> _queue;
+
+ CRITICAL_SECTION _cs;
+ HANDLE _hEvent;
+ EQueueStatus _status;
+
+ VOID Lock();
+ VOID Release();
+
+ /**
+ * This member function really adds an item into the time sorted queue list.
+ *
+ * @param waitTime - the time to wait until the callback is desired to run
+ * @param hContact - the contact to perform the action for
+ * @param param - a caller defined parameter passed to the callback function
+ *
+ * @retval TRUE - The item is added to the queue successfully.
+ * @retval FALSE - The item is not added to the queue.
+ **/
+ BOOL InternalAdd(INT waitTime, HANDLE hContact, PVOID param);
+};
+
+#endif // __CONTACTASYNCQUEUE_H__ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_db.cpp b/plugins/UserInfoEx/src/mir_db.cpp
new file mode 100644
index 0000000000..e4596df4cc
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_db.cpp
@@ -0,0 +1,1334 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_db.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include <m_metacontacts.h>
+#include "ctrl_base.h"
+#include "mir_string.h"
+#include "mir_db.h"
+
+namespace DB {
+
+namespace MetaContact {
+
+/**
+ *
+ *
+ **/
+INT_PTR SubCount(HANDLE hMetaContact)
+{
+ INT_PTR result = CallService(MS_MC_GETNUMCONTACTS, (WPARAM) hMetaContact, 0);
+ return (result == CALLSERVICE_NOTFOUND) ? -1 : result;
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR SubDefNum(HANDLE hMetaContact)
+{
+ INT_PTR result = CallService(MS_MC_GETDEFAULTCONTACTNUM, (WPARAM) hMetaContact, 0);
+ return (result == CALLSERVICE_NOTFOUND) ? -1 : result;
+}
+
+/**
+ *
+ *
+ **/
+HANDLE Sub(HANDLE hMetaContact, INT idx)
+{
+ if (idx != -1) {
+ INT_PTR result = CallService(MS_MC_GETSUBCONTACT, (WPARAM) hMetaContact, (LPARAM) idx);
+ return (result == CALLSERVICE_NOTFOUND) ? NULL : (HANDLE) result;
+ }
+ return NULL;
+}
+
+/**
+ *
+ *
+ **/
+BOOLEAN IsSub(HANDLE hContact)
+{
+ return myGlobals.szMetaProto &&
+ DB::Setting::GetByte(myGlobals.szMetaProto, "Enabled", TRUE) &&
+ DB::Setting::GetByte(hContact, myGlobals.szMetaProto, "IsSubcontact", FALSE);
+}
+
+/**
+ *
+ *
+ **/
+HANDLE GetMeta(HANDLE hContact)
+{
+ HANDLE result;
+ if (myGlobals.szMetaProto){
+ result = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+ if (result == (HANDLE)CALLSERVICE_NOTFOUND) {
+ result = NULL;
+ }
+ }
+ else {
+ result = NULL;
+ }
+ return (HANDLE) result;
+}
+
+} /* namespace MetaContact */
+
+/**
+* This namespace contains all functions used to access or modify contacts in the database.
+**/
+namespace Contact {
+
+/**
+ * This function retrieves the display name for a contact.
+ * @param hContact - handle to the contact
+ * @return Returns the display name of a contact.
+ **/
+LPTSTR DisplayName(HANDLE hContact)
+{
+ return (LPTSTR) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR);
+}
+
+/**
+ * This function is used to retrieve a contact's basic protocol
+ * @param hContact - handle to the contact
+ * @return This function returns the basic protocol of a contact.
+ **/
+LPSTR Proto(HANDLE hContact)
+{
+ if (hContact) {
+ INT_PTR result;
+ result = CallService(MS_PROTO_GETCONTACTBASEACCOUNT, (WPARAM) hContact, NULL);
+ return (LPSTR) ((result == CALLSERVICE_NOTFOUND) ? NULL : result);
+ }
+ return NULL;
+}
+
+/**
+ * Gets the number of contacts in the database, which does not count the user
+ * @param hContact - handle to the contact
+ * @return Returns the number of contacts. They can be retrieved using
+ * contact/findfirst and contact/findnext
+ **/
+INT_PTR GetCount()
+{
+ return CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+}
+
+/**
+ * This function searches the first contact in the database and returns its handle.
+ * @retval HANDLE - handle of the next contact in the database
+ * @retval NULL - no more contacts in the database
+ **/
+HANDLE FindFirst()
+{
+ return (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+}
+
+/**
+ * This function searches the next contact in the database and returns its handle.
+ * @param hContact - handle to the contact
+ * @retval HANDLE - handle of the next contact in the database
+ * @retval NULL - no more contacts in the database
+ **/
+HANDLE FindNext(HANDLE hContact)
+{
+ return (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+}
+
+/**
+ * Simply adds a new contact without setting up any protocol or something else
+ * @return HANDLE The function returns the HANDLE of the new contact
+ **/
+HANDLE Add()
+{
+ return (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0);
+}
+
+/**
+ * This function deletes a contact from the database.
+ * @param hContact - handle to the contact
+ **/
+BYTE Delete(HANDLE hContact)
+{
+ return CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0) != 0;
+}
+
+/**
+ * This function trys to guess, when an ICQ contact was added to database.
+ **/
+DWORD WhenAdded(DWORD dwUIN, LPCSTR pszProto)
+{
+ DBEVENTINFO dbei;
+ HANDLE edbe;
+ DWORD dwEvtUIN;
+
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ for (edbe = DB::Event::FindFirst(NULL); edbe != NULL; edbe = DB::Event::FindNext(edbe)) {
+ // get eventtype and compare
+ if (!DB::Event::GetInfo(edbe, &dbei) && dbei.eventType == EVENTTYPE_ADDED) {
+ if (!DB::Event::GetInfoWithData(edbe, &dbei)) {
+ // extract UIN and compare with given one
+ CopyMemory(&dwEvtUIN, dbei.pBlob, sizeof(DWORD));
+ MIR_FREE(dbei.pBlob);
+ if (dwEvtUIN == dwUIN) {
+ return dbei.timestamp;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+} /* Contact */
+
+namespace Module {
+
+/**
+ * Deletes all settings in the module.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to delete the setting from (e.g. USERINFO)
+ * return: nothing
+ **/
+VOID Delete(HANDLE hContact, LPCSTR pszModule)
+{
+ CEnumList Settings;
+ if (!Settings.EnumSettings(hContact, pszModule)) {
+ INT i;
+ for (i = 0; i < Settings.getCount(); i++) {
+ DB::Setting::Delete(hContact, pszModule, Settings[i]);
+ }
+ }
+}
+
+/**
+ * Enum Proc for DBModule_IsEmpty
+ * @param pszSetting - the setting
+ * @param lParam - DBCONTACTENUMSETTINGS - (LPARAM)&dbces
+ * @retval TRUE - always true
+ **/
+static INT IsEmptyEnumProc(LPCSTR pszSetting, LPARAM lParam)
+{
+ return 1;
+}
+
+/**
+ * This function tests, whether a module is empty for the given contact or not
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @retval TRUE - the module is empty
+ * @retval FALSE - the module contains settings
+ **/
+BOOLEAN IsEmpty(HANDLE hContact, LPCSTR pszModule)
+{
+ DBCONTACTENUMSETTINGS dbces;
+ dbces.pfnEnumProc = IsEmptyEnumProc;
+ dbces.szModule = pszModule;
+ dbces.ofsSettings = 0;
+ dbces.lParam = 0;
+ return (0 > CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)hContact, (LPARAM)&dbces));
+}
+
+/**
+ * This function tests, whether a module belongs to a metacontact protocol
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @retval TRUE - the module belongs to a metacontact protocol
+ * @retval FALSE - the module belongs to a other protocol
+ **/
+BOOLEAN IsMeta(LPCSTR pszModule)
+{
+ if(myGlobals.szMetaProto)
+ return !mir_strcmp(pszModule, myGlobals.szMetaProto);
+ return !mir_strcmp(pszModule, "MetaContacts");
+}
+
+/**
+ * This function tests, whether a module is a meta contact, and user wants to scan it for settings
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @retval TRUE - the module is empty
+ * @retval FALSE - the module contains settings
+ **/
+BOOLEAN IsMetaAndScan (LPCSTR pszModule)
+{
+ return DB::Setting::GetByte(SET_META_SCAN, TRUE) && IsMeta(pszModule);
+}
+
+} /* namespace Module */
+
+namespace Setting {
+
+/**
+ * This function calls MS_DB_CONTACT_GETSETTING_STR service to get database values.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ * @param destType - desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE Get(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+ BYTE result;
+ DBCONTACTGETSETTING dgs;
+
+ dgs.szModule = pszModule;
+ dgs.szSetting = pszSetting;
+ dgs.pValue = dbv;
+ dbv->type = 0;
+
+ // read value without translation to specific type
+ result = CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM) hContact, (LPARAM) &dgs) != 0;
+
+ // Is value read successfully and destination type set?
+ if (!result && destType) {
+ result = DB::Variant::ConvertString(dbv, destType);
+ }
+ return result;
+}
+
+/**
+ * This function reads a value from the database and returns it as an ansi encoded string.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ *
+ * @return string value
+ **/
+LPSTR GetAString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+ if (GetAString(hContact, pszModule, pszSetting, &dbv) == 0){
+ if (DB::Variant::dbv2String(&dbv, DBVT_WCHAR) == 0) {
+ return dbv.pszVal;
+ }
+ DB::Variant::Free(&dbv);
+ }
+ return NULL;
+}
+
+/**
+ * This function reads a value from the database and returns it as an unicode encoded string.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ *
+ * @return string value
+ **/
+LPWSTR GetWString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+ if (GetWString(hContact, pszModule, pszSetting, &dbv) == 0) {
+ if (DB::Variant::dbv2String(&dbv, DBVT_WCHAR) == 0) {
+ return dbv.pwszVal;
+ }
+ DB::Variant::Free(&dbv);
+ }
+ return NULL;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_GETSETTING_STR service to get database values.
+ * It searches in pszModule first and if the setting does not exist there it tries proto to retrieve it.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param szProto - the contact's protocol to read the setting from (e.g. ICQ)
+ * @param szSetting - the setting to read
+ * @param destType - desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE GetEx(HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+ BYTE result;
+ result = !pszModule || Get(hContact, pszModule, pszSetting, dbv, destType);
+ // try to read setting from the contact's protocol module
+ if (result && pszProto) {
+ result = Get(hContact, pszProto, pszSetting, dbv, destType) != 0;
+ // try to get setting from a metasubcontact
+ if (result && DB::Module::IsMetaAndScan(pszProto)) {
+ const INT_PTR def = DB::MetaContact::SubDefNum(hContact);
+ HANDLE hSubContact;
+ // try to get setting from the default subcontact first
+ if (def > -1 && def < INT_MAX) {
+ hSubContact = DB::MetaContact::Sub(hContact, def);
+ if (hSubContact != NULL) {
+ result = DB::Setting::GetEx(hSubContact, pszModule, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType) != 0;
+ }
+ }
+ // scan all subcontacts for the setting
+ if (result) {
+ const INT_PTR cnt = DB::MetaContact::SubCount(hContact);
+ if (cnt < INT_MAX) {
+ INT_PTR i;
+ for (i = 0; result && i < cnt; i++) {
+ if (i != def) {
+ hSubContact = DB::MetaContact::Sub(hContact, i);
+ if (hSubContact != NULL) {
+ result = DB::Setting::GetEx(hSubContact, pszModule, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType) != 0;
+ } } } } } } }
+ return result;
+}
+
+/**
+ * This function is used by the controls of the details dialog and calls MS_DB_CONTACT_GETSETTING_STR service
+ * to get database values. It searches in pszModule first and if the setting does not exist there it tries proto
+ * to retrieve it.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSubModule - the module to read the setting from a meta subcontract (e.g. USERINFO)
+ * @param pszProto - the contact's protocol to read the setting from (e.g. ICQ)
+ * @param pszSetting - the setting to read
+ * @param destType - desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @return This function returns the WORD which contains the source of information.
+ **/
+WORD GetCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSubModule, LPCSTR pszProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+ WORD wFlags = 0;
+
+ // read setting from given module
+ if (hContact && pszModule && *pszModule && !Get(hContact, pszModule, pszSetting, dbv, destType)) {
+ wFlags |= CTRLF_HASCUSTOM;
+ if (Exists(hContact, pszProto, pszSetting)) {
+ wFlags |= CTRLF_HASPROTO;
+ }
+ }
+ // read setting from contact's basic protocol
+ else if (pszProto && *pszProto) {
+ // try to read the setting from the basic protocol
+ if (!Get(hContact, pszProto, pszSetting, dbv, destType)) {
+ wFlags |= CTRLF_HASPROTO;
+ }
+ // try to read the setting from the sub contacts' modules
+ else if (DB::Module::IsMetaAndScan(pszProto)) {
+ const INT_PTR def = DB::MetaContact::SubDefNum(hContact);
+ HANDLE hSubContact;
+ // try to get setting from the default subcontact first
+ if (def > -1 && def < INT_MAX) {
+ hSubContact = DB::MetaContact::Sub(hContact, def);
+ if (hSubContact != NULL) {
+ wFlags = GetCtrl(hSubContact, pszSubModule, NULL, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType);
+ if (wFlags != 0) {
+ wFlags &= ~CTRLF_HASCUSTOM;
+ wFlags |= CTRLF_HASMETA;
+ }
+ }
+ }
+ // copy the missing settings from the other subcontacts
+ if (wFlags == 0) {
+ INT_PTR i;
+ const INT_PTR cnt = DB::MetaContact::SubCount(hContact);
+ for (i = 0; i < cnt; i++) {
+ if (i != def) {
+ hSubContact = DB::MetaContact::Sub(hContact, i);
+ if (hSubContact != NULL) {
+ wFlags = GetCtrl(hSubContact, pszSubModule, NULL, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType);
+ if (wFlags != 0) {
+ wFlags &= ~CTRLF_HASCUSTOM;
+ wFlags |= CTRLF_HASMETA;
+ break;
+ } } } } } } }
+ if (wFlags == 0) {
+ dbv->type = DBVT_DELETED;
+ }
+ return wFlags;
+}
+
+/**
+ * This function reads a setting from database into a predefined portion of memory
+ * and convert numbers into a string, too.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ * @param pszValue - buffer, that retrieves the value
+ * @param cchValue - number of characters the buffer can take
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE GetStatic(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR pszValue, INT cchValue)
+{
+ DBVARIANT dbv;
+ DBCONTACTGETSETTING sVal;
+
+ if (pszValue && cchValue) {
+ pszValue[0] = 0;
+ dbv.pszVal = pszValue;
+ dbv.cchVal = cchValue;
+ dbv.type = DBVT_ASCIIZ;
+
+ sVal.pValue = &dbv;
+ sVal.szModule = pszModule;
+ sVal.szSetting = pszSetting;
+
+ if (!CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&sVal)) {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ _itoa(dbv.bVal, pszValue, 10);
+ break;
+ case DBVT_WORD:
+ _itoa(dbv.wVal, pszValue, 10);
+ break;
+ case DBVT_DWORD:
+ _itoa(dbv.dVal, pszValue, 10);
+ }
+ return (pszValue[0] == 0);
+ }
+ }
+ return 1;
+}
+
+/**
+ * This function reads a byte from the database. If required it converts it to a byte value
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ * @param errorValue - value to return if something goes wrong
+ *
+ * @return byte value
+ **/
+BYTE GetByte(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue)
+{
+ DBVARIANT dbv;
+ BYTE result;
+ if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+ result = errorValue;
+ }
+ else {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ result = dbv.bVal;
+ break;
+ case DBVT_WORD:
+ result = (dbv.wVal < 0x0100) ? (BYTE) dbv.wVal : errorValue;
+ break;
+ case DBVT_DWORD:
+ result = (dbv.wVal < 0x00000100) ? (BYTE) dbv.dVal : errorValue;
+ break;
+ default:
+ DB::Variant::Free(&dbv);
+ result = errorValue;
+ }
+ }
+ return result;
+}
+
+/**
+ * This function reads a word from the database. If required it converts it to a word value
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ * @param errorValue - value to return if something goes wrong
+ *
+ * @return word value
+ **/
+WORD GetWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue)
+{
+ DBVARIANT dbv;
+ WORD result;
+ if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+ result = errorValue;
+ }
+ else {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ result = 0x00ff & dbv.bVal;
+ break;
+ case DBVT_WORD:
+ result = dbv.wVal;
+ break;
+ case DBVT_DWORD:
+ result = (dbv.wVal < 0x00010000) ? (WORD) dbv.dVal : errorValue;
+ break;
+ default:
+ DB::Variant::Free(&dbv);
+ result = errorValue;
+ }
+ }
+ return result;
+}
+
+/**
+ * This function reads a double word from the database. If required it converts it to a double word value
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ * @param errorValue - value to return if something goes wrong
+ *
+ * @return double word value
+ **/
+DWORD GetDWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue)
+{
+ DBVARIANT dbv;
+ DWORD result;
+ if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+ result = errorValue;
+ }
+ else {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ result = 0x000000ff & dbv.bVal;
+ break;
+ case DBVT_WORD:
+ result = 0x0000ffff & dbv.wVal;
+ break;
+ case DBVT_DWORD:
+ result = dbv.dVal;
+ break;
+ default:
+ DB::Variant::Free(&dbv);
+ result = errorValue;
+ }
+ }
+ return result;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a DBVARIANT structure to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param dbv - the DBVARIANT to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteVariant(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, const DBVARIANT *dbv)
+{
+ DBCONTACTWRITESETTING cws;
+
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ memcpy(&cws.value, dbv, sizeof(DBVARIANT));
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a BYTE to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the byte to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteByte(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE value)
+{
+ DBCONTACTWRITESETTING cws;
+
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_BYTE;
+ cws.value.bVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a WORD to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the word to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD value)
+{
+ DBCONTACTWRITESETTING cws;
+
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_WORD;
+ cws.value.wVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a DWORD to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the double word to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteDWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD value)
+{
+ DBCONTACTWRITESETTING cws;
+
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_DWORD;
+ cws.value.dVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an ansi string to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the string to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteAString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value)
+{
+ DBCONTACTWRITESETTING cws;
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_ASCIIZ;
+ cws.value.pszVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an unicode string to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the string to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteWString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value)
+{
+ DBCONTACTWRITESETTING cws;
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_WCHAR;
+ cws.value.pwszVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an utf8 string to the database.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to write
+ * @param value - the string to store
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE WriteUString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value)
+{
+ DBCONTACTWRITESETTING cws;
+ cws.szModule = pszModule;
+ cws.szSetting = pszSetting;
+ cws.value.type = DBVT_UTF8;
+ cws.value.pszVal = value;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function checks for the existence of the given setting in the database
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to check
+ *
+ * @retval TRUE - setting exists
+ * @retval FALSE - setting does not exist
+ **/
+BOOLEAN Exists(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ if (pszModule && pszSetting) {
+ DBCONTACTGETSETTING cgs;
+ DBVARIANT dbv;
+ CHAR szDummy[1];
+
+ dbv.pszVal = szDummy;
+ dbv.cchVal = sizeof(szDummy);
+ dbv.type = 0;
+ cgs.pValue = &dbv;
+ cgs.szModule = pszModule;
+ cgs.szSetting = pszSetting;
+ if (!CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM) hContact, (LPARAM) &cgs)) {
+ return (dbv.type > DBVT_DELETED);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * This function deletes the given setting from database
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszSetting - the setting to read
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+BYTE Delete(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+ DBCONTACTGETSETTING cgs;
+ cgs.szModule = pszModule;
+ cgs.szSetting = pszSetting;
+ return CallService(MS_DB_CONTACT_DELETESETTING, (WPARAM) hContact, (LPARAM) &cgs) != 0;
+}
+
+/**
+ * This function deletes all reluctant settings of an setting array such as My-phoneXX.
+ * @param hContact - handle to the contact
+ * @param pszModule - the module to read the setting from (e.g. USERINFO)
+ * @param pszFormat - the format, telling what a array of settings is ment
+ * @param iStart - the first index of the setting to delete
+ *
+ * @return nothing
+ **/
+VOID DeleteArray(HANDLE hContact, LPCSTR pszModule, LPCSTR pszFormat, INT iStart)
+{
+ CHAR pszSetting[MAXSETTING];
+ do {
+ mir_snprintf(pszSetting, MAXSETTING, pszFormat, iStart++);
+ }
+ while (!DB::Setting::Delete(hContact, pszModule, pszSetting));
+}
+
+/**
+ * This function can prevent a setting from being stored to database permanently.
+ * @param pszSetting - the setting to read
+ * @param enabled - if set to 'true' the setting will not be stored in database
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+BYTE Resident(LPCSTR pszSetting, const bool enabled)
+{
+ return CallService(MS_DB_SETSETTINGRESIDENT, (WPARAM) enabled, (LPARAM) pszSetting) != 0;
+}
+
+} /* namespace Setting */
+
+namespace Variant {
+
+BYTE Free(DBVARIANT *dbv)
+{
+ return CallService(MS_DB_CONTACT_FREEVARIANT, 0, (LPARAM) dbv) != 0;
+}
+
+/**
+ * This function converts a string value of the DBVARIANT to the destination type
+ * but keeps all other values as is.
+ * @param dbv - pointer to DBVARIANT structure which is to manipulate
+ * @param destType - one of (DBVT_ASCIIZ, DBVT_UTF8 or DBVT_WCHAR)
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE ConvertString(DBVARIANT* dbv, const BYTE destType)
+{
+ if (dbv) {
+ switch (dbv->type) {
+ // source value is of type "ascii"
+ case DBVT_ASCIIZ:
+ {
+ switch (destType) {
+ // destination type is "utf8"
+ case DBVT_UTF8:
+ {
+ LPSTR tmpBuf = mir_utf8encode(dbv->pszVal);
+ mir_free(dbv->pszVal);
+ dbv->pszVal = tmpBuf;
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // destination type is "wchar"
+ case DBVT_WCHAR:
+ {
+ LPWSTR tmpBuf = mir_a2u(dbv->pszVal);
+ mir_free(dbv->pszVal);
+ dbv->pwszVal = tmpBuf;
+ dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+ }
+ }
+ } break;
+ // source value is of type "utf8"
+ case DBVT_UTF8:
+ {
+ switch (destType) {
+ // destination type is "ascii"
+ case DBVT_ASCIIZ:
+ {
+ mir_utf8decode(dbv->pszVal, NULL);
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // destination type is "wchar"
+ case DBVT_WCHAR:
+ {
+ LPSTR savePtr = dbv->pszVal;
+ dbv->pszVal = NULL;
+ mir_utf8decode(savePtr, &dbv->pwszVal);
+ mir_free(savePtr);
+ dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+ }
+ }
+ } break;
+ // source value is of type "wchar"
+ case DBVT_WCHAR:
+ {
+ switch (destType) {
+ // destination type is "ascii"
+ case DBVT_ASCIIZ:
+ {
+ LPSTR tmpBuf = mir_u2a(dbv->pwszVal);
+ mir_free(dbv->pwszVal);
+ dbv->pszVal = tmpBuf;
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // destination type is "utf8"
+ case DBVT_UTF8:
+ {
+ LPSTR tmpBuf = mir_utf8encodeW(dbv->pwszVal);
+ mir_free(dbv->pwszVal);
+ dbv->pszVal = tmpBuf;
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ }
+ }
+ }
+ }
+ return dbv->type == DBVT_DELETED;
+ }
+ return 1;
+}
+
+/**
+ * This function completely converts a DBVARIANT to the destination string type.
+ * It includes BYTE, WORD, DWORD and all string types
+ * @param dbv - pointer to DBVARIANT structure which is to manipulate
+ * @param destType - one of (DBVT_ASCIIZ, DBVT_UTF8 or DBVT_WCHAR)
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+BYTE dbv2String(DBVARIANT* dbv, const BYTE destType)
+{
+ if (dbv) {
+ switch (destType) {
+ // destination type is "utf8" or "ascii"
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ {
+ CHAR buf[32];
+ switch (dbv->type) {
+ // source value is of type "byte"
+ case DBVT_BYTE:
+ {
+ _ultoa(dbv->bVal, buf, 10);
+ dbv->pszVal = mir_strdup(buf);
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of type "word"
+ case DBVT_WORD:
+ {
+ _ultoa(dbv->wVal, buf, 10);
+ dbv->pszVal = mir_strdup(buf);
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of type "dword"
+ case DBVT_DWORD:
+ {
+ _ultoa(dbv->dVal, buf, 10);
+ dbv->pszVal = mir_strdup(buf);
+ dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of any string type
+ case DBVT_ASCIIZ:
+ case DBVT_WCHAR:
+ case DBVT_UTF8:
+ {
+ return ConvertString(dbv, destType);
+ }
+ }
+ } break;
+ // destination type is "wchar"
+ case DBVT_WCHAR:
+ {
+ WCHAR buf[32];
+ switch (dbv->type) {
+ // source value is of type "byte"
+ case DBVT_BYTE:
+ {
+ _ultow(dbv->bVal, buf, 10);
+ dbv->pwszVal = mir_wcsdup(buf);
+ dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of type "word"
+ case DBVT_WORD:
+ {
+ _ultow(dbv->wVal, buf, 10);
+ dbv->pwszVal = mir_wcsdup(buf);
+ dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of type "dword"
+ case DBVT_DWORD:
+ {
+ _ultow(dbv->dVal, buf, 10);
+ dbv->pwszVal = mir_wcsdup(buf);
+ dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+ } break;
+ // source value is of any string type
+ case DBVT_ASCIIZ:
+ case DBVT_WCHAR:
+ case DBVT_UTF8:
+ {
+ return ConvertString(dbv, destType);
+ }
+ }
+ }
+ }
+ return dbv->type != destType;
+ }
+ return 1;
+}
+
+} /* namespace Variant */
+
+namespace Event {
+
+/**
+ * This function searches for the first event for the given contact.
+ * @param hContact - the handle of the contact to search events for
+ *
+ * @return This function returns the HANDLE of the first event for the given contact.
+ **/
+HANDLE FindFirst(HANDLE hContact)
+{
+ return (HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0);
+}
+
+/**
+ * This function searches for the last event for the given contact.
+ * @param hContact - the handle of the contact to search events for
+ *
+ * @return This function returns the HANDLE of the last event for the given contact.
+ **/
+HANDLE FindLast(HANDLE hContact)
+{
+ return (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0);
+}
+
+/**
+ * This function searches for the next event in the chain, which follows the given event.
+ * @param hEvent - the handle of the event where to continue searching
+ *
+ * @return This function returns the HANDLE of the next event in the event chain.
+ **/
+HANDLE FindNext(HANDLE hEvent)
+{
+ return (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function searches for the previous event in the chain, which follows the given event.
+ * @param hEvent - the handle of the event where to continue searching
+ *
+ * @return This function returns the HANDLE of the previous event in the event chain.
+ **/
+HANDLE FindPrev(HANDLE hEvent)
+{
+ return (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function initializes the DBEVENTINFO structure and calls
+ * the MS_DB_EVENT_GET service to retrieve information about an event.
+ * @param hEvent - the handle of the event to get information for
+ * @param dbei - the pointer to a DBEVENTINFO structure, which retrieves all information.
+ *
+ * @retval 0 - success
+ * @retval nonezero - failure
+ **/
+BYTE GetInfo(HANDLE hEvent, DBEVENTINFO *dbei)
+{
+ dbei->cbSize = sizeof(DBEVENTINFO);
+ dbei->cbBlob = 0;
+ dbei->pBlob = NULL;
+ return CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)dbei) != 0;
+}
+
+/**
+ * This function initializes the DBEVENTINFO structure and calls
+ * the MS_DB_EVENT_GET service to retrieve information about an event.
+ * @param hEvent - the handle of the event to get information for
+ * @param dbei - the pointer to a DBEVENTINFO structure, which retrieves all information.
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+BYTE GetInfoWithData(HANDLE hEvent, DBEVENTINFO *dbei)
+{
+ BYTE result;
+ dbei->cbSize = sizeof(DBEVENTINFO);
+ if (!dbei->cbBlob) {
+ INT_PTR size = BlobSizeOf(hEvent);
+ dbei->cbBlob = (size != -1) ? (DWORD)size : 0;
+ }
+ if(dbei->cbBlob) {
+ dbei->pBlob = (PBYTE) mir_alloc(dbei->cbBlob);
+ if (dbei->pBlob == NULL) {
+ dbei->cbBlob = 0;
+ }
+ }
+ else {
+ dbei->pBlob = NULL;
+ }
+
+ result = CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)dbei) != 0;
+ if (result && dbei->pBlob) {
+ mir_free(dbei->pBlob);
+ dbei->pBlob = NULL;
+ }
+ return result;
+}
+
+/**
+ * This function returns the timestamp for the given event.
+ * @param hEvent - the handle of the event to get the timestamp for
+ *
+ * @retval 0 if no timestamp is available
+ * @retval timestamp
+ **/
+DWORD TimeOf(HANDLE hEvent)
+{
+ DBEVENTINFO dbei;
+ if (!GetInfo(hEvent, &dbei)) {
+ return dbei.timestamp;
+ }
+ return 0;
+}
+
+/**
+ * This function returns the number of bytes required to retrieve
+ * binary data associated with the event.
+ * @param hEvent - the handle of the event to get the number of bytes for
+ *
+ * @retval size of event data
+ * @retval -1 if hEvent is invalid
+ **/
+INT_PTR BlobSizeOf(HANDLE hEvent)
+{
+ return CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function compares two DBEVENTINFO structures against each other.
+ * It compares the timestamp, eventType and module names.
+ * @param d1 - pointer to the first DBEVENTINFO structure
+ * @param d2 - pointer to the second DBEVENTINFO structure
+ * @param Data - default false, if true compare also blob data
+.*
+ * @retval TRUE - The structures describe the same event.
+ * @retval FALSE - The two structure's events differ from each other.
+ **/
+static FORCEINLINE
+BOOLEAN IsEqual(const DBEVENTINFO *d1, const DBEVENTINFO *d2, bool Data)
+{
+ BOOLEAN res = d1 && d2 &&
+ (d1->timestamp == d2->timestamp) &&
+ (d1->eventType == d2->eventType) &&
+ (d1->cbBlob == d2->cbBlob) &&
+ (!d1->szModule || !d2->szModule || !stricmp(d1->szModule, d2->szModule))
+ ;
+ if(Data) {
+ return res &&
+ (!d1->pBlob || !d2->pBlob || !memcmp(d1->pBlob,d2->pBlob,d1->cbBlob))
+ ;
+ }
+ return res;
+}
+
+/**
+ * This functions searches for an existing event in the database, which matches
+ * the information provided by 'dbei'. In order to fasten up the search e.g.
+ * while checking many events, this function stars searching from the last
+ * found event.
+ * @param hContact - the handle of the contact to search events for
+ * @param hDbExistingEvent - an existing database event to start the search from.
+ * @param dbei - the pointer to a DBEVENTINFO structure
+ *
+ * @retval TRUE - the event identified by its information in @c dbei exists.
+ * @retval FALSE - no event with the information of @c dbei exists.
+ *
+ **/
+BOOLEAN Exists(HANDLE hContact, HANDLE& hDbExistingEvent, DBEVENTINFO *dbei)
+{
+ BOOLEAN result = FALSE;
+ DBEVENTINFO edbei;
+ HANDLE sdbe,
+ edbe;
+
+ if (!hDbExistingEvent) {
+ hDbExistingEvent = FindFirst(hContact);
+ if (hDbExistingEvent) {
+ if (!GetInfo(hDbExistingEvent, &edbei)) {
+ if ((dbei->timestamp < edbei.timestamp)) {
+ return FALSE;
+ }
+ if(IsEqual(dbei, &edbei, false)) {
+ if (!GetInfoWithData(hDbExistingEvent, &edbei)) {
+ if(IsEqual(dbei, &edbei, true)) {
+ mir_free(edbei.pBlob);
+ return TRUE;
+ }
+ mir_free(edbei.pBlob);
+ }
+ }
+ }
+ edbe = FindLast(hContact);
+ if (edbe == hDbExistingEvent) {
+ return FALSE;
+ }
+ hDbExistingEvent = edbe;
+ }
+ }
+ if (hDbExistingEvent) {
+ sdbe = hDbExistingEvent;
+ for ( edbe = sdbe;
+ edbe && !GetInfo(edbe, &edbei) && (dbei->timestamp <= edbei.timestamp);
+ edbe = FindPrev(edbe)) {
+ hDbExistingEvent = edbe;
+ //compare without data (faster)
+ if ( result = IsEqual(dbei, &edbei, false)) {
+ if(NULL == (result = !GetInfoWithData(edbe, &edbei))) continue;
+ //compare with data
+ result = IsEqual(dbei, &edbei, true);
+ mir_free(edbei.pBlob);
+ if (result) {
+ break;
+ }
+ }
+ } /*end for*/
+
+ if (!result) {
+ for ( edbe = FindNext(sdbe);
+ edbe && !GetInfo(edbe, &edbei) && (dbei->timestamp >= edbei.timestamp);
+ edbe = FindNext(edbe)) {
+ hDbExistingEvent = edbe;
+ //compare without data (faster)
+ if ( result = IsEqual(dbei, &edbei, false)) {
+ if(NULL == (result = !GetInfoWithData(edbe, &edbei))) continue;
+ //compare with data
+ result = IsEqual(dbei, &edbei, true);
+ mir_free(edbei.pBlob);
+ if (result) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+
+} /* namespace Events */
+
+INT CEnumList::EnumProc(LPCSTR pszName, DWORD ofsModuleName, LPARAM lParam)
+{
+ if (pszName) {
+ ((CEnumList*)lParam)->Insert(pszName);
+ }
+ return 0;
+}
+
+INT CEnumList::EnumSettingsProc(LPCSTR pszName, LPARAM lParam)
+{
+ return EnumProc(pszName, 0, lParam);
+}
+
+INT CEnumList::CompareProc(LPCSTR p1, LPCSTR p2)
+{
+ if (p1) {
+ if (p2) {
+ return strcmp(p1, p2);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+CEnumList::CEnumList() : LIST<CHAR>(50, (FTSortFunc)CEnumList::CompareProc)
+{
+}
+
+CEnumList::~CEnumList()
+{
+ INT i, cnt;
+ LPSTR p;
+
+ for (i = 0, cnt = getCount(); i < cnt; i++) {
+ p = (*this)[i];
+ if (p) {
+ mir_free(p);
+ }
+ }
+ destroy();
+}
+
+LPSTR CEnumList::Insert(LPCSTR str)
+{
+ LPSTR p = mir_strdup(str);
+ if (p && !insert(p)) {
+ mir_free(p);
+ p = NULL;
+ }
+ return p;
+}
+
+INT_PTR CEnumList::EnumModules()
+{
+ return CallService(MS_DB_MODULES_ENUM, (WPARAM)this, (LPARAM)CEnumList::EnumProc);
+}
+
+/**
+ * @retval -1 - no settings to enumerate
+ * @retval 0 - success
+ **/
+INT_PTR CEnumList::EnumSettings(HANDLE hContact, LPCSTR pszModule)
+{
+ DBCONTACTENUMSETTINGS dbces;
+
+ dbces.pfnEnumProc = (DBSETTINGENUMPROC)CEnumList::EnumSettingsProc;
+ dbces.szModule = pszModule;
+ dbces.lParam = (LPARAM)this;
+ dbces.ofsSettings = 0;
+ return CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)hContact, (LPARAM)&dbces);
+}
+
+} /* namespace DB */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_db.h b/plugins/UserInfoEx/src/mir_db.h
new file mode 100644
index 0000000000..e06a8788a6
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_db.h
@@ -0,0 +1,229 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_db.h $
+Revision : $Revision: 189 $
+Last change on : $Date: 2010-09-11 01:31:38 +0400 (Сб, 11 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_database.h>
+
+namespace DB {
+
+namespace MetaContact{
+ INT_PTR SubCount (HANDLE hMetaContact);
+ INT_PTR SubDefNum (HANDLE hMetaContact);
+ HANDLE Sub (HANDLE hMetaContact, INT idx);
+
+ BOOLEAN IsSub (HANDLE hContact);
+ HANDLE GetMeta (HANDLE hContact);
+
+} /* namespace MetaContact */
+
+/**
+ * This namespace contains all functions used to access
+ * or modify contacts in the database.
+ **/
+namespace Contact {
+ LPTSTR DisplayName (HANDLE hContact);
+ LPSTR Proto (HANDLE hContact);
+
+ INT_PTR GetCount ();
+ HANDLE FindFirst ();
+ HANDLE FindNext (HANDLE hContact);
+
+ HANDLE Add();
+ BYTE Delete (HANDLE hContact);
+
+ DWORD WhenAdded (DWORD dwUIN, LPCSTR szBaseProto);
+
+} /* namespace Contact */
+
+namespace Module {
+ VOID Delete (HANDLE hContact, LPCSTR pszModule);
+ BOOLEAN IsEmpty (HANDLE hContact, LPCSTR pszModule);
+ BOOLEAN IsMeta (LPCSTR pszModule);
+ BOOLEAN IsMetaAndScan (LPCSTR pszModule);
+
+} /* namespace Module */
+
+/**
+ * This namespace defines all functions used to read and write settings from the database.
+ **/
+namespace Setting {
+
+ BYTE Get (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+ static FORCEINLINE
+ BYTE GetAsIs (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, 0); }
+ static FORCEINLINE
+ BYTE GetAString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_ASCIIZ); }
+ LPSTR GetAString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ static FORCEINLINE
+ BYTE GetWString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_WCHAR); }
+ LPWSTR GetWString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ static FORCEINLINE
+ BYTE GetUString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_UTF8); }
+
+ BYTE GetEx (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+ static FORCEINLINE
+ BYTE GetAsIsEx (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetEx(hContact, pszModule, szProto, pszSetting, dbv, 0); }
+ static FORCEINLINE
+ LPSTR GetAStringEx (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_ASCIIZ) && dbv.type == DBVT_ASCIIZ) ? dbv.pszVal : NULL; }
+ static FORCEINLINE
+ LPWSTR GetWStringEx (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_WCHAR) && dbv.type == DBVT_WCHAR) ? dbv.pwszVal : NULL; }
+ static FORCEINLINE
+ LPSTR GetUStringEx (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_UTF8) && dbv.type == DBVT_UTF8) ? dbv.pszVal : NULL; }
+
+ WORD GetCtrl (HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+ static FORCEINLINE
+ WORD GetAsIsCtrl (HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, 0); }
+ static FORCEINLINE
+ WORD GetAStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_ASCIIZ); }
+ static FORCEINLINE
+ WORD GetWStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_WCHAR); }
+ static FORCEINLINE
+ WORD GetUStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_UTF8); }
+
+ BYTE GetStatic (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR szValue, INT cchValue);
+
+ BYTE GetByte (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue);
+ static FORCEINLINE
+ BYTE GetByte (LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue) { return GetByte(NULL, pszModule, pszSetting, errorValue); }
+ static FORCEINLINE
+ BYTE GetByte (LPCSTR pszSetting, BYTE errorValue) { return GetByte(MODNAME, pszSetting, errorValue); }
+
+ WORD GetWord (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue);
+ static FORCEINLINE
+ WORD GetWord (LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue) { return GetWord(NULL, pszModule, pszSetting, errorValue); }
+ static FORCEINLINE
+ WORD GetWord (LPCSTR pszSetting, WORD errorValue) { return GetWord(MODNAME, pszSetting, errorValue); }
+
+ DWORD GetDWord (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue);
+ static FORCEINLINE
+ DWORD GetDWord (LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue) { return GetDWord(NULL, pszModule, pszSetting, errorValue); }
+ static FORCEINLINE
+ DWORD GetDWord (LPCSTR pszSetting, DWORD errorValue) { return GetDWord(MODNAME, pszSetting, errorValue); }
+
+ /**
+ * write values to the database.
+ **/
+ BYTE WriteVariant (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, const DBVARIANT *dbv);
+
+ BYTE WriteByte (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE value);
+ static FORCEINLINE
+ BYTE WriteByte (LPCSTR pszModule, LPCSTR pszSetting, BYTE value) { return WriteByte(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteByte (LPCSTR pszSetting, BYTE value) { return WriteByte(MODNAME, pszSetting, value); }
+
+ BYTE WriteWord (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD value);
+ static FORCEINLINE
+ BYTE WriteWord (LPCSTR pszModule, LPCSTR pszSetting, WORD value) { return WriteWord(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteWord (LPCSTR pszSetting, WORD value) { return WriteWord(MODNAME, pszSetting, value); }
+
+ BYTE WriteDWord (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD value);
+ static FORCEINLINE
+ BYTE WriteDWord (LPCSTR pszModule, LPCSTR pszSetting, DWORD value) { return WriteDWord(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteDWord (LPCSTR pszSetting, DWORD value) { return WriteDWord(MODNAME, pszSetting, value); }
+
+ BYTE WriteAString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value);
+ static FORCEINLINE
+ BYTE WriteAString (LPCSTR pszModule, LPCSTR pszSetting, LPSTR value) { return WriteAString(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteAString (LPCSTR pszSetting, LPSTR value) { return WriteAString(MODNAME, pszSetting, value); }
+
+ BYTE WriteWString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value);
+ static FORCEINLINE
+ BYTE WriteWString (LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value) { return WriteWString(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteWString (LPCSTR pszSetting, LPWSTR value) { return WriteWString(MODNAME, pszSetting, value); }
+
+ BYTE WriteUString (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value);
+ static FORCEINLINE
+ BYTE WriteUString (LPCSTR pszModule, LPCSTR pszSetting, LPSTR value) { return WriteUString(NULL, pszModule, pszSetting, value); }
+ static FORCEINLINE
+ BYTE WriteUString (LPCSTR pszSetting, LPSTR value) { return WriteUString(MODNAME, pszSetting, value); }
+
+
+ #define GetTString GetWString
+ #define GetTStringEx GetWStringEx
+ #define GetTStringCtrl GetWStringCtrl
+ #define WriteTString WriteWString
+
+
+ /**
+ * misc operations
+ **/
+ BOOLEAN Exists (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ BYTE Delete (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+ VOID DeleteArray (HANDLE hContact, LPCSTR pszModule, LPCSTR pszFormat, INT iStart);
+ BYTE Resident (LPCSTR pszSetting, const bool enabled);
+
+} /* namespace Setting */
+
+namespace Variant {
+ BYTE ConvertString (DBVARIANT* dbv, const BYTE destType);
+ BYTE dbv2String (DBVARIANT* dbv, const BYTE destType);
+ BYTE Free (DBVARIANT* dbv);
+} /* namespace Variant */
+
+namespace Event {
+
+ HANDLE FindFirst (HANDLE hContact);
+ HANDLE FindLast (HANDLE hContact);
+ HANDLE FindNext (HANDLE hEvent);
+ HANDLE FindPrev (HANDLE hEvent);
+ BYTE GetInfo (HANDLE hEvent, DBEVENTINFO *dbei);
+ BYTE GetInfoWithData(HANDLE hEvent, DBEVENTINFO *dbei);
+ DWORD GetTime (HANDLE hEvent);
+ INT_PTR BlobSizeOf (HANDLE hEvent);
+ BOOLEAN Exists (HANDLE hContact, HANDLE& hDbExistingEvent, DBEVENTINFO *dbei);
+} /* namespace Events */
+
+/**
+ * enumerating
+ **/
+class CEnumList : public ::LIST<CHAR>
+{
+ static INT EnumProc (LPCSTR pszName, DWORD ofsModuleName, LPARAM lParam);
+ static INT EnumSettingsProc(LPCSTR pszName, LPARAM lParam);
+
+ static INT CompareProc (LPCSTR p1, LPCSTR p2);
+
+public:
+ CEnumList();
+ ~CEnumList();
+
+ LPSTR Insert(LPCSTR str);
+
+ INT_PTR EnumModules();
+ INT_PTR EnumSettings(HANDLE hContact, LPCSTR pszModule);
+};
+
+} /* namespace DB */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_icolib.cpp b/plugins/UserInfoEx/src/mir_icolib.cpp
new file mode 100644
index 0000000000..ef5d22e487
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_icolib.cpp
@@ -0,0 +1,401 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_icolib.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+typedef struct _ICODESC
+{
+ LPSTR pszName;
+ LPSTR pszDesc;
+ LPSTR pszSection;
+ WORD idResource;
+ BYTE size;
+} ICODESC;
+
+HICON ghDefIcon = NULL;
+
+static ICODESC icoDesc[] =
+{
+ // common
+ { ICO_COMMON_IM, "IM Naming", SECT_COMMON, IDI_MIRANDA, 0 },
+ { ICO_COMMON_PASSWORD, "Password", SECT_COMMON, IDI_PASSWORD, 0 },
+ { ICO_COMMON_FEMALE, "Female", SECT_COMMON, IDI_FEMALE, 0 },
+ { ICO_COMMON_MALE, "Male", SECT_COMMON, IDI_MALE, 0 },
+ { ICO_COMMON_BIRTHDAY, "Birthday", SECT_COMMON, IDI_BIRTHDAY, 0 },
+ { ICO_COMMON_CLOCK, "Timezone", SECT_COMMON, IDI_CLOCK, 1 },
+ { ICO_COMMON_MARITAL, "Marital status", SECT_COMMON, IDI_MARITAL, 0 },
+ { ICO_COMMON_ADDRESS, "Address", SECT_COMMON, IDI_TREE_ADDRESS, 0 },
+ { ICO_COMMON_ANNIVERSARY,"Anniversary", SECT_COMMON, IDI_ANNIVERSARY, 0 },
+
+ //zodiac
+ { ICO_ZOD_AQUARIUS, "Aquarius", SECT_COMMON, IDI_ZOD_AQUARIUS, 128 },
+ { ICO_ZOD_ARIES, "Aries", SECT_COMMON, IDI_ZOD_ARIES, 128 },
+ { ICO_ZOD_CANCER, "Cancer", SECT_COMMON, IDI_ZOD_CANCER, 128 },
+ { ICO_ZOD_CAPRICORN, "Capricorn", SECT_COMMON, IDI_ZOD_CAPRICORN, 128 },
+ { ICO_ZOD_GEMINI, "Gemini", SECT_COMMON, IDI_ZOD_GEMINI, 128 },
+ { ICO_ZOD_LEO, "Leo", SECT_COMMON, IDI_ZOD_LEO, 128 },
+ { ICO_ZOD_LIBRA, "Libra", SECT_COMMON, IDI_ZOD_LIBRA, 128 },
+ { ICO_ZOD_PISCES, "Pisces", SECT_COMMON, IDI_ZOD_PISCES, 128 },
+ { ICO_ZOD_SAGITTARIUS, "Sagittarius", SECT_COMMON, IDI_ZOD_SAGITTARIUS, 128 },
+ { ICO_ZOD_SCORPIO, "Scorpio", SECT_COMMON, IDI_ZOD_SCORPIO, 128 },
+ { ICO_ZOD_TAURUS, "Taurus", SECT_COMMON, IDI_ZOD_TAURUS, 128 },
+ { ICO_ZOD_VIRGO, "Virgo", SECT_COMMON, IDI_ZOD_VIRGO, 128 },
+ // later with new icon pack version
+ //{ ICO_ZOD_UNKNOWN, "Unknown", SECT_COMMON, IDI_ZOD_UNKNOWN, 128 },
+
+ // lists
+ { ICO_LST_MODULES, "Export: Modules", SECT_COMMON, IDI_LST_MODULES, 0 },
+ { ICO_LST_FOLDER, "Export: Folder", SECT_COMMON, IDI_LST_FOLDER, 0 },
+ { ICO_TREE_DEFAULT, "Default", SECT_TREE, IDI_LST_FOLDER, 0 },
+
+ // dialogs
+ { ICO_DLG_DETAILS, "Details Infobar", SECT_DLG, IDI_DLG_DETAILS, 48 },
+ { ICO_DLG_PHONE, "Phone Infobar", SECT_DLG, IDI_DLG_PHONE, 1 },
+ { ICO_DLG_EMAIL, "E-Mail Infobar", SECT_DLG, IDI_DLG_EMAIL, 1 },
+ { ICO_DLG_EXPORT, "Export VCard", SECT_DLG, IDI_EXPORT, 1 },
+ { ICO_DLG_IMPORT, "Import VCard", SECT_DLG, IDI_IMPORT, 1 },
+ { ICO_DLG_ANNIVERSARY, "Anniversary Infobar", SECT_DLG, IDI_ANNIVERSARY, 1 },
+
+ // button icons
+ { ICO_BTN_UPDATE, "Update", SECT_BUTTONS, IDI_BTN_UPDATE, 0 },
+ { ICO_BTN_IMPORT, "Import", SECT_BUTTONS, IDI_IMPORT, 0 },
+ { ICO_BTN_EXPORT, "Export", SECT_BUTTONS, IDI_EXPORT, 0 },
+ { ICO_BTN_OK, "Ok", SECT_BUTTONS, IDI_BTN_OK, 0 },
+ { ICO_BTN_CANCEL, "Cancel", SECT_BUTTONS, IDI_BTN_CLOSE, 0 },
+ { ICO_BTN_APPLY, "Apply", SECT_BUTTONS, IDI_BTN_APPLY, 0 },
+ { ICO_BTN_GOTO, "Goto", SECT_BUTTONS, IDI_BTN_GOTO, 0 },
+ { ICO_BTN_PHONE, "Phone", SECT_BUTTONS, IDI_BTN_PHONE, 0 },
+ { ICO_BTN_FAX, "Fax", SECT_BUTTONS, IDI_BTN_FAX, 0 },
+ { ICO_BTN_CELLULAR, "Cellular", SECT_BUTTONS, IDI_BTN_CELLULAR, 0 },
+ { ICO_BTN_CUSTOMPHONE, "Custom Phone", SECT_BUTTONS, IDI_BTN_CUSTOMPHONE, 0 },
+ { ICO_BTN_EMAIL, "e-mail", SECT_BUTTONS, IDI_BTN_EMAIL, 0 },
+ { ICO_BTN_DOWNARROW, "Down arrow", SECT_BUTTONS, IDI_BTN_DOWNARROW, 0 },
+ { ICO_BTN_ADD, "Add", SECT_BUTTONS, IDI_BTN_ADD, 0 },
+ { ICO_BTN_EDIT, "Edit", SECT_BUTTONS, IDI_BTN_EDIT, 0 },
+ { ICO_BTN_DELETE, "Delete", SECT_BUTTONS, IDI_BTN_DELETE, 0 },
+ { ICO_BTN_SEARCH, "Search", SECT_BUTTONS, IDI_SEARCH, 0 },
+ { ICO_BTN_EXIMPORT, "Ex-/Import", SECT_BUTTONS, IDI_BTN_EXIMPORT, 0 },
+ { ICO_BTN_BDAY_BACKUP, "Backup Birthday", SECT_BUTTONS, IDI_BTN_BIRTHDAY_BACKUP,0 },
+ //{ ICO_BTN_YES, "Yes", SECT_BUTTONS, IDI_BTN_YES, 0 },
+ //{ ICO_BTN_NO, "No", SECT_BUTTONS, IDI_BTN_NO, 0 },
+ //{ ICO_BTN_IGNORE, "Ignore", SECT_BUTTONS, IDI_BTN_IGNORE, 0 },
+
+ //birthday and anniversary
+ { ICO_RMD_DTB0, "Birthday today", SECT_REMIND, IDI_RMD_DTB0, 0 },
+ { ICO_RMD_DTB1, "Birthday tomorrow", SECT_REMIND, IDI_RMD_DTB1, 0 },
+ { ICO_RMD_DTB2, "Birthday in 2 days", SECT_REMIND, IDI_RMD_DTB2, 0 },
+ { ICO_RMD_DTB3, "Birthday in 3 days", SECT_REMIND, IDI_RMD_DTB3, 0 },
+ { ICO_RMD_DTB4, "Birthday in 4 days", SECT_REMIND, IDI_RMD_DTB4, 0 },
+ { ICO_RMD_DTB5, "Birthday in 5 days", SECT_REMIND, IDI_RMD_DTB5, 0 },
+ { ICO_RMD_DTB6, "Birthday in 6 days", SECT_REMIND, IDI_RMD_DTB6, 0 },
+ { ICO_RMD_DTB7, "Birthday in 7 days", SECT_REMIND, IDI_RMD_DTB7, 0 },
+ { ICO_RMD_DTB8, "Birthday in 8 days", SECT_REMIND, IDI_RMD_DTB8, 0 },
+ { ICO_RMD_DTB9, "Birthday in 9 days", SECT_REMIND, IDI_RMD_DTB9, 0 },
+ { ICO_RMD_DTBX, "Birthday later", SECT_REMIND, IDI_RMD_DTBX, 0 },
+
+ { ICO_RMD_DTA0, "Anniversary today", SECT_REMIND, IDI_RMD_DTA0, 0 },
+ { ICO_RMD_DTA1, "Anniversary tomorrow", SECT_REMIND, IDI_RMD_DTA1, 0 },
+ { ICO_RMD_DTA2, "Anniversary in 2 days", SECT_REMIND, IDI_RMD_DTA2, 0 },
+ { ICO_RMD_DTA3, "Anniversary in 3 days", SECT_REMIND, IDI_RMD_DTA3, 0 },
+ { ICO_RMD_DTA4, "Anniversary in 4 days", SECT_REMIND, IDI_RMD_DTA4, 0 },
+ { ICO_RMD_DTA5, "Anniversary in 5 days", SECT_REMIND, IDI_RMD_DTA5, 0 },
+ { ICO_RMD_DTA6, "Anniversary in 6 days", SECT_REMIND, IDI_RMD_DTA6, 0 },
+ { ICO_RMD_DTA7, "Anniversary in 7 days", SECT_REMIND, IDI_RMD_DTA7, 0 },
+ { ICO_RMD_DTA8, "Anniversary in 8 days", SECT_REMIND, IDI_RMD_DTA8, 0 },
+ { ICO_RMD_DTA9, "Anniversary in 9 days", SECT_REMIND, IDI_RMD_DTA9, 0 },
+ { ICO_RMD_DTAX, "Anniversary later", SECT_REMIND, IDI_RMD_DTAX, 0 },
+};
+
+/**
+ * This function finds the default iconpack file and return its path.
+ *
+ * @param - none
+ *
+ * @return This function returns the relative path to an existing icon pack.
+ **/
+LPTSTR IcoLib_GetDefaultIconFileName()
+{
+ static LPTSTR path[] = {
+ _T("Icons\\uinfoex_icons.dll"),
+ _T("Plugins\\uinfoex_icons.dll"),
+ _T("Customize\\Icons\\uinfoex_icons.dll")
+ };
+ TCHAR absolute[MAX_PATH];
+
+ for (INT i = 0; i < SIZEOF(path); i++)
+ {
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)path[i], (LPARAM)absolute);
+ if (PathFileExists(absolute))
+ {
+ return path[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * This function checks the version of an iconpack.
+ * If the icon pack's version differs from the desired one,
+ * dialog with a warning is displayed.
+ *
+ * @param szIconPack - This is the path to the icon pack.
+ * It can be absolute or relative.
+ *
+ * @return nothing
+ **/
+static VOID IcoLib_CheckIconPackVersion(LPTSTR szIconPack)
+{
+ if (DB::Setting::GetByte(SET_ICONS_CHECKFILEVERSION, TRUE))
+ {
+ if (szIconPack)
+ {
+ TCHAR szAbsolutePath[MAX_PATH];
+ HMODULE hIconDll;
+
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szIconPack, (LPARAM)szAbsolutePath);
+
+ hIconDll = LoadLibrary(szAbsolutePath);
+ if (hIconDll)
+ {
+ CHAR szFileVersion[64];
+
+ if (!LoadStringA(hIconDll, IDS_ICOPACKVERSION, szFileVersion, sizeof(szFileVersion)) ||
+ mir_strcmp(szFileVersion, "__UserInfoEx_IconPack_1.2__"))
+ {
+ MsgErr(NULL, LPGENT("Warning: Your current IconPack's version differs from the one UserInfoEx is designed for.\nSome icons may not be displayed correctly"));
+ }
+ FreeLibrary(hIconDll);
+ }
+ }
+ else
+ {
+ MsgErr(NULL, LPGENT("Warning: No IconPack found in one of the following directories: 'customize\\icons', 'icons' or 'plugins'!"));
+ }
+ }
+}
+
+/**
+ * Returns a icon, identified by a name
+ *
+ * @param pszIcon - name of the icon
+ *
+ * @return: HICON if the icon is loaded, NULL otherwise
+ **/
+HICON IcoLib_GetIcon(LPCSTR pszIcon)
+{
+ return (pszIcon) ? Skin_GetIcon(pszIcon) : NULL;
+}
+
+/**
+ * Returns a icon, identified by a name
+ *
+ * @param hIconItem - this is the pointer to an IconItem structure in icolib.
+ *
+ * @return: HICON if the icon is loaded, NULL otherwise
+ **/
+HICON IcoLib_GetIconByHandle(HANDLE hIconItem)
+{
+ return Skin_GetIconByHandle(hIconItem);
+}
+
+/**
+ * Set the icon of each control in the list
+ *
+ * @param hDlg - handle to the dialog control, that owns the controls
+ * @param pCtrl - list to all controls and its icon names
+ * @param numCtrls - number of elements in the pCtrl list
+ *
+ * @return nothing
+ **/
+VOID IcoLib_SetCtrlIcons(HWND hDlg, const ICONCTRL* pCtrl, BYTE numCtrls)
+{
+ HICON hIcon;
+ BYTE i;
+ HWND hCtrl;
+
+ for (i = 0; i < numCtrls; i++)
+ {
+ hIcon = IcoLib_GetIcon(pCtrl[i].pszIcon);
+ if (pCtrl[i].idCtrl)
+ {
+ hCtrl = GetDlgItem(hDlg, pCtrl[i].idCtrl);
+ switch (pCtrl[i].Message)
+ {
+ case STM_SETICON:
+ case STM_SETIMAGE:
+ {
+ ShowWindow(hCtrl, hIcon ? SW_SHOW : SW_HIDE);
+ }
+ case BM_SETIMAGE:
+ {
+ SendMessage(hCtrl, pCtrl[i].Message, IMAGE_ICON, (LPARAM) hIcon);
+ }
+ }
+ }
+ else
+ {
+ SendMessage(hDlg, pCtrl[i].Message, ICON_BIG, (LPARAM) hIcon);
+ }
+ }
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param szIconID - This is the uniquely identifying string for an icon.
+ * This string is the setting name in the database and should
+ * only use ASCII characters.
+ * @param szDescription - This is the description displayed in the options dialog.
+ * @param szSection - This is the subsection, where the icon is organized in the options dialog.
+ * @param szDefaultFile - This is the validated path to the default icon file.
+ * @param idIcon - This is the ResourceID of the icon in the default file.
+ * @param Size - This is the desired size of the icon to load.
+ * 0: default size for small icons (16x16)
+ * 1: default size for normal icons (32x32)
+ * @param hDefIcon - This is the default icon to use if the default icon
+ * file does not exist and no custom icon is set up in the config.
+ *
+ * @return This function returns the HANDLE of the icon item.
+ **/
+static HANDLE IcoLib_RegisterIconHandleEx(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, LPTSTR szDefaultFile, INT idIcon, INT Size, HICON hDefIcon)
+{
+ HANDLE hIconHandle = NULL;
+
+ if (szIconID && szDescription && szSection) {
+ SKINICONDESC sid = { 0 };
+ sid.cbSize = sizeof(sid);
+ sid.flags = SIDF_ALL_TCHAR;
+ sid.pszName = szIconID;
+ sid.ptszDescription = mir_a2t(szDescription);
+ sid.ptszSection = mir_a2t(szSection);
+
+ if (sid.ptszDescription && sid.ptszSection) {
+ switch (Size) {
+ // small icons (16x16)
+ case 0:
+ sid.cx = GetSystemMetrics(SM_CXSMICON);
+ sid.cy = GetSystemMetrics(SM_CYSMICON);
+ break;
+
+ // normal icons (32x32)
+ case 1:
+ sid.cx = GetSystemMetrics(SM_CXICON);
+ sid.cy = GetSystemMetrics(SM_CYICON);
+ break;
+
+ // custom icon size
+ default:
+ sid.cx = sid.cy = Size;
+ break;
+ }
+
+ sid.ptszDefaultFile = szDefaultFile;
+ if (sid.ptszDefaultFile && sid.ptszDefaultFile[0])
+ sid.iDefaultIndex = -idIcon;
+ else {
+ sid.hDefaultIcon = hDefIcon;
+ sid.iDefaultIndex = -1;
+ }
+ hIconHandle = Skin_AddIcon(&sid);
+ }
+ MIR_FREE(sid.ptszDescription);
+ MIR_FREE(sid.ptszSection);
+ }
+ return hIconHandle;
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param szIconID - This is the uniquely identifying string for an icon.
+ * This string is the setting name in the database and should
+ * only use ASCII characters.
+ * @param szDescription - This is the description displayed in the options dialog.
+ * @param szSection - This is the subsection, where the icon is organized in the options dialog.
+ * @param idIcon - This is the ResourceID of the icon in the default file
+ * @param Size - This is the desired size of the icon to load.
+ * 0: default size for small icons (16x16)
+ * 1: default size for normal icons (32x32)
+ *
+ * @return This function returns the HANDLE of the icon item.
+ **/
+HANDLE IcoLib_RegisterIconHandle(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size)
+{
+ return IcoLib_RegisterIconHandleEx(szIconID, szDescription, szSection, IcoLib_GetDefaultIconFileName(), idIcon, Size, ghDefIcon);
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param szIconID - This is the uniquely identifying string for an icon.
+ * This string is the setting name in the database and should
+ * only use ASCII characters.
+ * @param szDescription - This is the description displayed in the options dialog.
+ * @param szSection - This is the subsection, where the icon is organized in the options dialog.
+ * @param idIcon - This is the ResourceID of the icon in the default file
+ * @param Size - This is the desired size of the icon to load.
+ * 0: default size for small icons (16x16)
+ * 1: default size for normal icons (32x32)
+ *
+ * @return This function returns the HICON of the icon itself.
+ **/
+HICON IcoLib_RegisterIcon(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size)
+{
+ return IcoLib_GetIconByHandle(IcoLib_RegisterIconHandle(szIconID, szDescription, szSection, idIcon, Size));
+}
+
+/**
+ * Add default icons to the skin library or load customized icons
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID IcoLib_LoadModule()
+{
+ LPTSTR szDefaultFile;
+ INT_PTR i;
+
+ // search for default icon file
+ szDefaultFile = IcoLib_GetDefaultIconFileName();
+
+ IcoLib_CheckIconPackVersion(szDefaultFile);
+
+ // load default icon if required
+ ghDefIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON,
+ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
+
+ for (i = 0; i < SIZEOF(icoDesc); i++) {
+ IcoLib_RegisterIconHandleEx(
+ icoDesc[i].pszName, icoDesc[i].pszDesc, icoDesc[i].pszSection,
+ szDefaultFile, icoDesc[i].idResource, icoDesc[i].size, ghDefIcon);
+ }
+}
+
diff --git a/plugins/UserInfoEx/src/mir_icolib.h b/plugins/UserInfoEx/src/mir_icolib.h
new file mode 100644
index 0000000000..d5ac62e9fa
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_icolib.h
@@ -0,0 +1,148 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_icolib.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_ICONS_H_INCLUDED_
+#define _UINFOEX_ICONS_H_INCLUDED_ 1
+
+#include "m_icolib.h"
+
+// sections
+#define SECT_COMMON "UserInfoEx"
+#define SECT_ZODIAC "UserInfoEx/Zodiacs"
+#define SECT_DLG "UserInfoEx/Dialogs"
+#define SECT_BUTTONS "UserInfoEx/Buttons"
+#define SECT_TREE "UserInfoEx/TreeView"
+#define SECT_REMIND "UserInfoEx/Reminder"
+
+// icons
+#define ICO_COMMON_IM MODNAME"_common_im"
+#define ICO_COMMON_FEMALE MODNAME"_common_female"
+#define ICO_COMMON_MALE MODNAME"_common_male"
+#define ICO_COMMON_BIRTHDAY MODNAME"_common_birthday"
+#define ICO_COMMON_ANNIVERSARY MODNAME"_common_anniversary"
+#define ICO_COMMON_CLOCK MODNAME"_common_clock"
+#define ICO_COMMON_MARITAL MODNAME"_common_marital"
+#define ICO_COMMON_PASSWORD MODNAME"_common_password"
+#define ICO_COMMON_ADDRESS MODNAME"_common_address"
+#define ICO_DLG_DETAILS MODNAME"_dlg_details"
+#define ICO_DLG_PHONE MODNAME"_dlg_phone"
+#define ICO_DLG_EMAIL MODNAME"_dlg_email"
+#define ICO_DLG_EXPORT MODNAME"_dlg_export"
+#define ICO_DLG_IMPORT MODNAME"_dlg_import"
+#define ICO_DLG_ANNIVERSARY MODNAME"_dlg_anniversary"
+#define ICO_DLG_SEARCH MODNAME"_dlg_search"
+#define ICO_LST_MODULES MODNAME"_lst_modules"
+#define ICO_LST_FOLDER MODNAME"_lst_folder"
+#define ICO_BTN_UPDATE MODNAME"_btn_update"
+#define ICO_BTN_OK MODNAME"_btn_ok"
+#define ICO_BTN_CANCEL MODNAME"_btn_cancel"
+#define ICO_BTN_APPLY MODNAME"_btn_apply"
+#define ICO_BTN_GOTO MODNAME"_btn_goto"
+#define ICO_BTN_ADD MODNAME"_btn_add"
+#define ICO_BTN_EDIT MODNAME"_btn_edit"
+#define ICO_BTN_DELETE MODNAME"_btn_delete"
+#define ICO_BTN_IMPORT MODNAME"_btn_import"
+#define ICO_BTN_EXPORT MODNAME"_btn_export"
+#define ICO_BTN_NOTES MODNAME"_btn_notes"
+#define ICO_BTN_ABOUT MODNAME"_btn_about"
+#define ICO_BTN_PROFILE MODNAME"_btn_profile"
+#define ICO_BTN_DOWNARROW MODNAME"_btn_downarrow"
+#define ICO_BTN_PHONE MODNAME"_btn_phone"
+#define ICO_BTN_FAX MODNAME"_btn_fax"
+#define ICO_BTN_CELLULAR MODNAME"_btn_cellular"
+#define ICO_BTN_CUSTOMPHONE MODNAME"_btn_customphone"
+#define ICO_BTN_EMAIL MODNAME"_btn_email"
+#define ICO_BTN_SEARCH MODNAME"_btn_search"
+#define ICO_BTN_EXIMPORT MODNAME"_btn_eximport"
+#define ICO_BTN_BDAY_BACKUP MODNAME"_btn_bdaybackup"
+#define ICO_BTN_YES MODNAME"_btn_yes"
+#define ICO_BTN_NO MODNAME"_btn_no"
+#define ICO_BTN_IGNORE MODNAME"_btn_ignore"
+#define ICO_ZOD_AQUARIUS MODNAME"_zod_aquarius"
+#define ICO_ZOD_ARIES MODNAME"_zod_aries"
+#define ICO_ZOD_CANCER MODNAME"_zod_cancer"
+#define ICO_ZOD_CAPRICORN MODNAME"_zod_capricorn"
+#define ICO_ZOD_GEMINI MODNAME"_zod_gemini"
+#define ICO_ZOD_LEO MODNAME"_zod_leo"
+#define ICO_ZOD_LIBRA MODNAME"_zod_libra"
+#define ICO_ZOD_PISCES MODNAME"_zod_pisces"
+#define ICO_ZOD_SAGITTARIUS MODNAME"_zod_sagittarius"
+#define ICO_ZOD_SCORPIO MODNAME"_zod_scorpio"
+#define ICO_ZOD_TAURUS MODNAME"_zod_taurus"
+#define ICO_ZOD_VIRGO MODNAME"_zod_virgo"
+#define ICO_ZOD_UNKNOWN MODNAME"_zod_unknown"
+#define ICO_TREE_DEFAULT MODNAME"_tree_default"
+
+#define ICO_RMD_DTB0 MODNAME"_rmd_dtb0"
+#define ICO_RMD_DTB1 MODNAME"_rmd_dtb1"
+#define ICO_RMD_DTB2 MODNAME"_rmd_dtb2"
+#define ICO_RMD_DTB3 MODNAME"_rmd_dtb3"
+#define ICO_RMD_DTB4 MODNAME"_rmd_dtb4"
+#define ICO_RMD_DTB5 MODNAME"_rmd_dtb5"
+#define ICO_RMD_DTB6 MODNAME"_rmd_dtb6"
+#define ICO_RMD_DTB7 MODNAME"_rmd_dtb7"
+#define ICO_RMD_DTB8 MODNAME"_rmd_dtb8"
+#define ICO_RMD_DTB9 MODNAME"_rmd_dtb9"
+#define ICO_RMD_DTBX MODNAME"_rmd_dtbx"
+
+#define ICO_RMD_DTA0 MODNAME"_rmd_dta0"
+#define ICO_RMD_DTA1 MODNAME"_rmd_dta1"
+#define ICO_RMD_DTA2 MODNAME"_rmd_dta2"
+#define ICO_RMD_DTA3 MODNAME"_rmd_dta3"
+#define ICO_RMD_DTA4 MODNAME"_rmd_dta4"
+#define ICO_RMD_DTA5 MODNAME"_rmd_dta5"
+#define ICO_RMD_DTA6 MODNAME"_rmd_dta6"
+#define ICO_RMD_DTA7 MODNAME"_rmd_dta7"
+#define ICO_RMD_DTA8 MODNAME"_rmd_dta8"
+#define ICO_RMD_DTA9 MODNAME"_rmd_dta9"
+#define ICO_RMD_DTAX MODNAME"_rmd_dtax"
+
+#define SET_ICONS_CHECKFILEVERSION "CheckIconPackVersion"
+#define SET_ICONS_BUTTONS "ButtonIcons"
+
+#define ICONINDEX(id) max((min((id), IDI_LASTICON)) - IDI_FIRST_ICON, 0)
+
+typedef struct TIconCtrl
+{
+ LPCSTR pszIcon;
+ UINT Message;
+ WORD idCtrl;
+} ICONCTRL, *LPICONCTRL;
+
+LPTSTR IcoLib_GetDefaultIconFileName();
+VOID IcoLib_SetCtrlIcons(HWND hDlg, const ICONCTRL* pCtrl, BYTE numCtrls);
+
+HANDLE IcoLib_RegisterIconHandle(LPSTR szName, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size);
+HICON IcoLib_RegisterIcon(LPSTR szName, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size);
+HICON IcoLib_GetIcon(LPCSTR pszIcon);
+HICON IcoLib_GetIconByHandle(HANDLE hIconItem);
+
+VOID IcoLib_LoadModule();
+
+#endif /* _UINFOEX_ICONS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/mir_menuitems.cpp b/plugins/UserInfoEx/src/mir_menuitems.cpp
new file mode 100644
index 0000000000..9f140fb4b7
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_menuitems.cpp
@@ -0,0 +1,651 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_menuitems.cpp $
+Revision : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "svc_Reminder.h"
+
+#include "svc_email.h"
+#include "svc_homepage.h"
+#include ".\ex_import\svc_ExImport.h"
+
+enum ECascadeType {
+ MCAS_DISABLED = 2,
+ MCAS_ALL = 4,
+ MCAS_EXIMPORT = 8,
+ MCAS_NOTINITIATED = 128
+};
+
+INT hMenuItemRefresh = NULL;
+HGENMENU *hMenuItemAccount = NULL;
+
+/**
+ * Helper function to remove all menu items backward (first item second group).
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param pItems - poiter to HGENMENU array
+ * @param Count - number of array elements
+ *
+ * @return 0 on success, -1 on failure
+ **/
+INT_PTR RemoveMenuItems(HGENMENU * pItems, int Count)
+{
+ if (!Count || !pItems) {
+ return -1;
+ }
+ while (Count--) {
+ if (pItems[Count]) {
+ CallService(MO_REMOVEMENUITEM, (WPARAM)pItems[Count], 0);
+ pItems[Count] = NULL;
+ }
+ }
+ return 0;
+}
+
+/**
+ * This function rebuilds the contactmenu. If user selected to cascade menus,
+ * a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID RebuildContact()
+{
+ int flag = 0;
+ BYTE item = 0;
+ CLISTMENUITEM mi;
+
+ HGENMENU mhRoot = HGENMENU_ROOT;
+ HGENMENU mhExIm = HGENMENU_ROOT;
+ static HGENMENU hMenuItem[4] = {NULL, NULL, NULL, NULL };
+
+ SvcEMailRebuildMenu();
+ SvcHomepageRebuildMenu();
+
+ // load options
+ flag = DB::Setting::GetByte(SET_MI_CONTACT, MCAS_NOTINITIATED);
+ if (flag == MCAS_NOTINITIATED){
+ flag = MCAS_EXIMPORT|TRUE;
+ DB::Setting::WriteByte(SET_MI_CONTACT, flag);
+ }
+
+ // delete all MenuItems and set all bytes 0 to avoid problems
+ RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ // support new genmenu style
+ mi.flags = CMIF_ROOTHANDLE;
+ mi.hParentMenu = HGENMENU_ROOT;
+
+ switch (flag)
+ {
+ case 3:
+ //cascade off
+ mhRoot = mhExIm = HGENMENU_ROOT;
+ hMenuItem[item++] = NULL;
+ break;
+ case 5:
+ //cascade all
+ mi.position = 1000050000;
+ mi.popupPosition = 1000050000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.pszName = LPGEN(MODULELONGNAME);
+ mhRoot = Menu_AddContactMenuItem(&mi);
+ hMenuItem[item++] = mhRoot;
+ mhExIm = mhRoot;
+ break;
+ case 9:
+ //cascade Ex/Import
+ mi.position = 1000050100;
+ mi.popupPosition = 1000050100;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+ mi.pszName = LPGEN("Ex-/Import contact");
+ mhExIm = Menu_AddContactMenuItem(&mi);
+ hMenuItem[item++] = mhExIm;
+ mhRoot = HGENMENU_ROOT;
+ break;
+ default:
+ //disable Menue
+ return;
+ }
+ mi.popupPosition = NULL;
+
+ // ContactDetailsPS's menuitem
+ {
+ mi.hParentMenu = mhRoot;
+ mi.pszService = MS_USERINFO_SHOWDIALOG;
+ mi.pszName = LPGEN("User &Details");
+ mi.position = 1000050000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.hotKey = MAKELPARAM(VK_F3, MOD_ALT);
+ hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+ mi.hotKey = NULL;
+ }
+
+ // VCard's Ex/Import menuitems
+ { mi.hParentMenu = mhExIm;
+
+ // Export
+ mi.pszService = MS_USERINFO_VCARD_EXPORT;
+ mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Export") : LPGEN("&Export User Details");
+ mi.position = 1000050200;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+
+ // Import
+ mi.pszService = MS_USERINFO_VCARD_IMPORT;
+ mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Import") : LPGEN("&Import User Details");
+ mi.position = 1000050300;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+ }
+}
+
+/**
+ * This function rebuilds the mainmenu. If user selected to cascade menus,
+ * a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID RebuildMain()
+{
+ int flag = 0;
+ BYTE item = 0;
+ CLISTMENUITEM mi;
+
+ HGENMENU mhRoot = HGENMENU_ROOT;
+ HGENMENU mhExIm = HGENMENU_ROOT;
+ static HGENMENU hMenuItem[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+ // load options
+ flag = DB::Setting::GetByte(SET_MI_MAIN, MCAS_NOTINITIATED);
+ if (flag == MCAS_NOTINITIATED){
+ flag = MCAS_ALL|TRUE;
+ DB::Setting::WriteByte(SET_MI_MAIN, flag);
+ }
+
+ // delete all MenuItems and set all bytes 0 to avoid problems
+ RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ // support new genmenu style
+ mi.flags = CMIF_ROOTHANDLE;
+ mi.hParentMenu = HGENMENU_ROOT;
+
+ switch (flag)
+ {
+ case 3:
+ //cascade off
+ mhRoot = mhExIm = HGENMENU_ROOT;
+ hMenuItem[item++] = NULL;
+ break;
+ case 5:
+ //cascade all
+ mi.position = 500050000;
+ mi.popupPosition = 500050000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.pszName = LPGEN(MODULELONGNAME);
+ mhRoot = Menu_AddMainMenuItem(&mi);
+ hMenuItem[item++] = mhRoot;
+ mhExIm = mhRoot;
+ break;
+ case 9:
+ //cascade Ex/Import
+ mi.position = 500050000;
+ mi.popupPosition = 500050000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+ mi.pszName = LPGEN("Ex-/Import contact");
+ mhExIm = Menu_AddMainMenuItem(&mi);
+ hMenuItem[item++] = mhExIm;
+ mhRoot = HGENMENU_ROOT;
+ break;
+ default:
+ //disable Menue
+ return;
+ }
+ mi.popupPosition = NULL;
+
+ // details dialog
+ {
+ mi.hParentMenu = mhRoot;
+ mi.pszService = MS_USERINFO_SHOWDIALOG;
+ mi.pszName = LPGEN("View/Change My &Details...");
+ mi.position = 500050000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ }
+
+ // VCard's Ex/Import menuitems
+ { mi.hParentMenu = mhExIm;
+
+ // Export
+ mi.pszService = MS_USERINFO_VCARD_EXPORTALL;
+ mi.pszName = LPGEN("Export all contacts");
+ mi.position = 500150000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+
+ // Import
+ mi.pszService = MS_USERINFO_VCARD_IMPORTALL;
+ mi.pszName = LPGEN("Import all contacts");
+ mi.position = 500151000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ }
+
+ mi.hParentMenu = mhRoot;
+
+ // reminder
+ {
+ const BOOLEAN bRemindMenus =
+ DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) &&
+ DB::Setting::GetByte(SET_REMIND_MENUENABLED, DEFVAL_REMIND_MENUENABLED);
+ if (bRemindMenus) {
+ // make backup of each protocol based birthday
+ mi.pszService = MS_USERINFO_REMINDER_AGGRASIVEBACKUP;
+ mi.pszName = LPGEN("Backup birthdays");
+ mi.position = 500253000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_BDAY_BACKUP);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ // Check anniversaries
+ mi.pszService = MS_USERINFO_REMINDER_CHECK;
+ mi.pszName = LPGEN("Check anniversaries");
+ mi.position = 500251000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_SEARCH);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ }
+ else {
+ hMenuItem[item++] = NULL;
+ hMenuItem[item++] = NULL;
+ }
+ // Refresh Contact Details
+ mi.pszService = MS_USERINFO_REFRESH;
+ mi.pszName = LPGEN("Refresh Contact Details");
+ mi.position = 500254000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ }
+
+ // anniversary list
+ {
+ mi.pszService = MS_USERINFO_REMINDER_LIST;
+ mi.pszName = LPGEN("Anniversary list");
+ mi.position = 500252000;
+ mi.hIcon = IcoLib_GetIcon(ICO_COMMON_ANNIVERSARY);
+ hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+ }
+}
+
+/**
+ * This function rebuilds the clist context menu (clist main groupmenu). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID RebuildGroup()
+{
+ int flag = 0;
+ BYTE item = 0;
+ CLISTMENUITEM mi;
+ GroupMenuParam gmp = {0};
+
+ HGENMENU mhRoot = HGENMENU_ROOT;
+ HGENMENU mhExIm = HGENMENU_ROOT;
+ static HGENMENU hMenuItem[3] = {NULL, NULL, NULL };
+
+ // load options
+ flag = DB::Setting::GetByte(SET_MI_GROUP, MCAS_NOTINITIATED);
+ if (flag == MCAS_NOTINITIATED){
+ flag = MCAS_EXIMPORT|TRUE;
+ DB::Setting::WriteByte(SET_MI_GROUP, flag);
+ }
+
+ // delete all MenuItems and set all bytes 0 to avoid problems
+ RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ // create service name main (prevent to generate {(Null)/Ex-/Import Group} in db) and set pointer to end it
+ char text[ 200 ];
+ strcpy( text, "UserInfo");
+ mi.pszService = text;
+ char* tDest = text + strlen( text );
+
+ // support new genmenu style
+ mi.flags = CMIF_ROOTHANDLE;
+ mi.hParentMenu = HGENMENU_ROOT;
+
+ switch (flag)
+ {
+ case 3:
+ //cascade off
+ mhRoot = mhExIm = HGENMENU_ROOT;
+ hMenuItem[item++] = NULL;
+ break;
+ case 5:
+ //cascade all
+ mi.position = 250000;
+ mi.popupPosition = 250000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.pszName = LPGEN(MODULELONGNAME);
+ mhRoot = Menu_AddGroupMenuItem(0, &mi);
+ hMenuItem[item++] = mhRoot;
+ mhExIm = mhRoot;
+ break;
+ case 9:
+ //cascade Ex/Import
+ mi.position = 250100;
+ mi.popupPosition = 250100;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+ mi.pszName = LPGEN("Ex-/Import contact");
+ mhExIm = Menu_AddGroupMenuItem(0, &mi);
+ hMenuItem[item++] = mhExIm;
+ mhRoot = HGENMENU_ROOT;
+ break;
+ default:
+ //disable Menue
+ return;
+ }
+ mi.popupPosition = NULL;
+
+ // VCard's Ex/Import menuitems
+ { mi.hParentMenu = mhExIm;
+
+ // Export
+ mi.pszService = MS_USERINFO_VCARD_EXPORTALL;
+ mi.pszName = LPGEN("Export all contacts");
+ mi.position = 250200;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ hMenuItem[item++] = Menu_AddGroupMenuItem(0, &mi);
+
+ // Import
+ mi.pszService = MS_USERINFO_VCARD_IMPORTALL;
+ mi.pszName = LPGEN("Import all contacts");
+ mi.position = 250300;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ hMenuItem[item++] = Menu_AddGroupMenuItem(0, &mi);
+ }
+}
+
+/******************************
+ * (Sub)GroupMenu
+ ******************************/
+
+/**
+ * This function rebuilds the group context menu (clist main groupmenu). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID RebuildSubGroup()
+{
+ int flag = 0;
+ BYTE item = 0;
+ CLISTMENUITEM mi;
+ GroupMenuParam gmp = {0};
+
+ HGENMENU mhRoot = HGENMENU_ROOT;
+ HGENMENU mhExIm = HGENMENU_ROOT;
+ static HGENMENU hMenuItem[3] = {NULL, NULL, NULL };
+
+ // load options
+ flag = DB::Setting::GetByte(SET_MI_SUBGROUP, MCAS_NOTINITIATED);
+ if (flag == MCAS_NOTINITIATED){
+ flag = MCAS_DISABLED|TRUE;
+ DB::Setting::WriteByte(SET_MI_SUBGROUP, flag);
+ }
+
+ // delete all MenuItems and set all bytes 0 to avoid problems
+ RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ // create service name main (prevent to generate {(Null)/Ex-/Import Group} in db) and set pointer to end it
+ char text[ 200 ];
+ strcpy( text, "UserInfo");
+ mi.pszService = text;
+ char* tDest = text + strlen( text );
+
+ // support new genmenu style
+ mi.flags = CMIF_ROOTHANDLE;
+ mi.hParentMenu = HGENMENU_ROOT;
+
+ switch (flag)
+ {
+ case 3:
+ //cascade off
+ mhRoot = mhExIm = HGENMENU_ROOT;
+ hMenuItem[item++] = NULL;
+ break;
+ case 5:
+ //cascade all
+ mi.position = 1050000;
+ mi.popupPosition = 1050000;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.pszName = LPGEN(MODULELONGNAME);
+ mhRoot = Menu_AddSubGroupMenuItem(0, &mi);
+ hMenuItem[item++] = mhRoot;
+ mhExIm = mhRoot;
+ break;
+ case 9:
+ //cascade Ex/Import
+ mi.position = 1050100;
+ mi.popupPosition = 1050100;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+ mi.pszName = LPGEN("Ex-/Import Group");
+ mhExIm = Menu_AddSubGroupMenuItem(0, &mi);
+ hMenuItem[item++] = mhExIm;
+ mhRoot = HGENMENU_ROOT;
+ break;
+ default:
+ //disable Menue
+ return;
+ }
+ mi.popupPosition = NULL;
+
+ // VCard's Ex/Import menuitems
+ { mi.hParentMenu = mhExIm;
+
+ // Export
+ strcpy( tDest, "/ExportGroup"); //mi.pszService
+ if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Group_Service);
+ mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Export") : LPGEN("&Export Group");
+ mi.position = 1050200;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ gmp.lParam=0;
+ gmp.wParam=TRUE;
+ hMenuItem[item++] = Menu_AddSubGroupMenuItem(&gmp, &mi);
+
+ // Import
+ strcpy( tDest, "/ImportGroup"); //mi.pszService
+ if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Group_Service);
+ mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Import") : LPGEN("&Import Group");
+ mi.position = 1050300;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ gmp.lParam=0;
+ gmp.wParam=FALSE;
+ hMenuItem[item++] = Menu_AddSubGroupMenuItem(&gmp, &mi);
+ }
+}
+
+/******************************
+ * Account Menu
+ ******************************/
+
+/**
+ * This function rebuilds the account context menu (clist status <account>). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param wParam - 0 not used
+ * @param lParam - clear bit for old menu items
+ * 0 don't delete old items (its calld by ME_CLIST_PREBUILDSTATUSMENU hook)
+ * other then 0 delete old items first
+ *
+ * @return always 0
+ **/
+INT_PTR RebuildAccount(WPARAM wParam, LPARAM lParam)
+{
+ const BYTE mItems = 3; // menuitems to create
+ int flag = 0, mProtoCount = 0;
+ BYTE i = 0, item = 0;
+ TCHAR sztName[MAXSETTING];
+ PROTOACCOUNT* pAccountName = NULL;
+ CLISTMENUITEM mi;
+
+ mProtoCount = pcli->menuProtoCount;
+
+ HGENMENU mhRoot = HGENMENU_ROOT;
+ HGENMENU mhExIm = HGENMENU_ROOT;
+
+ // on call by hook or first start
+ if (!lParam || !hMenuItemAccount) {
+ size_t sizeNew = mItems * mProtoCount * sizeof(HGENMENU);
+ hMenuItemAccount = (HGENMENU*) mir_realloc(hMenuItemAccount,sizeNew);
+ // set all bytes 0 to avoid problems
+ memset(hMenuItemAccount, 0, sizeNew);
+ }
+ // on options change
+ else {
+ // delete all MenuItems backward (first item second group)
+ RemoveMenuItems (hMenuItemAccount, mItems * mProtoCount);
+ }
+
+ // load options
+ flag = DB::Setting::GetByte(SET_MI_ACCOUNT, MCAS_NOTINITIATED);
+ if (flag == MCAS_NOTINITIATED){
+ flag = MCAS_EXIMPORT|TRUE;
+ DB::Setting::WriteByte(SET_MI_ACCOUNT, flag);
+ }
+
+
+ // loop for all account names
+ for (i = 0; i < mProtoCount; i++) {
+
+ // set all bytes 0 to avoid problems
+ item = 0;
+ mhRoot = 0;
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ mhRoot = pcli->menuProtos[i].pMenu;
+ if ( mhRoot == NULL )
+ break;
+ pAccountName = ProtoGetAccount(pcli->menuProtos[i].szProto);
+
+ // create service name main (account module name) and set pointer to end it
+ char text[ 200 ];
+ strcpy( text, pcli->menuProtos[i].szProto);
+ mi.pszService = text;
+ char* tDest = text + strlen( text );
+
+ // support new genmenu style
+ mi.flags = CMIF_ROOTHANDLE|CMIF_TCHAR|CMIF_KEEPUNTRANSLATED;
+ mi.hParentMenu = mhRoot;
+
+ switch (flag)
+ {
+ case 3:
+ //cascade off
+ mhExIm = mhRoot;
+ // seperator
+ mi.position = 50100;
+ hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+ break;
+ case 5:
+ //cascade all
+ mi.position = 50100;
+ mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+ mi.ptszName = TranslateT(MODULELONGNAME);
+ hMenuItemAccount[mItems*i + item] = Menu_AddStatusMenuItem(&mi);
+ mhRoot = hMenuItemAccount[mItems*i + item++];
+ mhExIm = mhRoot;
+ break;
+ case 9:
+ //cascade Ex/Import
+ mi.position = 50100;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+ mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("Ex-/Import"));
+ mi.ptszName = sztName;
+ hMenuItemAccount[mItems*i + item] = Menu_AddStatusMenuItem(&mi);
+ mhExIm = hMenuItemAccount[mItems*i + item++];
+ break;
+ default:
+ //disable Menue
+ return 0;
+ }
+
+ // VCard's Ex/Import menuitems
+ {
+ mi.hParentMenu = mhExIm;
+
+ // Export
+ strcpy( tDest, "/ExportAccount"); //mi.pszService
+ if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Account_Service);
+ mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("&Export"));
+ mi.ptszName = sztName;
+ mi.position = 50200;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+ hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+
+ // Import
+ strcpy( tDest, "/ImportAccount"); //mi.pszService
+ if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Account_Service);
+ mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("&Import"));
+ mi.ptszName = sztName;
+ mi.position = 50300;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+ hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+ }
+ }
+ return 0;
+}
+
+
+VOID RebuildMenu()
+{
+ RebuildMain();
+ RebuildContact();
+ RebuildGroup();
+ RebuildSubGroup();
+ RebuildAccount(NULL, 1);
+ return;
+}
diff --git a/plugins/UserInfoEx/src/mir_menuitems.h b/plugins/UserInfoEx/src/mir_menuitems.h
new file mode 100644
index 0000000000..1d749ba592
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_menuitems.h
@@ -0,0 +1,47 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_menuitems.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UINFOEX_MENUITEMS_H_INCLUDED_
+#define _UINFOEX_MENUITEMS_H_INCLUDED_
+
+extern INT hMenuItemRefresh;
+extern HGENMENU *hMenuItemAccount;
+
+
+VOID RebuildMenu();
+
+VOID RebuildMain();
+VOID RebuildContact();
+VOID RebuildGroup();
+VOID RebuildSubGroup();
+INT_PTR RebuildAccount(WPARAM wParam, LPARAM lParam);
+
+
+#endif /* _UINFOEX_MENUITEMS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/mir_string.cpp b/plugins/UserInfoEx/src/mir_string.cpp
new file mode 100644
index 0000000000..7ab895ecc6
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_string.cpp
@@ -0,0 +1,167 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_string.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+char *mir_strncpy(char *pszDest, const char *pszSrc, const size_t cchDest)
+{
+ if (!pszDest || !pszSrc || !cchDest)
+ return NULL;
+ pszDest = strncpy(pszDest, pszSrc, cchDest-1);
+ pszDest[cchDest-1] = 0;
+ return pszDest;
+}
+
+wchar_t *mir_wcsncpy(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest)
+{
+ if (!pszDest || !pszSrc || !cchDest)
+ return NULL;
+ pszDest = wcsncpy(pszDest, pszSrc, cchDest-1);
+ pszDest[cchDest-1] = 0;
+ return pszDest;
+}
+
+char *mir_strncat(char *pszDest, const char *pszSrc, const size_t cchDest)
+{
+ if (!pszDest || !pszSrc || !cchDest)
+ return NULL;
+ strncat(pszDest, pszSrc, cchDest-1);
+ pszDest[cchDest-1] = 0;
+ return pszDest;
+}
+
+wchar_t *mir_wcsncat(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest)
+{
+ if (!pszDest || !pszSrc || !cchDest)
+ return NULL;
+ pszDest = wcsncat(pszDest, pszSrc, cchDest-1);
+ pszDest[cchDest-1] = 0;
+ return pszDest;
+}
+
+char *mir_strncat_c(char *pszDest, const char cSrc)
+{
+ size_t size = sizeof(char) * (strlen(pszDest) + 2); //cSrc = 1 + 1 forNULL temination
+ if (!pszDest)
+ pszDest = (char *) mir_alloc(size);
+ else
+ pszDest = (char *) mir_realloc(pszDest, size);
+ pszDest[size-2] = cSrc;
+ pszDest[size-1] = 0;
+ return pszDest;
+}
+
+wchar_t *mir_wcsncat_c(wchar_t *pwszDest, const wchar_t wcSrc) {
+ size_t size = sizeof(wchar_t) * (wcslen(pwszDest) + 2);
+ if (!pwszDest)
+ pwszDest = (wchar_t *) mir_alloc(size);
+ else
+ pwszDest = (wchar_t *) mir_realloc(pwszDest, size);
+ pwszDest[size-2] = wcSrc;
+ pwszDest[size-1] = 0;
+ return pwszDest;
+}
+
+char *mir_strnerase(char *pszDest, size_t sizeFrom, size_t sizeTo) {
+ char *pszReturn = NULL;
+ size_t sizeNew, sizeLen = strlen(pszDest);
+ if (sizeFrom >= 0 && sizeFrom < sizeLen && sizeTo >= 0 && sizeTo <= sizeLen && sizeFrom < sizeTo) {
+ sizeNew = sizeLen - (sizeTo - sizeFrom);
+ size_t sizeCopy = sizeNew - sizeFrom;
+ pszReturn = (char *) mir_alloc(sizeNew + 1);
+ memcpy(pszReturn, pszDest, sizeFrom);
+ memcpy(pszReturn + sizeFrom, pszDest + sizeTo, sizeCopy);
+ pszReturn[sizeNew] = 0;
+ }
+
+ pszDest = (char *) mir_realloc(pszDest, sizeNew + 1);
+ pszDest = mir_strcpy(pszDest, pszReturn);
+ mir_free(pszReturn);
+ return pszDest;
+}
+
+size_t mir_vsnwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, va_list& argList)
+{
+ size_t iRet, cchMax;
+
+ if (!pszDest || !pszFormat || !*pszFormat)
+ return -1;
+
+ cchMax = cchDest - 1;
+ iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
+ if (iRet < 0) pszDest[0] = 0;
+ else if (iRet >= cchMax) {
+ pszDest[cchMax] = 0;
+ iRet = cchMax;
+ }
+ return iRet;
+}
+
+size_t mir_snwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, ...)
+{
+ size_t iRet;
+ va_list argList;
+
+ va_start(argList, pszFormat);
+ iRet = mir_vsnwprintf(pszDest, cchDest, pszFormat, argList);
+ va_end(argList);
+ return iRet;
+}
+
+int mir_IsEmptyA(char *str)
+{
+ if(str == NULL || str[0] == 0)
+ return 1;
+ int i = 0;
+ while (str[i]) {
+ if (str[i]!=' ' &&
+ str[i]!='\r' &&
+ str[i]!='\n')
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+int mir_IsEmptyW(wchar_t *str)
+{
+ if(str == NULL || str[0] == 0)
+ return 1;
+ int i = 0;
+ while (str[i]) {
+ if (str[i]!=' ' &&
+ str[i]!='\r' &&
+ str[i]!='\n')
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
diff --git a/plugins/UserInfoEx/src/mir_string.h b/plugins/UserInfoEx/src/mir_string.h
new file mode 100644
index 0000000000..6e2dad0425
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_string.h
@@ -0,0 +1,88 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_string.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _MIR_STRING_H_INCLUDED_
+#define _MIR_STRING_H_INCLUDED_
+
+#define mir_wcsdup mir_wstrdup
+
+
+ #define mir_tcslen mir_wcslen
+ #define mir_tcscpy mir_wcscpy
+ #define mir_tcsncpy mir_wcsncpy
+ #define mir_tcsncat mir_wcsncat
+ #define mir_tcsdup mir_wcsdup
+ #define mir_tcscmp mir_wcscmp
+ #define mir_tcsncmp mir_wcsncmp
+ #define mir_tcsicmp mir_wcsicmp
+ #define mir_tcsnicmp mir_wcsnicmp
+ #define mir_tcschr mir_wcschr
+ #define mir_tcsrchr mir_wcsrchr
+ #define mir_tcsncat_c mir_wcsncat_c
+ #define mir_IsEmpty mir_IsEmptyW
+
+
+
+#define mir_strlen(s) (((s)!=0)?strlen(s):0)
+#define mir_strcpy(d,s) (((s)!=0&&(d)!=0)?strcpy(d,s):0)
+#define mir_strcmp(s1,s2) ((s1)==0||(s2)==0||strcmp((s1),(s2)))
+#define mir_strncmp(s1,s2,n) ((s1)==0||(s2)==0||strncmp((s1),(s2),(n)))
+#define mir_stricmp(s1,s2) ((s1)==0||(s2)==0||_stricmp((s1),(s2)))
+#define mir_strnicmp(s1,s2,n) ((s1)==0||(s2)==0||_strnicmp((s1),(s2),(n)))
+#define mir_strchr(s,c) (((s)!=0)?strchr((s),(c)):0)
+#define mir_strrchr(s,c) (((s)!=0)?strrchr((s),(c)):0)
+
+#define mir_wcslen(s) (((s)!=0)?wcslen(s):0)
+#define mir_wcscpy(d,s) (((s)!=0&&(d)!=0)?wcscpy(d,s):0)
+#define mir_wcscmp(s1,s2) ((s1)==0||(s2)==0||wcscmp((s1),(s2)))
+#define mir_wcsncmp(s1,s2,n) ((s1)==0||(s2)==0||wcsncmp((s1),(s2),(n)))
+#define mir_wcsicmp(s1,s2) ((s1)==0||(s2)==0||_wcsicmp((s1),(s2)))
+#define mir_wcsnicmp(s1,s2,n) ((s1)==0||(s2)==0||_wcsnicmp((s1),(s2),(n)))
+#define mir_wcschr(s,c) (((s)!=0)?wcschr((s),(c)):0)
+#define mir_wcsrchr(s,c) (((s)!=0)?wcsrchr((s),(c)):0)
+
+char * mir_strncpy(char *pszDest, const char *pszSrc, const size_t cchDest);
+wchar_t * mir_wcsncpy(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest);
+
+char * mir_strncat(char *pszDest, const char *pszSrc, const size_t cchDest);
+wchar_t * mir_wcsncat(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest);
+
+char * mir_strncat_c(char *pszDest, const char cSrc);
+char * mir_wcsncat_c(char *pszDest, const char cSrc);
+
+size_t mir_vsnwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, va_list& argList);
+size_t mir_snwprintf (wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, ...);
+
+char * mir_strnerase(char *pszDest, size_t sizeFrom, size_t sizeTo);
+
+int mir_IsEmptyA(char *str);
+int mir_IsEmptyW(wchar_t *str);
+
+#endif /* _MIR_STRING_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/psp_about.cpp b/plugins/UserInfoEx/src/psp_about.cpp
new file mode 100644
index 0000000000..0e8362848e
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_about.cpp
@@ -0,0 +1,124 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_about.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the about/nodes information propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcEdit(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, const CHAR* pszSetting)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ if (!mir_stricmp(pszSetting, SET_CONTACT_MYNOTES))
+ SetDlgItemText(hDlg, IDC_PAGETITLE, LPGENT("My Notes:"));
+ else
+ SetDlgItemText(hDlg, IDC_PAGETITLE, LPGENT("About:"));
+
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ABOUT, pszSetting, DBVT_TCHAR));
+
+ // remove static edge in aero mode
+ if (IsAeroMode())
+ SetWindowLongPtr(GetDlgItem(hDlg, EDIT_ABOUT), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hDlg, EDIT_ABOUT), GWL_EXSTYLE)&~WS_EX_STATICEDGE);
+
+ SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_SETEVENTMASK, 0, /*ENM_KEYEVENTS|*/ENM_LINK|ENM_CHANGE);
+ SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_AUTOURLDETECT, TRUE, NULL);
+ if (!lParam) SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_LIMITTEXT, 1024, NULL);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ /**
+ * notification handler for richedit control
+ **/
+ case EDIT_ABOUT:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+
+ /**
+ * notification handler for a link within the richedit control
+ **/
+ case EN_LINK:
+ return CEditCtrl::GetObj(((LPNMHDR)lParam)->hwndFrom)->LinkNotificationHandler((ENLINK*)lParam);
+ }
+ }
+ return FALSE;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_ABOUT:
+ {
+ if (HIWORD(wParam) == EN_CHANGE)
+ {
+ CBaseCtrl *pResult;
+
+ pResult = CBaseCtrl::GetObj((HWND)lParam);
+ if (PtrIsValid(pResult) && (pResult->_cbSize == sizeof(CBaseCtrl)))
+ {
+ pResult->OnChangedByUser(HIWORD(wParam));
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_anniversary.cpp b/plugins/UserInfoEx/src/psp_anniversary.cpp
new file mode 100644
index 0000000000..4872c655db
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_anniversary.cpp
@@ -0,0 +1,347 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_anniversary.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_annivedit.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the anniversary add/edit dialog
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+static INT_PTR CALLBACK DlgProc_AnniversaryEditor(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ MAnnivDate* pDlgEditAnniv = (MAnnivDate*)lParam;
+
+ if (!PtrIsValid(pDlgEditAnniv))
+ break;
+ SetUserData(hDlg, lParam);
+
+ // set icons
+ if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+ {
+ SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+ SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+ }
+ SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_ANNIVERSARY));
+
+ // translate controls
+ SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ TranslateDialogDefault(hDlg);
+
+ // init controls
+ EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), pDlgEditAnniv->Id() != ANID_BIRTHDAY);
+ SetDlgItemText(hDlg, EDIT_CATEGORY, pDlgEditAnniv->Description());
+ return TRUE;
+ }
+
+ case WM_CTLCOLORSTATIC:
+ {
+ SetBkColor((HDC)wParam, RGB(255, 255, 255));
+ }
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_CATEGORY:
+ {
+ if (HIWORD(wParam) == EN_UPDATE)
+ {
+ EnableWindow(GetDlgItem(hDlg, IDOK), GetWindowTextLength((HWND)lParam) > 0);
+ }
+ }
+ break;
+
+ case IDOK:
+ {
+ MAnnivDate* pDlgEditAnniv = (MAnnivDate*)GetUserData(hDlg);
+
+ // read new description
+ {
+ HWND hEdit = GetDlgItem(hDlg, EDIT_CATEGORY);
+ INT len = Edit_GetTextLength(hEdit);
+ LPTSTR pszText;
+
+ if (
+ len == 0 ||
+ (pszText = (LPTSTR)_alloca((len + 1) * sizeof(TCHAR))) == NULL ||
+ !Edit_GetText(hEdit, pszText, len + 1)
+ )
+ {
+ MsgErr(hDlg, LPGENT("Please enter a valid Description first!"));
+ break;
+ }
+
+ if (_tcsicmp(pszText, pDlgEditAnniv->Description())) {
+ pDlgEditAnniv->Description(pszText);
+ pDlgEditAnniv->SetFlags(MAnnivDate::MADF_HASCUSTOM|MAnnivDate::MADF_CHANGED);
+ }
+ }
+ }
+ case IDCANCEL:
+ return EndDialog(hDlg, LOWORD(wParam));
+ }
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Dialog procedure for the anniversary propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcAnniversary(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj (hDlg, EDIT_AGE, SET_CONTACT_AGE, DBVT_BYTE));
+ pCtrlList->insert(CAnnivEditCtrl::CreateObj (hDlg, EDIT_ANNIVERSARY_DATE, NULL));
+
+ // hContact == NULL or reminder disabled
+ CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE)->EnableReminderCtrl(lParam != NULL);
+
+ SendDlgItemMessage(hDlg, EDIT_AGE, EM_LIMITTEXT, 3, 0);
+ SendDlgItemMessage(hDlg, SPIN_AGE, UDM_SETRANGE32, 0, 200);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_ICONCHANGED:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_COMMON_BIRTHDAY, STM_SETIMAGE, ICO_BIRTHDAY },
+ { ICO_BTN_ADD, BM_SETIMAGE, BTN_ADD },
+ { ICO_BTN_DELETE, BM_SETIMAGE, BTN_DELETE }
+ };
+ IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+ }
+ return FALSE;
+ }
+ }
+ break; /* case 0 */
+
+ case EDIT_ANNIVERSARY_DATE:
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (!PspIsLocked(hDlg) && PtrIsValid(pDateCtrl))
+ {
+ LPNMHDR lpNmhdr = (LPNMHDR)lParam;
+
+ switch (lpNmhdr->code)
+ {
+ case DTN_DATETIMECHANGE:
+ {
+ pDateCtrl->OnDateChanged((LPNMDATETIMECHANGE) lParam);
+ }
+ break;
+ case DTN_DROPDOWN:
+ {
+ HWND hMonthCal = DateTime_GetMonthCal(lpNmhdr->hwndFrom);
+ SetWindowLongPtr(hMonthCal, GWL_STYLE, GetWindowLongPtr(hMonthCal, GWL_STYLE)|MCS_WEEKNUMBERS);
+ InvalidateRect(hMonthCal, NULL, TRUE);
+ }
+ }
+ }
+ }
+ return FALSE;
+ } /* switch (((LPNMHDR)lParam)->idFrom) */
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_REMIND:
+ {
+ if (!PspIsLocked(hDlg) && HIWORD(wParam) == EN_UPDATE)
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (PtrIsValid(pDateCtrl))
+ {
+ pDateCtrl->OnRemindEditChanged();
+ }
+ }
+ }
+ return FALSE;
+
+ /**
+ * name: BTN_MENU
+ * desc: the button to dropdown the list to show all items is pressed
+ **/
+ case BTN_MENU:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (PtrIsValid(pDateCtrl))
+ {
+ pDateCtrl->OnMenuPopup();
+ }
+ }
+ }
+ return FALSE;
+
+ /**
+ * name: BTN_ADD
+ * desc: creates a new dialog to add a new anniversary
+ **/
+ case BTN_ADD:
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl))
+ {
+ MAnnivDate Date;
+ if (IDOK == DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_EDITOR), hDlg, (DLGPROC)DlgProc_AnniversaryEditor, (LPARAM)&Date))
+ {
+ SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+ if (!pDateCtrl->AddDate(Date))
+ {
+ pDateCtrl->SetCurSel(pDateCtrl->NumDates() - 1);
+ }
+ }
+ }
+ }
+ return FALSE;
+
+ /**
+ * name: BTN_EDIT
+ * desc: edit the currently selected anniversary
+ **/
+ case BTN_EDIT:
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl))
+ {
+ MAnnivDate *pDate = pDateCtrl->Current();
+
+ if (!pDate)
+ {
+ MsgErr(hDlg, LPGENT("No valid date selected for editing!"));
+ }
+ else if (
+ IDOK == DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_EDITOR), hDlg, DlgProc_AnniversaryEditor, (LPARAM)pDate)&&
+ (pDate->Flags() & (MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED))
+ )
+ {
+ SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+ pDateCtrl->SetCurSel(pDateCtrl->CurrentIndex());
+ }
+ }
+ }
+ return FALSE;
+
+ /**
+ * name: BTN_DELETE
+ * desc: user wants to delete an anniversary
+ **/
+ case BTN_DELETE:
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl))
+ {
+ MAnnivDate *pCurrent;
+
+ pCurrent = pDateCtrl->Current();
+ if (pCurrent)
+ {
+ INT rc = MsgBox(hDlg, MB_YESNO|MB_ICON_QUESTION|MB_NOPOPUP, LPGENT("Delete"), NULL,
+ LPGENT("Do you really want to delete the %s?"), pCurrent->Description());
+
+ if (rc == IDYES)
+ {
+ pDateCtrl->DeleteDate(pDateCtrl->CurrentIndex());
+ }
+ }
+ }
+ }
+ return FALSE;
+
+ /**
+ * name: CHECK_REMIND
+ * desc: state of reminder checkbox is changed
+ **/
+ case RADIO_REMIND1:
+ case RADIO_REMIND2:
+ case RADIO_REMIND3:
+ {
+ CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+ if (PtrIsValid(pDateCtrl) && HIWORD(wParam) == BN_CLICKED)
+ {
+ pDateCtrl->OnReminderChecked();
+ }
+ }
+ return FALSE;
+
+ } /* switch (LOWORD(wParam)) */
+ }
+ break;
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_base.cpp b/plugins/UserInfoEx/src/psp_base.cpp
new file mode 100644
index 0000000000..e015131ad6
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_base.cpp
@@ -0,0 +1,111 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_base.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+VOID UpDate_CountryIcon(HWND hCtrl, int countryID) {
+ HICON hIcon = LoadFlagIcon(countryID);
+ HICON hOld = Static_SetIcon(hCtrl, hIcon);
+ ShowWindow(hCtrl, hIcon ? SW_SHOW : SW_HIDE);
+ Skin_ReleaseIcon(hOld);
+}
+
+/**
+ * Default dialog procedure, which handles common functions
+ **/
+INT_PTR CALLBACK PSPBaseProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CCtrlList *pCtrlList;
+
+ pCtrlList = CCtrlList::GetObj(hDlg);
+ if (PtrIsValid(pCtrlList)) {
+ switch (uMsg) {
+ case WM_INITDIALOG: return TRUE;
+
+ /**
+ * set propertysheet page's background white in aero mode
+ **/
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLORDLG: {
+ if (IsAeroMode())
+ return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+ } break;
+
+ /**
+ * Set text color of edit boxes according to the source of information they display.
+ **/
+ case WM_CTLCOLOREDIT:
+ return pCtrlList->OnSetTextColour((HWND)lParam, (HDC)wParam);
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0: {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ LPSTR pszProto;
+
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_RESET: {
+ pCtrlList->OnReset();
+ } break;
+
+ case PSN_INFOCHANGED: {
+ if (PSGetBaseProto(hDlg, pszProto) && *pszProto) {
+ BOOL bChanged = (GetWindowLongPtr(hDlg, DWLP_MSGRESULT)&PSP_CHANGED)|pCtrlList->OnInfoChanged(hContact, pszProto);
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+ }
+ } break;
+
+ case PSN_APPLY: {
+ if (PSGetBaseProto(hDlg, pszProto) && *pszProto) {
+ pCtrlList->OnApply(hContact, pszProto);
+ }
+ } break;
+ }
+ } break;
+ }
+ } break;
+
+ case WM_COMMAND:
+ {
+ if (!PspIsLocked(hDlg)) {
+ pCtrlList->OnChangedByUser(LOWORD(wParam), HIWORD(wParam));
+ }
+ } break;
+
+ case WM_DESTROY:
+ {
+ // destroy all control objects and the list
+ pCtrlList->Release();
+ }
+ }
+ }
+ return 0;
+}
diff --git a/plugins/UserInfoEx/src/psp_base.h b/plugins/UserInfoEx/src/psp_base.h
new file mode 100644
index 0000000000..f2ddbba23f
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_base.h
@@ -0,0 +1,51 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_base.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_PSP_BASE_INCLUDE_
+#define _UI_PSP_BASE_INCLUDE_
+
+INT_PTR CALLBACK PSPBaseProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+INT_PTR CALLBACK PSPProcAnniversary(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcCompany(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactHome(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactProfile(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactWork(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcGeneral(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcOrigin(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcEdit(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, const CHAR* pszSetting);
+static FORCEINLINE INT_PTR CALLBACK PSPProcMyNotes(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{ return PSPProcEdit(hDlg, uMsg, wParam, lParam, SET_CONTACT_MYNOTES); }
+static FORCEINLINE INT_PTR CALLBACK PSPProcAbout(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{ return PSPProcEdit(hDlg, uMsg, wParam, lParam, SET_CONTACT_ABOUT); }
+
+VOID UpDate_CountryIcon(HWND hCtrl, int countryID);
+
+#endif /* _UI_PSP_BASE_INCLUDE_ */
diff --git a/plugins/UserInfoEx/src/psp_company.cpp b/plugins/UserInfoEx/src/psp_company.cpp
new file mode 100644
index 0000000000..c77028e9b0
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_company.cpp
@@ -0,0 +1,76 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_company.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the company contact information propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcCompany(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ LPIDSTRLIST pList;
+ UINT nList;
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_COMPANY, SET_CONTACT_COMPANY, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_DEPARTMENT, SET_CONTACT_COMPANY_DEPARTMENT, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_OFFICE, SET_CONTACT_COMPANY_OFFICE, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_POSITION, SET_CONTACT_COMPANY_POSITION, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_SUPERIOR, SET_CONTACT_COMPANY_SUPERIOR, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ASSISTENT, SET_CONTACT_COMPANY_ASSISTENT, DBVT_TCHAR));
+
+ GetOccupationList(&nList, &pList);
+ pCtrlList->insert( CCombo::CreateObj(hDlg, EDIT_OCCUPATION, SET_CONTACT_COMPANY_OCCUPATION, DBVT_WORD, pList, nList));
+ }
+ }
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_contact.cpp b/plugins/UserInfoEx/src/psp_contact.cpp
new file mode 100644
index 0000000000..fb38591004
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_contact.cpp
@@ -0,0 +1,347 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_contact.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_contact.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the home contact information propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcContactHome(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ TCHAR szAddr[MAX_PATH];
+ HANDLE hContact = (HANDLE)lParam;
+ LPIDSTRLIST pList;
+ UINT nList;
+
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ mir_sntprintf(szAddr, MAX_PATH, _T("%s (%s)"), TranslateT("Address"), TranslateT("Home"));
+ SetDlgItemText(hDlg, IDC_PAGETITLE, szAddr);
+ SendDlgItemMessage(hDlg, BTN_GOTO, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open in Browser"), MBF_TCHAR);
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET, SET_CONTACT_STREET, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY, SET_CONTACT_CITY, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP, SET_CONTACT_ZIP, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE, SET_CONTACT_STATE, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_HOMEPAGE, SET_CONTACT_HOMEPAGE, DBVT_TCHAR));
+
+ GetCountryList(&nList, &pList);
+ pCtrlList->insert( CCombo::CreateObj(hDlg, EDIT_COUNTRY, SET_CONTACT_COUNTRY, DBVT_WORD, pList, nList));
+
+ break;
+ }
+ }
+ return FALSE;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ LPCSTR pszProto;
+ HWND hCtrl;
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ BYTE bChanged = 0;
+
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ // phone numbers
+ hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_PHONE, TranslateT(SET_CONTACT_PHONE), hContact, USERINFO, pszProto, SET_CONTACT_PHONE);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_FAX, TranslateT(SET_CONTACT_FAX), hContact, USERINFO, pszProto, SET_CONTACT_FAX);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_CELLULAR, TranslateT(SET_CONTACT_CELLULAR), hContact, USERINFO, pszProto, SET_CONTACT_CELLULAR);
+ bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_CUSTOMPHONE, 0, hContact, USERINFO, pszProto, SET_CONTACT_MYPHONE_CAT, SET_CONTACT_MYPHONE_VAL);
+ SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+
+ // emails
+ hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Primary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Secondary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL0);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Tertiary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL1);
+ bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_EMAIL, 0, hContact, USERINFO, pszProto, SET_CONTACT_MYEMAIL_CAT, SET_CONTACT_MYEMAIL_VAL);
+ SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+ }
+ break;
+
+ case PSN_APPLY:
+ {
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_PHONE);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_FAX);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_CELLULAR);
+ CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_MYPHONE_CAT, SET_CONTACT_MYPHONE_VAL);
+ SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+
+ hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL0);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL1);
+ CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_MYEMAIL_CAT, SET_CONTACT_MYEMAIL_VAL);
+ SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+ }
+ break;
+
+ case PSN_ICONCHANGED:
+ {
+ HICON hIcon;
+
+ hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+ SendDlgItemMessage(hDlg, BTN_GOTO, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetDlgItemText(hDlg, BTN_GOTO, hIcon ? _T("") : _T("->"));
+
+ hIcon = IcoLib_GetIcon(ICO_COMMON_ADDRESS);
+ SendDlgItemMessage(hDlg, ICO_ADDRESS, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ ShowWindow(GetDlgItem(hDlg, ICO_ADDRESS), hIcon ? SW_SHOW : SW_HIDE);
+
+ SendDlgItemMessage(hDlg, EDIT_PHONE, WM_SETICON, NULL, NULL);
+ SendDlgItemMessage(hDlg, EDIT_EMAIL, WM_SETICON, NULL, NULL);
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_HOMEPAGE:
+ {
+ if (HIWORD(wParam) == EN_UPDATE)
+ {
+ EnableWindow(GetDlgItem(hDlg, BTN_GOTO), GetWindowTextLength((HWND)lParam) > 0);
+ }
+ }
+ break;
+
+ case BTN_GOTO:
+ {
+ CEditCtrl::GetObj(hDlg, EDIT_HOMEPAGE)->OpenUrl();
+ }
+ break;
+
+ case EDIT_COUNTRY:
+ if(HIWORD(wParam) == CBN_SELCHANGE) {
+ LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+ }
+ break;
+ }
+ }
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+ * Dialog procedure for the company's contact information propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcContactWork(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ TCHAR szAddr[MAX_PATH];
+ HANDLE hContact = (HANDLE)lParam;
+ LPIDSTRLIST pList;
+ UINT nList;
+
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ mir_sntprintf(szAddr, MAX_PATH, _T("%s (%s)"), TranslateT("Address & Contact"), TranslateT("Company"));
+ SetDlgItemText(hDlg, IDC_PAGETITLE, szAddr);
+ SendDlgItemMessage(hDlg, BTN_GOTO, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open in Browser"), MBF_TCHAR);
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET, SET_CONTACT_COMPANY_STREET, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY, SET_CONTACT_COMPANY_CITY, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP, SET_CONTACT_COMPANY_ZIP, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE, SET_CONTACT_COMPANY_STATE, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_HOMEPAGE, SET_CONTACT_COMPANY_HOMEPAGE, DBVT_TCHAR));
+
+ GetCountryList(&nList, &pList);
+ pCtrlList->insert( CCombo::CreateObj(hDlg, EDIT_COUNTRY, SET_CONTACT_COMPANY_COUNTRY, DBVT_WORD, pList, nList));
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ LPCSTR pszProto;
+ HWND hCtrl;
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ BYTE bChanged = 0;
+
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ // phone numbers
+ hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_PHONE, TranslateT(SET_CONTACT_PHONE), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_PHONE);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_FAX, TranslateT(SET_CONTACT_FAX), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_FAX);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_CELLULAR, TranslateT(SET_CONTACT_CELLULAR), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_CELLULAR);
+ bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_CUSTOMPHONE, 0, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYPHONE_CAT, SET_CONTACT_COMPANY_MYPHONE_VAL);
+ SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+
+ // emails
+ hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Primary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Secondary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL0);
+ bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Tertiary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL1);
+ bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_EMAIL, 0, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYEMAIL_CAT, SET_CONTACT_COMPANY_MYEMAIL_VAL);
+ SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+ }
+ break;
+
+ case PSN_APPLY:
+ {
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_PHONE);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_FAX);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_CELLULAR);
+ CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYPHONE_CAT, SET_CONTACT_COMPANY_MYPHONE_VAL);
+ SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+
+ hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL0);
+ CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL1);
+ CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYEMAIL_CAT, SET_CONTACT_COMPANY_MYEMAIL_VAL);
+ SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+ }
+ break;
+
+ case PSN_ICONCHANGED:
+ {
+ HICON hIcon;
+
+ hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+ SendDlgItemMessage(hDlg, BTN_GOTO, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetDlgItemText(hDlg, BTN_GOTO, hIcon ? _T("") : _T("->"));
+
+ hIcon = IcoLib_GetIcon(ICO_COMMON_ADDRESS);
+ SendDlgItemMessage(hDlg, ICO_ADDRESS, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ ShowWindow(GetDlgItem(hDlg, ICO_ADDRESS), hIcon ? SW_SHOW : SW_HIDE);
+
+ SendDlgItemMessage(hDlg, EDIT_PHONE, WM_SETICON, NULL, NULL);
+ SendDlgItemMessage(hDlg, EDIT_EMAIL, WM_SETICON, NULL, NULL);
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_HOMEPAGE:
+ {
+ if (HIWORD(wParam) == EN_UPDATE)
+ {
+ EnableWindow(GetDlgItem(hDlg, BTN_GOTO), GetWindowTextLength((HWND)lParam) > 0);
+ }
+ }
+ break;
+
+ case BTN_GOTO:
+ {
+ CEditCtrl::GetObj(hDlg, EDIT_HOMEPAGE)->OpenUrl();
+ }
+ break;
+
+ case EDIT_COUNTRY:
+ if(HIWORD(wParam) == CBN_SELCHANGE) {
+ LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+ }
+ break;
+ }
+ }
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_general.cpp b/plugins/UserInfoEx/src/psp_general.cpp
new file mode 100644
index 0000000000..9b9a15a58e
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_general.cpp
@@ -0,0 +1,211 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_general.cpp $
+Revision : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "ctrl_tzcombo.h"
+#include "psp_base.h"
+#include "svc_reminder.h"
+
+/**
+ * Dialog procedure for the contact information propertysheetpage
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcGeneral(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ LPIDSTRLIST pList;
+ UINT nList;
+ HFONT hBoldFont;
+
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ TranslateDialogDefault(hDlg);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_TITLE, SET_CONTACT_TITLE, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_FIRSTNAME, SET_CONTACT_FIRSTNAME, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_SECONDNAME, SET_CONTACT_SECONDNAME, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_LASTNAME, SET_CONTACT_LASTNAME, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_NICK, SET_CONTACT_NICK, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_DISPLAYNAME, "CList", SET_CONTACT_MYHANDLE, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_PARTNER, SET_CONTACT_PARTNER, DBVT_TCHAR));
+
+ GetNamePrefixList(&nList, &pList);
+ pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_PREFIX, SET_CONTACT_PREFIX, DBVT_BYTE, pList, nList));
+
+ // marital groupbox
+ GetMaritalList(&nList, &pList);
+ pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_MARITAL, SET_CONTACT_MARITAL, DBVT_BYTE, pList, nList));
+
+ GetLanguageList(&nList, &pList);
+ pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG1, SET_CONTACT_LANG1, DBVT_TCHAR, pList, nList));
+ pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG2, SET_CONTACT_LANG2, DBVT_TCHAR, pList, nList));
+ pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG3, SET_CONTACT_LANG3, DBVT_TCHAR, pList, nList));
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ char* pszProto;
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ BOOLEAN bEnable;
+ DBVARIANT dbv;
+ CCtrlFlags Flags;
+
+ if (PSGetBaseProto(hDlg, pszProto) && *pszProto)
+ {
+ Flags.W = DB::Setting::GetTStringCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv);
+ if (Flags.B.hasCustom || Flags.B.hasProto || Flags.B.hasMeta)
+ {
+ if (dbv.type == DBVT_BYTE)
+ {
+ CheckDlgButton(hDlg, RADIO_FEMALE, (dbv.bVal == 'F'));
+ CheckDlgButton(hDlg, RADIO_MALE, (dbv.bVal == 'M'));
+
+ bEnable = !hContact || Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+ EnableWindow(GetDlgItem(hDlg, RADIO_FEMALE), bEnable);
+ EnableWindow(GetDlgItem(hDlg, RADIO_MALE), bEnable);
+ }
+ else
+ {
+ DB::Variant::Free(&dbv);
+ }
+ }
+ }
+ }
+ break;
+
+ case PSN_APPLY:
+ {
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ // gender
+ {
+ BYTE gender
+ = SendDlgItemMessage(hDlg, RADIO_FEMALE, BM_GETCHECK, NULL, NULL)
+ ? 'F'
+ : SendDlgItemMessage(hDlg, RADIO_MALE, BM_GETCHECK, NULL, NULL)
+ ? 'M'
+ : 0;
+
+ if (gender) DB::Setting::WriteByte(hContact, hContact ? USERINFO : pszProto, SET_CONTACT_GENDER, gender);
+ else DB::Setting::Delete(hContact, hContact ? USERINFO : pszProto, SET_CONTACT_GENDER);
+ }
+ }
+ break;
+
+ case PSN_ICONCHANGED:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_COMMON_FEMALE, STM_SETIMAGE, ICO_FEMALE },
+ { ICO_COMMON_MALE, STM_SETIMAGE, ICO_MALE },
+ { ICO_COMMON_MARITAL, STM_SETIMAGE, ICO_MARITAL },
+ };
+ IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ HANDLE hContact;
+ LPCSTR pszProto;
+
+ switch (LOWORD(wParam))
+ {
+ case RADIO_FEMALE:
+ {
+ if (!PspIsLocked(hDlg) && HIWORD(wParam) == BN_CLICKED)
+ {
+ DBVARIANT dbv;
+
+ PSGetContact(hDlg, hContact);
+ PSGetBaseProto(hDlg, pszProto);
+
+ if (!DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) ||
+ dbv.type != DBVT_BYTE ||
+ (dbv.bVal != 'F' && SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL))
+ )
+ {
+ SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+ }
+ }
+ }
+ break;
+
+ case RADIO_MALE:
+ {
+ if (!PspIsLocked(hDlg) && HIWORD(wParam) == BN_CLICKED)
+ {
+ DBVARIANT dbv;
+
+ PSGetContact(hDlg, hContact);
+ PSGetBaseProto(hDlg, pszProto);
+
+ if (!DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) ||
+ dbv.type != DBVT_BYTE ||
+ (dbv.bVal != 'M' && SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL))
+ )
+ {
+ SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_options.cpp b/plugins/UserInfoEx/src/psp_options.cpp
new file mode 100644
index 0000000000..9399e90f63
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_options.cpp
@@ -0,0 +1,1570 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_options.cpp $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "mir_menuitems.h"
+
+#include "svc_Contactinfo.h"
+#include "svc_Avatar.h"
+#include "svc_Email.h"
+#include "svc_Gender.h"
+#include "svc_Homepage.h"
+#include "svc_Phone.h"
+#include "svc_Refreshci.h"
+#include "svc_Reminder.h"
+#include "svc_Timezone.h"
+#include "svc_Timezone_old.h"
+#include "flags\svc_flags.h"
+#include "psp_options.h"
+
+#define PSM_ENABLE_TABITEM (WM_USER+106)
+
+static MenuOptionsList ctrl_Menu[]=
+{
+ {SET_MI_MAIN, CHECK_OPT_MI_MAIN, RADIO_OPT_MI_MAIN_NONE, RADIO_OPT_MI_MAIN_ALL, RADIO_OPT_MI_MAIN_EXIMPORT},
+ {SET_MI_CONTACT, CHECK_OPT_MI_CONTACT, RADIO_OPT_MI_CONTACT_NONE, RADIO_OPT_MI_CONTACT_ALL, RADIO_OPT_MI_CONTACT_EXIMPORT},
+ {SET_MI_GROUP, CHECK_OPT_MI_GROUP, RADIO_OPT_MI_GROUP_NONE, RADIO_OPT_MI_GROUP_ALL, RADIO_OPT_MI_GROUP_EXIMPORT},
+ {SET_MI_SUBGROUP, CHECK_OPT_MI_SUBGROUP, RADIO_OPT_MI_SUBGROUP_NONE, RADIO_OPT_MI_SUBGROUP_ALL, RADIO_OPT_MI_SUBGROUP_EXIMPORT},
+// {SET_MI_STATUS, CHECK_OPT_MI_STATUS, RADIO_OPT_MI_STATUS_NONE, RADIO_OPT_MI_STATUS_ALL, RADIO_OPT_MI_STATUS_EXIMPORT},
+ {SET_MI_ACCOUNT, CHECK_OPT_MI_ACCOUNT, RADIO_OPT_MI_ACCOUNT_NONE, RADIO_OPT_MI_ACCOUNT_ALL, RADIO_OPT_MI_ACCOUNT_EXIMPORT},
+};
+
+/**
+ *
+ *
+ **/
+static FORCEINLINE VOID NotifyParentOfChange(HWND hDlg)
+{
+ SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+/**
+ * This function clears all CList extra icons from the given column.
+ *
+ * @param ExtraIconColumnType - the clist column to clear
+ *
+ * @return nothing
+ **/
+static VOID ClearAllExtraIcons(INT ExtraIconColumnType)
+{
+ IconExtraColumn iec;
+ HANDLE hContact;
+
+ iec.cbSize = sizeof(IconExtraColumn);
+ iec.hImage = INVALID_HANDLE_VALUE;
+ iec.ColumnType = ExtraIconColumnType;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact))
+ {
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+}
+
+/**
+ * Sends a PSN_INFOCHANGED notify to the handle.
+ *
+ * @param hWnd - the dialog's window handle
+ *
+ * @return nothing
+ **/
+static VOID SendNotify_InfoChanged(HWND hDlg)
+{
+ PSHNOTIFY pshn;
+
+ // extended setting
+ pshn.hdr.idFrom = 0;
+ pshn.hdr.code = PSN_EXPERTCHANGED;
+ pshn.lParam = SendMessage(GetParent(GetParent(hDlg)), PSM_ISEXPERT, NULL, NULL) ? TRUE : FALSE;
+ SendMessage(hDlg, WM_NOTIFY, 0, (LPARAM)&pshn);
+
+ // send info changed message
+ pshn.hdr.code = PSN_INFOCHANGED;
+ SendMessage(hDlg, WM_NOTIFY, NULL, (LPARAM)&pshn);
+}
+
+/**
+ *
+ *
+ **/
+static INT FORCEINLINE ComboBox_FindByItemDataPtr(HWND hCombo, LPARAM pData)
+{
+ INT nItemIndex;
+
+ for (nItemIndex = ComboBox_GetCount(hCombo);
+ (nItemIndex >= 0) && (ComboBox_GetItemData(hCombo, nItemIndex) != pData);
+ nItemIndex--);
+ return nItemIndex;
+}
+
+/**
+ *
+ *
+ **/
+static VOID FORCEINLINE ComboBox_SetCurSelByItemDataPtr(HWND hCombo, LPARAM pData)
+{
+ ComboBox_SetCurSel(hCombo, ComboBox_FindByItemDataPtr(hCombo, pData));
+}
+
+/**
+ *
+ *
+ **/
+static VOID FORCEINLINE ComboBox_AddItemWithData(HWND hCombo, LPTSTR ptszText, LPARAM pData)
+{
+ ComboBox_SetItemData(hCombo, ComboBox_AddString(hCombo, TranslateTS(ptszText)), pData);
+}
+
+/**
+ * This function fills a combobox with the advanced column names for clist extra icons.
+ *
+ * @param hCombo - the combobox's window handle
+ *
+ * @return nothing
+ **/
+static VOID ComboBox_FillExtraIcons(HWND hCombo)
+{
+ if (hCombo)
+ {
+ ComboBox_AddItemWithData(hCombo, LPGENT("<none>"), -1);
+
+ /* check if Clist Nicer */
+ if (ServiceExists("CListFrame/SetSkinnedFrame"))
+ {
+ const struct CComboList {
+ INT nColumn;
+ LPTSTR ptszName;
+ } ExtraIcons[] = {
+ { EXTRA_ICON_ADV1, LPGENT("Advanced #1 (ICQ X-Status)")},
+ { EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+ { EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+ { EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+ { EXTRA_ICON_RES0, LPGENT("Reserved, unused")},
+ { EXTRA_ICON_RES1, LPGENT("Reserved #1")},
+ { EXTRA_ICON_RES2, LPGENT("Reserved #2")},
+ { EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+ };
+
+ for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+ {
+ ComboBox_AddItemWithData(hCombo,
+ TranslateTS(ExtraIcons[i].ptszName),
+ ExtraIcons[i].nColumn );
+ }
+ }
+ /* check if Clist modern*/
+ else if (ServiceExists("CList/HideContactAvatar"))
+ {
+ const struct CComboList {
+ INT nColumn;
+ LPTSTR ptszName;
+ } ExtraIcons[] = {
+ { EXTRA_ICON_ADV1, LPGENT("Advanced #1")},
+ { EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+ { EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+ { EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+ { EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+ { EXTRA_ICON_PROTO, LPGENT("Protocol")},
+ { EXTRA_ICON_VISMODE, LPGENT("Visibility/Chat activity")},
+ };
+
+ for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+ {
+ ComboBox_AddItemWithData(hCombo,
+ TranslateTS(ExtraIcons[i].ptszName),
+ ExtraIcons[i].nColumn );
+ }
+ }
+ /*check if Clist MW*/
+ else if (ServiceExists("CLUI/GetConnectingIconForProtocol"))
+ {
+ const struct CComboList {
+ INT nColumn;
+ LPTSTR ptszName;
+ } ExtraIcons[] = {
+ { EXTRA_ICON_ADV1, LPGENT("Advanced #1")},
+ { EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+ { EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+ { EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+ { EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+ { EXTRA_ICON_PROTO, LPGENT("Protocol Type")},
+ };
+
+ for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+ {
+ ComboBox_AddItemWithData(hCombo,
+ TranslateTS(ExtraIcons[i].ptszName),
+ ExtraIcons[i].nColumn );
+ }
+ }
+ ComboBox_SetCurSel(hCombo, NULL);
+ }
+}
+
+/**
+ * This function enables a dialog item
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param bEnabled - TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @retval TRUE on success
+ * @retval FALSE on failure
+ **/
+static BOOLEAN EnableDlgItem(HWND hDlg, const INT idCtrl, BOOLEAN bEnabled)
+{
+ return EnableWindow(GetDlgItem(hDlg, idCtrl), bEnabled);
+}
+
+/**
+ * This function enables a list of dialog items, if they were enabled in the resource editor.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the array of dialog items' identifiers
+ * @param countCtrl - the number of items in the array of dialog items
+ * @param bEnabled - TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @return bEnabled
+ **/
+static BOOLEAN InitialEnableControls(HWND hDlg, const INT *idCtrl, int countCtrl, BOOLEAN bEnabled)
+{
+ HWND hCtrl;
+
+ while (countCtrl-- > 0)
+ {
+ hCtrl = GetDlgItem(hDlg, idCtrl[countCtrl]);
+ EnableWindow(hCtrl, IsWindowEnabled(hCtrl) && bEnabled);
+ }
+ return bEnabled;
+}
+
+/**
+ * This function enables a list of dialog items.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the array of dialog items' identifiers
+ * @param countCtrl - the number of items in the array of dialog items
+ * @param bEnabled - TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @return bEnabled
+ **/
+static BOOLEAN EnableControls(HWND hDlg, const INT *idCtrl, int countCtrl, BOOLEAN bEnabled)
+{
+ while (countCtrl-- > 0)
+ {
+ EnableDlgItem(hDlg, idCtrl[countCtrl], bEnabled);
+ }
+ return bEnabled;
+}
+
+/**
+ * This function checks an dialog button according to the value, read from the database
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting from the database to use
+ * @param bDefault - the default value to use, if no database setting exists
+ *
+ * @return This function returns the value from database or the default value.
+ **/
+static BOOLEAN DBGetCheckBtn(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BOOLEAN bDefault)
+{
+ BOOLEAN val = (DB::Setting::GetByte(pszSetting, bDefault) & 1) == 1;
+ CheckDlgButton(hDlg, idCtrl, val);
+ return val;
+}
+
+/**
+ * This function writes a byte (flag = 1) to database according to the checkstate
+ * of the dialog button identified by 'idCtrl'.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting to write the button state to
+ *
+ * @return checkstate
+ **/
+static BOOLEAN DBWriteCheckBtn(HWND hDlg, const INT idCtrl, LPCSTR pszSetting)
+{
+ BOOLEAN val = IsDlgButtonChecked(hDlg, idCtrl);
+ int Temp = DB::Setting::GetByte(pszSetting, 0);
+ Temp &= ~1;
+ DB::Setting::WriteByte(pszSetting, Temp |= val );
+ return val;
+}
+
+/**
+ * This function reads a DWORD from database and interprets it as an color value
+ * to set to the color control.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting from the database to use
+ * @param bDefault - the default value to use, if no database setting exists
+ *
+ * @return nothing
+ **/
+static VOID DBGetColor(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, DWORD bDefault)
+{
+ SendDlgItemMessage(hDlg, idCtrl, CPM_SETCOLOUR, 0, DB::Setting::GetDWord(pszSetting, bDefault));
+}
+
+/**
+ * This function writes a DWORD to database according to the value
+ * of the color control identified by 'idCtrl'.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting to write the button state to
+ *
+ * @return nothing
+ **/
+static VOID DBWriteColor(HWND hDlg, const INT idCtrl, LPCSTR pszSetting)
+{
+ DB::Setting::WriteDWord(pszSetting, SendDlgItemMessage(hDlg, idCtrl, CPM_GETCOLOUR, 0, 0));
+}
+
+/**
+ * This function writes a BYTE to database according to the value
+ * read from the edit control identified by 'idCtrl'.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting to write the button state to
+ * @param defVal - this is the default value used by the GetByte() function in order
+ * to check whether updating the value is required or not.
+ *
+ * @retval TRUE - the database value was updated
+ * @retval FALSE - no database update needed
+ **/
+static BOOLEAN DBWriteEditByte(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BYTE defVal)
+{
+ BYTE v;
+ BOOL t;
+
+ v = (BYTE)GetDlgItemInt(hDlg, idCtrl, &t, FALSE);
+ if (t && (v != DB::Setting::GetByte(pszSetting, defVal)))
+ {
+ return DB::Setting::WriteByte(pszSetting, v) == 0;
+ }
+ return FALSE;
+}
+
+/**
+ * This function writes a WORD to database according to the value
+ * read from the edit control identified by 'idCtrl'.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting to write the button state to
+ * @param defVal - this is the default value used by the GetWord() function in order
+ * to check whether updating the value is required or not.
+ *
+ * @retval TRUE - the database value was updated
+ * @retval FALSE - no database update needed
+ **/
+static BOOLEAN DBWriteEditWord(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, WORD defVal)
+{
+ WORD v;
+ BOOL t;
+
+ v = (WORD)GetDlgItemInt(hDlg, idCtrl, &t, FALSE);
+ if (t && (v != DB::Setting::GetWord(pszSetting, defVal)))
+ {
+ return DB::Setting::WriteWord(pszSetting, v) == 0;
+ }
+ return FALSE;
+}
+
+/**
+ * This function writes a BYTE to database according to the currently
+ * selected item of a combobox identified by 'idCtrl'.
+ *
+ * @param hWnd - the dialog's window handle
+ * @param idCtrl - the dialog item's identifier
+ * @param pszSetting - the setting to write the button state to
+ * @param defVal - this is the default value used by the GetByte() function in order
+ * to check whether updating the value is required or not.
+ *
+ * @retval TRUE - the database value was updated
+ * @retval FALSE - no database update needed
+ **/
+static BOOLEAN DBWriteComboByte(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BYTE defVal)
+{
+ BYTE v;
+
+ v = (BYTE)SendDlgItemMessage(hDlg, idCtrl, CB_GETCURSEL, NULL, NULL);
+ if (v != DB::Setting::GetByte(pszSetting, defVal))
+ {
+ return DB::Setting::WriteByte(pszSetting, v) == 0;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_CommonOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOLEAN bInitialized = 0;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ //this controls depend on clist-typ
+ //enable control if myGlobals.ExtraIconsServiceExist else disable
+ const int idExtraIcons[] = {
+ GROUP_OPT_EXTRAICONS,
+ TXT_OPT_GENDER, COMBO_OPT_GENDER,
+ /*TXT_OPT_FLAGS,*/ COMBO_OPT_FLAGS,
+// CHECK_OPT_FLAGSUNKNOWN, CHECK_OPT_FLAGSMSGSTATUS,
+ TXT_OPT_DEFAULTICONS,
+ CHECK_OPT_HOMEPAGEICON, CHECK_OPT_PHONEICON, CHECK_OPT_EMAILICON,
+ CHECK_OPT_ZODIACAVATAR
+ };
+
+ TranslateDialogDefault(hDlg);
+
+#ifdef _DEBUG // new feature, not in release yet
+ ShowWindow(GetDlgItem(hDlg, CHECK_OPT_ZODIACAVATAR),SW_SHOW);
+#else
+ ShowWindow(GetDlgItem(hDlg, CHECK_OPT_ZODIACAVATAR),SW_HIDE);
+#endif
+
+ // init extra icons options
+ ShowWindow(GetDlgItem(hDlg, TXT_OPT_EXTRAICONS),myGlobals.ExtraIconsServiceExist?SW_SHOW:SW_HIDE);
+ ShowWindow(GetDlgItem(hDlg, TXT_OPT_GENDER), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, COMBO_OPT_GENDER), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, CHECK_OPT_GENDER), myGlobals.ExtraIconsServiceExist?SW_SHOW:SW_HIDE);
+ ShowWindow(GetDlgItem(hDlg, TXT_OPT_FLAGS), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, COMBO_OPT_FLAGS), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ if (InitialEnableControls(hDlg, idExtraIcons, SIZEOF(idExtraIcons), myGlobals.HaveCListExtraIcons) && !myGlobals.ExtraIconsServiceExist)
+ {
+ ComboBox_FillExtraIcons(GetDlgItem(hDlg, COMBO_OPT_GENDER));
+ ComboBox_FillExtraIcons(GetDlgItem(hDlg, COMBO_OPT_FLAGS));
+ }
+
+ SendNotify_InfoChanged(hDlg);
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ bInitialized = 0;
+
+ // menu item settings
+ for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+ int flag = DB::Setting::GetByte(ctrl_Menu[i].pszKey, 2);
+ // check button and enable / disable control
+ int idEnable[] = { ctrl_Menu[i].idCheckbox + 1, ctrl_Menu[i].idNONE, ctrl_Menu[i].idALL, ctrl_Menu[i].idEXIMPORT };
+ EnableControls(hDlg, idEnable, SIZEOF(idEnable), DBGetCheckBtn(hDlg, ctrl_Menu[i].idCheckbox, ctrl_Menu[i].pszKey,0));
+ // set radio button state
+ int id = ctrl_Menu[i].idNONE; //default
+ if ((flag & 4) == 4) id = ctrl_Menu[i].idALL;
+ else if ((flag & 8) == 8) id = ctrl_Menu[i].idEXIMPORT;
+ CheckRadioButton(hDlg, ctrl_Menu[i].idNONE, ctrl_Menu[i].idEXIMPORT, id);
+ }
+
+ // extra icon settings
+ if (!myGlobals.ExtraIconsServiceExist)
+ {
+ ComboBox_SetCurSelByItemDataPtr(GetDlgItem(hDlg, COMBO_OPT_GENDER),
+ (LPARAM)DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER));
+ ComboBox_SetCurSelByItemDataPtr(GetDlgItem(hDlg, COMBO_OPT_FLAGS),
+ (LPARAM)DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn", SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT));
+ }
+ else
+ {
+ DBGetCheckBtn(hDlg, CHECK_OPT_GENDER, SET_CLIST_EXTRAICON_GENDER2, 0);
+ }
+ DBGetCheckBtn (hDlg, CHECK_OPT_HOMEPAGEICON, SET_CLIST_EXTRAICON_HOMEPAGE, DEFVAL_CLIST_EXTRAICON_HOMEPAGE);
+ DBGetCheckBtn (hDlg, CHECK_OPT_EMAILICON, SET_CLIST_EXTRAICON_EMAIL, DEFVAL_CLIST_EXTRAICON_EMAIL);
+ DBGetCheckBtn (hDlg, CHECK_OPT_PHONEICON, SET_CLIST_EXTRAICON_PHONE, DEFVAL_CLIST_EXTRAICON_PHONE);
+ CheckDlgButton(hDlg, CHECK_OPT_FLAGSUNKNOWN, gFlagsOpts.bUseUnknownFlag);
+ CheckDlgButton(hDlg, CHECK_OPT_FLAGSMSGSTATUS, gFlagsOpts.bShowStatusIconFlag);
+
+ // misc
+ DBGetCheckBtn(hDlg, CHECK_OPT_ZODIACAVATAR, SET_ZODIAC_AVATARS, FALSE);
+
+ bInitialized = 1;
+ }
+ break;
+
+ case PSN_APPLY:
+ // menu item settings
+ {
+ for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+ int flag = IsDlgButtonChecked(hDlg, ctrl_Menu[i].idCheckbox);
+ flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idNONE)? 2:0;
+ flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idALL)? 4:0;
+ flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idEXIMPORT)? 8:0;
+ DB::Setting::WriteByte(ctrl_Menu[i].pszKey, (BYTE) flag);
+ }
+
+ RebuildMenu();
+ }
+
+ // extra icon settings
+ BOOL FlagsClistChange = 0;
+ BOOL FlagsMsgWndChange = 0;
+ {
+ BYTE valNew;
+ valNew = IsDlgButtonChecked(hDlg, CHECK_OPT_FLAGSUNKNOWN);
+ if (gFlagsOpts.bUseUnknownFlag != valNew) {
+ gFlagsOpts.bUseUnknownFlag = valNew;
+ DB::Setting::WriteByte(MODNAMEFLAGS,"UseUnknownFlag",valNew);
+ FlagsClistChange++;
+ FlagsMsgWndChange++;
+ }
+ valNew = IsDlgButtonChecked(hDlg, CHECK_OPT_FLAGSMSGSTATUS);
+ if (gFlagsOpts.bShowStatusIconFlag != valNew) {
+ gFlagsOpts.bShowStatusIconFlag = valNew;
+ DB::Setting::WriteByte(MODNAMEFLAGS,"ShowStatusIconFlag",valNew);
+ FlagsMsgWndChange++;
+ }
+
+ if (!myGlobals.ExtraIconsServiceExist)
+ {
+ // Enable/Disable extra icon gender modules and write new values to database
+ BYTE bOldColumn = DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER);
+ BYTE bNewColumn = (BYTE)ComboBox_GetItemData(
+ GetDlgItem(hDlg,COMBO_OPT_GENDER),
+ SendDlgItemMessage(hDlg, COMBO_OPT_GENDER, CB_GETCURSEL, NULL, NULL));
+ if (bOldColumn != bNewColumn) {
+ ClearAllExtraIcons(bOldColumn);
+ SvcGenderEnableExtraIcons(bNewColumn, TRUE);
+ }
+
+ // Enable/Disable extra icon Flags and write new values to database
+ bNewColumn = (BYTE)ComboBox_GetItemData(
+ GetDlgItem(hDlg,COMBO_OPT_FLAGS),
+ SendDlgItemMessage(hDlg, COMBO_OPT_FLAGS, CB_GETCURSEL, NULL, NULL));
+ if (gFlagsOpts.idExtraColumn != bNewColumn ||
+ gFlagsOpts.bShowExtraImgFlag!=(bNewColumn!=((BYTE)-1))) {
+ SvcFlagsEnableExtraIcons(bNewColumn, TRUE);
+ FlagsClistChange++;
+ }
+ }
+ else
+ {
+ SvcGenderEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_GENDER)? 1:-1, TRUE);
+ }
+
+ if(FlagsClistChange) EnsureExtraImages();
+ if(FlagsMsgWndChange) UpdateStatusIcons(NULL);
+
+ SvcHomepageEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_HOMEPAGEICON), TRUE);
+ SvcEMailEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_EMAILICON), TRUE);
+ SvcPhoneEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_PHONEICON), TRUE);
+
+ }
+
+ // misc
+ BOOLEAN bEnabled = IsDlgButtonChecked(hDlg, CHECK_OPT_ZODIACAVATAR);
+ DB::Setting::WriteByte(SET_ZODIAC_AVATARS, bEnabled);
+ NServices::NAvatar::Enable(bEnabled);
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case COMBO_OPT_GENDER:
+ case COMBO_OPT_FLAGS:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE && bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+ case CHECK_OPT_MI_MAIN:
+ case CHECK_OPT_MI_CONTACT:
+ case CHECK_OPT_MI_GROUP:
+ case CHECK_OPT_MI_SUBGROUP:
+// case CHECK_OPT_MI_STATUS:
+ case CHECK_OPT_MI_ACCOUNT:
+ {
+ for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+ if (ctrl_Menu[i].idCheckbox == LOWORD(wParam))
+ {
+ const INT idMenuItems[] = { ctrl_Menu[i].idCheckbox + 1, ctrl_Menu[i].idNONE, ctrl_Menu[i].idALL, ctrl_Menu[i].idEXIMPORT };
+ EnableControls(hDlg, idMenuItems, SIZEOF(idMenuItems),
+ Button_GetCheck((HWND)lParam) && ServiceExists(MS_CLIST_REMOVEMAINMENUITEM));
+ break;
+ }
+ }
+ if (bInitialized) NotifyParentOfChange(hDlg);
+ }
+ break;
+ case RADIO_OPT_MI_MAIN_ALL:
+ case RADIO_OPT_MI_MAIN_NONE:
+ case RADIO_OPT_MI_MAIN_EXIMPORT:
+ case RADIO_OPT_MI_CONTACT_ALL:
+ case RADIO_OPT_MI_CONTACT_NONE:
+ case RADIO_OPT_MI_CONTACT_EXIMPORT:
+ case RADIO_OPT_MI_GROUP_ALL:
+ case RADIO_OPT_MI_GROUP_NONE:
+ case RADIO_OPT_MI_GROUP_EXIMPORT:
+ case RADIO_OPT_MI_SUBGROUP_ALL:
+ case RADIO_OPT_MI_SUBGROUP_NONE:
+ case RADIO_OPT_MI_SUBGROUP_EXIMPORT:
+// case RADIO_OPT_MI_STATUS_ALL:
+// case RADIO_OPT_MI_STATUS_NONE:
+// case RADIO_OPT_MI_STATUS_EXIMPORT:
+ case RADIO_OPT_MI_ACCOUNT_ALL:
+ case RADIO_OPT_MI_ACCOUNT_NONE:
+ case RADIO_OPT_MI_ACCOUNT_EXIMPORT:
+ case CHECK_OPT_HOMEPAGEICON:
+ case CHECK_OPT_EMAILICON:
+ case CHECK_OPT_PHONEICON:
+ case CHECK_OPT_GENDER:
+ case CHECK_OPT_FLAGSUNKNOWN:
+ case CHECK_OPT_FLAGSMSGSTATUS:
+ case CHECK_OPT_ZODIACAVATAR:
+ {
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_AdvancedOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOLEAN bInitialized = 0;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hDlg);
+
+ EnableDlgItem(hDlg, CHECK_OPT_METASCAN, myGlobals.szMetaProto != NULL);
+
+ SendNotify_InfoChanged(hDlg);
+ } return TRUE;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_INFOCHANGED:
+ {
+ bInitialized = 0;
+
+ DBGetCheckBtn(hDlg, CHECK_OPT_ICOVERSION, SET_ICONS_CHECKFILEVERSION, TRUE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_BUTTONICONS, SET_ICONS_BUTTONS, TRUE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_METASCAN, SET_META_SCAN, TRUE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_SREMAIL_ENABLED, SET_EXTENDED_EMAILSERVICE, TRUE);
+ if(tmi.getTimeZoneTime) {
+ CheckDlgButton(hDlg, CHECK_OPT_AUTOTIMEZONE, TRUE);
+ EnableWindow(GetDlgItem(hDlg, CHECK_OPT_AUTOTIMEZONE), FALSE);
+ }
+ else {
+ DBGetCheckBtn(hDlg, CHECK_OPT_AUTOTIMEZONE, SET_OPT_AUTOTIMEZONE, TRUE);
+ }
+
+ bInitialized = 1;
+ } break;
+
+ case PSN_APPLY:
+ {
+ DBWriteCheckBtn(hDlg, CHECK_OPT_ICOVERSION, SET_ICONS_CHECKFILEVERSION);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_BUTTONICONS, SET_ICONS_BUTTONS);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_METASCAN, SET_META_SCAN);
+
+ DBWriteCheckBtn(hDlg, CHECK_OPT_SREMAIL_ENABLED, SET_EXTENDED_EMAILSERVICE);
+ if (!tmi.getTimeZoneTime) {
+ DBWriteCheckBtn(hDlg, CHECK_OPT_AUTOTIMEZONE, SET_OPT_AUTOTIMEZONE);
+ if (IsDlgButtonChecked(hDlg, CHECK_OPT_AUTOTIMEZONE)) {
+ SvcTimezoneSyncWithWindows();
+ }
+ }
+ }
+ }
+ } break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case CHECK_OPT_ICOVERSION:
+ case CHECK_OPT_BUTTONICONS:
+ case CHECK_OPT_METASCAN:
+ case CHECK_OPT_SREMAIL_ENABLED:
+ case CHECK_OPT_AUTOTIMEZONE:
+ {
+ if (bInitialized) {
+ NotifyParentOfChange(hDlg);
+ }
+ } break;
+
+ case BTN_OPT_RESET:
+ {
+ BOOLEAN WantReset;
+
+ WantReset = MsgBox(hDlg,
+ MB_ICON_WARNING|MB_YESNO,
+ LPGENT("Question"),
+ LPGENT("Reset factory defaults"),
+ LPGENT("This will delete all settings, you've made!\nAll TreeView settings, window positions and any other settings!\n\nAre you sure to procceed?"));
+
+ if (WantReset) {
+ HANDLE hContact;
+ DB::CEnumList Settings;
+
+ // delete all skin icons
+ if (!Settings.EnumSettings(NULL, "SkinIcons")) {
+ INT i;
+ LPSTR s;
+
+ for (i = 0; i < Settings.getCount(); i++) {
+ s = Settings[i];
+ if (!mir_strnicmp(s, "UserInfoEx", 10)) {
+ DB::Setting::Delete(NULL, "SkinIcons", s);
+ }
+ }
+ }
+ // delete global settings
+ DB::Module::Delete(NULL, USERINFO"Ex");
+ DB::Module::Delete(NULL, USERINFO"ExW");
+
+ // delete old contactsettings
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+ DB::Setting::Delete(hContact, USERINFO, "PListColWidth0");
+ DB::Setting::Delete(hContact, USERINFO, "PListColWidth1");
+ DB::Setting::Delete(hContact, USERINFO, "PListColWidth2");
+ DB::Setting::Delete(hContact, USERINFO, "EMListColWidth0");
+ DB::Setting::Delete(hContact, USERINFO, "EMListColWidth1");
+ DB::Setting::Delete(hContact, USERINFO, "BirthRemind");
+ DB::Setting::Delete(hContact, USERINFO, "RemindBirthday");
+ DB::Setting::Delete(hContact, USERINFO, "RemindDaysErlier");
+ DB::Setting::Delete(hContact, USERINFO, "vCardPath");
+
+ DB::Module::Delete(hContact, USERINFO"Ex");
+ DB::Module::Delete(hContact, USERINFO"ExW");
+ }
+
+ SendMessage(GetParent(hDlg), PSM_FORCECHANGED, NULL, NULL);
+ MsgBox(hDlg, MB_ICON_INFO,
+ LPGENT("Ready"),
+ LPGENT("Everything is done!"),
+ LPGENT("All settings are reset to default values now!"));
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_DetailsDlgOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOLEAN bInitialized = 0;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hDlg);
+ SendNotify_InfoChanged(hDlg);
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ bInitialized = 0;
+
+ // init colors
+ DBGetCheckBtn(hDlg, CHECK_OPT_CLR, SET_PROPSHEET_SHOWCOLOURS, TRUE);
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_CLR, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, CHECK_OPT_CLR));
+ DBGetColor(hDlg, CLR_NORMAL, SET_PROPSHEET_CLRNORMAL, RGB(90, 90, 90));
+ DBGetColor(hDlg, CLR_USER, SET_PROPSHEET_CLRCUSTOM, RGB(0, 10, 130));
+ DBGetColor(hDlg, CLR_BOTH, SET_PROPSHEET_CLRBOTH, RGB(0, 160, 10));
+ DBGetColor(hDlg, CLR_CHANGED, SET_PROPSHEET_CLRCHANGED, RGB(190, 0, 0));
+ DBGetColor(hDlg, CLR_META, SET_PROPSHEET_CLRMETA, RGB(120, 40, 130));
+
+ // treeview options
+ DBGetCheckBtn(hDlg, CHECK_OPT_GROUPS, SET_PROPSHEET_GROUPS, TRUE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_SORTTREE, SET_PROPSHEET_SORTITEMS, FALSE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_AEROADAPTION, SET_PROPSHEET_AEROADAPTION, TRUE);
+
+ // common options
+ DBGetCheckBtn(hDlg, CHECK_OPT_READONLY, SET_PROPSHEET_PCBIREADONLY, FALSE);
+ DBGetCheckBtn(hDlg, CHECK_OPT_CHANGEMYDETAILS, SET_PROPSHEET_CHANGEMYDETAILS, FALSE);
+ Button_Enable(GetDlgItem(hDlg, CHECK_OPT_CHANGEMYDETAILS), myGlobals.CanChangeDetails);
+
+ bInitialized = 1;
+ break;
+ }
+
+ case PSN_APPLY:
+ {
+ DBWriteCheckBtn(hDlg, CHECK_OPT_CLR, SET_PROPSHEET_SHOWCOLOURS);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_GROUPS, SET_PROPSHEET_GROUPS);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_SORTTREE, SET_PROPSHEET_SORTITEMS);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_READONLY, SET_PROPSHEET_PCBIREADONLY);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_AEROADAPTION, SET_PROPSHEET_AEROADAPTION);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_CHANGEMYDETAILS, SET_PROPSHEET_CHANGEMYDETAILS);
+
+ DBWriteColor(hDlg, CLR_NORMAL, SET_PROPSHEET_CLRNORMAL);
+ DBWriteColor(hDlg, CLR_USER, SET_PROPSHEET_CLRCUSTOM);
+ DBWriteColor(hDlg, CLR_BOTH, SET_PROPSHEET_CLRBOTH);
+ DBWriteColor(hDlg, CLR_CHANGED, SET_PROPSHEET_CLRCHANGED);
+ DBWriteColor(hDlg, CLR_META, SET_PROPSHEET_CLRMETA);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case CHECK_OPT_CLR:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bChecked = SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL);
+ const INT idCtrl[] = { CLR_NORMAL, CLR_USER, CLR_BOTH, CLR_CHANGED, CLR_META, TXT_OPT_CLR_NORMAL,
+ TXT_OPT_CLR_USER, TXT_OPT_CLR_BOTH, TXT_OPT_CLR_CHANGED, TXT_OPT_CLR_META };
+
+ EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bChecked);
+ }
+ }
+ case CHECK_OPT_GROUPS:
+ case CHECK_OPT_SORTTREE:
+ case CHECK_OPT_AEROADAPTION:
+ case CHECK_OPT_READONLY:
+ case CHECK_OPT_CHANGEMYDETAILS:
+ case CHECK_OPT_MI_CONTACT:
+ {
+ if (bInitialized && HIWORD(wParam) == BN_CLICKED)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+
+ default:
+ {
+ if (bInitialized && HIWORD(wParam) == CPN_COLOURCHANGED)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+static INT_PTR CALLBACK DlgProc_ReminderOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOLEAN bInitialized = 0;
+
+ switch (uMsg) {
+
+ case WM_INITDIALOG:
+ {
+ HWND hCtrl;
+
+ TranslateDialogDefault(hDlg);
+
+ ShowWindow(GetDlgItem(hDlg, TXT_REMIND5), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, EDIT_EXTRAICON), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, CHECK_REMIND_SECURED), myGlobals.UseDbxTree?SW_HIDE:SW_SHOW);
+
+ SendDlgItemMessage(hDlg, ICO_BIRTHDAY, STM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_DLG_ANNIVERSARY));
+
+ // set colours
+ SendDlgItemMessage(hDlg, EDIT_REMIND, EM_LIMITTEXT, 2, 0);
+ SendDlgItemMessage(hDlg, SPIN_REMIND, UDM_SETRANGE32, 0, 50);
+ SendDlgItemMessage(hDlg, EDIT_REMIND2, EM_LIMITTEXT, 4, 0);
+ SendDlgItemMessage(hDlg, SPIN_REMIND2, UDM_SETRANGE32, 1, 8760);
+ SendDlgItemMessage(hDlg, EDIT_REMIND_SOUNDOFFSET, EM_LIMITTEXT, 2, 0);
+ SendDlgItemMessage(hDlg, SPIN_REMIND_SOUNDOFFSET, UDM_SETRANGE32, 0, 50);
+
+ ComboBox_FillExtraIcons(GetDlgItem(hDlg, EDIT_EXTRAICON));
+
+ if (hCtrl = GetDlgItem(hDlg, EDIT_REMIND_ENABLED))
+ {
+ ComboBox_AddString(hCtrl, TranslateT("Reminder disabled"));
+ ComboBox_AddString(hCtrl, TranslateT("Anniversaries only"));
+ ComboBox_AddString(hCtrl, TranslateT("Bithdays only"));
+ ComboBox_AddString(hCtrl, TranslateT("everything"));
+ }
+ if (hCtrl = GetDlgItem(hDlg, EDIT_BIRTHMODULE))
+ {
+ ComboBox_AddString(hCtrl, TranslateT("mBirthday"));
+ ComboBox_AddString(hCtrl, TranslateT("UserInfo (default)"));
+ }
+ SendNotify_InfoChanged(hDlg);
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ bInitialized = 0;
+ BOOLEAN bEnabled;
+
+ // set reminder options
+ bEnabled = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+ SendDlgItemMessage(hDlg, EDIT_REMIND_ENABLED, CB_SETCURSEL, bEnabled, NULL);
+ DlgProc_ReminderOpts(hDlg, WM_COMMAND, MAKEWPARAM(EDIT_REMIND_ENABLED, CBN_SELCHANGE),
+ (LPARAM)GetDlgItem(hDlg, EDIT_REMIND_ENABLED));
+
+ DBGetCheckBtn(hDlg, CHECK_REMIND_MI, SET_REMIND_MENUENABLED, DEFVAL_REMIND_MENUENABLED);
+ DBGetCheckBtn(hDlg, CHECK_REMIND_FLASHICON, SET_REMIND_FLASHICON, FALSE);
+ DBGetCheckBtn(hDlg, CHECK_REMIND_VISIBLEONLY, SET_REMIND_CHECKVISIBLE, DEFVAL_REMIND_CHECKVISIBLE);
+ DBGetCheckBtn(hDlg, CHECK_REMIND_STARTUP, SET_REMIND_CHECKON_STARTUP, FALSE);
+ DBGetCheckBtn(hDlg, CHECK_REMIND_SECURED, SET_REMIND_SECUREBIRTHDAY, FALSE);
+
+ SetDlgItemInt(hDlg, EDIT_REMIND, DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), FALSE);
+ SetDlgItemInt(hDlg, EDIT_REMIND_SOUNDOFFSET, DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET), FALSE);
+ SetDlgItemInt(hDlg, EDIT_REMIND2, DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL), FALSE);
+ if (!myGlobals.ExtraIconsServiceExist) {
+ for (int i = 0; i < ComboBox_GetCount(GetDlgItem(hDlg, EDIT_EXTRAICON)); i++)
+ {
+ if ((BYTE)ComboBox_GetItemData(GetDlgItem(hDlg,EDIT_EXTRAICON),i) == DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1))
+ {
+ SendDlgItemMessage(hDlg, EDIT_EXTRAICON, CB_SETCURSEL, i, NULL);
+ break;
+ }
+ }
+ }
+ SendDlgItemMessage(hDlg, EDIT_BIRTHMODULE, CB_SETCURSEL, DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE), NULL);
+ {
+ MTime mtLast;
+ TCHAR szTime[MAX_PATH];
+
+ mtLast.DBGetStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+ mtLast.UTCToLocal();
+ mtLast.TimeFormat(szTime, SIZEOF(szTime));
+
+ SetDlgItemText(hDlg, TXT_REMIND_LASTCHECK, szTime);
+ }
+
+ bInitialized = 1;
+ }
+ break;
+
+ case PSN_APPLY:
+ {
+ BYTE bColumn, bNewVal;
+ BOOLEAN bReminderCheck = FALSE;
+
+ // save checkbox options
+ DBWriteCheckBtn(hDlg, CHECK_REMIND_MI, SET_REMIND_MENUENABLED);
+ DBWriteCheckBtn(hDlg, CHECK_REMIND_FLASHICON, SET_REMIND_FLASHICON);
+ DBWriteCheckBtn(hDlg, CHECK_REMIND_VISIBLEONLY, SET_REMIND_CHECKVISIBLE);
+ DBWriteCheckBtn(hDlg, CHECK_REMIND_STARTUP, SET_REMIND_CHECKON_STARTUP);
+ DBWriteCheckBtn(hDlg, CHECK_REMIND_SECURED, SET_REMIND_SECUREBIRTHDAY);
+
+ DBWriteEditByte(hDlg, EDIT_REMIND_SOUNDOFFSET, SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET);
+ DBWriteEditWord(hDlg, EDIT_REMIND2, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL);
+ bReminderCheck = DBWriteEditWord(hDlg, EDIT_REMIND, SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);
+
+ // save primary birthday module
+ BYTE bOld = DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE); // = 1
+ BYTE bNew = (BYTE)ComboBox_GetCurSel(GetDlgItem(hDlg,EDIT_BIRTHMODULE));
+ if (bOld != bNew) {
+ //keep the database clean
+ MAnnivDate mdb;
+ HANDLE hContact = NULL;
+ DBWriteComboByte(hDlg, EDIT_BIRTHMODULE, SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE);
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ mdb.DBMoveBirthDate(hContact,bOld,bNew);
+ }
+ }
+
+ // save new clist extra icon
+ bColumn = DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1);
+ bNewVal = (BYTE)ComboBox_GetItemData(
+ GetDlgItem(hDlg,EDIT_EXTRAICON),
+ SendDlgItemMessage(hDlg, EDIT_EXTRAICON, CB_GETCURSEL, NULL, NULL));
+ if (bColumn != bNewVal)
+ {
+ ClearAllExtraIcons(bColumn);
+ DB::Setting::WriteByte(SET_REMIND_EXTRAICON, bNewVal);
+ bReminderCheck = TRUE;
+ }
+
+ // update current reminder state
+ bNewVal = (BYTE)SendDlgItemMessage(hDlg, EDIT_REMIND_ENABLED, CB_GETCURSEL, NULL, NULL);
+ if (DB::Setting::GetByte(SET_REMIND_ENABLED, 1) != bNewVal)
+ {
+ DB::Setting::WriteByte(SET_REMIND_ENABLED, bNewVal);
+ if (bNewVal == REMIND_OFF)
+ {
+ ClearAllExtraIcons(bColumn);
+ SvcReminderEnable(FALSE);
+ bReminderCheck = FALSE;
+ }
+ else {
+ bReminderCheck = TRUE;
+ }
+ }
+ // update all contact list extra icons
+ if (bReminderCheck)
+ {
+ SvcReminderEnable(TRUE); // reinit reminder options from db
+ SvcReminderCheckAll(NOTIFY_CLIST); // notify
+ }
+ RebuildMain();
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_REMIND_ENABLED:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ INT bEnabled = ComboBox_GetCurSel((HWND)lParam) > 0;
+ const INT idCtrl[] = {
+ CHECK_REMIND_MI, EDIT_REMIND, EDIT_REMIND2, SPIN_REMIND, SPIN_REMIND2, TXT_REMIND,
+ TXT_REMIND2, TXT_REMIND3, TXT_REMIND4, TXT_REMIND6, TXT_REMIND7, TXT_REMIND8, TXT_REMIND9,
+ TXT_REMIND_LASTCHECK, CHECK_REMIND_FLASHICON, EDIT_BIRTHMODULE, CHECK_REMIND_VISIBLEONLY,
+ CHECK_REMIND_SECURED, CHECK_REMIND_STARTUP, EDIT_REMIND_SOUNDOFFSET, SPIN_REMIND_SOUNDOFFSET
+ };
+
+ EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bEnabled);
+ bEnabled &= myGlobals.HaveCListExtraIcons;
+
+ EnableDlgItem(hDlg, TXT_REMIND5, bEnabled);
+ EnableDlgItem(hDlg, EDIT_EXTRAICON, bEnabled);
+ }
+ }
+
+ case EDIT_EXTRAICON:
+ case EDIT_BIRTHMODULE:
+ {
+ if (bInitialized && HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+
+ case CHECK_REMIND_MI:
+ case CHECK_REMIND_FLASHICON:
+ case CHECK_REMIND_VISIBLEONLY:
+ case CHECK_REMIND_STARTUP:
+ case CHECK_REMIND_SECURED:
+ {
+ if (bInitialized && HIWORD(wParam) == BN_CLICKED)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+
+ /*
+ * The user changes the number of days in advance of an anniversary to be notified by popups and clist extra icon.
+ */
+ case EDIT_REMIND:
+ {
+ if (bInitialized && HIWORD(wParam) == EN_UPDATE)
+ {
+ BOOL t;
+ WORD v;
+
+ v = (WORD)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+ if (t && (v != DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET)))
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ break;
+
+ /*
+ * The user changes the number of days in advance of an anniversary to be notified by sound.
+ */
+ case EDIT_REMIND_SOUNDOFFSET:
+ {
+ if (bInitialized && HIWORD(wParam) == EN_UPDATE)
+ {
+ BOOL t;
+ BYTE v;
+
+ v = (BYTE)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+ if (t && (v != DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET)))
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ break;
+
+ /*
+ * The user changes the notification interval
+ */
+ case EDIT_REMIND2:
+ {
+ if (bInitialized && HIWORD(wParam) == EN_UPDATE)
+ {
+ BOOL t;
+ WORD v;
+
+ v = (WORD)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+ if (t && (v != DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL)))
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_Popups(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static BOOLEAN bInitialized = 0;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hDlg);
+ SendNotify_InfoChanged(hDlg);
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ BYTE bDelay, isEnabled;
+
+ bInitialized = 0;
+
+ DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_MSGBOX, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX);
+ DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_PROGRESS, "PopupProgress", FALSE);
+ // disable if popup plugin dos not sopport buttons inside popop
+ if (!myGlobals.PopUpActionsExist) {
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_MSGBOX, FALSE);
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_PROGRESS, FALSE);
+ }
+ else if (!(DB::Setting::GetDWord(0, "PopUp","Actions", 0) & 1)) {
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_MSGBOX, FALSE);
+ }
+
+ // enable/disable popups
+ isEnabled = DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_ENABLED, SET_POPUP_ENABLED, DEFVAL_POPUP_ENABLED);
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_ENABLED, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, CHECK_OPT_POPUP_ENABLED));
+
+ // set colortype checkboxes and color controls
+ DBGetColor(hDlg, CLR_BBACK, SET_POPUP_BIRTHDAY_COLOR_BACK, RGB(192, 180, 30));
+ DBGetColor(hDlg, CLR_BTEXT, SET_POPUP_BIRTHDAY_COLOR_TEXT, RGB(0, 0, 0));
+ switch (DB::Setting::GetByte(SET_POPUP_BIRTHDAY_COLORTYPE, POPUP_COLOR_CUSTOM))
+ {
+ case POPUP_COLOR_DEFAULT:
+ {
+ CheckDlgButton(hDlg, CHECK_OPT_POPUP_DEFCLR, TRUE);
+ }
+ break;
+
+ case POPUP_COLOR_WINDOWS:
+ {
+ CheckDlgButton(hDlg, CHECK_OPT_POPUP_WINCLR, TRUE);
+ }
+ }
+
+ DBGetColor(hDlg, CLR_ABACK, SET_POPUP_ANNIVERSARY_COLOR_BACK, RGB(90, 190, 130));
+ DBGetColor(hDlg, CLR_ATEXT, SET_POPUP_ANNIVERSARY_COLOR_TEXT, RGB(0, 0, 0));
+ switch (DB::Setting::GetByte(SET_POPUP_ANNIVERSARY_COLORTYPE, POPUP_COLOR_CUSTOM))
+ {
+ case POPUP_COLOR_DEFAULT:
+ {
+ CheckDlgButton(hDlg, CHECK_OPT_POPUP_ADEFCLR, TRUE);
+ }
+ break;
+ case POPUP_COLOR_WINDOWS:
+ {
+ CheckDlgButton(hDlg, CHECK_OPT_POPUP_AWINCLR, TRUE);
+ }
+ }
+
+ if (isEnabled)
+ {
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_DEFCLR, BN_CLICKED), NULL);
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_ADEFCLR, BN_CLICKED), NULL);
+ }
+ // set delay values
+ bDelay = DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+ switch (bDelay)
+ {
+ case 0:
+ {
+ CheckDlgButton(hDlg, RADIO_OPT_POPUP_DEFAULT, TRUE);
+ if (isEnabled)
+ {
+ EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+ }
+ }
+ break;
+
+ case 255:
+ {
+ CheckDlgButton(hDlg, RADIO_OPT_POPUP_PERMANENT, TRUE);
+ if (isEnabled)
+ {
+ EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+ }
+ }
+ break;
+
+ default:
+ {
+ CheckDlgButton(hDlg, RADIO_OPT_POPUP_CUSTOM, TRUE);
+ SetDlgItemInt(hDlg, EDIT_DELAY, bDelay, FALSE);
+ }
+ }
+ bInitialized = TRUE;
+ }
+ break;
+
+ case PSN_APPLY:
+ {
+ DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_MSGBOX, SET_POPUPMSGBOX);
+ DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_PROGRESS, "PopupProgress");
+ DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_ENABLED, SET_POPUP_ENABLED);
+
+ // save popup style for birthdays
+ DBWriteColor(hDlg, CLR_BBACK, SET_POPUP_BIRTHDAY_COLOR_BACK);
+ DBWriteColor(hDlg, CLR_BTEXT, SET_POPUP_BIRTHDAY_COLOR_TEXT);
+ DB::Setting::WriteByte(SET_POPUP_BIRTHDAY_COLORTYPE,
+ SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_DEFCLR, BM_GETCHECK, NULL, NULL)
+ ? POPUP_COLOR_DEFAULT
+ : SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_WINCLR, BM_GETCHECK, NULL, NULL)
+ ? POPUP_COLOR_WINDOWS
+ : POPUP_COLOR_CUSTOM);
+
+ // save popup style for anniversaries
+ DBWriteColor(hDlg, CLR_ABACK, SET_POPUP_ANNIVERSARY_COLOR_BACK);
+ DBWriteColor(hDlg, CLR_ATEXT, SET_POPUP_ANNIVERSARY_COLOR_TEXT);
+ DB::Setting::WriteByte(SET_POPUP_ANNIVERSARY_COLORTYPE,
+ SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_ADEFCLR, BM_GETCHECK, NULL, NULL)
+ ? POPUP_COLOR_DEFAULT
+ : SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_AWINCLR, BM_GETCHECK, NULL, NULL)
+ ? POPUP_COLOR_WINDOWS
+ : POPUP_COLOR_CUSTOM);
+
+ // save delay
+ if (SendDlgItemMessage(hDlg, RADIO_OPT_POPUP_PERMANENT, BM_GETCHECK, NULL, NULL))
+ {
+ DB::Setting::WriteByte(SET_POPUP_DELAY, 255);
+ }
+ else if (SendDlgItemMessage(hDlg, RADIO_OPT_POPUP_CUSTOM, BM_GETCHECK, NULL, NULL))
+ {
+ TCHAR szDelay[4];
+ GetDlgItemText(hDlg, EDIT_DELAY, szDelay, SIZEOF(szDelay));
+ DB::Setting::WriteByte(SET_POPUP_DELAY, (BYTE)_tcstol(szDelay, NULL, 10));
+ }
+ else
+ {
+ DB::Setting::Delete(NULL, MODNAME, SET_POPUP_DELAY);
+ }
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case BTN_PREVIEW:
+ {
+ POPUPDATAT_V2 ppd;
+
+ ZeroMemory(&ppd, sizeof(POPUPDATAT_V2));
+ ppd.iSeconds = (INT)DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+ mir_tcsncpy(ppd.lptzText, TranslateT("This is the reminder message"), MAX_SECONDLINE);
+
+ // Birthday
+ mir_tcsncpy(ppd.lptzContactName, TranslateT("Birthday"), SIZEOF(ppd.lptzContactName));
+ ppd.lchIcon = IcoLib_GetIcon(ICO_RMD_DTB0);
+ if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_WINCLR))
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else if (!IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_DEFCLR))
+ {
+ ppd.colorBack = SendDlgItemMessage(hDlg, CLR_BBACK, CPM_GETCOLOUR, 0, 0);
+ ppd.colorText = SendDlgItemMessage(hDlg, CLR_BTEXT, CPM_GETCOLOUR, 0, 0);
+ }
+ PUAddPopUpT(&ppd);
+
+ // Anniversary
+ mir_tcsncpy(ppd.lptzContactName, TranslateT("Anniversary"), SIZEOF(ppd.lptzContactName));
+ ppd.lchIcon = IcoLib_GetIcon(ICO_RMD_DTAX);
+ if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_WINCLR))
+ {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_DEFCLR))
+ {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else
+ {
+ ppd.colorBack = SendDlgItemMessage(hDlg, CLR_ABACK, CPM_GETCOLOUR, 0, 0);
+ ppd.colorText = SendDlgItemMessage(hDlg, CLR_ATEXT, CPM_GETCOLOUR, 0, 0);
+ }
+ PUAddPopUpT(&ppd);
+ }
+ break;
+
+ case CHECK_OPT_POPUP_MSGBOX:
+ case CHECK_OPT_POPUP_PROGRESS:
+ {
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+
+ case CHECK_OPT_POPUP_ENABLED:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ const BOOL bEnabled = SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL);
+ const INT idCtrl[] = {
+ CHECK_OPT_POPUP_DEFCLR, CHECK_OPT_POPUP_WINCLR,
+ CLR_BBACK, TXT_OPT_POPUP_CLR_BACK,
+ CLR_BTEXT, TXT_OPT_POPUP_CLR_TEXT,
+ CHECK_OPT_POPUP_ADEFCLR, CHECK_OPT_POPUP_AWINCLR,
+ CLR_ABACK, TXT_OPT_POPUP_CLR_ABACK,
+ CLR_ATEXT, TXT_OPT_POPUP_CLR_ATEXT,
+ RADIO_OPT_POPUP_DEFAULT, RADIO_OPT_POPUP_CUSTOM,
+ RADIO_OPT_POPUP_PERMANENT, EDIT_DELAY
+ };
+
+ EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bEnabled);
+
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ break;
+
+ case CHECK_OPT_POPUP_DEFCLR:
+ case CHECK_OPT_POPUP_WINCLR:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ INT bDefClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_DEFCLR, BM_GETCHECK, NULL, NULL);
+ INT bWinClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_WINCLR, BM_GETCHECK, NULL, NULL);
+
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_DEFCLR, !bWinClr);
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_WINCLR, !bDefClr);
+ EnableDlgItem(hDlg, CLR_BBACK, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_BACK, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, CLR_BTEXT, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_TEXT, !(bDefClr || bWinClr));
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ break;
+
+ case CHECK_OPT_POPUP_ADEFCLR:
+ case CHECK_OPT_POPUP_AWINCLR:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ INT bDefClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_ADEFCLR, BM_GETCHECK, NULL, NULL);
+ INT bWinClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_AWINCLR, BM_GETCHECK, NULL, NULL);
+
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_ADEFCLR, !bWinClr);
+ EnableDlgItem(hDlg, CHECK_OPT_POPUP_AWINCLR, !bDefClr);
+ EnableDlgItem(hDlg, CLR_ABACK, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_ABACK, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, CLR_ATEXT, !(bDefClr || bWinClr));
+ EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_ATEXT, !(bDefClr || bWinClr));
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ break;
+
+ case RADIO_OPT_POPUP_DEFAULT:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+ case RADIO_OPT_POPUP_CUSTOM:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ EnableDlgItem(hDlg, EDIT_DELAY, TRUE);
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+ case RADIO_OPT_POPUP_PERMANENT:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+ if (bInitialized)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ break;
+ case EDIT_DELAY:
+ if (bInitialized && HIWORD(wParam) == EN_UPDATE)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ break;
+ default:
+ if (bInitialized && HIWORD(wParam) == CPN_COLOURCHANGED)
+ {
+ NotifyParentOfChange(hDlg);
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * This hook handler function is called on opening the options dialog
+ * to tell miranda, which pages userinfoex wants to add.
+ *
+ * @param wParam - options dialog's internal datastructure,
+ * @param lParam - not used
+ *
+ * @retval MIR_OK
+ **/
+static INT OnInitOptions(WPARAM wParam, LPARAM lParam)
+{
+ DlgContactInfoInitTreeIcons();
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.position = 95400;
+ odp.hInstance = ghInst;
+ odp.pszTitle = MODNAME;
+ odp.pszGroup = LPGEN("Plugins");
+ odp.cbSize = sizeof(odp);
+
+ // Common page
+ odp.pszTab = LPGEN("Common");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_COMMON);
+ odp.pfnDlgProc = DlgProc_CommonOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ Options_AddPage(wParam, &odp);
+
+ // Advanced page
+ odp.pszTab = LPGEN("Advanced");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ADVANCED);
+ odp.pfnDlgProc = DlgProc_AdvancedOpts;
+ odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY;
+ Options_AddPage(wParam, &odp);
+
+ // Details Dialog page
+ odp.pszTab = LPGEN("Details Dialog");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_DETAILSDLG);
+ odp.pfnDlgProc = DlgProc_DetailsDlgOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ Options_AddPage(wParam, &odp);
+
+ // Reminder page
+ odp.pszTab = LPGEN("Reminder");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_REMINDER);
+ odp.pfnDlgProc = DlgProc_ReminderOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ Options_AddPage(wParam, &odp);
+
+ // Popups page
+ if (ServiceExists(MS_POPUP_ADDPOPUPT)) {
+ odp.pszTitle = MODNAME;
+ odp.pszGroup = LPGEN("Popups");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_POPUP);
+ odp.pfnDlgProc = DlgProc_Popups;
+ odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY;
+ Options_AddPage(wParam, &odp);
+ }
+ return MIR_OK;
+}
+
+/**
+ * This function loads the options module.
+ *
+ * @param none
+ *
+ * @retval nothing
+ **/
+VOID OptionsLoadModule()
+{
+ HookEvent(ME_OPT_INITIALISE, OnInitOptions);
+}
diff --git a/plugins/UserInfoEx/src/psp_options.h b/plugins/UserInfoEx/src/psp_options.h
new file mode 100644
index 0000000000..fbed92549a
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_options.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_options.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UINFOEX_OPTIONS_H_INCLUDED_
+#define _UINFOEX_OPTIONS_H_INCLUDED_
+
+struct MenuOptionsList {
+ LPCSTR pszKey;
+ const int idCheckbox;
+ const int idNONE;
+ const int idALL;
+ const int idEXIMPORT;
+};
+
+VOID OptionsLoadModule();
+#endif /* _UINFOEX_OPTIONS_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/psp_origin.cpp b/plugins/UserInfoEx/src/psp_origin.cpp
new file mode 100644
index 0000000000..3859a4e7bc
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_origin.cpp
@@ -0,0 +1,176 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_origin.cpp $
+Revision : $Revision: 191 $
+Last change on : $Date: 2010-09-20 13:52:01 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "ctrl_tzcombo.h"
+#include "psp_base.h"
+#include "svc_reminder.h"
+
+/**
+ * This is the dialog procedure for the advanced contact information propertysheetpage.
+ *
+ * @param hDlg - handle to the dialog window
+ * @param uMsg - the message to handle
+ * @param wParam - parameter
+ * @param lParam - parameter
+ *
+ * @return different values
+ **/
+INT_PTR CALLBACK PSPProcOrigin(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+ if (pCtrlList)
+ {
+ LPIDSTRLIST pList;
+ UINT nList;
+
+ HFONT hBoldFont;
+ PSGetBoldFont(hDlg, hBoldFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+ TranslateDialogDefault(hDlg);
+ SetTimer(hDlg, 1, 5000, NULL);
+
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET, SET_CONTACT_ORIGIN_STREET, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP, SET_CONTACT_ORIGIN_ZIP, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY, SET_CONTACT_ORIGIN_CITY, DBVT_TCHAR));
+ pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE, SET_CONTACT_ORIGIN_STATE, DBVT_TCHAR));
+
+ GetCountryList(&nList, &pList);
+ pCtrlList->insert(CCombo::CreateObj (hDlg, EDIT_COUNTRY, SET_CONTACT_ORIGIN_COUNTRY, DBVT_WORD, pList, nList));
+
+ pCtrlList->insert(CTzCombo::CreateObj(hDlg, EDIT_TIMEZONE, NULL));
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR) lParam)->idFrom)
+ {
+ case 0:
+ {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY) lParam)->lParam;
+ LPCSTR pszProto;
+
+ switch (((LPNMHDR) lParam)->code)
+ {
+ case PSN_INFOCHANGED:
+ {
+ BYTE bChanged = 0;
+
+ if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+ if (hContact)
+ {
+ MTime mt;
+
+ if (mt.DBGetStamp(hContact, USERINFO, SET_CONTACT_ADDEDTIME) && strstr(pszProto, "ICQ"))
+ {
+ DWORD dwStamp;
+
+ dwStamp = DB::Contact::WhenAdded(DB::Setting::GetDWord(hContact, pszProto, "UIN", 0), pszProto);
+ if (dwStamp > 0)
+ {
+ mt.FromStampAsUTC(dwStamp);
+ }
+ }
+ if (mt.IsValid())
+ {
+ TCHAR szTime[MAX_PATH];
+ LPTSTR ptr;
+
+ mt.UTCToLocal();
+ mt.DateFormatLong(szTime, SIZEOF(szTime));
+
+ _tcscat(szTime, _T(" - "));
+ ptr = szTime + _tcslen(szTime);
+ mt.TimeFormat(ptr, SIZEOF(szTime) - (ptr - szTime));
+ SetDlgItemText(hDlg, TXT_DATEADDED, szTime);
+ }
+ }
+
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+ }
+ break;
+
+ case PSN_ICONCHANGED:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_COMMON_ADDRESS, STM_SETIMAGE, ICO_ADDRESS },
+ { ICO_COMMON_CLOCK, STM_SETIMAGE, ICO_CLOCK },
+ };
+
+ IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+ }
+ }
+ }
+ } /* switch (((LPNMHDR)lParam)->idFrom) */
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case EDIT_COUNTRY:
+ if(HIWORD(wParam) == CBN_SELCHANGE) {
+ LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_TIMER:
+ {
+ TCHAR szTime[32];
+ CTzCombo::GetObj(hDlg, EDIT_TIMEZONE)->GetTime(szTime, SIZEOF(szTime));
+ SetDlgItemText(hDlg, TXT_TIME, szTime);
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ KillTimer(hDlg, 1);
+ }
+ }
+ return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_profile.cpp b/plugins/UserInfoEx/src/psp_profile.cpp
new file mode 100644
index 0000000000..e964db40f6
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_profile.cpp
@@ -0,0 +1,1464 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_profile.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes
+ **/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+#define LVF_EDITLABEL 8
+
+#define CELIF_CUSTOM 8 // entry is userdefined (e.g. MyPhoneXX)
+#define CELIF_SMS 16 // phone with ability to receive sms
+#define LIF_TIPVISIBLE 32 // set if infotip is visible
+
+typedef struct TCECListItem : CTRL {
+ LPIDSTRLIST idstrList;
+ INT idstrListCount;
+ INT iListItem;
+ LPTSTR pszText[2];
+} LCITEM, *LPLCITEM;
+
+typedef struct TListCtrl : CTRL {
+ HWND hList;
+ HWND hTip;
+ POINT ptTip;
+ INT iHotItem;
+ INT iHotSubItem;
+ HFONT hFont;
+
+ struct {
+ HWND hEdit; // handle to edit window
+ HWND hBtn; // button to open dropdown list
+ RECT rcCombo;
+ struct {
+ HWND hDrop; // dropdown list
+ INT iItem; // currently selected item of the dropdown
+ } dropDown;
+ LPLCITEM pItem; // the item beiing edited
+ INT iItem; // zero based index to item in the listview
+ INT iSubItem; // zero based index to subitem
+ INT iTopIndex; // zero based index to first visible item on list
+ } labelEdit;
+} LISTCTRL, *LPLISTCTRL;
+
+typedef INT (*MISERVICE)(WPARAM wParam, LPARAM lParam);
+
+typedef struct TProfileEntries {
+ LPTSTR szGroup;
+ LPCSTR szCatFmt;
+ LPCSTR szValFmt;
+ MIRANDASERVICE GetList;
+} PROFILEENTRY, *LPPROFILEENTRY;
+
+static const PROFILEENTRY pFmt[3] = {
+ { LPGENT("Past"), "Past%d", "Past%dText", (MIRANDASERVICE)GetPastList },
+ { LPGENT("Affiliation"),"Affiliation%d", "Affiliation%dText", (MIRANDASERVICE)GetAffiliationsList },
+ { LPGENT("Interest"), "Interest%dCat", "Interest%dText", (MIRANDASERVICE)GetInterestsList }
+};
+
+static WNDPROC OldListViewProc = NULL; // listview control's default window procedure
+static WNDPROC OldEditProc = NULL; // edit control's default window procedure
+static WNDPROC OldDropdownProc = NULL; // listbox control's default window procedure
+
+static INT_PTR CALLBACK ProfileList_LabelEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+extern COLORREF clrBoth;
+extern COLORREF clrChanged;
+extern COLORREF clrCustom;
+extern COLORREF clrNormal;
+extern COLORREF clrMeta;
+
+
+/**
+ * name: ProfileList_AddGroup
+ * desc: add a group to the listview
+ * param: hList - handle to listview control
+ * pszText - text of new group
+ * return: index the where group was added
+ **/
+static INT ProfileList_AddGroup(HWND hList, LPTSTR pszText, INT iItem)
+{
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ lvi.pszText = pszText;
+ lvi.lParam = NULL;
+ return ListView_InsertItem(hList, &lvi);
+}
+
+/**
+ * name: ProfileList_GetItemText
+ * desc: returns the text of a listview item
+ * param: hList - handle to listview control
+ * iItem - item index
+ * iSubItem - subitem (column) index
+ * pszText - pointer of a buffer to retrieve the text
+ * ccText - number of maximal characters pszText can take
+ * return: number of read characters
+ **/
+static INT ProfileList_GetItemText(HWND hList, INT iItem, INT iSubItem, LPTSTR pszText, INT ccText)
+{
+ LVITEM lvi;
+ TCHAR szNull[2];
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.cchTextMax = ccText ? ccText : 2;
+ lvi.pszText = pszText ? pszText : szNull;
+ return SNDMSG(hList, LVM_GETITEMTEXT, iItem, (LPARAM)&lvi);
+}
+
+/**
+ * name: ProfileList_GetItemData
+ * desc: return the infostructure associated with the desired item
+ * param: hList - handle to listview control
+ * iItem - item index
+ * return: LPLCITEM structure
+ **/
+static LPLCITEM ProfileList_GetItemData(HWND hList, INT iItem)
+{
+ LVITEM lvi;
+
+ if (iItem < 0) return NULL;
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = iItem;
+ return (SendMessage(hList, LVM_GETITEM, NULL, (LPARAM)&lvi)) ? (LPLCITEM)lvi.lParam : NULL;
+}
+
+/**
+ * name: ProfileList_DeleteItem
+ * desc: delete an item from the listview
+ * param: hList - handle to listview control
+ * iItem - item index
+ *
+ * return: nothing
+ **/
+static VOID ProfileList_DeleteItem(HWND hList, INT iItem)
+{
+ LPLCITEM pItem = ProfileList_GetItemData(hList, iItem);
+
+ if (PtrIsValid(pItem)) {
+ if (pItem->pszText[0])
+ mir_free(pItem->pszText[0]);
+ if (pItem->pszText[1])
+ mir_free(pItem->pszText[1]);
+ mir_free(pItem);
+ }
+ ListView_DeleteItem(hList, iItem);
+}
+
+/**
+ * name: ProfileList_Clear
+ * desc: delete all list items and their data
+ *
+ **/
+static VOID ProfileList_Clear(HWND hList)
+{
+ LVITEM lvi;
+
+ lvi.iItem = 0;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+
+ while (ListView_GetItem(hList, &lvi)) {
+ if (PtrIsValid(lvi.lParam)) {
+ if (((LPLCITEM)lvi.lParam)->pszText[0])
+ mir_free(((LPLCITEM)lvi.lParam)->pszText[0]);
+ if (((LPLCITEM)lvi.lParam)->pszText[1])
+ mir_free(((LPLCITEM)lvi.lParam)->pszText[1]);
+ mir_free((LPVOID)lvi.lParam);
+ }
+ lvi.iItem++;
+ }
+ ListView_DeleteAllItems(hList);
+}
+
+/**
+ * name: ProfileList_EndLabelEdit
+ * desc: destroys the edit control and saves the text to the list control if desired
+ * param: hList - handle to listview control
+ * bSave - tells, whether to save changes (TRUE) or not (FALSE)
+ * return: returns 0 on success or nonzero
+ **/
+static INT ProfileList_EndLabelEdit(LPLISTCTRL pList, BOOLEAN bSave)
+{
+ HWND hEdit;
+
+ // check if labeledit is enabled
+ if (!PtrIsValid(pList) || !pList->hList || !pList->labelEdit.hEdit)
+ return 1;
+ // set hEdit NULL to indicate the endlabeledit call and prevent other calls
+ hEdit = pList->labelEdit.hEdit;
+ pList->labelEdit.hEdit = NULL;
+
+ if (bSave != FALSE && pList->labelEdit.pItem) {
+ WORD ccText;
+ LPTSTR szEdit = NULL;
+ BOOLEAN bChanged = FALSE;
+
+ // an list element was selected
+ if (pList->labelEdit.iSubItem && pList->labelEdit.dropDown.iItem != pList->labelEdit.pItem->iListItem && pList->labelEdit.dropDown.iItem >= 0 && pList->labelEdit.dropDown.iItem < pList->labelEdit.pItem->idstrListCount) {
+ pList->labelEdit.pItem->iListItem = pList->labelEdit.dropDown.iItem;
+ if (pList->labelEdit.pItem->pszText[0]) {
+ mir_free(pList->labelEdit.pItem->pszText[0]);
+ pList->labelEdit.pItem->pszText[0] = NULL;
+ }
+ bChanged = TRUE;
+ }
+ // value was edited
+ else {
+ if ((ccText = GetWindowTextLength(hEdit)) > 0 && (szEdit = (LPTSTR)mir_alloc((ccText + 2) * sizeof(TCHAR)))) {
+ GetWindowText(hEdit, szEdit, ccText + 1);
+ szEdit[ccText + 1] = 0;
+
+ if (!pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem]) {
+ pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem] = szEdit;
+ bChanged = TRUE;
+ }
+ else
+ if (_tcscmp(pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem], szEdit)) {
+ mir_free(pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem]);
+ pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem] = szEdit;
+ bChanged = TRUE;
+ }
+ else
+ mir_free(szEdit);
+ }
+ }
+ if (bChanged) {
+ pList->labelEdit.pItem->wFlags |= CTRLF_CHANGED;
+ pList->wFlags |= CTRLF_CHANGED;
+ SendMessage(GetParent(GetParent(pList->hList)), PSM_CHANGED, 0, 0);
+ }
+ }
+ if (pList->labelEdit.hBtn) DestroyWindow(pList->labelEdit.hBtn);
+ if (pList->labelEdit.dropDown.hDrop) DestroyWindow(pList->labelEdit.dropDown.hDrop);
+ DestroyWindow(hEdit);
+ ListView_RedrawItems(pList->hList, pList->labelEdit.iItem, pList->labelEdit.iItem);
+ ZeroMemory(&pList->labelEdit, sizeof(pList->labelEdit));
+ SetFocus(pList->hList);
+ return 0;
+}
+
+static INT ProfileList_EndLabelEdit(HWND hList, BOOLEAN bSave)
+{
+ return ProfileList_EndLabelEdit((LPLISTCTRL)GetUserData(hList), bSave);
+}
+
+/**
+ * name: ProfileList_BeginLabelEdit
+ * desc: create an edit control to edit the label of the selected item
+ * param: pList - handle to listview control's info structure
+ * iItem - item index
+ * iSubItem - subitem (column) index
+ * return: handle to the edit control
+ **/
+static HWND ProfileList_BeginLabelEdit(LPLISTCTRL pList, INT iItem, INT iSubItem)
+{
+ LVITEM lvi;
+ LPLCITEM pItem;
+ HANDLE hContact;
+ RECT rcList;
+
+ if (!PtrIsValid(pList))
+ return NULL;
+ if (pList->labelEdit.hEdit)
+ ProfileList_EndLabelEdit(pList, FALSE);
+
+ lvi.mask = LVIF_PARAM|LVIF_STATE;
+ lvi.stateMask = 0xFFFFFFFF;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+
+ if (!ListView_GetItem(pList->hList, &lvi))
+ return NULL;
+
+ pItem = (LPLCITEM)lvi.lParam;
+
+ PSGetContact(GetParent(pList->hList), hContact);
+
+ // do not edit deviders or protocol based contact information
+ if (!(lvi.state & LVIS_SELECTED) || !PtrIsValid(pItem) || (hContact && (pItem->wFlags & CTRLF_HASPROTO)))
+ return NULL;
+
+ ListView_EnsureVisible(pList->hList, iItem, FALSE);
+ ListView_GetSubItemRect(pList->hList, iItem, iSubItem, LVIR_BOUNDS, &pList->labelEdit.rcCombo);
+
+ if (lvi.iSubItem == 0) {
+ RECT rc2;
+
+ ListView_GetSubItemRect(pList->hList, iItem, 1, LVIR_BOUNDS, &rc2);
+ pList->labelEdit.rcCombo.right = rc2.left;
+ }
+ GetClientRect(pList->hList, &rcList);
+ pList->labelEdit.rcCombo.right = min(pList->labelEdit.rcCombo.right, rcList.right);
+ pList->labelEdit.rcCombo.left = max(pList->labelEdit.rcCombo.left, rcList.left);
+ InflateRect(&pList->labelEdit.rcCombo, -1, -1);
+
+ // create the button control for the combobox
+ if (!iSubItem && pItem->idstrList) {
+ pList->labelEdit.hBtn = CreateWindowEx(WS_EX_NOPARENTNOTIFY, UINFOBUTTONCLASS, NULL,
+ WS_VISIBLE|WS_CHILD|MBS_DOWNARROW,
+ pList->labelEdit.rcCombo.right - (pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top), pList->labelEdit.rcCombo.top,
+ pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+ pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+ pList->hList, NULL, ghInst, NULL);
+ if (pList->labelEdit.hBtn) {
+ SetWindowLongPtr(pList->labelEdit.hBtn, GWLP_ID, BTN_EDIT);
+ pList->labelEdit.rcCombo.right -= pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top;
+ }
+ }
+ else {
+ pList->labelEdit.rcCombo.bottom = 3 * pList->labelEdit.rcCombo.bottom - 2 * pList->labelEdit.rcCombo.top;
+ if (rcList.bottom < pList->labelEdit.rcCombo.bottom) {
+ OffsetRect(&pList->labelEdit.rcCombo, 0, rcList.bottom - pList->labelEdit.rcCombo.bottom - 2);
+ }
+ }
+ // create the edit control
+ pList->labelEdit.hEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY|WS_EX_CLIENTEDGE,
+ _T("EDIT"),
+ (!iSubItem && pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+ ? pItem->idstrList[pItem->iListItem].ptszTranslated
+ : (iSubItem >= 0 && iSubItem < 2 && pItem->pszText[iSubItem] && *pItem->pszText[iSubItem])
+ ? pItem->pszText[iSubItem]
+ : _T(""),
+ WS_VISIBLE|WS_CHILD|(iSubItem ? (WS_VSCROLL|ES_MULTILINE|ES_AUTOVSCROLL) : ES_AUTOHSCROLL),
+ pList->labelEdit.rcCombo.left, pList->labelEdit.rcCombo.top,
+ pList->labelEdit.rcCombo.right - pList->labelEdit.rcCombo.left,
+ pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+ pList->hList, NULL, ghInst, NULL);
+ if (!pList->labelEdit.hEdit)
+ return NULL;
+ SendMessage(pList->labelEdit.hEdit, WM_SETFONT, (WPARAM)(pList->hFont), 0);
+ SendMessage(pList->labelEdit.hEdit, EM_SETSEL, 0, (LPARAM)-1);
+ SetUserData(pList->labelEdit.hEdit, pList);
+ pList->labelEdit.dropDown.iItem = pItem->iListItem;
+ pList->labelEdit.iItem = iItem;
+ pList->labelEdit.iSubItem = iSubItem;
+ pList->labelEdit.iTopIndex = ListView_GetTopIndex(pList->hList);
+ pList->labelEdit.pItem = pItem;
+ SetFocus(pList->labelEdit.hEdit);
+ OldEditProc = (WNDPROC)SetWindowLongPtr(pList->labelEdit.hEdit, GWLP_WNDPROC, (LONG_PTR)ProfileList_LabelEditProc);
+ return pList->labelEdit.hEdit;
+}
+
+/**
+ * name: ProfileList_BeginLabelEdit
+ * desc: create an edit control to edit the label of the selected item
+ * param: hList - handle to listview control
+ * iItem - item index
+ * iSubItem - subitem (column) index
+ * return: handle to the edit control
+ **/
+static HWND ProfileList_BeginLabelEdit(HWND hList, INT iItem, INT iSubItem)
+{
+ return ProfileList_BeginLabelEdit((LPLISTCTRL)GetUserData(hList), iItem, iSubItem);
+}
+
+/**
+ * name: ProfileList_GetInsertIndex
+ * desc: finds index to add the new item to and adds an devider if necessary
+ * param: hList - handle to listcontrol to search for the index in
+ * pszList - database settings string, that identifies this category
+ *
+ * return: zero based index for the new item or -1 on failure
+ **/
+static INT ProfileList_GetInsertIndex(HWND hList, LPTSTR pszGroup)
+{
+ LVFINDINFO lfi;
+ INT iDevider,
+ iItem;
+
+ // search for the devider to add the new item under
+ lfi.flags = LVFI_STRING;
+ lfi.psz = pszGroup;
+ iDevider = SendMessage(hList, LVM_FINDITEM, (WPARAM)-1, (LPARAM)&lfi);
+
+ // devider does not exist yet, add it!
+ if (iDevider == -1) {
+ LVITEM lvi;
+
+ lvi.mask = LVIF_PARAM|LVIF_TEXT;
+ lvi.iSubItem = 0;
+ lvi.iItem = 0xFFFF;
+ lvi.lParam = NULL;
+ lvi.pszText = (LPTSTR)lfi.psz;
+ if ((iItem = ListView_InsertItem(hList, &lvi)) == -1) {
+ return -1;
+ }
+ iItem++;
+ }
+ else {
+ // search next devider to add new item just before
+ lfi.flags = LVFI_PARAM;
+ lfi.lParam = NULL;
+ if ((iItem = ListView_FindItem(hList, iDevider, &lfi)) == -1)
+ iItem = 0xFFFF;
+ }
+ return iItem;
+}
+
+/**
+ * name: ProfileList_AddNewItem
+ * desc: Ask's user for a type and adds new item to the list view
+ * param: pList - pointer to the listview's data structure
+ * pszList - database settings string, that identifies this category
+ *
+ * return: TRUE or FALSE
+ **/
+static BOOLEAN ProfileList_AddNewItem(HWND hDlg, LPLISTCTRL pList, const PROFILEENTRY *pEntry)
+{
+ LPLCITEM pItem;
+ LVITEM lvi;
+ HANDLE hContact;
+
+ if (PtrIsValid(pList) && (pItem = (LPLCITEM)mir_alloc(sizeof(LCITEM)))) {
+ PSGetContact(hDlg, hContact);
+ pItem->nType = CTRL_LIST_ITEM;
+ pItem->wFlags = hContact ? CTRLF_HASCUSTOM : 0;
+ pItem->iListItem = 0;
+ pItem->pszText[0] = NULL;
+ pItem->pszText[1] = NULL;
+ // get category list
+ pEntry->GetList((WPARAM)&pItem->idstrListCount, (LPARAM)&pItem->idstrList);
+
+ lvi.mask = LVIF_PARAM|LVIF_STATE;
+ lvi.stateMask = 0xFFFFFFFF;
+ lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+ lvi.iItem = ProfileList_GetInsertIndex(pList->hList, pEntry->szGroup);
+ lvi.iSubItem = 0;
+ lvi.lParam = (LPARAM)pItem;
+ if ((lvi.iItem = ListView_InsertItem(pList->hList, &lvi)) >= 0) {
+ ProfileList_BeginLabelEdit(pList, lvi.iItem, 0);
+ return TRUE;
+ }
+ mir_free(pItem);
+ MsgErr(hDlg, LPGENT("Sorry, but there is a problem with adding a new item of type \"%s\""), pEntry->szGroup);
+ }
+ return FALSE;
+}
+
+/**
+ * name: ProfileList_AddItemlistFromDB
+ * desc: reads an zero based indexed szList from the database of hContacts pszModule and fills a hList
+ * param: hList - HANDLE to the list to fill with two columns
+ * iItem - index of new listviewitem
+ * iImage - image to draw from a imagelist associated with the listview
+ * hContact - handle to the contact, whose information are read
+ * pszModule - the module the information are stored in
+ * szCatFormat - name of a database setting that holds the categorytext
+ * szValFormat - name of a database setting that holds the valuetext
+ * wFlags - flags to set for a new allocated item
+ *
+ * return number of added rows or -1 if listview's changed flag is set
+ **/
+static INT ProfileList_AddItemlistFromDB(
+ LPLISTCTRL pList,
+ INT &iItem,
+ LPIDSTRLIST idList,
+ UINT nList,
+ HANDLE hContact,
+ LPCSTR pszModule,
+ LPCSTR szCatFormat,
+ LPCSTR szValFormat,
+ WORD wFlags)
+{
+ DBVARIANT dbvVal, dbvCat;
+ LPLCITEM pItem;
+ LVITEM lvi;
+ UINT i, j = 0;
+ CHAR pszSetting[MAXSETTING];
+
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+
+ for (i = 0, lvi.iItem = iItem; ; i++) {
+ // read the setting from db
+ mir_snprintf(pszSetting, MAXSETTING, szValFormat, i);
+ if (DB::Setting::GetTString(hContact, pszModule, pszSetting, &dbvVal)) break;
+ if (dbvVal.type != DBVT_TCHAR) continue;
+ mir_snprintf(pszSetting, MAXSETTING, szCatFormat, i);
+ DB::Setting::GetAString(hContact, pszModule, pszSetting, &dbvCat);
+ // create the itemobject
+ if (!(pItem = (LPLCITEM)mir_alloc(sizeof(LCITEM)))) {
+ DB::Variant::Free(&dbvCat);
+ DB::Variant::Free(&dbvVal);
+ break;
+ }
+ // fill item struct
+ pItem->nType = CTRL_LIST_ITEM;
+ pItem->idstrList = idList;
+ pItem->idstrListCount = nList;
+ pItem->iListItem = 0;
+ pItem->pszText[0] = NULL;
+ pItem->pszText[1] = dbvVal.ptszVal;
+ pItem->wFlags = wFlags;
+ lvi.lParam = (LPARAM)pItem;
+
+ // get id-str-list-item for the category string
+ if (idList != NULL) {
+ for (j = 0; j < nList; j++) {
+ switch (dbvCat.type) {
+ case DBVT_BYTE:
+ if (dbvCat.bVal != (BYTE)idList[j].nID)
+ continue;
+ break;
+ case DBVT_WORD:
+ if (dbvCat.wVal != (WORD)idList[j].nID)
+ continue;
+ break;
+ case DBVT_DWORD:
+ if (dbvCat.dVal != (DWORD)idList[j].nID)
+ continue;
+ break;
+ case DBVT_ASCIIZ:
+ if (strcmp(dbvCat.pszVal, idList[j].pszText))
+ continue;
+ break;
+ }
+ pItem->iListItem = j;
+ break;
+ }
+ }
+ // item not found in the predefined category list?
+ if ((idList == NULL || j == nList) && dbvCat.type == DBVT_ASCIIZ) {
+ pItem->pszText[0] = mir_a2t(dbvCat.pszVal);
+ DB::Variant::Free(&dbvCat);
+ }
+ if ((lvi.iItem = ListView_InsertItem(pList->hList, &lvi)) < 0) {
+ mir_free(pItem);
+ DB::Variant::Free(&dbvCat);
+ DB::Variant::Free(&dbvVal);
+ break;
+ }
+ lvi.iItem++;
+ dbvCat.type = dbvVal.type = DBVT_DELETED;
+ }
+ iItem = lvi.iItem;
+ return i;
+}
+
+/**
+ * name: ProfileList_DropdownProc
+ * desc: procedure to catch messages for a listbox control for my own combobox
+ * param: hwnd - handle to the listview control's window
+ * msg - message sent to the control
+ * wParam - message specific parameter
+ * lParam - message specific parameter
+ * return: message specific
+ **/
+static INT_PTR CALLBACK ProfileList_DropdownProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LPLISTCTRL pList;
+
+ switch (msg) {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_ESCAPE:
+ SetFocus(GetParent(hwnd));
+ return 0;
+
+ case VK_RETURN:
+ case VK_F4:
+ {
+ LPIDSTRLIST pItem;
+
+ if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) return CB_ERR;
+ pList->labelEdit.dropDown.iItem = ListBox_GetCurSel(hwnd);
+ if (pList->labelEdit.dropDown.iItem >= 0 && PtrIsValid(pItem = (LPIDSTRLIST)ListBox_GetItemData(hwnd, pList->labelEdit.dropDown.iItem)))
+ SetWindowText(pList->labelEdit.hEdit, pItem->ptszTranslated);
+ else
+ pList->labelEdit.dropDown.iItem = -1;
+ SetFocus(pList->labelEdit.hEdit);
+ return 0;
+ }
+ }
+ break;
+ case WM_LBUTTONUP:
+ {
+ POINT pt;
+ LPIDSTRLIST pItem;
+
+ if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) return CB_ERR;
+ CallWindowProc(OldDropdownProc, hwnd, msg, wParam, lParam);
+
+ pt.x = (short)LOWORD(lParam);
+ pt.y = (short)HIWORD(lParam);
+ ClientToScreen(hwnd, &pt);
+
+ if (SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)) == HTVSCROLL)
+ return CB_ERR;
+
+ pList->labelEdit.dropDown.iItem = SendMessage(hwnd, LB_GETCURSEL, 0, 0);
+
+ if (pList->labelEdit.dropDown.iItem >= 0 && PtrIsValid(pItem = (LPIDSTRLIST)ListBox_GetItemData(hwnd, pList->labelEdit.dropDown.iItem))) {
+ SetWindowText(pList->labelEdit.hEdit, pItem->ptszTranslated);
+ }
+ else
+ pList->labelEdit.dropDown.iItem = -1;
+
+ ProfileList_EndLabelEdit(pList->hList, TRUE);
+ return 0;
+ }
+ case WM_KILLFOCUS:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+ if (GetFocus() == pList->labelEdit.hEdit) {
+ ShowWindow(hwnd, SW_HIDE);
+ return 0;
+ }
+ ProfileList_EndLabelEdit(pList, FALSE);
+ }
+ return 0;
+ }
+ return CallWindowProc(OldDropdownProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name: ProfileList_LabelEditProc
+ * desc: procedure to catch messages for an edit control for my own combobox
+ * param: hwnd - handle to the listview control's window
+ * msg - message sent to the control
+ * wParam - message specific parameter
+ * lParam - message specific parameter
+ * return: message specific
+ **/
+static INT_PTR CALLBACK ProfileList_LabelEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LPLISTCTRL pList;
+
+ switch (msg) {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_ESCAPE:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+ ProfileList_EndLabelEdit(pList, FALSE);
+ return 0;
+ case VK_F3:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) && pList->labelEdit.hBtn)
+ SendMessage(pList->hList, WM_COMMAND, MAKEWPARAM(BTN_EDIT, BN_CLICKED), (LPARAM)pList->labelEdit.hBtn);
+ return 0;
+ case VK_RETURN:
+ {
+ BOOLEAN bEditNext;
+ INT iItem;
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_WANTRETURN && !(GetKeyState(VK_CONTROL) & 0x8000))
+ break;
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+ bEditNext = !pList->labelEdit.iSubItem && !ProfileList_GetItemText(pList->hList, pList->labelEdit.iItem, 1, NULL, NULL);
+ iItem = pList->labelEdit.iItem;
+ ProfileList_EndLabelEdit(pList->hList, TRUE);
+ if (bEditNext) ProfileList_BeginLabelEdit(pList->hList, pList->labelEdit.iItem, 1);
+ }
+ return 0;
+ }
+ case VK_TAB:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+ LVITEM lvi;
+
+ lvi.mask = LVIF_STATE;
+ lvi.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
+ lvi.iItem = pList->labelEdit.iItem;
+
+ if (wParam == VK_TAB && !pList->labelEdit.iSubItem) {
+ lvi.iSubItem = 1;
+ lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+ ProfileList_EndLabelEdit(pList->hList, TRUE);
+ }
+ else {
+ UINT iSubItem = (wParam == VK_TAB) ? 0 : pList->labelEdit.iSubItem;
+
+ lvi.iSubItem = 0;
+ lvi.state = 0;
+
+ ProfileList_EndLabelEdit(pList, TRUE);
+
+ // unselect current list item
+ if (!ListView_SetItem(pList->hList, &lvi))
+ return 0;
+
+ // search for next valid list item (skip deviders)
+ lvi.iSubItem = iSubItem;
+ lvi.mask = LVIF_PARAM;
+ do {
+ if (wParam == VK_UP)
+ lvi.iItem--;
+ else
+ lvi.iItem++;
+
+ if (lvi.iItem == -1 || !ListView_GetItem(pList->hList, &lvi)) {
+ return 0;
+ }
+ } while (!lvi.lParam);
+
+ // select new list item
+ lvi.mask = LVIF_STATE;
+ lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+ if (!ListView_SetItem(pList->hList, &lvi)) {
+ return 0;
+ }
+ }
+ ProfileList_BeginLabelEdit(pList->hList, lvi.iItem, lvi.iSubItem);
+ return 0;
+ }
+ return 1;
+ }
+ break;
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS | CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam);
+ case WM_KILLFOCUS:
+ {
+ HWND hwndFocus = GetFocus();
+
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) &&
+ hwndFocus != pList->labelEdit.dropDown.hDrop &&
+ hwndFocus != pList->labelEdit.hEdit &&
+ hwndFocus != pList->labelEdit.hBtn)
+ ProfileList_EndLabelEdit(pList, hwndFocus == pList->hList);
+ return 0;
+ }
+ }
+ return CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name: ListSubclassProc
+ * desc: procedure to catch messages for a listview control, to handle tooltips
+ * param: hwnd - handle to the listview control's window
+ * msg - message sent to the control
+ * wParam - message specific parameter
+ * lParam - message specific parameter
+ * return: message specific
+ **/
+static INT_PTR CALLBACK ProfileList_SubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LPLISTCTRL pList;
+
+ switch (msg) {
+ case WM_KEYDOWN:
+ {
+ INT nCurSel, newSel;
+ LVITEM lvi;
+
+ switch (wParam) {
+ case VK_F2:
+ nCurSel = ListView_GetSelectionMark(hwnd);
+ if (nCurSel == -1) break;
+ ProfileList_BeginLabelEdit(hwnd, nCurSel, 0);
+ return 0;
+ case VK_F3:
+ nCurSel = ListView_GetSelectionMark(hwnd);
+ if (nCurSel == -1) break;
+ ProfileList_BeginLabelEdit(hwnd, nCurSel, 1);
+ return 0;
+ case VK_UP:
+ case VK_DOWN:
+ lvi.iItem = nCurSel = ListView_GetSelectionMark(hwnd);
+ lvi.iSubItem = 0;
+
+ // find next valid item to select
+ lvi.mask = LVIF_PARAM;
+ do {
+ if (wParam == VK_UP) lvi.iItem--;
+ else lvi.iItem++;
+ if (lvi.iItem == -1 || !ListView_GetItem(hwnd, &lvi)) {
+ return 0;
+ }
+ } while (!lvi.lParam);
+
+ ListView_EnsureVisible(hwnd, lvi.iItem, FALSE);
+ newSel = lvi.iItem;
+ lvi.iItem = nCurSel;
+ lvi.mask = LVIF_STATE;
+ lvi.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
+ lvi.state = 0;
+ ListView_SetItem(hwnd, &lvi);
+ lvi.iItem = newSel;
+ lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+ ListView_SetItem(hwnd, &lvi);
+ ListView_SetSelectionMark(hwnd, lvi.iItem);
+ return 0;
+ }
+ break;
+ }
+ case WM_MOUSEMOVE:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+ HDC hDC;
+ RECT rchWnd, rcItem;
+ SIZE textSize;
+ LVHITTESTINFO hi;
+ TOOLINFO ti;
+ BOOLEAN bReposition;
+ LPLCITEM pItem;
+
+ hi.pt.x = GET_X_LPARAM(lParam);
+ hi.pt.y = GET_Y_LPARAM(lParam);
+ ListView_SubItemHitTest(hwnd, &hi);
+
+ // show tip only if pointer is over an item
+ if (pList->iHotItem != hi.iItem || pList->iHotSubItem != hi.iSubItem) {
+ bReposition = pList->iHotItem != -1 || pList->iHotSubItem != -1;
+ pList->iHotItem = hi.iItem;
+ pList->iHotSubItem = hi.iSubItem;
+
+ if ((hi.flags & LVHT_ONITEMLABEL) && PtrIsValid(pItem = ProfileList_GetItemData(hwnd, hi.iItem))) {
+ GetWindowRect(hwnd, &rchWnd);
+ ListView_GetSubItemRect(hwnd, hi.iItem, hi.iSubItem, LVIR_BOUNDS, &rcItem);
+ // calculate size of text on the screen
+ if ((hDC = GetDC(GetParent(hwnd)))) {
+ SelectObject(hDC, (HFONT)SendMessage(GetParent(hwnd), WM_GETFONT, NULL, NULL));
+ GetTextExtentPoint32(hDC, pItem->pszText[hi.iSubItem], lstrlen(pItem->pszText[hi.iSubItem]), &textSize);
+ ReleaseDC(GetParent(hwnd), hDC);
+ }
+ // show tip only for text that is larger than te listview can display
+ if (textSize.cx > rchWnd.right - rchWnd.left || textSize.cx > rcItem.right - rcItem.left) {
+ ZeroMemory(&ti, sizeof(TOOLINFO));
+ ti.cbSize = sizeof(TOOLINFO);
+ ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS|TTF_TRANSPARENT;
+ ti.hinst = ghInst;
+ ti.hwnd = hwnd;
+ ti.uId = (UINT_PTR)hwnd;
+ ti.lpszText = pItem->pszText[hi.iSubItem];
+ ti.rect = rcItem;
+ SendMessage(pList->hTip, TTM_SETMAXTIPWIDTH, 0, 300);
+ SendMessage(pList->hTip, TTM_SETTOOLINFO, NULL, (LPARAM)&ti);
+ if (pList->iHotSubItem > 0) {
+ SendMessage(pList->hTip, TTM_SETTITLE, 1, (LPARAM)
+ ((pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+ ? pItem->idstrList[pItem->iListItem].ptszTranslated
+ : (pItem->pszText[0] && *pItem->pszText[0])
+ ? pItem->pszText[0]
+ : TranslateT("<empty>"))
+ );
+ InvalidateRect(pList->hTip, NULL, TRUE);
+ }
+ else
+ SendMessage(pList->hTip, TTM_SETTITLE, 0, (LPARAM)"");
+ SendMessage(pList->hTip, TTM_ACTIVATE, TRUE, (LPARAM)&ti);
+ pList->ptTip.x = rchWnd.left + GET_X_LPARAM(lParam) - 16;
+ pList->ptTip.y = rchWnd.top + rcItem.top;
+ // no TTN_SHOW is called if bReposition is TRUE, so repose here!
+ if (bReposition) {
+ RECT rcTip;
+ GetClientRect(pList->hTip, &rcTip);
+ SetWindowPos(pList->hTip, hwnd, pList->ptTip.x, pList->ptTip.y - rcTip.bottom, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+ pList->wFlags |= LIF_TIPVISIBLE;
+ return 0;
+ }
+ }
+ if (pList->wFlags & LIF_TIPVISIBLE) {
+ SendMessage(pList->hTip, TTM_ACTIVATE, FALSE, (LPARAM)&ti);
+ pList->wFlags &= ~LIF_TIPVISIBLE;
+ }
+ }
+ }
+ return 0;
+
+ // begin label edit
+ case WM_LBUTTONDBLCLK:
+ {
+ LVHITTESTINFO hi;
+
+ hi.pt.x = GET_X_LPARAM(lParam);
+ hi.pt.y = GET_Y_LPARAM(lParam);
+ if (ListView_SubItemHitTest(hwnd, &hi)) {
+ ProfileList_BeginLabelEdit(hwnd, hi.iItem, hi.iSubItem);
+ }
+ return TRUE;
+ }
+
+ case WM_NOTIFY:
+ if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+ break;
+
+ // ensure position of tooltip is on the topline of the item
+ if (((LPNMHDR)lParam)->hwndFrom == pList->hTip) {
+ RECT rcTip;
+ GetClientRect(pList->hTip, &rcTip);
+
+ switch (((LPNMHDR)lParam)->code) {
+ case TTN_SHOW:
+ SetWindowPos(pList->hTip, hwnd, pList->ptTip.x, pList->ptTip.y - rcTip.bottom, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
+ return TRUE;
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+
+ // show dropdown menu for category list
+ case BTN_EDIT:
+ {
+ INT i;
+ TCHAR szEdit[MAX_PATH];
+
+ if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) break;
+ GetWindowText(pList->labelEdit.hEdit, szEdit, MAX_PATH);
+
+ // need to create the dropdown list?
+ if (pList->labelEdit.dropDown.hDrop == NULL) {
+ const INT listHeight = 120;
+ RECT rc, rcList;
+ INT add;
+
+ // dropdown rect
+ GetClientRect(pList->hList, &rcList);
+ rc.left = pList->labelEdit.rcCombo.left;
+ rc.right = pList->labelEdit.rcCombo.right + pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top;
+
+ if (rcList.bottom < pList->labelEdit.rcCombo.bottom + listHeight) {
+ rc.bottom = pList->labelEdit.rcCombo.bottom - 7; // don't ask me why!
+ rc.top = rc.bottom - listHeight;
+ }
+ else {
+ rc.top = pList->labelEdit.rcCombo.bottom;
+ rc.bottom = rc.top + listHeight;
+ }
+
+ pList->labelEdit.dropDown.hDrop = CreateWindowEx(0,
+ _T("LISTBOX"), NULL, WS_CHILD|WS_BORDER|WS_VSCROLL|LBS_COMBOBOX|LBS_HASSTRINGS,
+ rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+ hwnd, NULL, ghInst, NULL);
+ if (!pList->labelEdit.dropDown.hDrop) return FALSE;
+ SetUserData(pList->labelEdit.dropDown.hDrop, pList);
+ OldDropdownProc = (WNDPROC)SetWindowLongPtr(pList->labelEdit.dropDown.hDrop, GWLP_WNDPROC, (LONG_PTR)ProfileList_DropdownProc);
+ SetWindowLongPtr(pList->labelEdit.dropDown.hDrop, GWLP_ID, LIST_DROPDOWN);
+ SendMessage(pList->labelEdit.dropDown.hDrop, WM_SETFONT, (WPARAM)SendMessage(GetParent(pList->hList), WM_GETFONT, 0, 0), 0);
+
+ // add items
+ for (i = 0; i < pList->labelEdit.pItem->idstrListCount; i++) {
+ add = ListBox_AddString(pList->labelEdit.dropDown.hDrop, pList->labelEdit.pItem->idstrList[i].ptszTranslated);
+ ListBox_SetItemData(pList->labelEdit.dropDown.hDrop, add, pList->labelEdit.pItem->idstrList + i);
+ if (!_tcscmp(szEdit, pList->labelEdit.pItem->idstrList[i].ptszTranslated))
+ ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, add);
+ }
+ }
+ else {
+ LPIDSTRLIST lpidList;
+
+ i = 0;
+ while (PtrIsValid(lpidList = (LPIDSTRLIST)ListBox_GetItemData(pList->labelEdit.dropDown.hDrop, i))) {
+ if (!_tcscmp(szEdit, lpidList->ptszTranslated)) {
+ ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, i);
+ break;
+ }
+ i++;
+ }
+ if (i == pList->labelEdit.pItem->idstrListCount)
+ ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, -1);
+ }
+ if (IsWindowVisible(pList->labelEdit.dropDown.hDrop)) {
+ SetFocus(pList->labelEdit.hEdit);
+ }
+ else {
+ ShowWindow(pList->labelEdit.dropDown.hDrop, SW_SHOW);
+ //SetFocus(pList->labelEdit.dropDown.hDrop);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case WM_MOUSEWHEEL:
+ case WM_VSCROLL:
+ case WM_HSCROLL:
+ {
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+ ProfileList_EndLabelEdit(pList, FALSE);
+ break;
+ }
+
+ case WM_KILLFOCUS:
+ {
+ HWND hwndFocus = GetFocus();
+
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) &&
+ pList->labelEdit.hEdit != hwndFocus &&
+ pList->labelEdit.dropDown.hDrop != hwndFocus &&
+ pList->labelEdit.hBtn != hwndFocus)
+ ProfileList_EndLabelEdit(pList, FALSE);
+ break;
+ }
+
+ case WM_DESTROY:
+ if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+ HFONT hFont;
+
+ ProfileList_EndLabelEdit(pList, FALSE);
+ ProfileList_Clear(hwnd);
+ if (PtrIsValid(hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0)) && hFont != pList->hFont)
+ DeleteObject(hFont);
+ DestroyWindow(pList->hTip);
+ mir_free(pList);
+ }
+ break;
+ }
+ return CallWindowProc(OldListViewProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name: DlgProcPspAbout()
+ * desc: dialog procedure
+ *
+ * return: 0 or 1
+ **/
+INT_PTR CALLBACK PSPProcContactProfile(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hList = GetDlgItem(hDlg, LIST_PROFILE);
+ LPLISTCTRL pList;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ LVCOLUMN lvc;
+ RECT rc;
+ LOGFONT lf;
+ HFONT hFont;
+ TOOLINFO ti;
+
+ if (!hList || !(pList = (LPLISTCTRL)mir_alloc(sizeof(LISTCTRL))))
+ return FALSE;
+ ZeroMemory(pList, sizeof(LISTCTRL));
+
+ TranslateDialogDefault(hDlg);
+ Ctrl_InitTextColours();
+
+ // init info structure
+ pList->hList = hList;
+ pList->nType = CTRL_LIST_PROFILE;
+ ZeroMemory(&pList->labelEdit, sizeof(pList->labelEdit));
+ SetUserData(hList, pList);
+
+ // set new window procedure
+ OldListViewProc = (WNDPROC)SetWindowLongPtr(hList, GWLP_WNDPROC, (LONG_PTR)&ProfileList_SubclassProc);
+
+ // remove static edge in aero mode
+ if (IsAeroMode())
+ SetWindowLongPtr(hList, GWL_EXSTYLE, GetWindowLongPtr(hList, GWL_EXSTYLE)&~WS_EX_STATICEDGE);
+
+ // insert columns into the listboxes
+ ListView_SetExtendedListViewStyle(hList, LVS_EX_FULLROWSELECT);
+
+
+ PSGetBoldFont(hDlg, hFont);
+ SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hFont, 0);
+
+ // set listfont
+ pList->hFont = (HFONT)SendMessage(hList, WM_GETFONT, 0, 0);
+ pList->wFlags |= LVF_EDITLABEL;
+ GetObject(pList->hFont, sizeof(lf), &lf);
+ lf.lfHeight -= 6;
+ hFont = CreateFontIndirect(&lf);
+ SendMessage(hList, WM_SETFONT, (WPARAM)hFont, 0);
+
+ GetClientRect(hList, &rc);
+ rc.right -= GetSystemMetrics(SM_CXVSCROLL);
+
+ // initiate the tooltips
+ pList->hTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
+ WS_POPUP|TTS_BALLOON|TTS_NOPREFIX|TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ hList, NULL, ghInst, NULL);
+ if (pList->hTip) {
+ SetWindowPos(pList->hTip, HWND_TOPMOST,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ ZeroMemory(&ti, sizeof(TOOLINFO));
+ ti.cbSize = sizeof(TOOLINFO);
+ ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS|TTF_TRANSPARENT;
+ ti.hinst = ghInst;
+ ti.hwnd = hList;
+ ti.uId = (UINT_PTR)hList;
+ SendMessage(pList->hTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+ SendMessage(pList->hTip, TTM_ACTIVATE, FALSE, (LPARAM)&ti);
+ }
+
+ // insert columns into the listboxes
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = rc.right / 8 * 3;
+ ListView_InsertColumn(hList, 0, &lvc);
+ lvc.cx = rc.right / 8 * 5;
+ ListView_InsertColumn(hList, 1, &lvc);
+ return TRUE;
+ }
+
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLORDLG:
+ if (IsAeroMode())
+ return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ {
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ LPCSTR pszProto;
+
+ if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hList))) break;
+
+ switch (((LPNMHDR)lParam)->code) {
+ // some account data may have changed so reread database
+ case PSN_INFOCHANGED:
+ {
+ BYTE msgResult = 0;
+ LPIDSTRLIST idList;
+ UINT nList;
+ BYTE i;
+ INT iItem = 0,
+ iGrp = 0,
+ numProtoItems,
+ numUserItems;
+
+ if (!(pList->wFlags & CTRLF_CHANGED) && PSGetBaseProto(hDlg, pszProto) && *pszProto != 0) {
+ ProfileList_Clear(hList);
+
+ // insert the past information
+ for (i = 0; i < 3; i++) {
+ pFmt[i].GetList((WPARAM)&nList, (LPARAM)&idList);
+ if ((numProtoItems = ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hContact, pszProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASPROTO)) < 0)
+ return FALSE;
+
+ // scan all basic protocols for the subcontacts
+ if (DB::Module::IsMetaAndScan(pszProto)) {
+ INT iDefault = CallService(MS_MC_GETDEFAULTCONTACTNUM, (WPARAM)hContact, NULL);
+ HANDLE hSubContact, hDefContact;
+ LPCSTR pszSubBaseProto;
+ INT j, numSubs;
+
+ if ((hDefContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, iDefault)) &&
+ (pszSubBaseProto = DB::Contact::Proto(hDefContact)))
+ {
+ if ((numProtoItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hDefContact, pszSubBaseProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+ return FALSE;
+
+ // copy the missing settings from the other subcontacts
+ numSubs = CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, NULL);
+ for (j = 0; j < numSubs; j++) {
+ if (j == iDefault) continue;
+ if (!(hSubContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, j))) continue;
+ if (!(pszSubBaseProto = DB::Contact::Proto(hSubContact))) continue;
+ if ((numProtoItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hSubContact, pszSubBaseProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+ return FALSE;
+ //if ((numUserItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hSubContact, USERINFO, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+ // return FALSE;
+ }
+ }
+ }
+ if ((numUserItems = ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hContact, USERINFO, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASCUSTOM)) < 0)
+ return FALSE;
+ if (numUserItems || numProtoItems) {
+ msgResult = PSP_CHANGED;
+ ProfileList_AddGroup(hList, pFmt[i].szGroup, iGrp);
+ iGrp = ++iItem;
+ }
+ }
+ }
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, msgResult);
+ break;
+ }
+ // user swiches to another propertysheetpage
+ case PSN_KILLACTIVE:
+ ProfileList_EndLabelEdit(hList, TRUE);
+ break;
+ // user selected to apply settings to the database
+ case PSN_APPLY:
+ if (pList->wFlags & CTRLF_CHANGED) {
+ BYTE iFmt = -1;
+ INT iItem;
+ LVITEM lvi;
+ TCHAR szGroup[MAX_PATH];
+ CHAR pszSetting[MAXSETTING];
+ LPLCITEM pItem;
+ LPSTR pszModule = USERINFO;
+
+ if (!hContact) PSGetBaseProto(hDlg, pszModule);
+
+ *szGroup = 0;
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.pszText = szGroup;
+ lvi.cchTextMax = MAX_PATH;
+
+ for (iItem = lvi.iItem = lvi.iSubItem = 0; ListView_GetItem(hList, &lvi); lvi.iItem++) {
+ if (!PtrIsValid(pItem = (LPLCITEM)lvi.lParam)) {
+ // delete reluctant items
+ if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+ DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szCatFmt, iItem);
+ DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szValFmt, iItem);
+ }
+ // find information about the group
+ for (iFmt = 0; iFmt < SIZEOF(pFmt); iFmt++) {
+ if (!_tcscmp(szGroup, pFmt[iFmt].szGroup)) {
+ break;
+ }
+ }
+ // indicate, no group was found. should not happen!!
+ if (iFmt == SIZEOF(pFmt)) {
+ *szGroup = 0;
+ iFmt = -1;
+ }
+ iItem = 0;
+ }
+ else
+ if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+ // save value
+ if (!pItem->pszText[1] || !*pItem->pszText[1])
+ continue;
+ if (!(pItem->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA))) {
+ mir_snprintf(pszSetting, MAXSETTING, pFmt[iFmt].szValFmt, iItem);
+ DB::Setting::WriteTString(hContact, pszModule, pszSetting, pItem->pszText[1]);
+ // save category
+ mir_snprintf(pszSetting, MAXSETTING, pFmt[iFmt].szCatFmt, iItem);
+ if (pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+ DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)pItem->idstrList[pItem->iListItem].pszText);
+ else
+ if (pItem->pszText[0] && *pItem->pszText[0])
+ DB::Setting::WriteTString(hContact, pszModule, pszSetting, (LPTSTR)pItem->pszText[0]);
+ else
+ DB::Setting::Delete(hContact, pszModule, pszSetting);
+ // redraw the item if required
+ if (pItem->wFlags & CTRLF_CHANGED) {
+ pItem->wFlags &= ~CTRLF_CHANGED;
+ ListView_RedrawItems(hList, lvi.iItem, lvi.iItem);
+ }
+ iItem++;
+ }
+ }
+ }
+ // delete reluctant items
+ if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+ DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szCatFmt, iItem);
+ DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szValFmt, iItem);
+ }
+
+ pList->wFlags &= ~CTRLF_CHANGED;
+ }
+ break;
+ }
+ break;
+ }
+
+ //
+ // handle notification messages from the list control
+ //
+ case LIST_PROFILE:
+ {
+ LPLISTCTRL pList = (LPLISTCTRL)GetUserData(((LPNMHDR)lParam)->hwndFrom);
+
+ switch (((LPNMHDR)lParam)->code) {
+ case NM_RCLICK:
+ {
+ HMENU hMenu = CreatePopupMenu();
+ MENUITEMINFO mii;
+ HANDLE hContact;
+ LVHITTESTINFO hi;
+ LPLCITEM pItem;
+ POINT pt;
+
+ if (!hMenu) return 1;
+ PSGetContact(hDlg, hContact);
+ GetCursorPos(&pt);
+ hi.pt = pt;
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hi.pt);
+ ListView_SubItemHitTest(((LPNMHDR)lParam)->hwndFrom, &hi);
+ pItem = ProfileList_GetItemData(((LPNMHDR)lParam)->hwndFrom, hi.iItem);
+
+ // insert menuitems
+ ZeroMemory(&mii, sizeof(MENUITEMINFO));
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_ID|MIIM_STRING;
+ // insert "Add" Menuitem
+ mii.wID = BTN_ADD_intEREST;
+ mii.dwTypeData = TranslateT("Add Interest");
+ InsertMenuItem(hMenu, 0, TRUE, &mii);
+ mii.wID = BTN_ADD_AFFLIATION;
+ mii.dwTypeData = TranslateT("Add Affliation");
+ InsertMenuItem(hMenu, 1, TRUE, &mii);
+ mii.wID = BTN_ADD_PAST;
+ mii.dwTypeData = TranslateT("Add Past");
+ InsertMenuItem(hMenu, 2, TRUE, &mii);
+
+ if (hi.iItem != -1 && PtrIsValid(pItem) && !(hContact && (pItem->wFlags & CTRLF_HASPROTO))) {
+ // insert separator
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(hMenu, 3, TRUE, &mii);
+ // insert "Delete" Menuitem
+ mii.fMask = MIIM_ID|MIIM_STRING;
+ mii.wID = BTN_EDIT_CAT;
+ mii.dwTypeData = TranslateT("Edit Category");
+ InsertMenuItem(hMenu, 4, TRUE, &mii);
+ mii.wID = BTN_EDIT_VAL;
+ mii.dwTypeData = TranslateT("Edit Value");
+ InsertMenuItem(hMenu, 5, TRUE, &mii);
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(hMenu, 6, TRUE, &mii);
+ // insert "Delete" Menuitem
+ mii.fMask = MIIM_ID|MIIM_STRING;
+ mii.wID = BTN_DEL;
+ mii.dwTypeData = TranslateT("Delete");
+ InsertMenuItem(hMenu, 7, TRUE, &mii);
+ }
+ TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hDlg, 0);
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ /*case LVN_BEGINSCROLL:
+ SetFocus(((LPNMHDR)lParam)->hwndFrom);
+ break;
+ */
+ case LVN_GETDISPINFO:
+ if (pList->labelEdit.iTopIndex != ListView_GetTopIndex(hList))
+ ProfileList_EndLabelEdit(((LPNMHDR)lParam)->hwndFrom, FALSE);
+ break;
+ case NM_CUSTOMDRAW:
+ {
+ LPNMLVCUSTOMDRAW cd = (LPNMLVCUSTOMDRAW)lParam;
+ LPLCITEM pItem = (LPLCITEM)cd->nmcd.lItemlParam;
+ RECT rc;
+
+ switch (cd->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);
+ return TRUE;
+
+ case CDDS_ITEMPREPAINT:
+ ListView_GetItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, &rc, LVIR_BOUNDS);
+ if (!PtrIsValid(pItem)) {
+ HFONT hBold, hFont;
+ TCHAR szText[MAX_PATH];
+
+ PSGetBoldFont(hDlg, hBold);
+ hFont = (HFONT)SelectObject(cd->nmcd.hdc, hBold);
+ SetTextColor(cd->nmcd.hdc, GetSysColor(COLOR_3DSHADOW));
+ ProfileList_GetItemText(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, 0, szText, MAX_PATH);
+ rc.left += 6;
+ DrawText(cd->nmcd.hdc, TranslateTS(szText), -1, &rc, DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
+
+ rc.bottom -= 2;
+ rc.top = rc.bottom - 1;
+ rc.left -= 6;
+ DrawEdge(cd->nmcd.hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
+
+ SelectObject(cd->nmcd.hdc, hFont);
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ // draw selected item
+ if ((cd->nmcd.uItemState & CDIS_SELECTED) || (pList->labelEdit.iItem == cd->nmcd.dwItemSpec)) {
+ SetTextColor(cd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ FillRect(cd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+ // draw background of unselected item
+ else {
+ SetTextColor(cd->nmcd.hdc,
+ (pItem->wFlags & CTRLF_CHANGED)
+ ? clrChanged : (pItem->wFlags & CTRLF_HASMETA)
+ ? clrMeta : ((pItem->wFlags & (CTRLF_HASCUSTOM)) && (pItem->wFlags & CTRLF_HASPROTO))
+ ? clrBoth : (pItem->wFlags & CTRLF_HASCUSTOM)
+ ? clrCustom : clrNormal);
+ FillRect(cd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ }
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NEWFONT|CDRF_NOTIFYSUBITEMDRAW);
+ return TRUE;
+
+ case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+ {
+ HFONT hoFont = (HFONT)SelectObject(cd->nmcd.hdc, pList->hFont);
+
+ ListView_GetSubItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, cd->iSubItem, LVIR_BOUNDS, &rc);
+ if (cd->iSubItem == 0) {
+ RECT rc2;
+ ListView_GetSubItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, 1, LVIR_BOUNDS, &rc2);
+ rc.right = rc2.left;
+ }
+ rc.left += 3;
+ DrawText(cd->nmcd.hdc,
+ pItem->pszText[cd->iSubItem]
+ ? pItem->pszText[cd->iSubItem]
+ : (cd->iSubItem == 0 && pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+ ? pItem->idstrList[pItem->iListItem].ptszTranslated
+ : TranslateT("<empty>"),
+ -1, &rc, DT_END_ELLIPSIS|DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
+ SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ } /* switch (cd->nmcd.dwDrawStage) */
+ break;
+ } /* case NM_CUSTOMDRAW: */
+ } /* (((LPNMHDR)lParam)->code) */
+ break;
+ }
+ }
+ break; /* case WM_NOTIFY: */
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case BTN_ADD_intEREST:
+ return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[2]);
+ case BTN_ADD_AFFLIATION:
+ return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[1]);
+ case BTN_ADD_PAST:
+ return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[0]);
+ case BTN_EDIT_CAT:
+ ProfileList_BeginLabelEdit(hList, ListView_GetSelectionMark(hList), 0);
+ break;
+ case BTN_EDIT_VAL:
+ ProfileList_BeginLabelEdit(hList, ListView_GetSelectionMark(hList), 1);
+ break;
+ case BTN_DEL:
+ if (IDYES == MsgBox(hDlg, MB_YESNO|MB_ICON_QUESTION, LPGENT("Question"), LPGENT("Delete an entry"), LPGENT("Do you really want to delete this entry?"))) {
+ INT iItem = ListView_GetSelectionMark(hList);
+ LPLISTCTRL pList = (LPLISTCTRL)GetUserData(hList);
+
+ ProfileList_DeleteItem(hList, iItem);
+ if (PtrIsValid(pList)) pList->wFlags |= CTRLF_CHANGED;
+ SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+ // check if to delete any devider
+ if (!ProfileList_GetItemData(hList, iItem--) && !ProfileList_GetItemData(hList, iItem))
+ ListView_DeleteItem(hList, iItem);
+ }
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
diff --git a/plugins/UserInfoEx/src/resdefines.h b/plugins/UserInfoEx/src/resdefines.h
new file mode 100644
index 0000000000..f92a65f805
--- /dev/null
+++ b/plugins/UserInfoEx/src/resdefines.h
@@ -0,0 +1,5 @@
+#ifdef UNICODE
+#define RICHEDIT_CLASS "RichEdit20W"
+#else
+#define RICHEDIT_CLASS "RichEdit20A"
+#endif
diff --git a/plugins/UserInfoEx/src/resource.h b/plugins/UserInfoEx/src/resource.h
new file mode 100644
index 0000000000..1419fcc501
--- /dev/null
+++ b/plugins/UserInfoEx/src/resource.h
@@ -0,0 +1,333 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDSKIP 20
+#define IDALL 21
+#define IDAPPLY 22
+#define IDI_MAIN 110
+#define IDD_EXPORT 601
+#define IDB_FLAGSPNG 602
+#define IDD_ANNIVERSARY_EDITOR 603
+#define IDD_ANNIVERSARY_LIST 604
+#define CURSOR_ADDGROUP 605
+#define IDR_VERSION2 608
+#define IDD_CHOSE_PROTOCOL 614
+#define IDI_DEFAULT 618
+#define IDD_DETAILS 1000
+#define IDD_CONTACT_GENERAL 1001
+#define IDD_CONTACT_ORIGIN 1002
+#define IDD_CONTACT_COMPANY 1003
+#define IDD_CONTACT_ABOUT 1004
+#define CHECK_OPT_FLAGSUNKNOWN 1005
+#define IDD_ADDPHONE 1006
+#define CHECK_OPT_FLAGSMSGSTATUS 1006
+#define IDD_ADDEMAIL 1007
+#define IDD_MSGBOX 1008
+#define IDD_MSGBOX2 1008
+#define IDD_OPT_ADVANCED 1009
+#define IDD_OPT_POPUP 1010
+#define IDD_CONTACT_HOME 1011
+#define IDD_CONTACT_ADDRESS 1011
+#define IDD_CONTACT_PROFILE 1012
+#define IDD_OPT_REMINDER 1015
+#define IDD_OPT_T 1015
+#define IDD_OPT_DETAILSDLG 1016
+#define TXT_TIME 1017
+#define IDD_CONTACT_HISTORY 1017
+#define TXT_ABOUT 1018
+#define IDD_COPYPROGRESS 1019
+#define IDD_EXPORT_DATAHISTORY 1020
+#define IDD_OPT_CLIST 1021
+#define IDD_OPT_COMMON 1021
+#define IDD_CONTACT_ANNIVERSARY 1022
+#define IDD_REFRESHDETAILS 1023
+#define IDD_MSGBOXDUMMI 1024
+#define STATIC_WHITERECT 1100
+#define STATIC_TREE 1101
+#define STATIC_LINE1 1102
+#define STATIC_LINE2 1103
+#define ICO_DLGLOGO 1105
+#define ICO_DLGLOGO2 1106
+#define ICO_MSGDLG 1106
+#define ICO_ADDRESS 1107
+#define ICO_FEMALE 1108
+#define ICO_MALE 1109
+#define ICO_MARITAL 1110
+#define ICO_BIRTHDAY 1111
+#define ICO_PASSWORD 1112
+#define ICO_CLOCK 1113
+#define TXT_NAME 1114
+#define TXT_DESCRIPTION 1115
+#define TXT_UPDATING 1116
+#define TXT_NICK 1119
+#define TXT_PASSWORD 1120
+#define TXT_AGE 1121
+#define TXT_REMIND 1122
+#define TXT_FEMALE 1122
+#define TXT_REMIND2 1123
+#define TXT_MALE 1123
+#define TXT_REMIND3 1124
+#define TXT_NUMCONTACT 1124
+#define TXT_NUMBIRTH 1125
+#define TXT_REMIND4 1125
+#define TXT_REMIND5 1126
+#define TXT_MESSAGE 1126
+#define TXT_OPT_CLR_NORMAL 1127
+#define TXT_REMIND6 1127
+#define TXT_OPT_CLR_USER 1128
+#define TXT_REMIND_LASTCHECK 1128
+#define TXT_OPT_CLR_BOTH 1129
+#define TXT_REMIND7 1129
+#define TXT_OPT_CLR_CHANGED 1130
+#define TXT_REMIND8 1130
+#define TXT_OPT_POPUP_CLR_BACK 1131
+#define TXT_OPT_CLR_META 1131
+#define TXT_REMIND9 1131
+#define TXT_OPT_POPUP_CLR_TEXT 1132
+#define TXT_OPT_POPUP_CLR_ABACK 1133
+#define TXT_OPT_POPUP_CLR_ATEXT 1134
+#define EDIT_TITLE 1201
+#define EDIT_FIRSTNAME 1202
+#define EDIT_SECONDNAME 1203
+#define EDIT_LASTNAME 1204
+#define EDIT_PREFIX 1205
+#define EDIT_NICK 1206
+#define EDIT_DISPLAYNAME 1207
+#define EDIT_PASSWORD 1208
+#define EDIT_STREET 1209
+#define EDIT_ZIP 1210
+#define EDIT_CITY 1211
+#define EDIT_STATE 1212
+#define EDIT_COUNTRY 1213
+#define EDIT_POSITION 1214
+#define EDIT_SUPERIOR 1215
+#define EDIT_ASSISTENT 1216
+#define EDIT_COMPANY 1217
+#define EDIT_DEPARTMENT 1218
+#define EDIT_ANNIVERSARY_DATE 1219
+#define EDIT_DEPARTMENT2 1219
+#define EDIT_POSITION2 1219
+#define EDIT_OFFICE 1219
+#define EDIT_AGE 1220
+#define EDIT_REMIND 1221
+#define EDIT_REMIND2 1222
+#define EDIT_LANG1 1223
+#define EDIT_REMIND_SOUNDOFFSET 1223
+#define EDIT_LANG2 1224
+#define EDIT_LANG3 1225
+#define EDIT_MARITAL 1226
+#define EDIT_PARTNER 1227
+#define EDIT_TIMEZONE 1228
+#define EDIT_HOMEPAGE 1229
+#define EDIT_CATEGORY 1230
+#define EDIT_AREA 1231
+#define EDIT_NUMBER 1232
+#define EDIT_PHONE 1233
+#define EDIT_EMAIL 1234
+#define EDIT_ABOUT 1235
+#define EDIT_NOTES 1236
+#define EDIT_OCCUPATION 1237
+#define EDIT_DELAY 1238
+#define LIST_PROFILE 1239
+#define LIST_PHONE 1240
+#define LIST_EMAIL 1241
+#define EDIT_TABAPPEAREANCE 1242
+#define SPIN_AGE 1301
+#define SPIN_REMIND 1302
+#define SPIN_REMIND2 1303
+#define SPIN_REMIND_SOUNDOFFSET 1304
+#define CLR_NORMAL 1400
+#define CLR_USER 1401
+#define CLR_BOTH 1402
+#define CLR_CHANGED 1403
+#define CLR_BBACK 1404
+#define CLR_META 1404
+#define CLR_BTEXT 1405
+#define CLR_ATEXT 1407
+#define IDC_CUSTOM1 1502
+#define BTN_DELETE 1502
+#define BTN_SEARCH 1502
+#define IDC_HEADERBAR 1502
+#define IDC_CUSTOM2 1503
+#define IDC_TREE1 1504
+#define IDC_TREE 1504
+#define IDC_COMBO1 1506
+#define EDIT_SORT 1506
+#define EDIT_EXTRAICON 1506
+#define EDIT_METASUBCONTACTS 1506
+#define EDIT_HISTORY_GROUPING 1506
+#define COMBO_VIEW 1506
+#define EDIT_BIRTHMODULE 1507
+#define EDIT_PATH 1512
+#define BTN_BROWSE 1513
+#define BTN_CHECK 1514
+#define BTN_UNCHECK 1515
+#define BTN_OPT_RESET 1515
+#define TEXT_ZODIAC 1516
+#define LIST_DROPDOWN 1517
+#define TXT_CHANGED 1522
+#define TXT_MODULE 1523
+#define CLR_ABACK 1523
+#define IDC_LINE 1526
+#define EDIT_REMIND_ENABLED 1528
+#define IDC_ZODIAC 1529
+#define STATIC_ADDRESS 1530
+#define LIST_META 1531
+#define BTN_DEFAULT 1533
+#define EDIT_MESSAGE 1538
+#define BTN_HISTORY_PASSWORD 1548
+#define CHECK_HISTORY_ENABLED 1550
+#define TXT_HISTORY1 1551
+#define GROUP_STATS 1553
+#define BTN_BROWSEDIR 1555
+#define CHECK_OPT_AUTOTIMEZONE 1561
+#define CHECK_OPT_SREMAIL_ENABLED 1562
+#define CHECK_OPT_SENDSMS_MENUITEMS3 1563
+#define IDNONE 1565
+#define CHECK_OPT_READONLYLABEL 1566
+#define TXT_DATEADDED 1566
+#define IDC_PROGRESS 1567
+#define IDC_CHECK1 1567
+#define IDC_PROGRESS2 1568
+#define IDC_CHECK2 1568
+#define STATIC_OPT_METAGROUP 1568
+#define TXT_CONTACT 1569
+#define EDIT_DAYS 1569
+#define TXT_SETTING 1570
+#define CHECK_DAYS 1570
+#define IDC_INFO 1571
+#define TXT_DAYS 1571
+#define CHECK_POPUP 1572
+#define GROUP_FILTER 1573
+#define GROUP_REMINDER 1574
+#define COMBO_OPT_GENDER 1575
+#define COMBO_OPT_FLAGS 1576
+#define TXT_OPT_GENDER 1577
+#define GROUP_OPT_EXTRAICONS 1578
+#define TXT_OPT_DEFAULTICONS 1579
+#define TXT_OPT_DEFAULTICONS2 1580
+#define ICO_COUNTRY 1580
+#define GROUP_OPT_EXTRAICONS2 1581
+#define IDC_PERCENT 1581
+#define TITLE_ZODIAC 1582
+#define TXT_OPT_GENDER2 1582
+#define TXT_OPT_FLAGS 1582
+#define TITLE_AGE 1583
+#define FRAME_AGE 1584
+#define IDC_STATIC_GROUP 1585
+#define CHECK_OPT_GENDER 1586
+#define TXT_OPT_EXTRAICONS 1587
+#define BTN_PREVIEW 1588
+#define IDC_PAGETITLE 1589
+#define RADIO_REMIND1 1590
+#define RADIO_REMIND2 1591
+#define IDC_PAGETITLEBG 1591
+#define RADIO_REMIND3 1592
+#define IDC_PAGETITLEBG2 1592
+#define CHECK_OPT_MI_MAIN 1610
+#define TXT_OPT_MI_MAIN 1611
+#define RADIO_OPT_MI_MAIN_NONE 1612
+#define RADIO_OPT_MI_MAIN_ALL 1613
+#define RADIO_OPT_MI_MAIN_EXIMPORT 1614
+#define CHECK_OPT_MI_CONTACT 1620
+#define TXT_OPT_MI_CONTACT 1621
+#define RADIO_OPT_MI_CONTACT_NONE 1622
+#define RADIO_OPT_MI_CONTACT_ALL 1623
+#define RADIO_OPT_MI_CONTACT_EXIMPORT 1624
+#define CHECK_OPT_MI_GROUP 1630
+#define TXT_OPT_MI_GROUP 1631
+#define RADIO_OPT_MI_GROUP_NONE 1632
+#define RADIO_OPT_MI_GROUP_ALL 1633
+#define RADIO_OPT_MI_GROUP_EXIMPORT 1634
+#define CHECK_OPT_MI_SUBGROUP 1640
+#define TXT_OPT_MI_SUBGROUP 1641
+#define RADIO_OPT_MI_SUBGROUP_NONE 1642
+#define RADIO_OPT_MI_SUBGROUP_ALL 1643
+#define RADIO_OPT_MI_SUBGROUP_EXIMPORT 1644
+#define CHECK_OPT_MI_STATUS 1650
+#define TXT_OPT_MI_STATUS 1651
+#define RADIO_OPT_MI_STATUS_NONE 1652
+#define RADIO_OPT_MI_STATUS_ALL 1653
+#define RADIO_OPT_MI_STATUS_EXIMPORT 1654
+#define CHECK_OPT_MI_ACCOUNT 1660
+#define TXT_OPT_MI_ACCOUNT 1661
+#define RADIO_OPT_MI_ACCOUNT_NONE 1662
+#define RADIO_OPT_MI_ACCOUNT_ALL 1663
+#define RADIO_OPT_MI_ACCOUNT_EXIMPORT 1664
+#define CHECK_OPT_MI_RESTART 1690
+#define BTN_UPDATE 40001
+#define BTN_ADD 40002
+#define BTN_IMPORT 40002
+#define BTN_ADD_PHONE 40003
+#define BTN_IMPORT2 40003
+#define BTN_EXPORT 40003
+#define BTN_MENU 40003
+#define BTN_ADD_intEREST 40004
+#define BTN_EDIT 40004
+#define BTN_ADD_AFFLIATION 40005
+#define BTN_ADD_PAST 40006
+#define BTN_EDIT_CAT 40007
+#define BTN_EDIT_VAL 40008
+#define BTN_EDIT_MAIL 40009
+#define BTN_EDIT_PHONE 40010
+#define BTN_DEL 40011
+#define BTN_DEL_MAIL 40012
+#define BTN_DELALL 40012
+#define BTN_DEL_PHONE 40013
+#define BTN_COPY_MAIL 40014
+#define BTN_COPY_PHONE 40015
+#define BTN_GOTO 40016
+#define BTN_OK 40017
+#define BTN_CANCEL 40018
+#define BTN_APPLY 40019
+#define CHECK_REMIND 40101
+#define CHECK_REMIND_MI 40102
+#define CHECK_REMIND_FLASHICON 40103
+#define CHECK_SMS 40104
+#define CHECK_REMIND_STARTUP 40104
+#define CHECK_OPT_DETECTUTF 40105
+#define CHECK_REMIND_SECURED 40105
+#define CHECK_REMIND_HIDDEN 40106
+#define CHECK_REMIND_VISIBLEONLY 40106
+#define CHECK_OPT_ICOVERSION 40108
+#define CHECK_OPT_HOMEPAGEICON 40108
+#define CHECK_OPT_CLR 40109
+#define CHECK_OPT_REPLACECONTACTS 40109
+#define CHECK_OPT_GROUPS 40110
+#define CHECK_OPT_EMAILICON 40110
+#define CHECK_OPT_SORTTREE 40111
+#define CHECK_OPT_PHONEICON 40111
+#define CHECK_OPT_READONLY 40112
+#define CHECK_OPT_ZODIACAVATAR 40112
+#define CHECK_OPT_CHANGEMYDETAILS 40113
+#define CHECK_OPT_METACPY 40114
+#define CHECK_OPT_AEROADAPTION 40114
+#define CHECK_OPT_METASCAN 40115
+#define CHECK_OPT_POPUP_WINCLR 40116
+#define CHECK_OPT_BUTTONICONS 40116
+#define CHECK_OPT_POPUP_DEFCLR 40117
+#define CHECK_OPT_POPUP_ENABLED 40118
+#define CHECK_OPT_POPUP_AWINCLR 40119
+#define CHECK_OPT_POPUP_ADEFCLR 40120
+#define RADIO_OPT_POPUP_DEFAULT 40121
+#define RADIO_OPT_POPUP_CUSTOM 40122
+#define RADIO_OPT_POPUP_PERMANENT 40123
+#define RADIO_MALE 40124
+#define CHECK_OPT_POPUP_ENABLED2 40124
+#define CHECK_OPT_POPUP_PROGRESS 40124
+#define RADIO_FEMALE 40125
+#define CHECK_OPT_POPUP_PROGRESS2 40125
+#define CHECK_OPT_POPUP_MSGBOX 40125
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 623
+#define _APS_NEXT_COMMAND_VALUE 40201
+#define _APS_NEXT_CONTROL_VALUE 1589
+#define _APS_NEXT_SYMED_VALUE 601
+#endif
+#endif
diff --git a/plugins/UserInfoEx/src/svc_avatar.cpp b/plugins/UserInfoEx/src/svc_avatar.cpp
new file mode 100644
index 0000000000..f9d5515f52
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_avatar.cpp
@@ -0,0 +1,247 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_avatar.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "m_protocols.h"
+#include "m_png.h"
+#include "m_avatars.h"
+
+namespace NServices
+{
+ namespace NAvatar
+ {
+
+ static HANDLE ghChangedHook = NULL;
+
+ static INT GetContactAvatarFileName(LPCTSTR zodiac, LPSTR szFileName, INT cchFileName)
+ {
+ if (!CallService(MS_DB_GETPROFILEPATH, (WPARAM)cchFileName, (LPARAM)szFileName))
+ {
+ size_t len = mir_strlen(szFileName);
+
+ CHAR tmp[64];
+
+ if (WideCharToMultiByte(CP_ACP, 0, zodiac, 64, tmp, SIZEOF(tmp),0,0) > 0)
+ {
+ mir_snprintf(szFileName + len, cchFileName - len, "\\avatars\\%s.png", tmp);
+ }
+
+ return !PathFileExistsA(szFileName);
+ }
+ return 1;
+ }
+
+ /**
+ *
+ *
+ **/
+ static VOID SetZodiacAvatar(HANDLE hContact)
+ {
+ MAnnivDate mtb;
+
+ // try to load birthday for contact
+ if (!mtb.DBGetBirthDate(hContact))
+ {
+ MZodiac zodiac;
+ //ICONINFO iinfo;
+ CHAR szFileName[MAX_PATH];
+
+ // get zodiac for birthday
+ zodiac = mtb.Zodiac();
+
+ if (!GetContactAvatarFileName(zodiac.pszName, szFileName, SIZEOF(szFileName)))
+ {
+ // extract the bitmap from the icon
+ //GetIconInfo(zodiac.hIcon, &iinfo);
+
+ // save the bitmap to a file used as avatar later
+ //if (!SaveBitmapAsAvatar(iinfo.hbmColor, szFileName))
+ {
+ if (!CallService(MS_AV_SETAVATAR, (WPARAM)hContact, (LPARAM)szFileName))
+ {
+ DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 1);
+ }
+ }
+ }
+ }
+ }
+
+ VOID DeleteAvatar(HANDLE hContact)
+ {
+ if (hContact && DB::Setting::GetByte(hContact, "ContactPhoto", "IsZodiac", FALSE))
+ {
+ //AVATARCACHEENTRY *ace;
+ LPSTR szProto = DB::Contact::Proto(hContact);
+
+ DB::Setting::Delete(hContact, "ContactPhoto", "File");
+ DB::Setting::Delete(hContact, "ContactPhoto", "RFile");
+ DB::Setting::Delete(hContact, "ContactPhoto", "Backup");
+ DB::Setting::Delete(hContact, "ContactPhoto", "ImageHash");
+
+ DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+
+ /*
+ ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, NULL, (LPARAM)szProto);
+ if (ace)
+ {
+ if (!CallService(MS_AV_SETAVATAR, (WPARAM)hContact, (LPARAM)ace->szFilename))
+ {
+ DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+ }
+ }
+ */
+ }
+ }
+
+
+ /**
+ *
+ *
+ **/
+ static INT OnAvatarChanged(HANDLE hContact, AVATARCACHEENTRY *ace)
+ {
+ if (hContact)
+ {
+ // check valid parameters
+ if (ace)
+ {
+ if (// check for correct structure
+ ace->cbSize == sizeof(AVATARCACHEENTRY) &&
+ // set zodiac as avatar either if the desired avatar is invalid or a general protocol picture
+ ((ace->dwFlags & AVS_PROTOPIC) || !(ace->dwFlags & AVS_BITMAP_VALID)))
+ {
+ if (!DB::Setting::GetByte(hContact, "ContactPhoto", "IsZodiac", 0))
+ {
+ SetZodiacAvatar(hContact);
+ }
+ }
+ else
+ {
+ DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+ }
+ }
+
+ // avatar was deleted, so we can set up a zodiac avatar
+ else
+ {
+ SetZodiacAvatar(hContact);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID Enable(BOOLEAN bEnable)
+ {
+ HANDLE hContact;
+ DBVARIANT dbv;
+
+ if (bEnable && !ghChangedHook)
+ {
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ // don't set if avatar is locked!
+ if (!DB::Setting::GetByte(hContact, "ContactPhoto", "Locked", 0))
+ {
+ BOOLEAN bInvalidAvatar = TRUE;
+
+ // the relative file is valid
+ if (!DB::Setting::GetAString(hContact, "ContactPhoto", "RFile", &dbv))
+ {
+ CHAR absolute[MAX_PATH];
+ absolute[0] = '\0';
+
+ // check if file exists
+ if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)dbv.pszVal, (LPARAM)absolute))
+ {
+ FILE *f = fopen(absolute, "rb");
+ if (f) {
+ bInvalidAvatar = FALSE;
+ fclose(f);
+ }
+ }
+ DB::Variant::Free(&dbv);
+ }
+
+ // the absolute file is valid
+ if (bInvalidAvatar && !DBGetContactSetting(hContact, "ContactPhoto", "File", &dbv))
+ {
+ FILE *f = fopen(dbv.pszVal, "rb");
+ if (f) {
+ bInvalidAvatar = FALSE;
+ fclose(f);
+ }
+ DB::Variant::Free(&dbv);
+ }
+
+ // set the zodiac as avatar
+ if (bInvalidAvatar) {
+ SetZodiacAvatar(hContact);
+ }
+ }
+ }
+ ghChangedHook = HookEvent(ME_AV_AVATARCHANGED, (MIRANDAHOOK) OnAvatarChanged);
+ }
+ else if (!bEnable && ghChangedHook)
+ {
+ UnhookEvent(ghChangedHook);
+ ghChangedHook = NULL;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ DeleteAvatar(hContact);
+ }
+ }
+ }
+
+
+ /**
+ * name: OnModulesLoaded
+ * desc: initialize stuff, which require all standard modules to bee loaded
+ * params: none
+ * return: 0
+ **/
+ VOID OnModulesLoaded()
+ {
+ Enable(DB::Setting::GetByte(SET_ZODIAC_AVATARS, FALSE));
+ }
+
+ } /* namespace NAvatar */
+} /* namespace NServices */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_avatar.h b/plugins/UserInfoEx/src/svc_avatar.h
new file mode 100644
index 0000000000..acf00a83e9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_avatar.h
@@ -0,0 +1,41 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_avatar.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCAVATAR_H_INCLUDED_
+#define _UINFOEX_SVCAVATAR_H_INCLUDED_
+
+namespace NServices
+{
+ namespace NAvatar
+ {
+ VOID Enable (BOOLEAN bEnable);
+ VOID OnModulesLoaded ();
+ }
+} /* namespace NServices */
+
+#endif /* _UINFOEX_SVCAVATAR_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_constants.cpp b/plugins/UserInfoEx/src/svc_constants.cpp
new file mode 100644
index 0000000000..ed803f79f4
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_constants.cpp
@@ -0,0 +1,436 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_constants.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+static IDSTRLIST TmplLanguages[] = {
+ { 0, LPGEN("Unspecified"), 0},
+ {55, LPGEN("Afrikaans"), 0},
+ {58, LPGEN("Albanian"), 0},
+ { 1, LPGEN("Arabic"), 0},
+ {59, LPGEN("Armenian"), 0},
+ {68, LPGEN("Azerbaijani"), 0},
+ {72, LPGEN("Belorussian"), 0},
+ { 2, LPGEN("Bhojpuri"), 0},
+ {56, LPGEN("Bosnian"), 0},
+ { 3, LPGEN("Bulgarian"), 0},
+ { 4, LPGEN("Burmese"), 0},
+ { 5, LPGEN("Cantonese"), 0},
+ { 6, LPGEN("Catalan"), 0},
+ {61, LPGEN("Chamorro"), 0},
+ { 7, LPGEN("Chinese"), 0},
+ { 8, LPGEN("Croatian"), 0},
+ { 9, LPGEN("Czech"), 0},
+ {10, LPGEN("Danish"), 0},
+ {11, LPGEN("Dutch"), 0},
+ {12, LPGEN("English"), 0},
+ {13, LPGEN("Esperanto"), 0},
+ {14, LPGEN("Estonian"), 0},
+ {15, LPGEN("Farsi"), 0},
+ {16, LPGEN("Finnish"), 0},
+ {17, LPGEN("French"), 0},
+ {18, LPGEN("Gaelic"), 0},
+ {19, LPGEN("German"), 0},
+ {20, LPGEN("Greek"), 0},
+ {70, LPGEN("Gujarati"), 0},
+ {21, LPGEN("Hebrew"), 0},
+ {22, LPGEN("Hindi"), 0},
+ {23, LPGEN("Hungarian"), 0},
+ {24, LPGEN("Icelandic"), 0},
+ {25, LPGEN("Indonesian"), 0},
+ {26, LPGEN("Italian"), 0},
+ {27, LPGEN("Japanese"), 0},
+ {28, LPGEN("Khmer"), 0},
+ {29, LPGEN("Korean"), 0},
+ {69, LPGEN("Kurdish"), 0},
+ {30, LPGEN("Lao"), 0},
+ {31, LPGEN("Latvian"), 0},
+ {32, LPGEN("Lithuanian"), 0},
+ {65, LPGEN("Macedonian"), 0},
+ {33, LPGEN("Malay"), 0},
+ {63, LPGEN("Mandarin"), 0},
+ {62, LPGEN("Mongolian"), 0},
+ {34, LPGEN("Norwegian"), 0},
+ {57, LPGEN("Persian"), 0},
+ {35, LPGEN("Polish"), 0},
+ {36, LPGEN("Portuguese"), 0},
+ {60, LPGEN("Punjabi"), 0},
+ {37, LPGEN("Romanian"), 0},
+ {38, LPGEN("Russian"), 0},
+ {39, LPGEN("Serbo-Croatian"), 0},
+ {66, LPGEN("Sindhi"), 0},
+ {40, LPGEN("Slovak"), 0},
+ {41, LPGEN("Slovenian"), 0},
+ {42, LPGEN("Somali"), 0},
+ {43, LPGEN("Spanish"), 0},
+ {44, LPGEN("Swahili"), 0},
+ {45, LPGEN("Swedish"), 0},
+ {46, LPGEN("Tagalog"), 0},
+ {64, LPGEN("Taiwanese"), 0},
+ {71, LPGEN("Tamil"), 0},
+ {47, LPGEN("Tatar"), 0},
+ {48, LPGEN("Thai"), 0},
+ {49, LPGEN("Turkish"), 0},
+ {50, LPGEN("Ukrainian"), 0},
+ {51, LPGEN("Urdu"), 0},
+ {52, LPGEN("Vietnamese"), 0},
+ {67, LPGEN("Welsh"), 0},
+ {53, LPGEN("Yiddish"), 0},
+ {54, LPGEN("Yoruba"), 0},
+};
+
+static IDSTRLIST TmplOccupations[] = {
+ { 0, LPGEN("Unspecified"), 0},
+ { 1, LPGEN("Academic"), 0},
+ { 2, LPGEN("Administrative"), 0},
+ { 3, LPGEN("Art/Entertainment"), 0},
+ { 4, LPGEN("College Student"), 0},
+ { 5, LPGEN("Computers"), 0},
+ { 6, LPGEN("Community & Social"), 0},
+ { 7, LPGEN("Education"), 0},
+ { 8, LPGEN("Engineering"), 0},
+ { 9, LPGEN("Financial Services"), 0},
+ {10, LPGEN("Government"), 0},
+ {11, LPGEN("High School Student"), 0},
+ {12, LPGEN("Home"), 0},
+ {13, LPGEN("ICQ - Providing Help"), 0},
+ {14, LPGEN("Law"), 0},
+ {15, LPGEN("Managerial"), 0},
+ {16, LPGEN("Manufacturing"), 0},
+ {17, LPGEN("Medical/Health"), 0},
+ {18, LPGEN("Military"), 0},
+ {19, LPGEN("Non-Government Organization"), 0},
+ {20, LPGEN("Professional"), 0},
+ {21, LPGEN("Retail"), 0},
+ {22, LPGEN("Retired"), 0},
+ {23, LPGEN("Science & Research"), 0},
+ {24, LPGEN("Sports"), 0},
+ {25, LPGEN("Technical"), 0},
+ {26, LPGEN("University Student"), 0},
+ {27, LPGEN("Web Building"), 0},
+ {99, LPGEN("Other Services"), 0}
+};
+
+static IDSTRLIST TmplInterests[] = {
+ { 0, LPGEN("Unspecified"), 0},
+ {100, LPGEN("Art"), 0},
+ {101, LPGEN("Cars"), 0},
+ {102, LPGEN("Celebrity Fans"), 0},
+ {103, LPGEN("Collections"), 0},
+ {104, LPGEN("Computers"), 0},
+ {105, LPGEN("Culture & Literature"), 0},
+ {106, LPGEN("Fitness"), 0},
+ {107, LPGEN("Games"), 0},
+ {108, LPGEN("Hobbies"), 0},
+ {109, LPGEN("ICQ - Providing Help"), 0},
+ {110, LPGEN("Internet"), 0},
+ {111, LPGEN("Lifestyle"), 0},
+ {112, LPGEN("Movies/TV"), 0},
+ {113, LPGEN("Music"), 0},
+ {114, LPGEN("Outdoor Activities"), 0},
+ {115, LPGEN("Parenting"), 0},
+ {116, LPGEN("Pets/Animals"), 0},
+ {117, LPGEN("Religion"), 0},
+ {118, LPGEN("Science/Technology"), 0},
+ {119, LPGEN("Skills"), 0},
+ {120, LPGEN("Sports"), 0},
+ {121, LPGEN("Web Design"), 0},
+ {122, LPGEN("Nature and Environment"), 0},
+ {123, LPGEN("News & Media"), 0},
+ {124, LPGEN("Government"), 0},
+ {125, LPGEN("Business & Economy"), 0},
+ {126, LPGEN("Mystics"), 0},
+ {127, LPGEN("Travel"), 0},
+ {128, LPGEN("Astronomy"), 0},
+ {129, LPGEN("Space"), 0},
+ {130, LPGEN("Clothing"), 0},
+ {131, LPGEN("Parties"), 0},
+ {132, LPGEN("Women"), 0},
+ {133, LPGEN("Social science"), 0},
+ {134, LPGEN("60's"), 0},
+ {135, LPGEN("70's"), 0},
+ {136, LPGEN("80's"), 0},
+ {137, LPGEN("50's"), 0},
+ {138, LPGEN("Finance and corporate"), 0},
+ {139, LPGEN("Entertainment"), 0},
+ {140, LPGEN("Consumer electronics"), 0},
+ {141, LPGEN("Retail stores"), 0},
+ {142, LPGEN("Health and beauty"), 0},
+ {143, LPGEN("Media"), 0},
+ {144, LPGEN("Household products"), 0},
+ {145, LPGEN("Mail order catalog"), 0},
+ {146, LPGEN("Business services"), 0},
+ {147, LPGEN("Audio and visual"), 0},
+ {148, LPGEN("Sporting and athletic"), 0},
+ {149, LPGEN("Publishing"), 0},
+ {150, LPGEN("Home automation"), 0}
+};
+
+static IDSTRLIST TmplAffiliations[] = {
+ { 0, LPGEN("Unspecified"), 0},
+ {200, LPGEN("Alumni Org."), 0},
+ {201, LPGEN("Charity Org."), 0},
+ {202, LPGEN("Club/Social Org."), 0},
+ {203, LPGEN("Community Org."), 0},
+ {204, LPGEN("Cultural Org."), 0},
+ {205, LPGEN("Fan Clubs"), 0},
+ {206, LPGEN("Fraternity/Sorority"), 0},
+ {207, LPGEN("Hobbyists Org."), 0},
+ {208, LPGEN("International Org."), 0},
+ {209, LPGEN("Nature and Environment Org."), 0},
+ {210, LPGEN("Professional Org."), 0},
+ {211, LPGEN("Scientific/Technical Org."), 0},
+ {212, LPGEN("Self Improvement Group"), 0},
+ {213, LPGEN("Spiritual/Religious Org."), 0},
+ {214, LPGEN("Sports Org."), 0},
+ {215, LPGEN("Support Org."), 0},
+ {216, LPGEN("Trade and Business Org."), 0},
+ {217, LPGEN("Union"), 0},
+ {218, LPGEN("Volunteer Org."), 0},
+ {299, LPGEN("Other"), 0},
+};
+
+static IDSTRLIST TmplPast[] = {
+ { 0, LPGEN("Unspecified"), 0},
+ {300, LPGEN("Elementary School"), 0},
+ {301, LPGEN("High School"), 0},
+ {302, LPGEN("College"), 0},
+ {303, LPGEN("University"), 0},
+ {304, LPGEN("Military"), 0},
+ {305, LPGEN("TmplPast Work Place"), 0},
+ {306, LPGEN("TmplPast Organization"), 0},
+ {399, LPGEN("Other"), 0}
+};
+
+static IDSTRLIST TmplMarital[]={
+ { 0, LPGEN("Unspecified"), 0},
+ {10, LPGEN("Single"), 0},
+ {11, LPGEN("Close relationships"), 0},
+ {12, LPGEN("Engaged"), 0},
+ {20, LPGEN("Married"), 0},
+ {30, LPGEN("Divorced"), 0},
+ {31, LPGEN("Separated"), 0},
+ {40, LPGEN("Widowed"), 0}
+};
+
+static IDSTRLIST TmplPrefixes[]={
+ { 0, LPGEN("Unspecified"), 0},
+ {'j', LPGEN("jun."), 0},
+ {'s', LPGEN("sen."), 0}
+};
+
+static IDSTRLIST *MyCountries = NULL;
+static UINT MyCountriesCount = 0;
+
+/**
+ * This is a sort procedure, which compares two items of an IDSTRLIST array.
+ * It is used by qsort in SvcConstantsTranslateList and cares about the
+ * locale, which was set up in OS. This prevents e.g. , to be put onto
+ * the end of the list., but being sorted to the right position.
+ *
+ * @param p1 - (LPIDSTRLIST) first item to compare
+ * @param p2 - (LPIDSTRLIST) second item to compare
+ *
+ * returns -1, 0, 1 according to the comparison result of _tcscmp.
+ **/
+static int __cdecl ListSortProc(const LPIDSTRLIST p1, const LPIDSTRLIST p2)
+{
+ return lstrcmpi(p1->ptszTranslated, p2->ptszTranslated);
+}
+
+/**
+ * Translates the text of each item of an IDStrinList to users locale
+ * language and saves result in szTranslated member for later use and
+ * faster access to translated strings later.
+ *
+ * @param pList - pointer to list to translate
+ * @param nListCount - number of list items
+ *
+ * @return nothing
+ **/
+static VOID SvcConstantsTranslateList(LPIDSTRLIST pList, UINT nListCount/*, SortedList *pSorted*/)
+{
+ if (!pList[0].ptszTranslated)
+ {
+ for (UINT i = 0; i < nListCount; i++)
+ {
+ pList[i].ptszTranslated = (LPTSTR)CallService(MS_LANGPACK_PCHARTOTCHAR, 0, (LPARAM)pList[i].pszText);
+ }
+ // Ignore last item, if it is a "Other" item.
+ if (!strcmp(pList[nListCount-1].pszText, LPGEN("Other"))) nListCount--;
+
+ // Sort list according translated text and ignore first item.
+ qsort(pList+1, nListCount-1, sizeof(pList[0]),
+ (INT (*)(const VOID*, const VOID*))ListSortProc);
+ }
+}
+
+/**
+ * This function uses the country list provided by the core to create ower own one.
+ * The core's list is extended by a translated value. The cached translation is meant
+ * to improve speed uppon adding items to a combobox.
+ *
+ * @param pList - LPIDSTRLIST pointer, which retrieves the list pointer.
+ * @param pnListSize - pointer to an unsigned integer, which retrieves the number of items.
+ *
+ * @retval MIR_OK - indicates success
+ * @retval MIR_FAIL - indicates error
+ **/
+INT_PTR GetCountryList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ INT_PTR rc = MIR_OK;
+ if (!MyCountries)
+ {
+ struct CountryListEntry *country;
+
+ if (!CallService(MS_UTILS_GETCOUNTRYLIST, (WPARAM)&MyCountriesCount, (LPARAM)&country))
+ {
+ MyCountries = (IDSTRLIST*)mir_alloc(MyCountriesCount * sizeof(IDSTRLIST));
+ if (MyCountries)
+ {
+ for (UINT i = 0; i < MyCountriesCount; i++)
+ {
+ MyCountries[i].nID = country[i].id;
+ MyCountries[i].pszText = country[i].szName;
+ MyCountries[i].ptszTranslated = (LPTSTR)CallService(MS_LANGPACK_PCHARTOTCHAR, 0, (LPARAM)country[i].szName);
+ }
+ // Sort list according translated text and ignore first item.
+ qsort(MyCountries+1, MyCountriesCount-1, sizeof(MyCountries[0]),
+ (INT (*)(const VOID*, const VOID*))ListSortProc);
+ }
+ else
+ {
+ rc = MIR_FAIL;
+ }
+ }
+ else
+ {
+ rc = MIR_FAIL;
+ }
+ }
+ *pnListSize = MyCountriesCount;
+ *pList = MyCountries;
+ return rc;
+}
+
+INT_PTR GetMaritalList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplMarital);
+ *pList = TmplMarital;
+ SvcConstantsTranslateList(TmplMarital, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetLanguageList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplLanguages);
+ *pList = TmplLanguages;
+ SvcConstantsTranslateList(TmplLanguages, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetOccupationList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplOccupations);
+ *pList = TmplOccupations;
+ SvcConstantsTranslateList(TmplOccupations, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetInterestsList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplInterests);
+ *pList = TmplInterests;
+ SvcConstantsTranslateList(TmplInterests, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetPastList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplPast);
+ *pList = TmplPast;
+ SvcConstantsTranslateList(TmplPast, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetAffiliationsList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplAffiliations);
+ *pList = TmplAffiliations;
+ SvcConstantsTranslateList(TmplAffiliations, *pnListSize);
+ return MIR_OK;
+}
+
+INT_PTR GetNamePrefixList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+ *pnListSize = SIZEOF(TmplPrefixes);
+ *pList = TmplPrefixes;
+ SvcConstantsTranslateList(TmplPrefixes, *pnListSize);
+ return MIR_OK;
+}
+
+VOID SvcConstantsLoadModule(VOID)
+{
+ UINT nListSize;
+ LPIDSTRLIST pList;
+
+ // precache translation
+ GetMaritalList(&nListSize, &pList);
+ GetLanguageList(&nListSize, &pList);
+ GetCountryList(&nListSize, &pList);
+ GetOccupationList(&nListSize, &pList);
+ GetInterestsList(&nListSize, &pList);
+ GetPastList(&nListSize, &pList);
+ GetAffiliationsList(&nListSize, &pList);
+ GetNamePrefixList(&nListSize, &pList);
+}
+
+static VOID FORCEINLINE SvcConstantsClearList(UINT pnListSize, LPIDSTRLIST pList)
+{
+ if (pList)
+ {
+ for (UINT i = 0; i < pnListSize; i++)
+ {
+ MIR_FREE(pList[i].ptszTranslated);
+ }
+ }
+}
+
+VOID SvcConstantsUnloadModule(VOID)
+{
+ SvcConstantsClearList(SIZEOF(TmplMarital), TmplMarital);
+ SvcConstantsClearList(SIZEOF(TmplLanguages), TmplLanguages);
+ SvcConstantsClearList(SIZEOF(TmplOccupations), TmplOccupations);
+ SvcConstantsClearList(SIZEOF(TmplInterests), TmplInterests);
+ SvcConstantsClearList(SIZEOF(TmplPast), TmplPast);
+ SvcConstantsClearList(SIZEOF(TmplAffiliations), TmplAffiliations);
+ SvcConstantsClearList(SIZEOF(TmplPrefixes), TmplPrefixes);
+ SvcConstantsClearList(MyCountriesCount, MyCountries);
+ MIR_FREE(MyCountries);
+} \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_constants.h b/plugins/UserInfoEx/src/svc_constants.h
new file mode 100644
index 0000000000..d2c5a5c135
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_constants.h
@@ -0,0 +1,195 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_constants.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_CONSTANTS_INCLUDED_
+#define _SVC_CONSTANTS_INCLUDED_
+
+#define MODULELONGNAME "Extended UserInfo"
+#define USERINFO "UserInfo"
+#define MODNAME "UserInfoEx"
+#define MODNAMEFLAGS "Flags"
+
+#define MODULELONGNAMET _T(MODULELONGNAME)
+#define MODNAMET _T(MODNAME)
+
+#define MAXDATASIZE 1024 // maximum character count of most static, temporary, ...., strings
+#define MAXCATLEN 64 // maximum character count for a category string (phone, email, interest, ...)
+#define MAXSETTING 255 // maximum character count for a setting string
+#define MAXNAME 260 // maximum character count for a username
+#define MAXUID 260 // maximum character count for a uin
+
+// most important modules
+#define MOD_MBIRTHDAY "mBirthday"
+#define MOD_CLIST "CList"
+
+// database settings (propertysheet)
+#define SET_PROPSHEET_PCBIREADONLY "PBCIReadOnly"
+#define SET_PROPSHEET_READONLYLABEL "TILReadonly"
+#define SET_PROPSHEET_AEROADAPTION "AeroAdaption"
+#define SET_PROPSHEET_SHOWCOLOURS "ShowColours"
+#define SET_PROPSHEET_CLRNORMAL "colourNormal"
+#define SET_PROPSHEET_CLRCUSTOM "colourUser"
+#define SET_PROPSHEET_CLRBOTH "colourBoth"
+#define SET_PROPSHEET_CLRCHANGED "colourChanged"
+#define SET_PROPSHEET_CLRMETA "colourMeta"
+#define SET_PROPSHEET_SAVEVCARD "vCardOnExit"
+#define SET_PROPSHEET_GROUPS "TreeGroups"
+#define SET_PROPSHEET_SORTITEMS "TreeSortItems"
+#define SET_PROPSHEET_CHANGEMYDETAILS "ChangeMyDetails"
+#define SET_ABOUT_ACTIVEWINDOW "AboutActiveWin"
+#define SET_MI_MAIN "miMenu"
+#define SET_MI_CONTACT "miContact"
+#define SET_MI_GROUP "miGroup"
+#define SET_MI_SUBGROUP "miSubGroup"
+#define SET_MI_STATUS "miStatus"
+#define SET_MI_ACCOUNT "miAccount"
+
+#define SET_EXTENDED_EMAILSERVICE "emailEx"
+#define SET_CLIST_EXTRAICON_GENDER "GenderColumn"
+#define SET_CLIST_EXTRAICON_GENDER2 "cliGender"
+#define SET_CLIST_EXTRAICON_COUNTRY "CountryColumn"
+#define SET_CLIST_EXTRAICON_HOMEPAGE "cliHomepage"
+#define SET_CLIST_EXTRAICON_EMAIL "cliEmail"
+#define SET_CLIST_EXTRAICON_PHONE "cliPhone"
+#define SET_CLIST_EXTRAICON_FLAGS2 "cliFlags"
+#define SET_OPT_AUTOTIMEZONE "AutoTimezone"
+#define SET_ZODIAC_AVATARS "ZodicAvatars"
+#define SET_META_SCAN "MetaScan"
+// database settings (general psp)
+#define SET_ME_PASSWORD "Password"
+#define SET_CONTACT_TITLE "Title"
+#define SET_CONTACT_FIRSTNAME "FirstName"
+#define SET_CONTACT_SECONDNAME "SecondName"
+#define SET_CONTACT_LASTNAME "LastName"
+#define SET_CONTACT_FIRSTLASTNAME "FullName"
+#define SET_CONTACT_PREFIX "Prefix"
+#define SET_CONTACT_NICK "Nick"
+#define SET_CONTACT_MYHANDLE "MyHandle"
+#define SET_CONTACT_STREET "Street"
+#define SET_CONTACT_ZIP "Zip"
+#define SET_CONTACT_CITY "City"
+#define SET_CONTACT_STATE "State"
+#define SET_CONTACT_COUNTRY "Country"
+#define SET_CONTACT_GENDER "Gender"
+// database settings (advanced psp)
+#define SET_CONTACT_ORIGIN_STREET "OriginStreet"
+#define SET_CONTACT_ORIGIN_ZIP "OriginZip"
+#define SET_CONTACT_ORIGIN_CITY "OriginCity"
+#define SET_CONTACT_ORIGIN_STATE "OriginState"
+#define SET_CONTACT_ORIGIN_COUNTRY "OriginCountry"
+#define SET_CONTACT_LANG1 "Language1"
+#define SET_CONTACT_LANG2 "Language2"
+#define SET_CONTACT_LANG3 "Language3"
+#define SET_CONTACT_MARITAL "MaritalStatus"
+#define SET_CONTACT_PARTNER "Partner"
+#define SET_CONTACT_ANNIVERSARY "Anniv"
+#define SET_CONTACT_AGE "Age"
+#define SET_CONTACT_TIMEZONE "Timezone"
+#define SET_CONTACT_TIMEZONENAME "TzName"
+#define SET_CONTACT_TIMEZONEINDEX "TzIndex"
+#define SET_CONTACT_BIRTH "Birth"
+#define SET_CONTACT_BIRTHDAY "BirthDay"
+#define SET_CONTACT_BIRTHMONTH "BirthMonth"
+#define SET_CONTACT_BIRTHYEAR "BirthYear"
+#define SET_CONTACT_DOBD "DOBd"
+#define SET_CONTACT_DOBM "DOBm"
+#define SET_CONTACT_DOBY "DOBy"
+// database settings (company psp)
+#define SET_CONTACT_COMPANY_POSITION "CompanyPosition"
+#define SET_CONTACT_COMPANY_OCCUPATION "CompanyOccupation"
+#define SET_CONTACT_COMPANY_SUPERIOR "CompanySuperior"
+#define SET_CONTACT_COMPANY_ASSISTENT "CompanyAssistent"
+#define SET_CONTACT_COMPANY "Company"
+#define SET_CONTACT_COMPANY_DEPARTMENT "CompanyDepartment"
+#define SET_CONTACT_COMPANY_OFFICE "CompanyOffice"
+#define SET_CONTACT_COMPANY_STREET "CompanyStreet"
+#define SET_CONTACT_COMPANY_ZIP "CompanyZip"
+#define SET_CONTACT_COMPANY_CITY "CompanyCity"
+#define SET_CONTACT_COMPANY_STATE "CompanyState"
+#define SET_CONTACT_COMPANY_COUNTRY "CompanyCountry"
+#define SET_CONTACT_COMPANY_HOMEPAGE "CompanyHomepage"
+// database settings (about psp)
+#define SET_CONTACT_ABOUT "About"
+#define SET_CONTACT_MYNOTES "MyNotes"
+// database settings (... psp)
+#define SET_CONTACT_PHONE "Phone"
+#define SET_CONTACT_FAX "Fax"
+#define SET_CONTACT_CELLULAR "Cellular"
+#define SET_CONTACT_EMAIL "e-mail"
+#define SET_CONTACT_EMAIL0 "e-mail0"
+#define SET_CONTACT_EMAIL1 "e-mail1"
+#define SET_CONTACT_HOMEPAGE "Homepage"
+#define SET_CONTACT_COMPANY_PHONE "CompanyPhone"
+#define SET_CONTACT_COMPANY_FAX "CompanyFax"
+#define SET_CONTACT_COMPANY_CELLULAR "CompanyCellular"
+#define SET_CONTACT_COMPANY_EMAIL "Companye-mail"
+#define SET_CONTACT_COMPANY_EMAIL0 "Companye-mail0"
+#define SET_CONTACT_COMPANY_EMAIL1 "Companye-mail1"
+
+#define SET_CONTACT_MYPHONE_VAL "MyPhone%d"
+#define SET_CONTACT_MYPHONE_CAT "MyPhone%dCat"
+#define SET_CONTACT_COMPANY_MYPHONE_VAL "MyCompanyPhone%d"
+#define SET_CONTACT_COMPANY_MYPHONE_CAT "MyCompanyPhone%dCat"
+#define SET_CONTACT_MYEMAIL_VAL "Mye-mail%d"
+#define SET_CONTACT_MYEMAIL_CAT "Mye-mail%dCat"
+#define SET_CONTACT_COMPANY_MYEMAIL_VAL "MyCompanye-mail%d"
+#define SET_CONTACT_COMPANY_MYEMAIL_CAT "MyCompanye-mail%dCat"
+
+#define SET_CONTACT_ADDEDTIME "ContactAddTime"
+// default values for some of the options
+#define DEFVAL_GETCONTACTINFO_ENABLED 1
+
+#define DEFVAL_CLIST_EXTRAICON_GENDER 2
+#define DEFVAL_CLIST_EXTRAICON_COUNTRY 3
+#define DEFVAL_CLIST_EXTRAICON_HOMEPAGE 1
+#define DEFVAL_CLIST_EXTRAICON_EMAIL 1
+#define DEFVAL_CLIST_EXTRAICON_PHONE 1
+
+typedef struct CIDList
+{
+ INT nID;
+ LPCSTR pszText;
+ LPTSTR ptszTranslated;
+
+} IDSTRLIST, *LPIDSTRLIST;
+
+INT_PTR GetMaritalList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetLanguageList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetCountryList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetOccupationList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetInterestsList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetPastList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetAffiliationsList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetNamePrefixList(LPUINT pListSize, LPIDSTRLIST *pList);
+
+VOID SvcConstantsLoadModule(VOID);
+VOID SvcConstantsUnloadModule(VOID);
+
+#endif /* _SVC_CONSTANTS_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_contactinfo.cpp b/plugins/UserInfoEx/src/svc_contactinfo.cpp
new file mode 100644
index 0000000000..6a54880e67
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_contactinfo.cpp
@@ -0,0 +1,798 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_contactinfo.cpp $
+Revision : $Revision: 203 $
+Last change on : $Date: 2010-09-26 18:21:04 +0400 (Вс, 26 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "m_contacts.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+#include "svc_contactinfo.h"
+#include "svc_gender.h"
+#include "Flags\svc_countrylistext.h"
+
+#define CI_TCHAR(ci) (((ci)->dwFlag & CNF_UNICODE) ? DBVT_WCHAR : DBVT_ASCIIZ)
+
+#define NAMEORDERCOUNT 8
+static BYTE gNameOrder[NAMEORDERCOUNT]; // name order as set up for contact list
+
+/**
+ * This function translates the DBVARIANT structure to an CONTACTINFO structure
+ * and keeps the original data type.
+ *
+ * @warning ci MUST NOT be NULL and dbv must be freed by caller on failure!
+ *
+ * @param dbv - DBVARIANT to take the data for translation from
+ * @param ci - CONTACTINFO structure to translate to
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+static FORCEINLINE INT_PTR VarToVarCI(const DBVARIANT *dbv, CONTACTINFO *ci)
+{
+ switch (dbv->type) {
+ case DBVT_ASCIIZ:
+ case DBVT_WCHAR: {
+ // string translation is to be done by caller!!!
+ ci->pszVal = dbv->ptszVal;
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ } break;
+
+ case DBVT_BYTE: {
+ ci->type = CNFT_BYTE;
+ ci->bVal = dbv->bVal;
+ } break;
+
+ case DBVT_WORD: {
+ ci->type = CNFT_WORD;
+ ci->wVal = dbv->wVal;
+ } break;
+
+ case DBVT_DWORD: {
+ ci->type = CNFT_DWORD;
+ ci->dVal = dbv->dVal;
+ } break;
+
+ default: {
+ ci->type = 0;
+ }
+ }
+ return ci->type == 0;
+}
+
+/**
+ * This function tries to read a setting from the contact's protocol module.
+ *
+ * @warning ci MUST NOT be NULL!
+ *
+ * @param ci - pointer to a CONTACTINFO structure holding information about the contact
+ * @param pszSetting - the desired setting to read
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIVar(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+
+ if (DB::Setting::Get(ci->hContact, ci->szProto, pszSetting, &dbv, CI_TCHAR(ci)) == 0) {
+ if (VarToVarCI(&dbv, ci)) {
+ // On a error, we need to make sure, read data is cleared out!
+ DB::Variant::Free(&dbv);
+ }
+ }
+ else {
+ ci->type = 0;
+ }
+ return ci->type == 0;
+}
+
+/**
+ * This function tries to read a setting from a certain module (e.g. USERINFO) and if failed it
+ * tries once again with the baseprotocol of the contact (e.g. ICQ). If nothing was found anyway
+ * and this is an metacontact it can have a look into subcontacts to retrieve the information.
+ * This depends on the settings the user did.
+ *
+ * @warning ci MUST NOT be NULL!
+ *
+ * @param ci - pointer to a CONTACTINFO structure holding information about the contact
+ * @param pszSetting - the desired setting to read
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIVarEx(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+ DBVARIANT dbv;
+
+ if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, pszSetting, &dbv, CI_TCHAR(ci)) == 0) {
+ if (VarToVarCI(&dbv, ci)) {
+ // On a error, we need to make sure, read data is cleared out!
+ DB::Variant::Free(&dbv);
+ }
+ }
+ else {
+ ci->type = 0;
+ }
+ return ci->type == 0;
+}
+
+/**
+ * This function tries to read a Language from a certain module (e.g. USERINFO) and if failed it
+ * tries once again with the baseprotocol of the contact (e.g. ICQ). If nothing was found anyway
+ * and this is an metacontact it can have a look into subcontacts to retrieve the information.
+ * This depends on the settings the user did.
+ *
+ * @warning ci MUST NOT be NULL!
+ *
+ * @param ci - pointer to a CONTACTINFO structure holding information about the contact
+ * @param pszSetting - the desired setting to read
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCILangEx(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+ if (0 == GCIVarEx(ci, pszSetting)) {
+ if(ci->type!= CNFT_ASCIIZ) {
+ //lang was safed in database as code
+ LPIDSTRLIST pList;
+ UINT nList, i, res = 0;
+ switch (ci->type) {
+ case CNFT_BYTE: res = ci->bVal; break;
+ case CNFT_WORD: res = ci->wVal; break;
+ case CNFT_DWORD: res = ci->dVal; break;
+ default: return 1;
+ }
+ GetLanguageList(&nList, &pList);
+ for(i = 0; i<nList; i++) {
+ if(pList[i].nID == res) {
+ //use untranslate string (pszText member)
+ ci->pszVal = (ci->dwFlag & CNF_UNICODE)
+ ? (LPTSTR) mir_a2u(pList[i].pszText)
+ : (LPTSTR) mir_strdup(pList[i].pszText);
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ return 0;
+ }
+ } /*end for*/
+ ci->type = 0;
+ ci->pszVal = NULL;
+ }
+ }
+ else {
+ ci->type = 0;
+ }
+ return ci->type == 0;
+}
+
+/**
+ * This function read a setting from the baseprotocol of the contact (e.g. ICQ).
+ *
+ * @warning ci MUST NOT be NULL!
+ *
+ * @param ci - pointer to a CONTACTINFO structure holding information about the contact
+ * @param pszSetting - the desired setting to read
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIStr(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+ const BYTE type = CI_TCHAR(ci);
+ DBVARIANT dbv;
+
+ if (DB::Setting::Get(ci->hContact, ci->szProto, pszSetting, &dbv, type) == 0) {
+ if (DB::Variant::dbv2String(&dbv, type) == 0) {
+ ci->pszVal = dbv.ptszVal;
+ }
+ else {
+ DB::Variant::Free(&dbv);
+ ci->pszVal = NULL;
+ }
+ }
+ else {
+ ci->pszVal = NULL;
+ }
+ ci->type = (ci->pszVal) ? CNFT_ASCIIZ : 0;
+ return ci->type == 0;
+}
+
+/**
+ * Format the full name for the contact.
+ *
+ * @params ci - CONTACTINFO structure
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIFirstLast(CONTACTINFO *ci)
+{
+ DBVARIANT dbvf, dbvl;
+ size_t cbf, cbl;
+
+ BYTE type = CI_TCHAR(ci);
+
+ if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, SET_CONTACT_FIRSTNAME, &dbvf, type))
+ {
+ dbvf.type = DBVT_DELETED;
+ }
+ if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, SET_CONTACT_LASTNAME, &dbvl, type))
+ {
+ dbvl.type = DBVT_DELETED;
+ }
+
+ if (type == DBVT_WCHAR)
+ {
+ // both firstname and lastname are valid
+ if (dbvf.type == DBVT_WCHAR && dbvl.type == DBVT_WCHAR)
+ {
+ cbf = mir_wcslen(dbvf.pwszVal);
+ cbl = mir_wcslen(dbvl.pwszVal);
+
+ ci->pszVal = (LPTSTR) mir_alloc((cbf + cbl + 2) * sizeof(WCHAR));
+ if (ci->pszVal)
+ {
+ mir_snwprintf((LPWSTR) ci->pszVal, cbf + cbl + 2, L"%s %s", dbvf.pwszVal, dbvl.pwszVal);
+ }
+ DB::Variant::Free(&dbvf);
+ DB::Variant::Free(&dbvl);
+ }
+ // set firstname as result
+ else if (dbvf.type == DBVT_WCHAR)
+ {
+ ci->pszVal = (LPTSTR) dbvf.pwszVal;
+ DB::Variant::Free(&dbvl);
+ }
+ // set lastname as result
+ else if (dbvl.type == DBVT_WCHAR)
+ {
+ ci->pszVal = (LPTSTR) dbvl.pwszVal;
+ DB::Variant::Free(&dbvf);
+ }
+ else
+ {
+ ci->pszVal = NULL;
+ DB::Variant::Free(&dbvf);
+ DB::Variant::Free(&dbvl);
+ }
+ }
+ else
+ {
+ // both firstname and lastname are valid
+ if (dbvf.type == DBVT_ASCIIZ && dbvl.type == DBVT_ASCIIZ)
+ {
+ cbf = mir_strlen(dbvf.pszVal);
+ cbl = mir_strlen(dbvl.pszVal);
+
+ ci->pszVal = (LPTSTR) mir_alloc((cbf + cbl + 2) * sizeof(CHAR));
+ if (ci->pszVal)
+ {
+ mir_snprintf((LPSTR) ci->pszVal, cbf + cbl + 2, "%s %s", dbvf.pszVal, dbvl.pszVal);
+ }
+ DB::Variant::Free(&dbvf);
+ DB::Variant::Free(&dbvl);
+ }
+ // set firstname as result
+ else if (dbvf.type == DBVT_ASCIIZ)
+ {
+ ci->pszVal = (LPTSTR) dbvf.pszVal;
+ DB::Variant::Free(&dbvl);
+ }
+ // set lastname as result
+ else if (dbvl.type == DBVT_ASCIIZ)
+ {
+ ci->pszVal = (LPTSTR) dbvl.pszVal;
+ DB::Variant::Free(&dbvf);
+ }
+ else
+ {
+ ci->pszVal = NULL;
+ DB::Variant::Free(&dbvf);
+ DB::Variant::Free(&dbvl);
+ }
+ }
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ return ci->type == 0;
+}
+
+/**
+ * return the country name
+ *
+ * @param ci - pointer to a CONTACTINFO structure holding information about the contact
+ * @param pszSetting - the desired setting to read the countrys id from
+ *
+ * @retval 0 - if setting was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCICountry(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+ if (0 == GCIVarEx(ci, pszSetting)) {
+ if (ci->type != CNFT_ASCIIZ) {
+ // country id was safed in database as code
+ UINT res = 0;
+ switch (ci->type) {
+ case CNFT_WORD: res = ci->wVal; break;
+ case CNFT_DWORD: res = ci->dVal; break;
+ default: return 1;
+ }
+
+// LPSTR szCountry = res ? (LPSTR)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, res, 0) : NULL;
+ LPSTR szCountry = res ? (LPSTR)ServiceGetCountryByNumber(res, 0) : NULL;
+ if (szCountry) {
+ ci->pszVal = (ci->dwFlag & CNF_UNICODE)
+ ? (LPTSTR) mir_a2u(szCountry)
+ : (LPTSTR) mir_strdup(szCountry);
+ }
+ else {
+ ci->pszVal = NULL;
+ }
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ }
+ }
+ else {
+ ci->type = 0;
+ }
+ return ci->type == 0;
+}
+
+/**
+ * This is the service procedure to retrieve contact information
+ *
+ * @param wParam - not used
+ * @param lParam - pointer to a CONTACTINFO structure which tells what information is desired
+ *
+ * @retval 0 - if contact information was found and read correctly
+ * @retval 1 - if any error occured or setting was not found
+ **/
+INT_PTR GetContactInfo(WPARAM wParam, LPARAM lParam)
+{
+ CONTACTINFO *ci = (CONTACTINFO*) lParam;
+ INT_PTR result;
+
+ if (ci && ci->cbSize == sizeof(CONTACTINFO) && (ci->szProto != NULL || (ci->szProto = DB::Contact::Proto(ci->hContact)) != NULL))
+ {
+ switch (ci->dwFlag & 0x7F) {
+
+ //
+ // contact name
+ //
+ case CNF_FIRSTNAME: {
+ result = GCIVarEx(ci, SET_CONTACT_FIRSTNAME);
+ } break;
+
+ case CNF_LASTNAME: {
+ result = GCIVarEx(ci, SET_CONTACT_LASTNAME);
+ } break;
+
+ case CNF_FIRSTLAST: {
+ result = GCIVarEx(ci, SET_CONTACT_FIRSTLASTNAME); //first try to read "FullName"
+ if(result) result = GCIFirstLast(ci); //fallback to "FirstName" + "LastName"
+ } break;
+
+ case CNF_NICK: {
+ result = GCIVarEx(ci, SET_CONTACT_NICK);
+ } break;
+
+ case CNF_CUSTOMNICK: {
+ LPSTR s = ci->szProto;
+ ci->szProto = MOD_CLIST;
+ result = GCIVar(ci, SET_CONTACT_MYHANDLE);
+ ci->szProto = s;
+ } break;
+
+ case CNF_LANGUAGE1: {
+ result = GCILangEx(ci, SET_CONTACT_LANG1);
+ } break;
+
+ case CNF_LANGUAGE2: {
+ result = GCILangEx(ci, SET_CONTACT_LANG2);
+ } break;
+
+ case CNF_LANGUAGE3: {
+ result = GCILangEx(ci, SET_CONTACT_LANG3);
+ } break;
+
+ //
+ // private contact
+ //
+ case CNF_STREET: {
+ result = GCIVarEx(ci, SET_CONTACT_STREET);
+ } break;
+
+ case CNF_ZIP: {
+ result = GCIVarEx(ci, SET_CONTACT_ZIP);
+ } break;
+
+ case CNF_CITY: {
+ result = GCIVarEx(ci, SET_CONTACT_CITY);
+ } break;
+
+ case CNF_STATE: {
+ result = GCIVarEx(ci, SET_CONTACT_STATE);
+ } break;
+
+ case CNF_COUNTRY: {
+ result = GCICountry(ci, SET_CONTACT_COUNTRY);
+ } break;
+
+ case CNF_PHONE: {
+ result = GCIVarEx(ci, SET_CONTACT_PHONE);
+ } break;
+
+ case CNF_FAX: {
+ result = GCIVarEx(ci, SET_CONTACT_FAX);
+ } break;
+
+ case CNF_CELLULAR: {
+ result = GCIVarEx(ci, SET_CONTACT_CELLULAR);
+ } break;
+
+ case CNF_EMAIL: {
+ result = GCIVarEx(ci, SET_CONTACT_EMAIL);
+ } break;
+
+ case CNF_HOMEPAGE: {
+ result = GCIVarEx(ci, SET_CONTACT_HOMEPAGE);
+ } break;
+
+ //
+ // company information
+ //
+ case CNF_CONAME: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY);
+ } break;
+
+ case CNF_CODEPT: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_DEPARTMENT);
+ } break;
+
+ case CNF_COPOSITION: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_POSITION);
+ } break;
+
+ case CNF_COSTREET: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_STREET);
+ } break;
+
+ case CNF_COZIP: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_ZIP);
+ } break;
+
+ case CNF_COCITY: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_CITY);
+ } break;
+
+ case CNF_COSTATE: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_STATE);
+ } break;
+
+ case CNF_COCOUNTRY: {
+ result = GCICountry(ci, SET_CONTACT_COMPANY_COUNTRY);
+ } break;
+
+ case CNF_COPHONE: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_PHONE);
+ } break;
+
+ case CNF_COFAX: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_FAX);
+ } break;
+
+ case CNF_COCELLULAR: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_CELLULAR);
+ } break;
+
+ case CNF_COEMAIL: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_EMAIL);
+ } break;
+
+ case CNF_COHOMEPAGE: {
+ result = GCIVarEx(ci, SET_CONTACT_COMPANY_HOMEPAGE);
+ } break;
+
+ //
+ // personal information
+ //
+ case CNF_ABOUT: {
+ result = GCIVarEx(ci, SET_CONTACT_ABOUT);
+ } break;
+
+ case CNF_MYNOTES: {
+ result = GCIVarEx(ci, SET_CONTACT_MYNOTES);
+ } break;
+
+ case CNF_AGE: {
+ result = GCIVarEx(ci, SET_CONTACT_AGE);
+ } break; // returns age (byte, 0==unspecified) ??
+
+ case CNF_GENDER: {
+ ci->bVal = GenderOf(ci->hContact, ci->szProto);
+ ci->type = (ci->bVal != 0) ? CNFT_BYTE : 0;
+ result = ci->type == 0;
+ } break;
+
+ case CNF_BIRTHDAY: {
+ MAnnivDate mda;
+ result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+ if (result == 0) {
+ ci->bVal = (BYTE) mda.Day();
+ ci->type = CNFT_BYTE;
+ }
+ } break;
+
+ case CNF_BIRTHMONTH: {
+ MAnnivDate mda;
+ result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+ if (result == 0) {
+ ci->bVal = (BYTE) mda.Month();
+ ci->type = CNFT_BYTE;
+ }
+ } break;
+
+ case CNF_BIRTHYEAR: {
+ MAnnivDate mda;
+ result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+ if (result == 0) {
+ ci->wVal = (WORD) mda.Year();
+ ci->type = CNFT_WORD;
+ }
+ } break;
+
+ case CNF_BIRTHDATE: {
+ MAnnivDate mda;
+ result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+ if (result == 0) {
+ SYSTEMTIME st = mda.SystemTime();
+ ci->pszVal = NULL;
+ if (ci->dwFlag & CNF_UNICODE) {
+ WCHAR wszDate[80];
+ if (GetDateFormatW(LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, wszDate, SIZEOF(wszDate))) {
+ ci->pszVal = (LPTSTR)mir_wcsdup(wszDate);
+ }
+ }
+ else {
+ CHAR szDate[80];
+ if (GetDateFormatA(LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, szDate, SIZEOF(szDate))) {
+ ci->pszVal = (LPTSTR)mir_strdup(szDate);
+ }
+ }
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ result = ci->type == 0;
+ }
+ } break;
+
+ case CNF_TIMEZONE: {
+ //use new core tz interface
+ if(tmi.prepareList) {
+ 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;
+ }
+ else {
+ ci->pszVal = NULL;
+ }
+ }
+ //fallback use old UIEX method
+ else {
+ CTimeZone* ptz = GetContactTimeZone(ci->hContact, ci->szProto);
+ if (ptz) {
+ if (ci->dwFlag & CNF_UNICODE) {
+ ci->pszVal = (LPTSTR) mir_t2u(ptz->ptszDisplay);
+ }
+ else {
+ ci->pszVal = (LPTSTR) mir_t2a(ptz->ptszDisplay);
+ }
+ }
+ else {
+ /* If a timezone does not exist in CTzMgr, it is a invalid timezone,
+ because Windows and CTzMgr know all existing timezones and it
+ would not be shown anywhere anyway as UserInfoEx displays only
+ known windows timezones in the details dialog!
+ */
+ ci->pszVal = NULL;
+ }
+ }
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ result = ci->type == 0;
+ } break;
+
+ //
+ // information about IM specific stuff
+ //
+ case CNF_UNIQUEID: {
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ result = CallProtoService(ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+ result = GCIVar(ci, (LPCSTR) result);
+ }
+ } break;
+
+ case CNF_DISPLAYUID: {
+ if (!GCIVar(ci, "display_uid"))
+ result=0;
+ else {
+ result = CallProtoService(ci->szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
+ if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+ result = GCIVar(ci, (LPCSTR) result);
+ }
+ }
+ } break;
+
+ case CNF_DISPLAYNC:
+ case CNF_DISPLAY: {
+ INT i;
+ for (i = 0; i < NAMEORDERCOUNT; i++) {
+ switch (gNameOrder[i]) {
+ case 0: // custom name
+ {
+ // make sure we aren't in CNF_DISPLAYNC mode
+ // don't get custom name for NULL contact
+ if (ci->hContact != NULL && (ci->dwFlag & 0x7F) == CNF_DISPLAY) {
+ BYTE dwFlag = ci->dwFlag;
+ ci->dwFlag = (ci->dwFlag & CNF_UNICODE) | CNF_CUSTOMNICK;
+ if (!GetContactInfo(NULL, (LPARAM)ci)) {
+ ci->dwFlag = dwFlag;
+ return 0;
+ }
+ ci->dwFlag = dwFlag;
+ }
+ } break;
+ case 1: // nick
+ {
+ if (!GCIVarEx(ci, SET_CONTACT_NICK))
+ return 0;
+ } break;
+ case 2: // First Name
+ {
+ if (!GCIVarEx(ci, SET_CONTACT_FIRSTNAME))
+ return 0;
+ } break;
+ case 3: // E-mail
+ {
+ if (!GCIVarEx(ci, SET_CONTACT_EMAIL))
+ return 0;
+ } break;
+ case 4: // Last Name
+ {
+ if (!GCIVarEx(ci, SET_CONTACT_LASTNAME))
+ return 0;
+ } break;
+ case 5: // Unique id
+ {
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ result = CallProtoService(ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+ if (!GCIStr(ci, (LPCSTR) result))
+ return 0;
+ }
+ } break;
+ case 6: // first + last name
+ {
+ if (!GCIFirstLast(ci))
+ return 0;
+ } break;
+ default: // unknown contact
+ {
+ if (ci->dwFlag & CNF_UNICODE) {
+ ci->pszVal = (LPTSTR) mir_wcsdup(TranslateW(L"'(Unknown Contact)'"));
+ }
+ else {
+ ci->pszVal = (LPTSTR) mir_strdup(Translate("'(Unknown Contact)'"));
+ }
+ ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+ return ci->type == 0;
+ }
+ }
+ }
+ }
+
+ default: {
+ result = 1;
+ }
+ }
+ }
+ else
+ {
+ result = 1;
+ }
+ return result;
+}
+
+/**
+ * This is the implementation of the MS_DB_CONTACT_GETSETTING_STR_EX service.
+ *
+ * @param wParam - handle of the contact a setting was written for (must be NULL in this case)
+ * @param lParam - DBCONTACTGETSETTING structure holding information about the setting to read
+ *
+ * @retval 0 - success
+ * @retval 1 - error
+ **/
+static INT_PTR GetContactSettingStrExService(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTGETSETTING *cgs = (DBCONTACTGETSETTING*)lParam;
+ return DB::Setting::GetEx((HANDLE)wParam, USERINFO,
+ cgs->szModule, cgs->szSetting, cgs->pValue, cgs->pValue->type);
+}
+
+/**
+ * If the user changes the name order update the global value.
+ *
+ * @param wParam - handle of the contact a setting was written for (must be NULL in this case)
+ * @param lParam - DBCONTACTWRITESETTING structure holding information about the written data
+ * @return 0
+ **/
+static INT OnSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ if ((HANDLE)wParam == NULL) {
+ DBCONTACTWRITESETTING *pdbcws = (DBCONTACTWRITESETTING*) lParam;
+ if (!mir_strcmp(pdbcws->szModule, "Contact") &&
+ !mir_strcmp(pdbcws->szSetting, "NameOrder"))
+ {
+ memcpy(gNameOrder, pdbcws->value.pbVal,pdbcws->value.cpbVal);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Loads the module at startup and replaces the service.
+ *
+ * @param none
+ * @return nothing
+ **/
+VOID SvcContactInfoLoadModule()
+{
+ CreateServiceFunction(MS_DB_CONTACT_GETSETTING_STR_EX, GetContactSettingStrExService);
+ CreateServiceFunction(MS_CONTACT_GETCONTACTINFO, GetContactInfo);
+
+ DBVARIANT dbv;
+ if (DB::Setting::GetAString(NULL, "Contact", "NameOrder", &dbv)) {
+ BYTE i;
+ for (i = 0; i < NAMEORDERCOUNT; i++) {
+ gNameOrder[i] = i;
+ }
+ }
+ else {
+ memcpy(gNameOrder, dbv.pbVal, dbv.cpbVal);
+ DB::Variant::Free(&dbv);
+ }
+
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnSettingChanged);
+}
diff --git a/plugins/UserInfoEx/src/svc_contactinfo.h b/plugins/UserInfoEx/src/svc_contactinfo.h
new file mode 100644
index 0000000000..3509858fcd
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_contactinfo.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_contactinfo.h $
+Revision : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCCONTACTS_H_INCLUDED_
+#define _UINFOEX_SVCCONTACTS_H_INCLUDED_
+
+INT_PTR GetContactInfo(WPARAM wParam, LPARAM lParam);
+VOID SvcContactInfoLoadModule(VOID);
+
+#endif /* _UINFOEX_SVCCONTACTS_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_email.cpp b/plugins/UserInfoEx/src/svc_email.cpp
new file mode 100644
index 0000000000..a9a0b47686
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_email.cpp
@@ -0,0 +1,409 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_email.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_email.h"
+
+static HANDLE ghMenuItem = NULL;
+static HANDLE ghExtraIconDef = INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc = INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook = NULL;
+static HANDLE hApplyIconHook = NULL;
+static HANDLE hRebuildIconsHook = NULL;
+
+/**
+ * This function reads the email address of the contact.
+ *
+ * @param hContact - handle to contact to read email from
+ *
+ * @retval email address
+ * @retval NULL, if contact does not provide any email address
+ **/
+static LPSTR Get(HANDLE hContact)
+{
+ // ignore owner
+ if (hContact != NULL)
+ {
+ LPCSTR pszProto = DB::Contact::Proto(hContact);
+
+ if (pszProto != NULL)
+ {
+ LPCSTR e[2][4] = {
+ { SET_CONTACT_EMAIL, SET_CONTACT_EMAIL0, SET_CONTACT_EMAIL1, "Mye-mail0"},
+ { SET_CONTACT_COMPANY_EMAIL, SET_CONTACT_COMPANY_EMAIL0, SET_CONTACT_COMPANY_EMAIL1, "MyCompanye-mail0"}
+ };
+
+ INT i, j;
+ LPSTR pszEMail;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ pszEMail = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i][j]);
+ if (pszEMail)
+ {
+ if (strchr(pszEMail, '@'))
+ {
+ return pszEMail;
+ }
+ mir_free(pszEMail);
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Service function that sends emails
+ *
+ * @param wParam - handle to contact to send an email to
+ * @param lParam - not used
+ *
+ * @retval 0 if email was sent
+ * @retval 1 if no email can be sent
+ **/
+static INT_PTR MenuCommand(WPARAM wParam,LPARAM lParam)
+{
+ INT result;
+ LPSTR val = NULL;
+
+ __try
+ {
+ val = Get((HANDLE) wParam);
+ if (val)
+ {
+ LPSTR szUrl;
+ INT_PTR len;
+
+ len = mir_strlen(val) + strlen("mailto:");
+
+ szUrl = (LPSTR)_alloca(len + 1);
+
+ mir_snprintf(szUrl, len + 1, "mailto:%s", val);
+ mir_free(val);
+
+ result = CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ }
+ else
+ {
+ result = 1;
+ MsgBox((HWND)lParam, MB_OK, LPGENT("Send e-mail"), NULL, LPGENT("Memory allocation error!"));
+ }
+ }
+ __except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ if (val)
+ {
+ mir_free(val);
+ }
+
+ result = 1;
+ MsgErr((HWND)lParam, LPGENT("Memory allocation error!"));
+ }
+ return result;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon = IcoLib_GetIcon(ICO_BTN_EMAIL);
+ ghExtraIconDef = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+static INT OnCListApplyIcons(WPARAM wParam, LPARAM lParam)
+{
+ LPSTR val = Get((HANDLE)wParam);
+
+ if (!myGlobals.ExtraIconsServiceExist)
+ {
+ IconExtraColumn iec;
+
+ iec.cbSize = sizeof(IconExtraColumn);
+ iec.ColumnType = EXTRA_ICON_EMAIL;
+ if (val)
+ {
+ iec.hImage = ghExtraIconDef;
+ mir_free(val);
+ }
+ else
+ {
+ iec.hImage = INVALID_HANDLE_VALUE;
+ mir_free(val);
+ }
+ CallService(MS_CLIST_EXTRA_SET_ICON, wParam, (LPARAM)&iec);
+ }
+ else
+ {
+ EXTRAICON ico;
+ ico.cbSize=sizeof(ico);
+ ico.hContact=(HANDLE)wParam;
+ ico.hExtraIcon=ghExtraIconSvc;
+ ico.icoName=val?ICO_BTN_EMAIL:(char *)0;
+ mir_free(val);
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param wParam - (HANDLE)hContact
+ * @param lParam - (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+ if (hContact && pdbcws && pdbcws->szSetting &&
+ ((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+ (!mir_strncmp(pdbcws->szSetting, SET_CONTACT_EMAIL, 6) ||
+ !mir_strncmp(pdbcws->szSetting, SET_CONTACT_COMPANY_EMAIL, 13) ||
+ !mir_strncmp(pdbcws->szSetting, "mye-mail0", 9)))
+ {
+ OnCListApplyIcons((WPARAM)hContact, 0);
+ }
+ return 0;
+}
+
+/**
+ * This function decides whether to show menuitem for sending emails or not.
+ *
+ * @param wParam - handle to contact to send an email to
+ * @param lParam - not used
+ *
+ * @return always 0
+ **/
+static INT OnPreBuildMenu(WPARAM wParam, LPARAM lParam)
+{
+ CLISTMENUITEM mi;
+ LPSTR val;
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS;
+
+ val = Get((HANDLE)wParam);
+ if (val)
+ {
+ mir_free(val);
+ }
+ else
+ {
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)ghMenuItem, (LPARAM)&mi);
+ return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * This function enables or disables menuitems.
+ **/
+VOID SvcEMailRebuildMenu()
+{
+ static HANDLE hPrebuildMenuHook = NULL;
+
+ if (DB::Setting::GetByte(SET_EXTENDED_EMAILSERVICE, TRUE))
+ {
+ if (!hPrebuildMenuHook)
+ {
+ hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildMenu);
+ }
+
+ if (!ghMenuItem)
+ {
+ CLISTMENUITEM mi;
+
+ // insert contact menuitem
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000010000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_EMAIL);
+ mi.pszName = "&E-mail";
+ mi.pszService = MS_EMAIL_SENDEMAIL;
+ ghMenuItem = Menu_AddContactMenuItem(&mi);
+ }
+ }
+ else
+ {
+ if (hPrebuildMenuHook)
+ {
+ UnhookEvent(ME_CLIST_PREBUILDCONTACTMENU);
+ hPrebuildMenuHook = NULL;
+ }
+ if (ghMenuItem)
+ {
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)ghMenuItem, NULL);
+ ghMenuItem = NULL;
+ }
+ }
+}
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+VOID SvcEMailApplyCListIcons()
+{
+ HANDLE hContact;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact))
+ {
+ OnCListApplyIcons((WPARAM)hContact, 0);
+ }
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param bEnable - determines whether icons are enabled or not
+ * @param bUpdateDB - if true the database setting is updated, too.
+ **/
+VOID SvcEMailEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB)
+{
+ if (myGlobals.HaveCListExtraIcons)
+ {
+ if (bUpdateDB)
+ {
+ DB::Setting::WriteByte(SET_CLIST_EXTRAICON_EMAIL, bEnable);
+ }
+
+ if (bEnable) // E-mail checkt
+ {
+ // hook events
+ if (hChangedHook == NULL)
+ {
+ hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+ }
+ if (hApplyIconHook == NULL)
+ {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons);
+ }
+ if (myGlobals.ExtraIconsServiceExist)
+ {
+ if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+ {
+ EXTRAICON_INFO ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.name = "email"; //must be the same as the group name in extraicon
+ ico.description= "E-mail (uinfoex)";
+ ico.descIcon = ICO_BTN_EMAIL;
+ ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ ZeroMemory(&ico,sizeof(ico));
+ }
+ }
+ else if (hRebuildIconsHook == NULL)
+ {
+ hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+ OnCListRebuildIcons(0, 0);
+ }
+ }
+ else // E-mail uncheckt
+ {
+ if (hChangedHook)
+ {
+ UnhookEvent(hChangedHook);
+ hChangedHook = NULL;
+ }
+ if (hApplyIconHook)
+ {
+ UnhookEvent(hApplyIconHook);
+ hApplyIconHook = NULL;
+ }
+ if (hRebuildIconsHook)
+ {
+ UnhookEvent(hRebuildIconsHook);
+ hRebuildIconsHook = NULL;
+ }
+ }
+ SvcEMailApplyCListIcons();
+ }
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcEMailOnModulesLoaded()
+{
+ SvcEMailEnableExtraIcons(
+ myGlobals.ExtraIconsServiceExist ||
+ DB::Setting::GetByte(SET_CLIST_EXTRAICON_EMAIL,
+ DEFVAL_CLIST_EXTRAICON_EMAIL), FALSE);
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcEMailLoadModule()
+{
+ if (DB::Setting::GetByte(SET_EXTENDED_EMAILSERVICE, TRUE)) {
+ // create own email send command
+ if (!myDestroyServiceFunction(MS_EMAIL_SENDEMAIL))
+ CreateServiceFunction(MS_EMAIL_SENDEMAIL, MenuCommand);
+ }
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcEMailUnloadModule()
+{
+ // unhook event handlers
+ UnhookEvent(hChangedHook); hChangedHook = NULL;
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ UnhookEvent(hRebuildIconsHook); hRebuildIconsHook = NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_email.h b/plugins/UserInfoEx/src/svc_email.h
new file mode 100644
index 0000000000..5544f247f3
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_email.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_email.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCEMAIL_H_INCLUDED_
+#define _UINFOEX_SVCEMAIL_H_INCLUDED_
+
+VOID SvcEMailRebuildMenu();
+VOID SvcEMailEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcEMailOnModulesLoaded();
+VOID SvcEMailLoadModule();
+VOID SvcEMailUnloadModule();
+
+#endif /* _UINFOEX_SVCEMAIL_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_gender.cpp b/plugins/UserInfoEx/src/svc_gender.cpp
new file mode 100644
index 0000000000..433a7664b9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_gender.cpp
@@ -0,0 +1,284 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_gender.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_contacts.h"
+
+static HANDLE ghExtraIconF = INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconM = INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc = INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook = NULL;
+static HANDLE hApplyIconHook = NULL;
+static HANDLE hRebuildIconsHook = NULL;
+
+BYTE GenderOf(HANDLE hContact, LPCSTR pszProto)
+{
+ DBVARIANT dbv;
+
+ if (DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) == 0)
+ {
+ // gender must be byte and either M or F
+ if (dbv.type == DBVT_BYTE && (dbv.bVal == 'M' || dbv.bVal == 'F'))
+ {
+ return dbv.bVal;
+ }
+ DB::Variant::Free(&dbv);
+ }
+ return 0;
+}
+
+/**
+ * This function gets the gender of the contact from the database.
+ *
+ * @param hContact - handle to contact to read email from
+ *
+ * @retval F - contact is female
+ * @retval M - contact is male
+ * @retval 0 - contact does not provide its gender
+ **/
+BYTE GenderOf(HANDLE hContact)
+{
+ return GenderOf(hContact, DB::Contact::Proto(hContact));
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon = IcoLib_GetIcon(ICO_COMMON_FEMALE);
+ ghExtraIconF = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ hIcon = IcoLib_GetIcon(ICO_COMMON_MALE);
+ ghExtraIconM = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+ if (myGlobals.ExtraIconsServiceExist && (ghExtraIconSvc != INVALID_HANDLE_VALUE))
+ {
+ EXTRAICON ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.hContact = hContact;
+ ico.hExtraIcon = ghExtraIconSvc;
+ switch (GenderOf(hContact))
+ {
+ case 'M':
+ ico.icoName = ICO_COMMON_MALE;
+ break;
+ case 'F':
+ ico.icoName = ICO_COMMON_FEMALE;
+ break;
+ default:
+ ico.icoName = NULL;
+ }
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ else
+ {
+ IconExtraColumn iec;
+
+ iec.ColumnType = DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER);
+ if ((BYTE)iec.ColumnType != -1)
+ {
+ iec.cbSize = sizeof(IconExtraColumn);
+ switch (GenderOf(hContact))
+ {
+ case 'M':
+ iec.hImage = ghExtraIconM;
+ break;
+ case 'F':
+ iec.hImage = ghExtraIconF;
+ break;
+ default:
+ iec.hImage = INVALID_HANDLE_VALUE;
+ }
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param wParam - (HANDLE)hContact
+ * @param lParam - (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+ if (hContact && pdbcws && (pdbcws->value.type <= DBVT_BYTE) && !mir_stricmp(pdbcws->szSetting, SET_CONTACT_GENDER))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+VOID SvcGenderApplyCListIcons()
+{
+ HANDLE hContact;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param bEnable - determines whether icons are enabled or not
+ * @param bUpdateDB - if true the database setting is updated, too.
+ **/
+VOID SvcGenderEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB)
+{
+ bool bEnable = (bColumn!=((BYTE)-1));
+ if (myGlobals.HaveCListExtraIcons)
+ {
+ if (bUpdateDB)
+ {
+ if (myGlobals.ExtraIconsServiceExist)
+ {
+ DB::Setting::WriteByte(SET_CLIST_EXTRAICON_GENDER2, bColumn);
+ }
+ else
+ {
+ DB::Setting::WriteByte(SET_CLIST_EXTRAICON_GENDER, bColumn);
+ }
+ }
+
+ if (bEnable) // Gender checkt or dropdown select
+ {
+ if (myGlobals.ExtraIconsServiceExist)
+ {
+ if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+ {
+ EXTRAICON_INFO ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.name = "gender"; //must be the same as the group name in extraicon
+ ico.description="Gender (uinfoex)";
+ ico.descIcon = ICO_COMMON_MALE;
+ ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ }
+ }
+ else
+ {
+ if (hRebuildIconsHook == NULL)
+ {
+ hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+ OnCListRebuildIcons(0, 0);
+ }
+ }
+ // hook events
+ if (hChangedHook == NULL)
+ {
+ hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+ }
+ if (hApplyIconHook == NULL)
+ {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+ }
+ }
+ else
+ {
+ if (hChangedHook)
+ {
+ UnhookEvent(hChangedHook);
+ hChangedHook = NULL;
+ }
+ if (hApplyIconHook)
+ {
+ UnhookEvent(hApplyIconHook);
+ hApplyIconHook = NULL;
+ }
+ if (hRebuildIconsHook)
+ {
+ UnhookEvent(hRebuildIconsHook);
+ hRebuildIconsHook = NULL;
+ }
+ }
+ SvcGenderApplyCListIcons();
+ }
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcGenderLoadModule()
+{
+ if ( myGlobals.ExtraIconsServiceExist)
+ {
+ SvcGenderEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER2, 0), FALSE);
+ }
+ else
+ {
+ SvcGenderEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER), FALSE);
+ }
+}
+
+/**
+ * This function unloads the module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcGenderUnloadModule()
+{
+ // unhook event handlers
+ UnhookEvent(hChangedHook); hChangedHook = NULL;
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ UnhookEvent(hRebuildIconsHook); hRebuildIconsHook = NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_gender.h b/plugins/UserInfoEx/src/svc_gender.h
new file mode 100644
index 0000000000..d11ecdcd66
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_gender.h
@@ -0,0 +1,40 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_gender.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCGENDER_H_INCLUDED_
+#define _UINFOEX_SVCGENDER_H_INCLUDED_
+
+BYTE GenderOf(HANDLE hContact, LPCSTR pszProto);
+BYTE GenderOf(HANDLE hContact);
+
+VOID SvcGenderEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB);
+VOID SvcGenderLoadModule();
+VOID SvcGenderUnloadModule();
+
+#endif /* _UINFOEX_SVCGENDER_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_homepage.cpp b/plugins/UserInfoEx/src/svc_homepage.cpp
new file mode 100644
index 0000000000..77248960ac
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_homepage.cpp
@@ -0,0 +1,337 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_homepage.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+static HANDLE ghMenuItem = NULL;
+static HANDLE ghExtraIconDef = INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc = INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook = NULL;
+static HANDLE hApplyIconHook = NULL;
+static HANDLE hRebuildIconsHook = NULL;
+/**
+ * This function reads the homepage address of the contact.
+ *
+ * @param hContact - handle to contact to read email from
+ *
+ * @retval URL to contacts homepage
+ * @retval NULL if contact provides no homepage
+ **/
+static LPSTR Get(HANDLE hContact)
+{
+ // ignore owner
+ if (hContact != NULL)
+ {
+ LPCSTR pszProto = DB::Contact::Proto(hContact);
+
+ if (pszProto != NULL)
+ {
+ LPCSTR e[2] = { SET_CONTACT_HOMEPAGE, SET_CONTACT_COMPANY_HOMEPAGE };
+ LPSTR pszHomepage;
+
+ INT i;
+
+ for (i = 0; i < 2; i++)
+ {
+ pszHomepage = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i]);
+ if (pszHomepage)
+ return pszHomepage;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Service function that opens the default browser and displays the homepage.
+ *
+ * @param wParam - handle to contact to send an email to
+ * @param lParam - not used
+ *
+ * @retval 0 if email was sent
+ * @retval 1 if no email can be sent
+ **/
+static INT_PTR MenuCommand(WPARAM wParam, LPARAM lParam)
+{
+ LPSTR szUrl = Get((HANDLE)wParam);
+
+ if (szUrl)
+ {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ mir_free(szUrl);
+ }
+ else
+ {
+ MessageBox((HWND)lParam,
+ TranslateT("User has no valid homepage"),
+ TranslateT("View Homepage"), MB_OK);
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+ ghExtraIconDef = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+ LPSTR val = Get(hContact);
+
+ if (myGlobals.ExtraIconsServiceExist && (ghExtraIconSvc != INVALID_HANDLE_VALUE))
+ {
+ EXTRAICON ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.hContact = hContact;
+ ico.hExtraIcon = ghExtraIconSvc;
+ ico.icoName = (val) ? ICO_BTN_GOTO : NULL;
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ else
+ {
+ IconExtraColumn iec;
+
+ ZeroMemory(&iec, sizeof(iec));
+ iec.cbSize = sizeof(IconExtraColumn);
+ iec.ColumnType = EXTRA_ICON_WEB;
+ iec.hImage = (val) ? ghExtraIconDef : INVALID_HANDLE_VALUE;
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+ MIR_FREE(val);
+ return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param wParam - (HANDLE)hContact
+ * @param lParam - (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+ if (hContact && pdbcws && pdbcws->szSetting &&
+ ((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+ (!strncmp(pdbcws->szSetting, SET_CONTACT_HOMEPAGE, 8) ||
+ !strncmp(pdbcws->szSetting, SET_CONTACT_COMPANY_HOMEPAGE, 15)))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+ return 0;
+}
+
+/**
+ * This function decides whether to show menuitem for sending emails or not.
+ *
+ * @param wParam - handle to contact to send an email to
+ * @param lParam - not used
+ *
+ * @return always 0
+ **/
+static INT OnPreBuildMenu(WPARAM wParam, LPARAM lParam)
+{
+ CLISTMENUITEM mi;
+ LPSTR val;
+
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS;
+
+ val = Get((HANDLE)wParam);
+ if (val)
+ {
+ MIR_FREE(val);
+ }
+ else
+ {
+ mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)ghMenuItem, (LPARAM)&mi);
+ return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * enable or disable menuitem
+ *
+ * @param not used
+ * @return nothing
+ **/
+VOID SvcHomepageRebuildMenu()
+{
+ static HANDLE hPrebuildMenuHook = NULL;
+
+ if (!hPrebuildMenuHook)
+ hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildMenu);
+
+ if (!ghMenuItem) {
+ // insert contact menuitem
+ CLISTMENUITEM mi = { 0 };
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000010000;
+ mi.hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+ mi.pszName = "&Homepage";
+ mi.pszService = MS_USERINFO_HOMEPAGE_OPENURL;
+ ghMenuItem = Menu_AddContactMenuItem(&mi);
+ }
+}
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+VOID SvcHomepageApplyCListIcons()
+{
+ HANDLE hContact;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param bEnable - determines whether icons are enabled or not
+ * @param bUpdateDB - if true the database setting is updated, too.
+ **/
+VOID SvcHomepageEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB)
+{
+ if (myGlobals.HaveCListExtraIcons)
+ {
+ if (bUpdateDB)
+ {
+ DB::Setting::WriteByte(SET_CLIST_EXTRAICON_HOMEPAGE, bEnable);
+ }
+
+ if (bEnable)
+ {
+ // hook events
+ if (hChangedHook == NULL)
+ {
+ hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+ }
+ if (hApplyIconHook == NULL)
+ {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+ }
+ if (myGlobals.ExtraIconsServiceExist)
+ {
+ if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+ {
+ EXTRAICON_INFO ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.name = "homepage"; //must be the same as the group name in extraicon
+ ico.description = "Homepage (uinfoex)";
+ ico.descIcon = ICO_BTN_GOTO;
+ ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ }
+ }
+ else if (hRebuildIconsHook == NULL)
+ {
+ hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+ OnCListRebuildIcons(0, 0);
+ }
+ }
+ else
+ {
+ if (hChangedHook)
+ {
+ UnhookEvent(hChangedHook);
+ hChangedHook = NULL;
+ }
+ if (hApplyIconHook)
+ {
+ UnhookEvent(hApplyIconHook);
+ hApplyIconHook = NULL;
+ }
+ if (hRebuildIconsHook)
+ {
+ UnhookEvent(hRebuildIconsHook);
+ hRebuildIconsHook = NULL;
+ }
+ }
+ SvcHomepageApplyCListIcons();
+ }
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ *
+ * @param not used
+ * @return nothing
+ **/
+VOID SvcHomepageLoadModule()
+{
+ CreateServiceFunction(MS_USERINFO_HOMEPAGE_OPENURL, MenuCommand);
+ SvcHomepageEnableExtraIcons(
+ myGlobals.ExtraIconsServiceExist ||
+ DB::Setting::GetByte(SET_CLIST_EXTRAICON_HOMEPAGE, DEFVAL_CLIST_EXTRAICON_HOMEPAGE), FALSE);
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcHomepageUnloadModule()
+{
+ // unhook event handlers
+ UnhookEvent(hChangedHook); hChangedHook = NULL;
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ UnhookEvent(hRebuildIconsHook); hRebuildIconsHook = NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_homepage.h b/plugins/UserInfoEx/src/svc_homepage.h
new file mode 100644
index 0000000000..8ca7ce6ae9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_homepage.h
@@ -0,0 +1,38 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_homepage.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SvcHomepage_H_INCLUDED_
+#define _UINFOEX_SvcHomepage_H_INCLUDED_
+
+VOID SvcHomepageRebuildMenu();
+VOID SvcHomepageEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcHomepageLoadModule();
+VOID SvcHomepageUnloadModule();
+
+#endif /* _UINFOEX_SvcHomepage_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_phone.cpp b/plugins/UserInfoEx/src/svc_phone.cpp
new file mode 100644
index 0000000000..fab967c9a1
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_phone.cpp
@@ -0,0 +1,309 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_phone.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+
+enum EPhoneType
+{
+ PHONE_NONE,
+ PHONE_NORMAL,
+ PHONE_SMS
+};
+
+static HANDLE ghMenuItem = NULL;
+static HANDLE ghExtraIconDef[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+static HANDLE ghExtraIconSvc = INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook = NULL;
+static HANDLE hApplyIconHook = NULL;
+static HANDLE hRebuildIconsHook = NULL;
+
+/**
+ * This function reads the contact's phone number from database and returns its type.
+ *
+ * @param hContact - handle to contact to read email from
+ *
+ * @retval PHONE_SMS: The phone supports sms, so is a cellular
+ * @retval PHONE_NORMAL: The phone is a normal phone
+ * @retval PHONE_NONE: The contact does not provide any phone number
+ **/
+static INT_PTR Get(HANDLE hContact)
+{
+ INT_PTR nType = PHONE_NONE;
+
+ // ignore owner
+ if (hContact != NULL)
+ {
+ LPCSTR pszProto = DB::Contact::Proto(hContact);
+ if (pszProto != NULL)
+ {
+ LPCSTR e[2][4] = {
+ { SET_CONTACT_CELLULAR, SET_CONTACT_PHONE, "MyPhone0" },
+ { SET_CONTACT_COMPANY_CELLULAR, SET_CONTACT_COMPANY_PHONE, "MyCompanyPhone0" }
+ };
+
+ INT i, j;
+ LPSTR pszPhone;
+
+ for (i = 0; (i < 2) && (nType == PHONE_NONE); i++)
+ {
+ for (j = 0; (j < 3) && (nType == PHONE_NONE); j++)
+ {
+ pszPhone = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i][j]);
+ if (pszPhone)
+ {
+ nType = (strstr(pszPhone, " SMS")) ? PHONE_SMS : PHONE_NORMAL;
+ MIR_FREE(pszPhone);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return nType;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon = IcoLib_GetIcon(ICO_BTN_PHONE);
+ ghExtraIconDef[0] = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ hIcon = IcoLib_GetIcon(ICO_BTN_CELLULAR);
+ ghExtraIconDef[1] = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+ Skin_ReleaseIcon(hIcon);
+ return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+ if (!myGlobals.ExtraIconsServiceExist)
+ {
+ IconExtraColumn iec;
+ iec.cbSize = sizeof(IconExtraColumn);
+ iec.ColumnType = EXTRA_ICON_SMS;
+ switch (Get(hContact))
+ {
+ case PHONE_NORMAL:
+ {
+ iec.hImage = ghExtraIconDef[0];
+ }
+ break;
+
+ case PHONE_SMS:
+ {
+ iec.hImage = ghExtraIconDef[1];
+ }
+ break;
+
+ default:
+ {
+ iec.hImage = INVALID_HANDLE_VALUE;
+ }
+ }
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+ else
+ {
+ EXTRAICON ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.hContact = hContact;
+ ico.hExtraIcon = ghExtraIconSvc;
+ switch (Get(hContact))
+ {
+ case PHONE_NORMAL:
+ {
+ ico.icoName = ICO_BTN_PHONE;
+ }
+ break;
+
+ case PHONE_SMS:
+ {
+ ico.icoName = ICO_BTN_CELLULAR;
+ }
+ break;
+
+ default:
+ {
+ ico.icoName = (char *)0;
+ }
+ }
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param wParam - (HANDLE)hContact
+ * @param lParam - (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+ if (hContact && pdbcws && pdbcws->szSetting &&
+ ((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+ (!strcmp(pdbcws->szSetting, SET_CONTACT_PHONE) ||
+ !strcmp(pdbcws->szSetting, SET_CONTACT_CELLULAR) ||
+ !strcmp(pdbcws->szSetting, SET_CONTACT_COMPANY_PHONE) ||
+ !strcmp(pdbcws->szSetting, SET_CONTACT_COMPANY_CELLULAR) ||
+ !strncmp(pdbcws->szSetting, "MyPhone0", 8)))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param wParam - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ **/
+VOID SvcPhoneApplyCListIcons()
+{
+ HANDLE hContact;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact))
+ {
+ OnCListApplyIcons(hContact, 0);
+ }
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param bEnable - determines whether icons are enabled or not
+ * @param bUpdateDB - if true the database setting is updated, too.
+ **/
+VOID SvcPhoneEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB)
+{
+ if (myGlobals.HaveCListExtraIcons)
+ {
+ if (bUpdateDB)
+ {
+ DB::Setting::WriteByte(SET_CLIST_EXTRAICON_PHONE, bEnable);
+ }
+
+ // force module enabled, if extraicon plugin was found
+ if (bEnable)
+ {
+ // hook events
+ if (hChangedHook == NULL)
+ {
+ hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+ }
+ if (hApplyIconHook == NULL)
+ {
+ hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+ }
+ if (myGlobals.ExtraIconsServiceExist)
+ {
+ if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+ {
+ EXTRAICON_INFO ico;
+
+ ZeroMemory(&ico, sizeof(ico));
+ ico.cbSize = sizeof(ico);
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.name = "sms"; //must be the same as the group name in extraicon
+ ico.description = "(uinfoex)";
+ ico.descIcon = ICO_BTN_CELLULAR;
+ ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ }
+ }
+ else if (hRebuildIconsHook == NULL)
+ {
+ hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOK)OnCListRebuildIcons);
+ OnCListRebuildIcons(0, 0);
+ }
+ }
+ else
+ {
+ if (hChangedHook)
+ {
+ UnhookEvent(hChangedHook);
+ hChangedHook = NULL;
+ }
+ if (hApplyIconHook)
+ {
+ UnhookEvent(hApplyIconHook);
+ hApplyIconHook = NULL;
+ }
+ if (hRebuildIconsHook)
+ {
+ UnhookEvent(hRebuildIconsHook);
+ hRebuildIconsHook = NULL;
+ }
+ }
+ SvcPhoneApplyCListIcons();
+ }
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcPhoneLoadModule()
+{
+ SvcPhoneEnableExtraIcons(
+ myGlobals.ExtraIconsServiceExist ||
+ DB::Setting::GetByte(SET_CLIST_EXTRAICON_PHONE, DEFVAL_CLIST_EXTRAICON_PHONE), FALSE);
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcPhoneUnloadModule()
+{
+ // unhook event handlers
+ UnhookEvent(hChangedHook); hChangedHook = NULL;
+ UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+ UnhookEvent(hRebuildIconsHook); hRebuildIconsHook = NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_phone.h b/plugins/UserInfoEx/src/svc_phone.h
new file mode 100644
index 0000000000..20f7c249b5
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_phone.h
@@ -0,0 +1,38 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_phone.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCPHONE_H_INCLUDED_
+#define _UINFOEX_SVCPHONE_H_INCLUDED_
+
+VOID SvcPhoneApplyCListIcons();
+VOID SvcPhoneEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcPhoneLoadModule();
+VOID SvcPhoneUnloadModule();
+
+#endif /* _UINFOEX_SVCPHONE_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_refreshci.cpp b/plugins/UserInfoEx/src/svc_refreshci.cpp
new file mode 100644
index 0000000000..bddc9e60ca
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_refreshci.cpp
@@ -0,0 +1,936 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "mir_contactqueue.h"
+#include "mir_menuitems.h"
+#include "svc_refreshci.h"
+
+#define HM_PROTOACK (WM_USER+100)
+
+typedef INT_PTR (*PUpdCallback) (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PVOID UserData);
+
+/***********************************************************************************************************
+ * class CUpdProgress
+ ***********************************************************************************************************/
+
+class CUpdProgress
+{
+protected:
+ BOOLEAN _bBBCode; // TRUE if text renderer can handle BBCodes
+ BOOLEAN _bIsCanceled; // is set to TRUE uppon click on the CANCEL button
+ PUpdCallback _pFnCallBack; // a pointer to a callback function, which can be used
+ // to catch several messages by the caller.
+ PVOID _pData; // application defined data
+ HWND _hWnd; // window handle of the progress dialog/popup
+
+ /**
+ * This is the default window procedure, which is called for both progress dialog and popup.
+ * It handles some common messages and calls the user defined callback function if defined.
+ *
+ * @param pProgress - This is the pointer to the object of CUpdProgress.
+ * @param hWnd - HWND window handle of the progress dialog
+ * @param uMsg - message sent to the dialog
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return This method returns 0.
+ **/
+ static INT_PTR CALLBACK DefWndProc(CUpdProgress *pProgress, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ __try
+ {
+ if (PtrIsValid(pProgress))
+ {
+ switch (uMsg)
+ {
+ case UM_POPUPACTION:
+ case WM_COMMAND:
+ {
+ if (wParam == MAKEWORD(IDSKIP, BN_CLICKED))
+ {
+ pProgress->Destroy();
+ }
+ else
+ if (wParam == MAKEWORD(IDCANCEL, BN_CLICKED))
+ {
+ pProgress->_bIsCanceled = TRUE;
+ }
+ }
+ }
+ if (PtrIsValid(pProgress->_pFnCallBack))
+ {
+ pProgress->_pFnCallBack(hWnd, uMsg, wParam, lParam, pProgress->_pData);
+ }
+ }
+ }
+ __except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ { // code to handle exception
+ puts("Exception Occurred");
+ }
+ return 0;
+ }
+
+public:
+
+ virtual HWND Create (LPCTSTR szTitle, PUpdCallback pFnCallBack) = 0;
+ virtual VOID Destroy (VOID) {};
+ virtual VOID SetTitle (LPCTSTR szText) = 0;
+ virtual VOID SetText (LPCTSTR szText) = 0;
+
+ BOOLEAN IsVisible() const
+ {
+ return _hWnd != NULL;
+ }
+ /**
+ *
+ *
+ **/
+ BOOLEAN IsCanceled() const
+ {
+ return _bIsCanceled;
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID SetTitleParam(LPCTSTR szText, ...)
+ {
+ if (szText)
+ {
+ TCHAR buf[MAXDATASIZE];
+ va_list vl;
+
+ va_start(vl, szText);
+ if (mir_vsntprintf(buf, SIZEOF(buf), szText, vl) != -1)
+ {
+ SetTitle(buf);
+ }
+ va_end(vl);
+ }
+ }
+
+ /**
+ * This method is used to set the popups or dialogs message text.
+ * It takes text with parameters as sprintf does. If bbcodes are
+ * disabled this method automatically deletes them from the text.
+ *
+ * @param szText - the text to display. Can contain formats like
+ * sprintf does.
+ * @param ... - a list of parameters, which depends on the
+ * format of szText.
+ *
+ * @return nothing
+ **/
+ VOID SetTextParam(LPCTSTR szText, ...)
+ {
+ if (szText)
+ {
+ INT_PTR cch = mir_tcslen(szText);
+ LPTSTR fmt = (LPTSTR) mir_alloc((cch + 1) * sizeof(TCHAR));
+
+ if (fmt)
+ {
+ TCHAR buf[MAXDATASIZE];
+ va_list vl;
+
+ _tcscpy(fmt, szText);
+
+ // delete bbcodes
+ if (!_bBBCode)
+ {
+ LPTSTR s, e;
+
+ for (s = fmt, e = fmt + cch; s[0] != 0; s++)
+ {
+ if (s[0] == '[')
+ {
+ // leading bbcode tag (e.g.: [b], [u], [i])
+ if ((s[1] == 'b' || s[1] == 'u' || s[1] == 'i') && s[2] == ']')
+ {
+ memmove(s, s + 3, (e - s - 2) * sizeof(TCHAR));
+ e -= 3;
+ }
+ // ending bbcode tag (e.g.: [/b], [/u], [/i])
+ else if (s[1] == '/' && (s[2] == 'b' || s[2] == 'u' || s[2] == 'i') && s[3] == ']')
+ {
+ memmove(s, s + 4, (e - s - 3) * sizeof(TCHAR));
+ e -= 4;
+ }
+ }
+ }
+ }
+
+ va_start(vl, szText);
+ if (mir_vsntprintf(buf, SIZEOF(buf), fmt, vl) != -1)
+ {
+ SetText(buf);
+ }
+ va_end(vl);
+ mir_free(fmt);
+ }
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ CUpdProgress()
+ {
+ _hWnd = NULL;
+ _pFnCallBack = NULL;
+ _pData = NULL;
+ _bIsCanceled = FALSE;
+ _bBBCode = FALSE;
+ }
+
+ /**
+ *
+ *
+ **/
+ CUpdProgress(PVOID data)
+ {
+ _hWnd = NULL;
+ _pFnCallBack = NULL;
+ _pData = data;
+ _bIsCanceled = FALSE;
+ _bBBCode = FALSE;
+ }
+
+ ~CUpdProgress()
+ {
+ }
+
+};
+
+/***********************************************************************************************************
+ * class CDlgUpdProgress
+ ***********************************************************************************************************/
+
+class CDlgUpdProgress : public CUpdProgress
+{
+ /**
+ *
+ *
+ **/
+ static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_BTN_UPDATE, WM_SETICON, NULL },
+ { ICO_BTN_DOWNARROW, BM_SETIMAGE, IDSKIP },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }
+ };
+ IcoLib_SetCtrlIcons(hWnd, idIcon, DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? 2 : 1);
+
+ SendDlgItemMessage(hWnd, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hWnd, IDSKIP, BUTTONTRANSLATE, NULL, NULL);
+ SetUserData(hWnd, lParam);
+
+ TranslateDialogDefault(hWnd);
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID))
+ {
+ case STATIC_WHITERECT:
+ case IDC_INFO:
+ {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return GetSysColor(COLOR_WINDOW);
+ }
+ }
+ }
+ return FALSE;
+
+ }
+ return CUpdProgress::DefWndProc((CUpdProgress *) GetUserData(hWnd), hWnd, uMsg, wParam, lParam);
+ }
+
+public:
+
+ /**
+ *
+ *
+ **/
+ CDlgUpdProgress(PVOID data)
+ : CUpdProgress(data)
+ {
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+ {
+ _pFnCallBack = pFnCallBack;
+ _hWnd = CreateDialogParam(ghInst,
+ MAKEINTRESOURCE(IDD_REFRESHDETAILS),
+ 0,
+ (DLGPROC)CDlgUpdProgress::WndProc,
+ (LPARAM) this);
+ if (_hWnd)
+ {
+ SetTitle(szTitle);
+ ShowWindow(_hWnd, SW_SHOW);
+ }
+ return _hWnd;
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID Destroy()
+ {
+ if (_hWnd)
+ {
+ SetUserData(_hWnd, NULL);
+ EndDialog(_hWnd, IDOK);
+ _hWnd = NULL;
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetTitle(LPCTSTR szText)
+ {
+ SetWindowText(_hWnd, szText);
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetText(LPCTSTR szText)
+ {
+ SetDlgItemText(_hWnd, IDC_INFO, szText);
+ }
+
+};
+
+/***********************************************************************************************************
+ * class CPopupUpdProgress
+ ***********************************************************************************************************/
+
+class CPopupUpdProgress : public CUpdProgress
+{
+ LPCTSTR _szText;
+ POPUPACTION _popupButtons[2];
+
+ /**
+ *
+ *
+ **/
+ VOID UpdateText()
+ {
+ if (_szText)
+ {
+ INT_PTR cb = mir_tcslen(_szText) + 8;
+ LPTSTR pb = (LPTSTR) mir_alloc(cb * sizeof(TCHAR));
+
+ if (pb)
+ {
+ mir_tcscpy(pb, _szText);
+
+ SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TITLET, (LPARAM) pb);
+ }
+ }
+ }
+
+ /**
+ * This static member is the window procedure, which is used to modify the behaviour of
+ * a popup dialog, so that it can act as a replacement for the progress dialog.
+ * The major task of this method is to filter out some messages, who would lead to a crash,
+ * if passed to the default windows procedure.
+ *
+ **/
+ static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Filter out messages, which must not be passed to default windows procedure or even
+ // to the callback function!
+ switch (uMsg)
+ {
+ case UM_INITPOPUP:
+ case UM_CHANGEPOPUP:
+ case UM_FREEPLUGINDATA:
+ break;
+ default:
+ CUpdProgress::DefWndProc((CUpdProgress *) PUGetPluginData(hWnd), hWnd, uMsg, wParam, lParam);
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+
+public:
+
+ /**
+ *
+ *
+ **/
+ CPopupUpdProgress(PVOID data)
+ : CUpdProgress(data)
+ {
+ _szText = NULL;
+ _bBBCode = DB::Setting::GetByte("PopUp", "UseMText", FALSE);
+
+ _popupButtons[0].cbSize = sizeof(POPUPACTION);
+ _popupButtons[0].flags = PAF_ENABLED;
+ _popupButtons[0].lchIcon = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
+ _popupButtons[0].wParam = MAKEWORD(IDSKIP, BN_CLICKED);
+ _popupButtons[0].lParam = NULL;
+ strcpy(_popupButtons[0].lpzTitle, MODNAME"/Hide");
+
+ // cancel button
+ _popupButtons[1].cbSize = sizeof(POPUPACTION);
+ _popupButtons[1].flags = PAF_ENABLED;
+ _popupButtons[1].lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ _popupButtons[1].wParam = MAKEWORD(IDCANCEL, BN_CLICKED);
+ _popupButtons[1].lParam = NULL;
+ strcpy(_popupButtons[1].lpzTitle, MODNAME"/Cancel");
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+ {
+ POPUPDATAT_V2 pd;
+
+ ZeroMemory(&pd, sizeof(POPUPDATAT_V2));
+ pd.cbSize = sizeof(POPUPDATAT_V2);
+ pd.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ pd.iSeconds = -1;
+ pd.PluginData = this;
+ pd.PluginWindowProc = (WNDPROC)CPopupUpdProgress::WndProc;
+ pd.actionCount = SIZEOF(_popupButtons);
+ pd.lpActions = _popupButtons;
+
+ // dummy text
+ _szText = mir_tcsdup(szTitle);
+ mir_tcscpy(pd.lptzContactName, _szText);
+
+ _tcscpy(pd.lptzText, _T(" "));
+
+ _pFnCallBack = pFnCallBack;
+ _hWnd = (HWND) CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_RETURN_HWND|APF_NEWDATA);
+ return _hWnd;
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID Destroy()
+ {
+ if (_hWnd)
+ {
+ PUDeletePopUp(_hWnd);
+ _hWnd = NULL;
+ }
+ MIR_FREE(_szText);
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetTitle(LPCTSTR szText)
+ {
+ MIR_FREE(_szText);
+ _szText = mir_tcsdup(szText);
+ UpdateText();
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetText(LPCTSTR szText)
+ {
+ SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TEXTT, (LPARAM) mir_tcsdup(szText));
+ }
+};
+
+
+/***********************************************************************************************************
+ * class CContactUpdater
+ ***********************************************************************************************************/
+
+class CContactUpdater : public CContactQueue
+{
+ CUpdProgress* _pProgress; // pointer to the progress dialog/popup
+ HANDLE _hProtoAckEvent; // handle to protocol ack notification
+ HANDLE _hContact; // current contact being refreshed
+ PBYTE _hContactAcks; // array of acknoledgements for the current contact to wait for
+ INT_PTR _nContactAcks; // number of acknoledgements for the current contact to wait for
+
+ MIRANDA_CPP_PLUGIN_API(CContactUpdater);
+
+ /**
+ * This is a callback dialog procedure, which is assigned the update progress dialog to
+ * gain control over certain messages.
+ *
+ * @param hWnd - HWND window handle of the progress dialog
+ * @param uMsg - message sent to the dialog
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ * @param u - This is a parameter assigned by the CUpdProgress' constructur.
+ * In this case it is a pointer to this class's object.
+ *
+ * @return This method returns 0.
+ **/
+ static INT DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, CContactUpdater* u)
+ {
+ switch (uMsg)
+ {
+
+ /**
+ * User has clicked on the skip or cancel button.
+ **/
+ case UM_POPUPACTION:
+ case WM_COMMAND:
+ {
+ if (PtrIsValid(u))
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ u->Cancel();
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * This method handles ack broadcasts from the protocols to determine,
+ * whether a contact's information set's update is complete to continue
+ * with the next faster. By default the queue is configured to wait
+ * about 15s between contacts. If the protocol sends a complete ack,
+ * the time is shortend to 4s.
+ *
+ * @param wParam - not used
+ * @param ack - pointer to an ACKDATA structure containing all
+ * data for the acknoledgement.
+ *
+ * @return nothing
+ **/
+ INT __cdecl OnProtoAck(WPARAM wParam, ACKDATA *ack)
+ {
+ if (ack && ack->cbSize == sizeof(ACKDATA) && ack->hContact == _hContact && ack->type == ACKTYPE_GETINFO)
+ {
+ if (ack->hProcess || ack->lParam)
+ {
+ if (!_hContactAcks)
+ {
+ _nContactAcks = (INT_PTR)ack->hProcess;
+ _hContactAcks = (PBYTE)mir_calloc(sizeof(BYTE) * (INT_PTR)ack->hProcess);
+ }
+ if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
+ {
+ _hContactAcks[ack->lParam] = 1;
+ }
+
+ for (INT i = 0; i < _nContactAcks; i++)
+ {
+ if (_hContactAcks[i] == 0)
+ {
+ return 0;
+ }
+ }
+ }
+ // don't wait the full time, but continue immitiatly
+ ContinueWithNext();
+ }
+ return 0;
+ }
+
+ /**
+ * This method is called just before the worker thread is going to suspend,
+ * if the queue is empty.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ virtual VOID OnEmpty()
+ {
+ // This was the last contact, so destroy the progress window.
+ if (_hProtoAckEvent)
+ {
+ UnhookEvent(_hProtoAckEvent);
+ _hProtoAckEvent = NULL;
+ }
+
+ // free up last ackresult array
+ MIR_FREE(_hContactAcks);
+ _nContactAcks = 0;
+ _hContact = NULL;
+
+ // close progress bar
+ if (_pProgress)
+ {
+ _pProgress->Destroy();
+
+ delete _pProgress;
+ _pProgress = NULL;
+ }
+
+ // reset menu
+ if (hMenuItemRefresh)
+ {
+ CLISTMENUITEM clmi;
+
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME|CMIM_ICON;
+ clmi.pszName = LPGEN("Refresh Contact Details");
+ clmi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM)&clmi);
+ }
+ }
+
+ /**
+ * This virtual method is called by the derived CContactQueue class,
+ * if an action is requested for a queue item.
+ *
+ * @param hContact - the handle of the contact an action is requested for
+ * @param param - not used here
+ *
+ * @return nothing
+ **/
+ virtual VOID Callback(HANDLE hContact, PVOID param)
+ {
+ LPSTR pszProto = DB::Contact::Proto(hContact);
+
+ if (pszProto && pszProto[0])
+ {
+ MIR_FREE(_hContactAcks);
+ _nContactAcks = 0;
+ _hContact = hContact;
+
+ if (!_hProtoAckEvent)
+ {
+ _hProtoAckEvent = (HANDLE) ThisHookEvent(ME_PROTO_ACK, (EVENTHOOK) &CContactUpdater::OnProtoAck);
+ }
+
+ if (_pProgress)
+ {
+ _pProgress->SetTextParam(TranslateT("[b]%s (%S)...[/b]\n%d Contacts remaning"),
+ DB::Contact::DisplayName(_hContact), pszProto, Size());
+ }
+ if (IsProtoOnline(pszProto))
+ {
+ INT i;
+ for (i = 0; i < 3 && CallContactService(hContact, PSS_GETINFO, 0, 0); i++)
+ {
+ Sleep(3000);
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ * This is the default constructor
+ *
+ **/
+ CContactUpdater() : CContactQueue()
+ {
+ _hContactAcks = NULL;
+ _nContactAcks = 0;
+ _hContact = NULL;
+ _pProgress = NULL;
+ _hProtoAckEvent = NULL;
+ }
+
+ /**
+ *
+ *
+ **/
+ ~CContactUpdater()
+ {
+ RemoveAll();
+ OnEmpty();
+ }
+
+ /**
+ *
+ *
+ **/
+ BOOL QueueAddRefreshContact(HANDLE hContact, INT iWait)
+ {
+ LPSTR pszProto = DB::Contact::Proto(hContact);
+
+ if ((mir_strcmp(pszProto, "Weather")!=0) &&
+ (mir_strcmp(pszProto, "MetaContacts")!=0) &&
+ IsProtoOnline(pszProto))
+ {
+ return Add(iWait, hContact);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID RefreshAll()
+ {
+ HANDLE hContact;
+ INT iWait;
+
+ for (hContact = DB::Contact::FindFirst(), iWait = 100;
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ if (QueueAddRefreshContact(hContact, iWait))
+ {
+ iWait += 5000;
+ }
+ }
+ if (Size() && !_pProgress)
+ {
+ if (ServiceExists(MS_POPUP_CHANGETEXTT) && DB::Setting::GetByte("PopupProgress", FALSE))
+ {
+ _pProgress = new CPopupUpdProgress(this);
+ }
+ else
+ {
+ _pProgress = new CDlgUpdProgress(this);
+ }
+
+ _pProgress->Create(TranslateT("Refresh Contact Details"), (PUpdCallback) CContactUpdater::DlgProc);
+ _pProgress->SetText(TranslateT("Preparing..."));
+ }
+
+ // if there are contacts in the queue, change the main menu item to indicate it is meant for canceling.
+ if (hMenuItemRefresh && Size() > 0)
+ {
+ CLISTMENUITEM clmi;
+
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME|CMIM_ICON;
+ clmi.pszName = LPGEN("Abort Refreshing Contact Details");
+ clmi.hIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM) &clmi);
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID Cancel()
+ {
+ RemoveAll();
+ ContinueWithNext();
+ }
+
+};
+
+static CContactUpdater *ContactUpdater = NULL;
+
+/***********************************************************************************************************
+ * common helper functions
+ ***********************************************************************************************************/
+
+/**
+ * This function checks, whether at least one protocol is online!
+ *
+ * @param none
+ *
+ * @retval TRUE - At least one protocol is online.
+ * @retval FALSE - All protocols are offline.
+ **/
+static BOOL IsMirandaOnline()
+{
+ PROTOACCOUNT **pAcc;
+ INT i, nAccCount;
+ BOOL bIsOnline = FALSE;
+
+ if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+ {
+ for (i = 0; (i < nAccCount) && !bIsOnline; i++)
+ {
+ bIsOnline |= (IsProtoAccountEnabled(pAcc[i]) && IsProtoOnline(pAcc[i]->szModuleName));
+ }
+ }
+ return bIsOnline;
+}
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function being called by MS_USERINFO_REFRESH.
+ * It adds each contact, whose protocol is online, to the queue of contacts to refresh.
+ * The queue is running a separate thread, which is responsible for requesting the contact information
+ * one after another with a certain time to wait in between.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return This service function always returns 0.
+ **/
+static INT_PTR RefreshService(WPARAM wParam, LPARAM lParam)
+{
+ try
+ {
+ if (IsMirandaOnline())
+ {
+ if (!ContactUpdater)
+ {
+ ContactUpdater = new CContactUpdater();
+ }
+
+ if (ContactUpdater->Size() == 0)
+ {
+ ContactUpdater->RefreshAll();
+ }
+ else if (IDYES == MsgBox(NULL, MB_YESNO|MB_ICON_QUESTION, LPGENT("Refresh Contact Details"), NULL,
+ LPGENT("Do you want to cancel the current refresh procedure?")))
+ {
+ ContactUpdater->Cancel();
+ }
+ }
+ else
+ {
+ MsgErr(NULL, LPGENT("Miranda must be online for refreshing contact information!"));
+ }
+ }
+ catch(...)
+ {
+ MsgErr(NULL, LPGENT("The function caused an exception!"));
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * events
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+static INT OnContactAdded(WPARAM wParam, LPARAM lParam)
+{
+ try
+ {
+ DWORD dwStmp;
+
+ dwStmp = DB::Setting::GetDWord((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME, 0);
+ if (!dwStmp)
+ {
+ MTime mt;
+
+ mt.GetLocalTime();
+ mt.DBWriteStamp((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME);
+
+ // create updater, if not yet exists
+ if (!ContactUpdater)
+ {
+ ContactUpdater = new CContactUpdater();
+ }
+
+ // add to the end of the queue
+ ContactUpdater->AddIfDontHave(
+ (ContactUpdater->Size() > 0)
+ ? max(ContactUpdater->Get(ContactUpdater->Size() - 1)->check_time + 15000, 4000)
+ : 4000,
+ (HANDLE)wParam);
+ }
+ }
+ catch(...)
+ {
+ MsgErr(NULL, LPGENT("The function caused an exception!"));
+ }
+ return 0;
+}
+
+/**
+ * Miranda is going to shutdown soon, so any panding contact information refresh is to be terminated
+ * and the queue object must be deleted. Further refresh requests will be ignored.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return This function always returns 0.
+ **/
+static INT OnPreShutdown(WPARAM, LPARAM)
+{
+ if(ContactUpdater) {
+ delete ContactUpdater;
+ ContactUpdater = 0;
+ }
+ //MIR_DELETE(ContactUpdater);
+ return 0;
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcRefreshContactInfoLoadModule(VOID)
+{
+ CreateServiceFunction(MS_USERINFO_REFRESH, RefreshService);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
+ HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded);
+
+ HOTKEYDESC hk = { 0 };
+ hk.cbSize = sizeof(HOTKEYDESC);
+ hk.pszSection = MODNAME;
+ hk.pszName = "RefreshContactDetails";
+ hk.pszDescription = LPGEN("Refresh Contact Details");
+ hk.pszService = MS_USERINFO_REFRESH;
+ Hotkey_Register(&hk);
+}
diff --git a/plugins/UserInfoEx/src/svc_refreshci.h b/plugins/UserInfoEx/src/svc_refreshci.h
new file mode 100644
index 0000000000..4f9c551385
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_refreshci.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_REFRESH_USER_DETAILS_H_
+#define _SVC_REFRESH_USER_DETAILS_H_
+
+VOID SvcRefreshContactInfoLoadModule(VOID);
+
+#endif /* _SVC_REFRESH_USER_DETAILS_H_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_reminder.cpp b/plugins/UserInfoEx/src/svc_reminder.cpp
new file mode 100644
index 0000000000..f91ba47f33
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_reminder.cpp
@@ -0,0 +1,1216 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_reminder.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "m_skin.h"
+#include "m_clui.h"
+
+#include "svc_Gender.h"
+#include "svc_Reminder.h"
+#include "dlg_anniversarylist.h"
+
+/**
+ * The CEvent structure describes the next anniversary to remind of.
+ **/
+struct CEvent
+{
+ enum EType { NONE, BIRTHDAY, ANNIVERSARY };
+
+ EType _eType;
+ WORD _wDaysLeft;
+
+ CEvent();
+ CEvent(EType eType, WORD wDaysLeft);
+
+ BOOLEAN operator << (const CEvent& e);
+};
+
+typedef struct _REMINDEROPTIONS
+{
+ WORD wDaysEarlier;
+ BYTE bPopups;
+ BYTE bCListExtraIcon;
+ BYTE bFlashCList;
+ BYTE bCheckVisibleOnly;
+ BYTE RemindState;
+ CEvent evt;
+}
+REMINDEROPTIONS, *LPREMINDEROPTIONS;
+
+static HANDLE ExtraIcon = INVALID_HANDLE_VALUE;
+
+
+static HANDLE ghCListIA = NULL;
+static HANDLE ghCListIR = NULL;
+static HANDLE ghSettingsChanged = NULL;
+
+static UINT_PTR ghRemindTimer = 0;
+static UINT_PTR ghRemindDateChangeTimer = 0;
+
+HANDLE ghCListAnnivIcons[11];
+HANDLE ghCListBirthdayIcons[11];
+
+static REMINDEROPTIONS gRemindOpts;
+
+static VOID UpdateTimer(BOOLEAN bStartup);
+
+
+/***********************************************************************************************************
+ * struct CEvent
+ ***********************************************************************************************************/
+
+/**
+ * This is the default constructor.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+CEvent::CEvent()
+{
+ _wDaysLeft = 0xFFFF;
+ _eType = NONE;
+}
+
+/**
+ * This is the default constructor.
+ *
+ * @param eType - initial type
+ * @param wDaysLeft - initial days to event
+ *
+ * @return nothing
+ **/
+CEvent::CEvent(EType eType, WORD wDaysLeft)
+{
+ _wDaysLeft = wDaysLeft;
+ _eType = eType;
+}
+
+/**
+ * This operator dups the attributes of the given CEvent object if
+ * the event comes up earlier then the one of the object.
+ *
+ * @param evt - the reference to the event object whose attributes to assign.
+ *
+ * @retval TRUE - The values of @e evt have been assigned.
+ * @retval FALSE - The values are not assigned.
+ **/
+BOOLEAN CEvent::operator << (const CEvent& evt)
+{
+ if (_wDaysLeft > evt._wDaysLeft)
+ {
+ _wDaysLeft = evt._wDaysLeft;
+ _eType = evt._eType;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/***********************************************************************************************************
+ * notification functions
+ ***********************************************************************************************************/
+
+/**
+ * This function returns the icon for the given anniversary,
+ * which is the given number of days in advance.
+ *
+ * @param evt - structure specifying the next anniversary
+ *
+ * @return The function returns icolib's icon if found or NULL otherwise.
+ **/
+static HICON GetAnnivIcon(const CEvent &evt)
+{
+ HICON hIcon = NULL;
+
+ CHAR szIcon[MAXSETTING];
+
+ switch (evt._eType)
+ {
+ case CEvent::BIRTHDAY:
+ {
+ if (evt._wDaysLeft > 9)
+ {
+ hIcon = IcoLib_GetIcon(ICO_RMD_DTBX);
+ }
+ else
+ {
+ mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft);
+ hIcon = IcoLib_GetIcon(szIcon);
+ }
+ }
+ break;
+
+ case CEvent::ANNIVERSARY:
+ {
+ if (evt._wDaysLeft > 9)
+ {
+ hIcon = IcoLib_GetIcon(ICO_RMD_DTAX);
+ }
+ else
+ {
+ mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft);
+ hIcon = IcoLib_GetIcon(szIcon);
+ }
+ }
+ }
+ return hIcon;
+}
+
+/**
+ * This function adds the icon for the given anniversary, which is the given number of days
+ * in advance to the contact list's imagelist.
+ *
+ * @param evt - structure specifying the next anniversary
+ *
+ * @return The function returns the clist's extra icon handle if found and successfully added.
+ **/
+static HANDLE AddCListExtraIcon(const CEvent &evt)
+{
+ HANDLE hClistIcon;
+ HICON hIco = GetAnnivIcon(evt);
+ if (hIco)
+ {
+ hClistIcon = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIco, 0);
+ if (hClistIcon == (HANDLE)CALLSERVICE_NOTFOUND)
+ hClistIcon = INVALID_HANDLE_VALUE;
+
+ Skin_ReleaseIcon(hIco);
+ }
+ else hClistIcon = INVALID_HANDLE_VALUE;
+
+ return hClistIcon;
+}
+
+/**
+ * This function returns the clist extra icon handle for the given anniversary.
+ *
+ * @param evt - structure specifying the next anniversary
+ *
+ * @return The function returns the clist extra icon handle for the given anniversary.
+ **/
+static HANDLE GetCListExtraIcon(const CEvent &evt)
+{
+ if (gRemindOpts.bCListExtraIcon)
+ {
+ WORD wIndex = evt._wDaysLeft;
+
+ switch (evt._eType)
+ {
+ case CEvent::BIRTHDAY:
+ {
+ if (wIndex >= SIZEOF(ghCListBirthdayIcons))
+ {
+ wIndex = SIZEOF(ghCListBirthdayIcons) - 1;
+ }
+ // add the icon to clists imagelist if required
+ if (ghCListBirthdayIcons[wIndex] == INVALID_HANDLE_VALUE)
+ {
+ ghCListBirthdayIcons[wIndex] = AddCListExtraIcon(evt);
+ }
+ }
+ return ghCListBirthdayIcons[wIndex];
+
+ case CEvent::ANNIVERSARY:
+ {
+ if (wIndex >= SIZEOF(ghCListAnnivIcons))
+ {
+ wIndex = SIZEOF(ghCListAnnivIcons) - 1;
+ }
+ // add the icon to clists imagelist if required
+ if (ghCListAnnivIcons[wIndex] == INVALID_HANDLE_VALUE)
+ {
+ ghCListAnnivIcons[wIndex] = AddCListExtraIcon(evt);
+ }
+ }
+ return ghCListAnnivIcons[wIndex];
+ }
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+/**
+ * Displays an clist extra icon according to the kind of anniversary
+ * and the days in advance.
+ *
+ * @param evt - structure specifying the next anniversary
+ *
+ * @return nothing
+ **/
+static VOID NotifyWithExtraIcon(HANDLE hContact, const CEvent &evt)
+{
+ if (myGlobals.HaveCListExtraIcons && gRemindOpts.bCListExtraIcon)
+ {
+ if (!myGlobals.ExtraIconsServiceExist)
+ {
+ IconExtraColumn iec;
+
+ iec.cbSize = sizeof(IconExtraColumn);
+ iec.ColumnType = gRemindOpts.bCListExtraIcon;
+ iec.hImage = GetCListExtraIcon(evt);
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+ }
+ else
+ {
+ CHAR szIcon[MAXSETTING];
+ EXTRAICON ico;
+
+ ico.cbSize=sizeof(ico);
+ ico.hContact=hContact;
+ ico.hExtraIcon=ExtraIcon;
+ switch (evt._eType)
+ {
+ case CEvent::BIRTHDAY:
+ {
+ if (evt._wDaysLeft > 9)
+ {
+ ico.icoName=ICO_RMD_DTAX;
+ }
+ else
+ {
+ mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft);
+ ico.icoName=szIcon;
+ }
+ break;
+ }
+ case CEvent::ANNIVERSARY:
+ {
+ if (evt._wDaysLeft > 9)
+ {
+ ico.icoName=ICO_RMD_DTAX;
+ }
+ else
+ {
+ mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft);
+ ico.icoName=szIcon;
+ }
+ break;
+ }
+ default:
+ ico.icoName=(char *)0;
+ }
+ CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+ }
+ }
+}
+
+/**
+ * Message procedure for popup messages
+ *
+ * @param hWnd - handle to the popupwindow
+ * @param uMsg - message to handle
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return message specific
+ **/
+static INT_PTR CALLBACK PopupWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == STN_CLICKED)
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ PUDeletePopUp(hWnd);
+ return TRUE;
+ }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+/**
+ * Displays a popup
+ *
+ * @param hContact - contact to display popup for
+ * @param eventType - indicates which popup settings to apply
+ * @param DaysToAnniv - days left until anniversary occures
+ * @param pszDesc - this is the headline
+ * @param szMsg - message to display
+ *
+ * @return return value of the popup service
+ **/
+static INT NotifyWithPopup(HANDLE hContact, CEvent::EType eventType, INT DaysToAnniv, LPCTSTR pszDesc, LPCTSTR pszMsg)
+{
+ if (gRemindOpts.bPopups)
+ {
+ POPUPDATAT_V2 ppd;
+
+ ZeroMemory(&ppd, sizeof(POPUPDATAT_V2));
+ ppd.PluginWindowProc = (WNDPROC)PopupWindowProc;
+ ppd.iSeconds = (INT)DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+
+ if (hContact)
+ {
+ ppd.lchContact = hContact;
+ mir_sntprintf(ppd.lptzContactName, SIZEOF(ppd.lptzContactName),
+ _T("%s - %s"), TranslateTS(pszDesc), DB::Contact::DisplayName(hContact));
+ }
+ else
+ {
+ mir_tcsncpy(ppd.lptzContactName, TranslateT("Reminder"), SIZEOF(ppd.lptzContactName));
+ }
+ mir_tcsncpy(ppd.lptzText, pszMsg, MAX_SECONDLINE);
+
+ ppd.lchIcon = GetAnnivIcon(CEvent(eventType, DaysToAnniv));
+
+ switch (eventType)
+ {
+ case CEvent::BIRTHDAY:
+ switch (DB::Setting::GetByte(SET_POPUP_BIRTHDAY_COLORTYPE, POPUP_COLOR_CUSTOM))
+ {
+ case POPUP_COLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+
+ case POPUP_COLOR_CUSTOM:
+ ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_BACK, RGB(192,180,30));
+ ppd.colorText = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_TEXT, 0);
+ break;
+ }
+ break;
+
+ case CEvent::ANNIVERSARY:
+ switch (DB::Setting::GetByte(SET_POPUP_ANNIVERSARY_COLORTYPE, POPUP_COLOR_CUSTOM))
+ {
+ case POPUP_COLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+
+ case POPUP_COLOR_CUSTOM:
+ ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_BACK, RGB(90, 190, 130));
+ ppd.colorText = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_TEXT, 0);
+ break;
+ }
+ }
+ return PUAddPopUpT(&ppd);
+ }
+ return 1;
+}
+
+/**
+ * Flash contact list's contact icon.
+ *
+ * @param hContact - contact whose icon to flash
+ * @param evt - structure specifying the next anniversary
+ *
+ * @return nothing
+ **/
+static VOID NotifyFlashCListIcon(HANDLE hContact, const CEvent &evt)
+{
+ if (gRemindOpts.bFlashCList && evt._wDaysLeft == 0)
+ {
+ CLISTEVENT cle ={0};
+ TCHAR szMsg[MAX_PATH];
+
+ cle.cbSize = sizeof(CLISTEVENT);
+ cle.hContact = hContact;
+ cle.flags = CLEF_URGENT|CLEF_TCHAR;
+ cle.hDbEvent = NULL;
+
+ switch (evt._eType) {
+ case CEvent::BIRTHDAY:
+ {
+ mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s has %s today."),
+ DB::Contact::DisplayName(hContact),
+ TranslateT("Birthday"));
+ cle.hIcon = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY);
+ }
+ break;
+
+ case CEvent::ANNIVERSARY:
+ {
+ mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s has %s today."),
+ DB::Contact::DisplayName(hContact),
+ TranslateT("an anniversary"));
+ cle.hIcon = IcoLib_GetIcon(ICO_COMMON_ANNIVERSARY);
+ }
+ break;
+
+ default:
+ szMsg[0] = NULL;
+ }
+ cle.ptszTooltip = szMsg;
+
+ // pszService = NULL get error (crash),
+ // pszService = "dummy" get 'service not fount' and continue;
+ cle.pszService = "dummy";
+ cle.lParam = NULL;
+
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+ }
+}
+
+/**
+ * Play a sound for the nearest upcoming anniversary
+ *
+ * @param evt - structure specifying the next anniversary
+ *
+ * @retval 0 if sound was played
+ * @retval 1 otherwise
+ **/
+static BYTE NotifyWithSound(const CEvent &evt)
+{
+ if (evt._wDaysLeft <= min(DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET), gRemindOpts.wDaysEarlier))
+ {
+ switch (evt._eType)
+ {
+ case CEvent::BIRTHDAY:
+ SkinPlaySound(evt._wDaysLeft == 0 ? SOUND_BIRTHDAY_TODAY : SOUND_BIRTHDAY_SOON);
+ return 0;
+
+ case CEvent::ANNIVERSARY:
+ SkinPlaySound(SOUND_ANNIVERSARY);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/***********************************************************************************************************
+ * "check for anniversary" functions
+ ***********************************************************************************************************/
+
+static LPCTSTR ContactGender(HANDLE hContact)
+{
+ switch (GenderOf(hContact))
+ {
+ case 'M': return TranslateT("He");
+ case 'F': return TranslateT("She");
+ }
+ return TranslateT("He/She");
+}
+
+static BOOLEAN CheckAnniversaries(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify)
+{
+ INT numAnniversaries = 0;
+ INT Diff;
+ MAnnivDate mta;
+ INT i;
+ TCHAR szAnniv[MAX_PATH];
+ TCHAR strMsg[MAX_SECONDLINE];
+ BOOLEAN bOverflow = FALSE;
+ WORD wDaysEarlier;
+
+ if ((gRemindOpts.RemindState == REMIND_ANNIV) || (gRemindOpts.RemindState == REMIND_ALL))
+ {
+ for (i = 0; i < ANID_LAST && !mta.DBGetAnniversaryDate(hContact, i); i++)
+ {
+ mta.DBGetReminderOpts(hContact);
+
+ if (mta.RemindOption() != BST_UNCHECKED)
+ {
+ wDaysEarlier = (mta.RemindOption() == BST_CHECKED) ? mta.RemindOffset() : -1;
+ if (wDaysEarlier == (WORD)-1)
+ {
+ wDaysEarlier = gRemindOpts.wDaysEarlier;
+ }
+
+ Diff = mta.CompareDays(Now);
+ if ((Diff >= 0) && (Diff <= wDaysEarlier))
+ {
+ if (evt._wDaysLeft > Diff)
+ {
+ evt._wDaysLeft = Diff;
+ evt._eType = CEvent::ANNIVERSARY;
+ }
+ numAnniversaries++;
+
+ // create displayed text for popup
+ if (bNotify && !bOverflow)
+ {
+ // first anniversary found
+ if (numAnniversaries == 1)
+ {
+ mir_sntprintf(szAnniv, MAX_PATH,
+ TranslateT("%s has the following anniversaries:\0"),
+ ContactGender(hContact));
+ mir_tcsncpy(strMsg, szAnniv, mir_tcslen(szAnniv));
+ }
+ switch (Diff)
+ {
+ case 0:
+ {
+ mir_sntprintf(szAnniv, MAX_PATH,
+ TranslateT("%d. %s today\0"),
+ mta.Age(), mta.Description());
+ }
+ break;
+
+ case 1:
+ {
+ mir_sntprintf(szAnniv, MAX_PATH,
+ TranslateT("%d. %s tomorrow\0"),
+ mta.Age() + 1, mta.Description());
+ }
+ break;
+
+ default:
+ {
+ mir_sntprintf(szAnniv, MAX_PATH,
+ TranslateT("%d. %s in %d days\0"),
+ mta.Age() + 1, mta.Description(), Diff);
+ }
+ }
+ if (mir_tcslen(szAnniv) >= MAX_SECONDLINE - mir_tcslen(strMsg))
+ {
+ if (strMsg)
+ mir_tcsncat(strMsg, _T("\n...\0"), SIZEOF(strMsg));
+ else
+ mir_tcsncpy(strMsg, _T("\n...\0"), mir_tcslen(_T("\n...\0")));
+ bOverflow = TRUE;
+ }
+ else
+ {
+ if (strMsg)
+ mir_tcsncat(strMsg, _T("\n- \0"), SIZEOF(strMsg));
+ else
+ mir_tcsncpy(strMsg, _T("\n- \0"), mir_tcslen(_T("\n- \0")));
+ mir_tcsncat(strMsg, szAnniv, SIZEOF(strMsg));
+ }
+ }
+ }
+ }
+ }
+ }
+ // show one popup for all anniversaries
+ if (numAnniversaries != 0 && bNotify)
+ {
+ NotifyWithPopup(hContact, CEvent::ANNIVERSARY, Diff, LPGENT("Anniversaries"), strMsg);
+ }
+ return numAnniversaries != 0;
+}
+
+/**
+ * This function checks, whether a contact has a birthday and it is within the period of time to remind of or not.
+ *
+ * @param hContact - the contact to check
+ * @param Now - current time
+ * @param evt - the reference to a structure, which retrieves the resulting DTB
+ * @param bNotify - if TRUE, a popup will be displayed for a contact having birthday within the next few days.
+ * @param LastAnswer - this parameter is used for the automatic backup function
+ *
+ * @retval TRUE - contact has a birthday to remind of
+ * @retval FALSE - contact has no birthday or it is not within the desired period of time.
+ **/
+static BOOLEAN CheckBirthday(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer)
+{
+ BOOLEAN result = FALSE;
+
+ if (gRemindOpts.RemindState == REMIND_BIRTH || gRemindOpts.RemindState == REMIND_ALL)
+ {
+ MAnnivDate mtb;
+
+ if (!mtb.DBGetBirthDate(hContact))
+ {
+ INT Diff;
+ WORD wDaysEarlier;
+
+ mtb.DBGetReminderOpts(hContact);
+
+ // make backup of each protocol based birthday
+ if (DB::Setting::GetByte(SET_REMIND_SECUREBIRTHDAY, TRUE))
+ {
+ mtb.BackupBirthday(hContact, NULL, 0, LastAnwer);
+ }
+
+ if (mtb.RemindOption() != BST_UNCHECKED)
+ {
+ wDaysEarlier = (mtb.RemindOption() == BST_CHECKED) ? mtb.RemindOffset() : -1;
+ if (wDaysEarlier == (WORD)-1)
+ {
+ wDaysEarlier = gRemindOpts.wDaysEarlier;
+ }
+
+ Diff = mtb.CompareDays(Now);
+ if ((Diff >= 0) && (Diff <= wDaysEarlier))
+ {
+ if (evt._wDaysLeft > Diff)
+ {
+ evt._wDaysLeft = Diff;
+ evt._eType = CEvent::BIRTHDAY;
+ }
+
+ if (bNotify)
+ {
+ TCHAR szMsg[MAXDATASIZE];
+ WORD cchMsg = 0;
+
+ switch (Diff)
+ {
+ case 0:
+ {
+ cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s has birthday today."),
+ DB::Contact::DisplayName(hContact));
+ }
+ break;
+
+ case 1:
+ {
+ cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s has birthday tomorrow."),
+ DB::Contact::DisplayName(hContact));
+ }
+ break;
+
+ default:
+ {
+ cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+ TranslateT("%s has birthday in %d days."),
+ DB::Contact::DisplayName(hContact), Diff);
+ }
+ }
+ mir_sntprintf(szMsg + cchMsg, SIZEOF(szMsg) - cchMsg,
+ TranslateT("\n%s becomes %d years old."),
+ ContactGender(hContact), mtb.Age(&Now) + (Diff > 0));
+
+ NotifyWithPopup(hContact, CEvent::BIRTHDAY, Diff, mtb.Description(), szMsg);
+ }
+ result = TRUE;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+/**
+ * This function checks one contact. It is mainly used for clist extra icon rebuild notification handler.
+ *
+ * @param hContact - the contact to check
+ * @param Now - current time
+ * @param evt - the reference to a structure, which retrieves the resulting DTB
+ * @param bNotify - if TRUE, a popup will be displayed for a contact having birthday within the next few days.
+ * @param LastAnswer - this parameter is used for the automatic backup function
+ *
+ * @return nothing
+ **/
+static VOID CheckContact(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer = 0)
+{
+ // ignore meta subcontacts here as their birthday information are collected explicitly
+ if (hContact &&
+ (!gRemindOpts.bCheckVisibleOnly || !DB::Setting::GetByte(hContact, MOD_CLIST, "Hidden", FALSE)) &&
+ (!DB::MetaContact::IsSub(hContact)))
+ {
+ CEvent ca;
+
+ if (CheckBirthday(hContact, Now, ca, bNotify, LastAnwer) ||
+ CheckAnniversaries(hContact, Now, ca, bNotify))
+ {
+ evt << ca;
+
+ if (bNotify)
+ {
+ NotifyFlashCListIcon(hContact, ca);
+ }
+ }
+ NotifyWithExtraIcon(hContact, ca);
+ }
+}
+
+/**
+ * This function checks all contacts.
+ *
+ * @param notify - notification type
+ *
+ * @return nothing
+ **/
+VOID SvcReminderCheckAll(const ENotify notify)
+{
+ if (gRemindOpts.RemindState != REMIND_OFF)
+ {
+ HANDLE hContact;
+ CEvent evt;
+ MTime now;
+ WORD a1 = 0;
+
+ now.GetLocalTime();
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ CheckContact(hContact, now, evt, notify != NOTIFY_CLIST, &a1);
+ }
+
+ if (notify != NOTIFY_CLIST)
+ {
+ // play sound for the next anniversary
+ NotifyWithSound(evt);
+
+ // popup anniversary list
+ if (DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE))
+ {
+ DlgAnniversaryListShow(0, 0);
+ }
+
+ if (evt._wDaysLeft > gRemindOpts.wDaysEarlier && notify == NOTIFY_NOANNIV)
+ {
+ NotifyWithPopup(NULL, CEvent::NONE, 0, NULL, TranslateT("No anniversaries to remind of"));
+ }
+ }
+ UpdateTimer(FALSE);
+ }
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+/**
+ * This is the notification handler to tell reminder to reload required icons.
+ * The reminder only loads icons to clist, which are really required at the moment.
+ * This should help to save a bit memory.
+ *
+ * @param: wParam - not used
+ * @param: lParam - not used
+ *
+ * @return This function must return 0 in order to continue in the notification chain.
+ **/
+static INT OnCListRebuildIcons(WPARAM, LPARAM)
+{
+ UINT i;
+
+ for (i = 0; i < SIZEOF(ghCListAnnivIcons); i++)
+ {
+ ghCListAnnivIcons[i] = INVALID_HANDLE_VALUE;
+ }
+ for (i = 0; i < SIZEOF(ghCListBirthdayIcons); i++)
+ {
+ ghCListBirthdayIcons[i] = INVALID_HANDLE_VALUE;
+ }
+ return 0;
+}
+
+/**
+ * This function is the notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param hContact - handle to the contact whose extra icon is to apply
+ * @param lParam - not used
+ *
+ * @return This function must return 0 in order to continue in the notification chain.
+ **/
+INT OnCListApplyIcon(HANDLE hContact, LPARAM)
+{
+ if (gRemindOpts.RemindState != REMIND_OFF)
+ {
+ CEvent evt;
+ MTime now;
+
+ now.GetLocalTime();
+ CheckContact(hContact, now, evt, FALSE);
+ }
+ return 0;
+}
+
+/**
+ * This is a notification handler for changed contact settings.
+ * If any anniversary setting has changed for a meta sub contact,
+ * the parental meta contact is rescanned.
+ *
+ * @param hContact - handle of the contect the notification was fired for
+ * @param pdbcws - pointer to a DBCONTACTWRITESETTING structure
+ *
+ * @return This function must return 0 in order to continue in the notification chain.
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+ if (hContact && // valid contact not owner!
+ ghCListIA && // extraicons active
+ pdbcws && pdbcws->szSetting && // setting structure valid
+ (pdbcws->value.type < DBVT_DWORD) && // anniversary datatype
+ (gRemindOpts.RemindState != REMIND_OFF) && // reminder active
+ (!strncmp(pdbcws->szSetting, "Birth", 5) ||
+ !strncmp(pdbcws->szSetting, "Anniv", 5) ||
+ !strncmp(pdbcws->szSetting, "DOB", 3)))
+ {
+ HANDLE hMeta = DB::MetaContact::GetMeta(hContact);
+ WORD LastAnswer = IDNONE;
+ CEvent evt;
+ MTime now;
+
+ // check metacontact instead of subcontact
+ if (hMeta)
+ {
+ hContact = hMeta;
+ }
+ now.GetLocalTime();
+ if (!strcmp(pdbcws->szModule, SvcReminderGetMyBirthdayModule()))
+ {
+ CheckContact(hContact, now, evt, FALSE, &LastAnswer);
+ }
+ else
+ {
+ CheckContact(hContact, now, evt, FALSE, 0);
+ }
+ }
+ return 0;
+}
+
+#define TBB_IDBTN "CheckAnniv"
+#define TBB_ICONAME TOOLBARBUTTON_ICONIDPREFIX TBB_IDBTN TOOLBARBUTTON_ICONIDPRIMARYSUFFIX
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+
+VOID SvcReminderOnTopToolBarLoaded()
+{
+ TTBButton ttb = {0};
+ ttb.cbSize = sizeof(ttb);
+
+ ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
+ ttb.pszService = MS_USERINFO_REMINDER_CHECK;
+ ttb.name = ttb.pszTooltipUp = LPGEN("Check anniversaries");
+ ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_BIRTHDAY);
+ TopToolbar_AddButton(&ttb);
+}
+
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function for MS_USERINFO_REMINDER_CHECK.
+ *
+ * @param: wParam - not used
+ * @param: lParam - not used
+ *
+ * @return 0
+ **/
+static INT_PTR CheckService(WPARAM, LPARAM)
+{
+ if (gRemindOpts.RemindState != REMIND_OFF)
+ {
+ SvcReminderCheckAll(NOTIFY_NOANNIV);
+ }
+ return 0;
+}
+
+/**
+ * This is the service function for MS_USERINFO_REMINDER_AGGRASIVEBACKUP.
+ *
+ * @param hContact - handle to single contact or NULL to backup all
+ * @param lParam - if 1, the messagebox will not be displayed
+ *
+ * return: 0
+ **/
+static INT_PTR BackupBirthdayService(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ MAnnivDate mdb;
+
+ if (hContact)
+ {
+ if (!mdb.DBGetBirthDate(hContact))
+ {
+ mdb.BackupBirthday(hContact, NULL, TRUE);
+ }
+ }
+ else
+ {
+ WORD a1 = 0;
+
+ //walk through all the contacts stored in the DB
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ if (!DB::MetaContact::IsSub(hContact) && !mdb.DBGetBirthDate(hContact))
+ {
+ mdb.BackupBirthday(hContact, NULL, TRUE, &a1);
+ }
+ }
+ }
+
+ if (lParam != TRUE)
+ {
+ MSGBOX mBox;
+
+ mBox.cbSize = sizeof(MSGBOX);
+ mBox.hParent = NULL;
+ mBox.hiLogo = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY);
+ mBox.uType = MB_ICON_INFO;
+ mBox.ptszTitle = TranslateT("Update custom birthday");
+ mBox.ptszMsg = TranslateT("Backing up and syncing all birthdays complete!");
+ MsgBoxService(NULL, (LPARAM)&mBox);
+ }
+ return 0;
+}
+
+/**
+ * This function returns a constant pointer to the module the date should be saved to
+ *
+ * @param none
+ *
+ * @return module to write birthday information to, MOD_MBIRTHDAY by default
+ **/
+LPCSTR SvcReminderGetMyBirthdayModule()
+{
+ return ((DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE) == 1) ? USERINFO : MOD_MBIRTHDAY);
+}
+
+
+/***********************************************************************************************************
+ * timer stuff
+ ***********************************************************************************************************/
+
+/**
+ * Timer procedure, called if date changed. This updates clist icons.
+ *
+ * @param hwnd - not used
+ * @param uMsg - not used
+ * @param idEvent - not used
+ * @param dwTime - not used
+ * @return nothing
+ **/
+static VOID CALLBACK TimerProc_DateChanged(HWND, UINT, UINT_PTR, DWORD)
+{
+ static MTime last;
+ MTime now;
+
+ now.GetLocalTime();
+ if (now.Day() > last.Day() || now.Month() > last.Month() || now.Year() > last.Year()) {
+ SvcReminderCheckAll(NOTIFY_CLIST);
+ last = now;
+ }
+}
+
+/**
+ * Timer procedure, called again and again if the notification interval ellapsed
+ *
+ * @param hwnd - not used
+ * @param uMsg - not used
+ * @param idEvent - not used
+ * @param dwTime - not used
+ *
+ * @return nothing
+ **/
+static VOID CALLBACK TimerProc_Check(HWND, UINT, UINT_PTR, DWORD)
+{
+ SvcReminderCheckAll(NOTIFY_POPUP);
+}
+
+/**
+ * Load timers or update them.
+ *
+ * @param bStartup - is only TRUE if module is loaded to indicate startup process
+ *
+ * @return nothing
+ **/
+static VOID UpdateTimer(BOOLEAN bStartup)
+{
+ LONG wNotifyInterval = 60 * 60 * (LONG)DB::Setting::GetWord(MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL);
+ MTime now, last;
+
+ now.GetTimeUTC();
+
+ if (bStartup) {
+ last.DBGetStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+
+ // if last check occured at least one day before just do it on startup again
+ if (now.Year() > last.Year() || now.Month() > last.Month() || now.Day() > last.Day() || DB::Setting::GetByte(SET_REMIND_CHECKON_STARTUP, FALSE))
+ wNotifyInterval = 5;
+ else
+ wNotifyInterval -= now.Compare(last);
+
+ ghRemindDateChangeTimer = SetTimer(0, 0, 1000 * 60 * 5, (TIMERPROC)TimerProc_DateChanged);
+ }
+ else {
+ now.DBWriteStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+ }
+ // wait at least 5 seconds before checking at startup, to give miranda a better chance to load faster
+ KillTimer(0, ghRemindTimer);
+ ghRemindTimer = SetTimer(0, 0, 1000 * wNotifyInterval, TimerProc_Check);
+}
+
+/***********************************************************************************************************
+ * module loading & unloading
+ ***********************************************************************************************************/
+
+VOID SvcReminderEnable(BOOLEAN bEnable)
+{
+ if (bEnable) // Reminder is on
+ {
+ if (myGlobals.ExtraIconsServiceExist && (ExtraIcon == INVALID_HANDLE_VALUE))
+ {
+ EXTRAICON_INFO ico = {0};
+ ico.type = EXTRAICON_TYPE_ICOLIB;
+ ico.cbSize=sizeof(ico);
+ ico.name="Reminder";
+ ico.description="Reminder (uinfoex)";
+ ico.descIcon=ICO_COMMON_ANNIVERSARY;
+ ExtraIcon=(HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+ ZeroMemory(&ico,sizeof(ico));
+ }
+ // init hooks
+ if (!ghCListIR)
+ {
+ ghCListIR = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOK)OnCListRebuildIcons);
+ }
+
+ if (!ghCListIA)
+ {
+ ghCListIA = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcon);
+ }
+ if (!ghSettingsChanged && !myGlobals.UseDbxTree)
+ {
+ ghSettingsChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+ }
+
+ // reinit reminder options
+ gRemindOpts.RemindState = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+ gRemindOpts.wDaysEarlier = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);
+ gRemindOpts.bCListExtraIcon = DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1);
+ gRemindOpts.bCheckVisibleOnly = DB::Setting::GetByte(SET_REMIND_CHECKVISIBLE, DEFVAL_REMIND_CHECKVISIBLE);
+ gRemindOpts.bFlashCList = DB::Setting::GetByte(SET_REMIND_FLASHICON, FALSE);
+ gRemindOpts.bPopups = ServiceExists(MS_POPUP_ADDPOPUPT) && DB::Setting::GetByte(SET_POPUP_ENABLED, DEFVAL_POPUP_ENABLED);
+
+ // init the timer
+ UpdateTimer(TRUE);
+ }
+ else // Reminder is off
+ {
+ HANDLE hContact;
+
+ for (hContact = DB::Contact::FindFirst();
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ NotifyWithExtraIcon(hContact, CEvent());
+ }
+ gRemindOpts.RemindState = REMIND_OFF;
+ SvcReminderUnloadModule();
+ }
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcReminderOnModulesLoaded(VOID)
+{
+ // init clist extra icon structure
+ OnCListRebuildIcons(0, 0);
+
+ SvcReminderEnable(DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF);
+}
+
+/**
+ * This function initially loads all required stuff for reminder.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcReminderLoadModule(VOID)
+{
+ // init sounds
+ SKINSOUNDDESCEX ssd = { 0 };
+ ssd.cbSize = sizeof(ssd);
+ ssd.pszSection = MODNAME;
+
+ ssd.pszName = SOUND_BIRTHDAY_TODAY;
+ ssd.pszDescription = LPGEN("Birthday reminder");
+ ssd.pszDefaultFile = "Sounds\\BirthDay.wav";
+ Skin_AddSound(&ssd);
+
+ ssd.pszName = SOUND_BIRTHDAY_SOON;
+ ssd.pszDescription = LPGEN("Birthday reminder: it's coming");
+ ssd.pszDefaultFile = "Sounds\\BirthDayComing.wav";
+ Skin_AddSound(&ssd);
+
+ ssd.pszName = SOUND_ANNIVERSARY;
+ ssd.pszDescription = LPGEN("Anniversary Reminder");
+ ssd.pszDefaultFile = "Sounds\\Reminder.wav";
+ Skin_AddSound(&ssd);
+
+ // create service functions
+ CreateServiceFunction(MS_USERINFO_REMINDER_CHECK, CheckService);
+ CreateServiceFunction(MS_USERINFO_REMINDER_AGGRASIVEBACKUP, BackupBirthdayService);
+
+ // register hotkey
+ HOTKEYDESC hk = { 0 };
+ hk.cbSize = sizeof(HOTKEYDESC);
+ hk.pszSection = MODNAME;
+ hk.pszName = "ReminderCheck";
+ hk.pszDescription = LPGEN("Check anniversaries");
+ hk.pszService = MS_USERINFO_REMINDER_CHECK;
+ Hotkey_Register(&hk);
+}
+
+/**
+ * This function unloads the reminder module.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+VOID SvcReminderUnloadModule(VOID)
+{
+ // kill timers
+ KillTimer(0, ghRemindTimer);
+ ghRemindTimer = 0;
+ KillTimer(0, ghRemindDateChangeTimer);
+ ghRemindDateChangeTimer = 0;
+
+ // unhook event handlers
+ UnhookEvent(ghCListIR);
+ ghCListIR = 0;
+ UnhookEvent(ghCListIA);
+ ghCListIA = 0;
+ UnhookEvent(ghSettingsChanged);
+ ghSettingsChanged = 0;
+}
diff --git a/plugins/UserInfoEx/src/svc_reminder.h b/plugins/UserInfoEx/src/svc_reminder.h
new file mode 100644
index 0000000000..210250b9f3
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_reminder.h
@@ -0,0 +1,117 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_reminder.h $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVCREMINDER_H_
+#define _SVCREMINDER_H_
+
+#define POPUP_TYPE_BIRTHDAY 1
+#define POPUP_TYPE_ANNIVERSARY 2
+
+// for PopupDelayType
+#define POPUP_DELAY_DEFAULT 3
+#define POPUP_DELAY_CUSTOM 4
+#define POPUP_DELAY_PERMANENT 5
+
+// for PopupColorType
+#define POPUP_COLOR_DEFAULT 6
+#define POPUP_COLOR_WINDOWS 7
+#define POPUP_COLOR_CUSTOM 8
+
+#define SOUND_BIRTHDAY_TODAY "Birthday"
+#define SOUND_BIRTHDAY_SOON "BirthdayComing"
+#define SOUND_ANNIVERSARY "Anniversary"
+
+// databbase settings
+#define SET_REMIND_LASTCHECK "RemindLastCheck"
+#define SET_REMIND_ENABLED "RemindEnabled"
+#define SET_REMIND_OFFSET "RemindOffset"
+#define SET_REMIND_CHECKVISIBLE "RemindCheckVisible"
+#define SET_REMIND_NOTIFYINTERVAL "RemindNotifyInterval"
+#define SET_REMIND_FLASHICON "RemindFlashIcon"
+#define SET_REMIND_EXTRAICON "RemindExtraIcon"
+#define SET_REMIND_BIRTHMODULE "RemindBirthModule"
+#define SET_REMIND_MENUENABLED "RemindMenuEnabled"
+#define SET_REMIND_BIRTHDAY_ENABLED "RemindBirthday"
+#define SET_REMIND_BIRTHDAY_OFFSET "RemindBirthdayOffset"
+#define SET_REMIND_CHECKON_STARTUP "RemindStartupCheck"
+#define SET_REMIND_SECUREBIRTHDAY "RemindSecureBirthday"
+#define SET_REMIND_BIRTHDAY_IGNORED "RemindSecureIgnored"
+#define SET_REMIND_SOUNDOFFSET "RemindSoundOffset"
+#define SET_POPUP_ENABLED "PopupEnabled"
+#define SET_POPUP_BIRTHDAY_COLORTYPE "PopupBirthClrType"
+#define SET_POPUP_BIRTHDAY_COLOR_TEXT "PopupBirthClrBirthText"
+#define SET_POPUP_BIRTHDAY_COLOR_BACK "PopupBirthClrBirthBack"
+#define SET_POPUP_ANNIVERSARY_COLORTYPE "PopupAnnivClrType"
+#define SET_POPUP_ANNIVERSARY_COLOR_TEXT "PopupAnnivClrText"
+#define SET_POPUP_ANNIVERSARY_COLOR_BACK "PopupAnnivClrBack"
+#define SET_POPUP_DELAY "PopupDelay"
+
+// default values
+#define DEFVAL_REMIND_ENABLED REMIND_ALL
+#define DEFVAL_REMIND_MENUENABLED 1
+#define DEFVAL_REMIND_OFFSET 9
+#define DEFVAL_REMIND_SOUNDOFFSET 3
+#define DEFVAL_REMIND_NOTIFYINTERVAL 12
+#define DEFVAL_REMIND_BIRTHMODULE 1
+#define DEFVAL_POPUP_ENABLED 1
+#define DEFVAL_REMIND_CHECKVISIBLE 0
+#define HM_OPENMSG (WM_USER+1)
+
+/**
+ * typedefs:
+ **/
+enum EEnabled
+{
+ REMIND_OFF,
+ REMIND_BIRTH,
+ REMIND_ANNIV,
+ REMIND_ALL,
+};
+
+enum ENotify
+{
+ NOTIFY_CLIST, // notify with clist extra icon only
+ NOTIFY_POPUP, // notify with popup and clist extra icon
+ NOTIFY_NOANNIV // notify of no anniversary was found
+};
+
+/**
+ * Global functions:
+ **/
+VOID SvcReminderCheckAll(const ENotify notify);
+LPCSTR SvcReminderGetMyBirthdayModule(VOID);
+
+VOID SvcReminderOnTopToolBarLoaded(VOID);
+VOID SvcReminderOnModulesLoaded(VOID);
+
+VOID SvcReminderEnable(BOOLEAN bEnable);
+VOID SvcReminderLoadModule(VOID);
+VOID SvcReminderUnloadModule(VOID);
+
+#endif /* _SVCREMINDER_H_ */
diff --git a/plugins/UserInfoEx/src/svc_timezone.cpp b/plugins/UserInfoEx/src/svc_timezone.cpp
new file mode 100644
index 0000000000..d9d0b4688c
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone.cpp
@@ -0,0 +1,83 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: $
+Revision : $Revision: $
+Last change on : $Date: $
+Last change by : $Author: $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+#include "svc_timezone.h"
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This service function provides a TIME_ZONE_INFORMATION structure
+ * for the desired contact, in case the contact's timezone can be determined.
+.* parsed to new core tzi interface if present.
+ *
+ * @param wParam - HANDLE of the contact, to retrieve timezone information from.
+ * @param lParam - pointer to a TIME_ZONE_INFORMATION to fill.
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+INT_PTR GetContactTimeZoneInformation(WPARAM wParam,LPARAM lParam)
+{
+ //use new core tz interface
+ LPTIME_ZONE_INFORMATION pTimeZoneInformation = (LPTIME_ZONE_INFORMATION)lParam;
+ (*pTimeZoneInformation) = *tmi.getTzi(tmi.createByContact((HANDLE)wParam, 0));
+ return (pTimeZoneInformation == NULL);
+}
+
+/**
+ * This function returns the contact's local time.
+ *
+ * @param wParam - HANDLE of the contact, to retrieve timezone information from.
+ * @param lParam - pointer to a systemtime structure
+ *
+ * @return TRUE or FALSE
+ **/
+INT_PTR GetContactLocalTime(WPARAM wParam, LPARAM lParam)
+{
+ //use new core tz interface
+ LPSYSTEMTIME pSystemTime = (LPSYSTEMTIME)lParam;
+ return (INT_PTR)tmi.getTimeZoneTimeByContact((HANDLE)wParam, pSystemTime);
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcTimezoneLoadModule()
+{
+ CreateServiceFunction(MS_USERINFO_TIMEZONEINFO, GetContactTimeZoneInformation);
+ CreateServiceFunction(MS_USERINFO_LOCALTIME, GetContactLocalTime);
+}
diff --git a/plugins/UserInfoEx/src/svc_timezone.h b/plugins/UserInfoEx/src/svc_timezone.h
new file mode 100644
index 0000000000..1c2dee943d
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone.h
@@ -0,0 +1,51 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone.h $
+Revision : $Revision: 191 $
+Last change on : $Date: 2010-09-20 11:52:01 +0200 (Mo, 20. Sep 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_TIMEZONE_H_
+#define _SVC_TIMEZONE_H_
+
+/**
+ * This structure is used by GetTimeZoneInformationByIndex to retrieve
+ * timezone information from windows' registry
+ **/
+typedef struct _REG_TZI_FORMAT
+{
+ LONG Bias;
+ LONG StandardBias;
+ LONG DaylightBias;
+ SYSTEMTIME StandardDate;
+ SYSTEMTIME DaylightDate;
+} REG_TZI_FORMAT, *PREG_TZI_FORMAT;
+
+INT_PTR GetContactTimeZoneInformation(WPARAM wParam, LPARAM lParam);
+INT_PTR GetContactLocalTime(WPARAM wParam, LPARAM lParam);
+
+VOID SvcTimezoneLoadModule();
+
+#endif /* _SVC_TIMEZONE_H_ */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_timezone_old.cpp b/plugins/UserInfoEx/src/svc_timezone_old.cpp
new file mode 100644
index 0000000000..f602f68f5d
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone_old.cpp
@@ -0,0 +1,645 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone_old.cpp $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+#include "svc_timezone_old.h"
+
+#define TZREG "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
+#define TZREG_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"
+
+/**************************************************************************************************
+ * struct CTimeZone
+ **************************************************************************************************/
+
+/**
+ * This is the default constructure, which resets
+ * all attributes to NULL.
+ **/
+CTimeZone::CTimeZone()
+{
+ ZeroMemory(this, sizeof(*this));
+}
+
+/**
+ * The default construcor's task ist to clear out
+ * all pieces of the used memory.
+ **/
+CTimeZone::~CTimeZone()
+{
+ MIR_FREE(ptszName);
+ MIR_FREE(ptszDisplay);
+}
+
+/**
+ * This method can be used to basically convert a Windows
+ * timezone to the format, known by miranda.
+ *
+ * @warning This operation does not work vice versa in
+ * all cases, as there are sometimes more then
+ * one Windows timezones with the same Bias.
+ **/
+BYTE CTimeZone::ToMirandaTimezone() const
+{
+ return (BYTE) (Bias / 30);
+}
+
+/**
+ * This operator translates the content of this object to
+ * a TIME_ZONE_INFORMATION structure as it is required by
+ * several windows functions.
+ **/
+CTimeZone::operator TIME_ZONE_INFORMATION() const
+{
+ TIME_ZONE_INFORMATION tzi;
+
+ tzi.Bias = Bias;
+ tzi.DaylightBias = DaylightBias;
+ tzi.StandardBias = StandardBias;
+
+ memcpy(&tzi.DaylightDate, &DaylightDate, sizeof(DaylightDate));
+ memcpy(&tzi.StandardDate, &StandardDate, sizeof(DaylightDate));
+ return tzi;
+}
+
+/***********************************************************************************************************
+ * class CTzBias
+ ***********************************************************************************************************/
+
+class CTzBias : public LIST<CTimeZone>
+{
+ static INT sortFunc(const CTimeZone *tz1, const CTimeZone *tz2)
+ {
+ INT result = tz2->Bias - tz1->Bias;
+ // DO NOT USE mir_tcsicmp here as it does only return TRUE or FALSE!!!
+ // lstrcmpi takes care of umlauts e.g. ,,....
+ return (result || !tz1->ptszDisplay || !tz2->ptszDisplay) ? result : lstrcmpi(tz1->ptszDisplay, tz2->ptszDisplay);
+ }
+public:
+ CTzBias() : LIST<CTimeZone>(50, (FTSortFunc) CTzBias::sortFunc)
+ {
+ }
+
+ ~CTzBias()
+ {
+ // delete the list, items delete by CTzMgr
+ this->destroy();
+ }
+};
+/***********************************************************************************************************
+ * class CTzMgr
+ ***********************************************************************************************************/
+
+/**
+ * This class is a deriviant of miranda's SortedList and holds all timezones
+ * known by Windows. By default there is no API to list timezones, so we
+ * need to get the information directly from the registry. In order to avoid
+ * heavy reading operations from registry, this class has the task to cache
+ * all required information for much faster access.
+ **/
+class CTzMgr : public LIST<CTimeZone>
+{
+ CTzBias _bias;
+
+ static INT sortFunc(const CTimeZone *tz1, const CTimeZone *tz2)
+ {
+ // DO NOT USE mir_tcsicmp here as it does only return TRUE or FALSE!!!
+ return _tcsicmp(tz1->ptszName, tz2->ptszName);
+ }
+
+ /**
+ * This method clears the TzTzMgr's data.
+ **/
+ VOID destroy()
+ {
+ INT i;
+
+ // delete data
+ for (i = 0 ; i < count; i++)
+ {
+ delete (*this)[i];
+ }
+ // delete the list
+ LIST<CTimeZone>::destroy();
+ // delete the _bias list ????
+ //_bias.destroy();
+
+ }
+
+public:
+
+ const CTzBias& Bias;
+
+ CTzMgr()
+ :LIST<CTimeZone>(50, (FTSortFunc) CTzMgr::sortFunc),
+ _bias(), Bias(_bias)
+ {
+ }
+
+ /**
+ * This is the default destructor of the class.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ ~CTzMgr()
+ {
+ destroy();
+ }
+
+ /**
+ * This method loads all information about timezones from windows' registry.
+ **/
+ INT Init()
+ {
+ INT result;
+ HKEY hKeyRoot,
+ hKeyTz;
+ DWORD i,
+ cbData;
+ TCHAR szName[MAX_PATH],
+ szDisplay[MAX_PATH];
+ CTimeZone *pTimeZone;
+
+ result = RegOpenKey(HKEY_LOCAL_MACHINE, _T(TZREG), &hKeyRoot);
+ if (result != ERROR_SUCCESS)
+ {
+ result = RegOpenKey(HKEY_LOCAL_MACHINE, _T(TZREG_9X), &hKeyRoot);
+ }
+ if (result == ERROR_SUCCESS)
+ {
+ // clear out old list
+ this->destroy(); _bias.destroy();
+ for (i = 0; ERROR_SUCCESS == RegEnumKey(hKeyRoot, i, szName, SIZEOF(szName)); i++)
+ {
+ result = RegOpenKey(hKeyRoot, szName, &hKeyTz);
+ if (result == ERROR_SUCCESS)
+ {
+ pTimeZone = new CTimeZone();
+ if (pTimeZone)
+ {
+ cbData = sizeof(szDisplay);
+ result |= RegQueryValueEx(hKeyTz, _T("Display"), 0, 0, (LPBYTE)szDisplay, &cbData);
+
+ cbData = sizeof(REG_TZI_FORMAT);
+ result |= RegQueryValueEx(hKeyTz, _T("TZI"), 0, 0, (LPBYTE)pTimeZone, &cbData);
+
+ cbData = sizeof(DWORD);
+ if (RegQueryValueEx(hKeyTz, _T("Index"), 0, 0, (LPBYTE)(UINT_PTR)pTimeZone->dwIndex, &cbData) != ERROR_SUCCESS)
+ {
+ pTimeZone->dwIndex = TZINDEX_UNSPECIFIED;
+ }
+ if (result == ERROR_SUCCESS)
+ {
+ pTimeZone->ptszName = mir_tcsdup(szName);
+ pTimeZone->ptszDisplay = mir_tcsdup(szDisplay);
+ result = (insert(pTimeZone) == ERROR_SUCCESS);
+ }
+ if (result != ERROR_SUCCESS)
+ {
+ delete pTimeZone;
+ }
+ else
+ {
+ _bias.insert(pTimeZone);
+ }
+ }
+ RegCloseKey(hKeyTz);
+ }
+ }
+ RegCloseKey(hKeyRoot);
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to find a certain list entry according to
+ * a key, providing information about the entry to look for.
+ *
+ * @param result - Pointer to a pointer, retrieving the CTimeZone
+ * object, matching the criteria provided by key
+ * @param key - Pointer to a CTimeZone structure, providing
+ * information about the item to look for.
+ * The Bias member and/or pszDisplay member must
+ * be valid.
+ * @retval -1 : item not found
+ * @retval 0...count : index of the found item
+ **/
+ INT find(CTimeZone** pTimezone, CTimeZone* pKey) const
+ {
+ INT nItemIndex = -1;
+
+ if (pKey && pKey->ptszName)
+ {
+ nItemIndex = getIndex(pKey);
+ if (pTimezone)
+ {
+ *pTimezone = (nItemIndex == -1) ? NULL : items[nItemIndex];
+ }
+ }
+ return nItemIndex;
+ }
+
+ INT find(CTimeZone** pTimezone, LPTSTR ptszName) const
+ {
+ CTimeZone key;
+ INT nItemIndex;
+
+ key.ptszName = ptszName;
+ nItemIndex = find(pTimezone, &key);
+ key.ptszName = NULL; // prevents ptszName from being deleted by the destructor.
+ return nItemIndex;
+ }
+
+ /**
+ * This method is used to find a certain list entry according to
+ * a given dwTzIndex, providing information about the entry to look for.
+ *
+ * @param result - Pointer to a pointer, retrieving the CTimeZone
+ * object, matching the criteria provided by key
+ * @param dwTzIndex - Timezone index as read from Windows Registry
+ * @retval -1 : item not found
+ * @retval 0...count : index of the found item
+ **/
+ INT find(CTimeZone** result, DWORD dwTzIndex) const
+ {
+ INT nItemIndex = -1;
+ CTimeZone *ptz = NULL;
+
+ if (dwTzIndex != TZINDEX_UNSPECIFIED)
+ {
+ for (nItemIndex = 0; nItemIndex < count; nItemIndex++)
+ {
+ ptz = items[nItemIndex];
+ if (ptz && (ptz->dwIndex == dwTzIndex))
+ break;
+ }
+ }
+ if (result)
+ {
+ *result = ptz;
+ }
+ return ((nItemIndex == count) ? -1 : nItemIndex);
+ }
+
+};
+// global timezone TzMgr object
+static CTzMgr TzMgr;
+
+/***********************************************************************************************************
+ * public Service functions
+ ***********************************************************************************************************/
+
+/**
+ * This method trys to find some default windows timezone idices for a given
+ * miranda timezone.
+ *
+ * @param MirTz - this is a miranda timezone with values between -24 and 24.
+ *
+ * @return This method returns a @TZ_MAP struct of a windows timezone, which is maps
+ * the @MirTz value,name or {-1,NULL} if no windows timezone index exists.
+ **/
+static TZ_MAP MirTZ2WinTZ(const CHAR MirTz)
+{
+ /**
+ * This is an item of an array of timezones, which are known by both Miranda-IM
+ * and Windows. It is used to map an ICQ timezone against a Windows timezone
+ * for retrieving information about daylight saving time and more.
+ **/
+ static const TZ_MAP TzMap[] = {
+ { 0, _T("Dateline Standard Time")}, // GMT-12:00 Eniwetok; Kwajalein
+ {-1, _T("")}, // GMT-11:30
+ { 1, _T("Samoa Standard Time")}, // GMT-11:00 Midway Island; Samoa
+ {-1, _T("")}, // GMT-10:30
+ { 2, _T("Hawaiian Standard Time")}, // GMT-10:00 Hawaii
+ {-1, _T("")}, // GMT-9:30
+ { 3, _T("Alaskan Standard Time")}, // GMT-9:00 Alaska
+ {-1, _T("")}, // GMT-8:30
+ { 4, _T("Pacific Standard Time")}, // GMT-8:00 Pacific Time; Tijuana
+ {-1, _T("")}, // GMT-7:30
+ {15, _T("US Mountain Standard Time")}, // GMT-7:00 Arizona; Mountain Time
+ {-1, _T("")}, // GMT-6:30
+ {33, _T("Central America Standard Time")}, // GMT-6:00 Central Time; Central America; Saskatchewan
+ {-1, _T("")}, // GMT-5:30
+ {45, _T("SA Pacific Standard Time")}, // GMT-5:00 Eastern Time; Bogota; Lima; Quito
+ {-1, _T("")}, // GMT-4:30
+ {56, _T("Pacific SA Standard Time")}, // GMT-4:00 Atlantic Time; Santiago; Caracas; La Paz
+ {60, _T("Newfoundland Standard Time")}, // GMT-3:30 Newfoundland
+ {70, _T("SA Eastern Standard Time")}, // GMT-3:00 Greenland; Buenos Aires; Georgetown
+ {-1, _T("")}, // GMT-2:30
+ {75, _T("Mid-Atlantic Standard Time")}, // GMT-2:00 Mid-Atlantic
+ {-1, _T("")}, // GMT-1:30
+ {80, _T("Azores Standard Time")}, // GMT-1:00 Cape Verde Islands; Azores
+ {-1, _T("")}, // GMT-0:30
+ {85, _T("GMT Standard Time")}, // GMT+0:00 London; Dublin; Edinburgh; Lisbon; Casablanca
+ {-1, _T("")}, // GMT+0:30
+ {105, _T("Romance Standard Time")}, // GMT+1:00 Central European Time; West Central Africa; Warsaw
+ {-1, _T("")}, // GMT+1:30
+ {140, _T("South Africa Standard Time")}, // GMT+2:00 Jerusalem; Helsinki; Harare; Cairo; Bucharest; Athens
+ {-1, _T("")}, // GMT+2:30
+ {145, _T("Russian Standard Time")}, // GMT+3:00 Moscow; St. Petersburg; Nairobi; Kuwait; Baghdad
+ {160, _T("Iran Standard Time")}, // GMT+3:30 Tehran
+ {165, _T("Arabian Standard Time")}, // GMT+4:00 Baku; Tbilisi; Yerevan; Abu Dhabi; Muscat
+ {175, _T("Afghanistan Standard Time")}, // GMT+4:30 Kabul
+ {185, _T("West Asia Standard Time")}, // GMT+5:00 Calcutta; Chennai; Mumbai; New Delhi; Ekaterinburg
+ {200, _T("Sri Lanka Standard Time")}, // GMT+5:30 Sri Jayawardenepura
+ {201, _T("N. Central Asia Standard Time")}, // GMT+6:00 Astana; Dhaka; Almaty; Novosibirsk
+ {203, _T("Myanmar Standard Time")}, // GMT+6:30 Rangoon
+ {207, _T("North Asia Standard Time")}, // GMT+7:00 Bankok; Hanoi; Jakarta; Krasnoyarsk
+ {-1, _T("")}, // GMT+7:30
+ {210, _T("China Standard Time")}, // GMT+8:00 Perth; Taipei; Singapore; Hong Kong; Beijing
+ {-1, _T("")}, // GMT+8:30
+ {235, _T("Tokyo Standard Time")}, // GMT+9:00 Tokyo; Osaka; Seoul; Sapporo; Yakutsk
+ {245, _T("AUS Central Standard Time")}, // GMT+9:30 Darwin; Adelaide
+ {270, _T("Vladivostok Standard Time")}, // GMT+10:00 East Australia; Guam; Vladivostok
+ {-1, _T("")}, // GMT+10:30
+ {280, _T("Central Pacific Standard Time")}, // GMT+11:00 Magadan; Solomon Is.; New Caledonia
+ {-1, _T("")}, // GMT+11:30
+ {290, _T("New Zealand Standard Time")}, // GMT+12:00 Auckland; Wellington; Fiji; Kamchatka; Marshall Is.
+ {-1, _T("")}
+ };
+ return (MirTz >= -24 && MirTz <= 24) ? TzMap[24 - MirTz] : TzMap[49] ;
+}
+
+/**
+ * This function reads out the Timezone, associated with the given contact
+ *
+ * @param hContact - HANDLE of the contact to retrieve the timezone for.
+ * @param pszProto - contact's protocol
+ *
+ * @retval NULL - No timezone exists.
+ * @retval CTimeZone* - Pointer to the timezone.
+ **/
+CTimeZone* GetContactTimeZone(HANDLE hContact, LPCSTR pszProto)
+{
+ LPTSTR ptszName;
+ CTimeZone* pTimeZone = NULL;
+
+ // read windows timezone from database (include meta subcontacts)
+ ptszName = DB::Setting::GetTStringEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONENAME);
+ if (!ptszName || FAILED(TzMgr.find(&pTimeZone, ptszName)))
+ {
+ DBVARIANT dbv;
+ TZ_MAP MirTZ;
+
+ // try to get miranda's timezone index value
+ if (!myGlobals.TzIndexExist || DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONEINDEX, &dbv) || FAILED(TzMgr.find(&pTimeZone,dbv.dVal)))
+ {
+ // maybe a failure lets us read a string, so clear it out
+ DB::Variant::Free(&dbv);
+
+ // try to get miranda's timezone value
+ if (DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONE, &dbv) || (dbv.type != DBVT_BYTE))
+ {
+ // maybe a failure lets us read a string, so clear it out
+ DB::Variant::Free(&dbv);
+ }
+ else
+ {
+ MirTZ = MirTZ2WinTZ(dbv.cVal);
+ if (*MirTZ.Name != 0)
+ {
+ TzMgr.find(&pTimeZone, MirTZ.Name);
+ }
+ }
+ }
+ }
+ MIR_FREE(ptszName);
+ return pTimeZone;
+}
+
+/**
+ *
+ *
+ **/
+CTimeZone* GetContactTimeZone(HANDLE hContact)
+{
+ return GetContactTimeZone(hContact, DB::Contact::Proto(hContact));
+}
+
+/**
+ * This method trys to find the contact's windows timezone.
+ *
+ * @warning Make sure you convert @e dwIndex to CHAR if the function returns 1 in order to get
+ * the correct miranda timezone!
+ *
+ * @param hContact - the HANDLE of the contact to read timezone information for
+ * @param szProto - contact's protocol
+ * @param pTimeZone - Pointer to the pointer of a CTimeZone structure,
+ * which retrieves information about contact's timezone.
+ *
+ * @retval CTRLF_... flag - The index for a windows timezone was found for the contact.
+ * @retval 0 - There is no index, but if the contact's 'timezone' setting is valid,
+ * @e dwIndex retrieved its value. If not, dwIndex is -100 (unspecified).
+ **/
+WORD GetContactTimeZoneCtrl(HANDLE hContact, LPCSTR pszProto, CTimeZone** pTimeZone)
+{
+ WORD flags;
+ DBVARIANT dbv;
+ CTimeZone* pTz = NULL;
+
+ // try to read windows' timezone name from database
+ flags = DB::Setting::GetCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONENAME, &dbv, DBVT_TCHAR);
+ if (flags == 0 || FAILED(TzMgr.find(&pTz, dbv.ptszVal)))
+ {
+ DB::Variant::Free(&dbv);
+
+ // try to get miranda's timezone index value
+ if (myGlobals.TzIndexExist)
+ {
+ flags = DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONEINDEX, &dbv);
+ if (flags && FAILED(TzMgr.find(&pTz, dbv.dVal)))
+ {
+ flags = 0;
+ }
+ }
+ if (flags == 0)
+ {
+ // try to get miranda's timezone value
+ flags = DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONE, &dbv);
+ if (flags != 0)
+ {
+ TZ_MAP MirTZ;
+ MirTZ = MirTZ2WinTZ(dbv.cVal);
+ if ((*MirTZ.Name == 0) || FAILED(TzMgr.find(&pTz, MirTZ.Name)))
+ {
+ flags = 0;
+ }
+ }
+ }
+ }
+ if (pTimeZone && flags != 0)
+ {
+ *pTimeZone = pTz;
+ }
+ DB::Variant::Free(&dbv);
+ return flags;
+}
+
+/**
+ * This function returns the display name for the contact's timezone
+ *
+ * @param hContact - handle of the contact
+ *
+ * @return String containing display name.
+ **/
+LPCTSTR GetContactTimeZoneDisplayName(HANDLE hContact)
+{
+ CTimeZone *pTimeZone;
+
+ pTimeZone = GetContactTimeZone(hContact);
+ return (pTimeZone) ? pTimeZone->ptszDisplay : NULL;
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR EnumTimeZones(PEnumNamesProc enumProc, LPARAM lParam)
+{
+ INT_PTR i, c, r = 0;
+ CTimeZone *pTz;
+
+ for (i = 0, c = TzMgr.Bias.getCount(); i < c; i++)
+ {
+ pTz = TzMgr.Bias[i];
+ if (pTz)
+ {
+ r = enumProc(pTz, i, lParam);
+ if (r) break;
+ }
+ }
+ return r;
+}
+
+/**
+ *
+ *
+ **/
+static BOOL SvcTimezoneSyncWithWindowsProc(LPCSTR pszProto, INT bias)
+{
+ INT tz = (INT) ((CHAR)DB::Setting::GetByte(pszProto, SET_CONTACT_TIMEZONE, (BYTE)-100));
+ if (tz * 30 != bias)
+ {
+ DB::Setting::WriteByte(pszProto, SET_CONTACT_TIMEZONE, (BYTE)(bias / 30));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ *
+ *
+ **/
+VOID SvcTimezoneSyncWithWindows()
+{
+ PROTOACCOUNT **pAcc;
+ INT i, nAccCount;
+ TIME_ZONE_INFORMATION tzi;
+
+ ZeroMemory(&tzi, sizeof(tzi));
+ GetTimeZoneInformation(&tzi);
+
+ if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+ {
+ for (i = 0; i < nAccCount; i++)
+ {
+ // update local timezone as database setting
+ if (IsProtoAccountEnabled(pAcc[i]) && SvcTimezoneSyncWithWindowsProc(pAcc[i]->szModuleName, tzi.Bias))
+ {
+ // update my contact information on icq server
+ CallProtoService(pAcc[i]->szModuleName, PS_CHANGEINFOEX, CIXT_LOCATION, NULL);
+ }
+ }
+ }
+}
+
+/***********************************************************************************************************
+ * services use old UIEX timezone
+ ***********************************************************************************************************/
+
+/**
+ * This service function provides a TIME_ZONE_INFORMATION structure
+ * for the desired contact, in case the contact's timezone can be determined.
+ *
+ * @param wParam - HANDLE of the contact, to retrieve timezone information from.
+ * @param lParam - pointer to a TIME_ZONE_INFORMATION to fill.
+ *
+ * @retval 0 - success
+ * @retval 1 - failure
+ **/
+INT_PTR GetContactTimeZoneInformation_old(WPARAM wParam,LPARAM lParam)
+{
+ CTimeZone *pTimeZone;
+ TIME_ZONE_INFORMATION* pTimeZoneInformation = (TIME_ZONE_INFORMATION*)lParam;
+
+ pTimeZone = GetContactTimeZone((HANDLE)wParam);
+ if (pTimeZone && pTimeZoneInformation)
+ {
+ (*pTimeZoneInformation) = *pTimeZone;
+ }
+ return (pTimeZone == NULL) || (pTimeZoneInformation == NULL);
+}
+
+/**
+ * This function returns the contact's local time.
+ *
+ * @param wParam - HANDLE of the contact, to retrieve timezone information from.
+ * @param lParam - pointer to a systemtime structure
+ *
+ * @return TRUE or FALSE
+ **/
+INT_PTR GetContactLocalTime_old(WPARAM wParam, LPARAM lParam)
+{
+ MTime now;
+ LPSYSTEMTIME pSystemTime = (LPSYSTEMTIME)lParam;
+
+ now.GetLocalTime((HANDLE)wParam);
+ *pSystemTime = now.SystemTime();
+ return 0;
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcTimezoneLoadModule_old()
+{
+ TzMgr.Init();
+ CreateServiceFunction(MS_USERINFO_TIMEZONEINFO, GetContactTimeZoneInformation);
+ CreateServiceFunction(MS_USERINFO_LOCALTIME, GetContactLocalTime);
+ if (DB::Setting::GetByte(SET_OPT_AUTOTIMEZONE, TRUE))
+ {
+ SvcTimezoneSyncWithWindows();
+ }
+}
diff --git a/plugins/UserInfoEx/src/svc_timezone_old.h b/plugins/UserInfoEx/src/svc_timezone_old.h
new file mode 100644
index 0000000000..9704584e1a
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone_old.h
@@ -0,0 +1,102 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone_old.h $
+Revision : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_TIMEZONE_H_OLD
+#define _SVC_TIMEZONE_H_OLD
+
+#include "svc_timezone.h"
+#define TZINDEX_UNSPECIFIED -100
+
+/**
+ * This structure is used by GetTimeZoneInformationByIndex to retrieve
+ * timezone information from windows' registry
+ **/
+struct TZ_MAP
+{
+ DWORD Index;
+ LPTSTR Name;
+};
+
+/**
+ * This structure is an element of the CTzManager.
+ * It holds information about a timezone, which are required
+ * to display the correct time of a contact which is not
+ * in the same timezone as the owner contact.
+ **/
+struct CTimeZone : public REG_TZI_FORMAT
+{
+ LPTSTR ptszName;
+ LPTSTR ptszDisplay;
+ DWORD dwIndex; //old, only supportet in win9x
+
+ /**
+ * This is the default constructure, which resets
+ * all attributes to NULL.
+ **/
+ CTimeZone();
+
+ /**
+ * The default construcor's task ist to clear out
+ * all pieces of the used memory.
+ **/
+ ~CTimeZone();
+
+ /**
+ * This method can be used to basically convert a Windows
+ * timezone to the format, known by miranda.
+ *
+ * @warning This operation does not work vice versa in
+ * all cases, as there are sometimes more then
+ * one Windows timezones with the same Bias.
+ **/
+ BYTE ToMirandaTimezone() const;
+
+ /**
+ * This operator translates the content of this object to
+ * a TIME_ZONE_INFORMATION structure as it is required by
+ * several windows functions.
+ **/
+ operator TIME_ZONE_INFORMATION() const;
+};
+
+typedef INT_PTR (*PEnumNamesProc)(CTimeZone* pTimeZone, INT index, LPARAM lParam);
+
+CTimeZone* GetContactTimeZone(HANDLE hContact);
+CTimeZone* GetContactTimeZone(HANDLE hContact, LPCSTR pszProto);
+WORD GetContactTimeZoneCtrl(HANDLE hContact, LPCSTR pszProto, CTimeZone** pTimeZone);
+LPCTSTR GetContactTimeZoneDisplayName(HANDLE hContact);
+INT_PTR EnumTimeZones(PEnumNamesProc enumProc, LPARAM lParam);
+
+INT_PTR GetContactTimeZoneInformation_old(WPARAM wParam, LPARAM lParam);
+INT_PTR GetContactLocalTime_old(WPARAM wParam, LPARAM lParam);
+
+VOID SvcTimezoneSyncWithWindows();
+VOID SvcTimezoneLoadModule_old();
+
+#endif /* _SVC_TIMEZONE_H_OLD */ \ No newline at end of file
diff --git a/plugins/UserInfoEx/src/version.h b/plugins/UserInfoEx/src/version.h
new file mode 100644
index 0000000000..8ee84378bf
--- /dev/null
+++ b/plugins/UserInfoEx/src/version.h
@@ -0,0 +1,51 @@
+/*
+
+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 __MAJOR_VERSION 0
+#define __MINOR_VERSION 8
+#define __RELEASE_NUM 4 // due to beta builders
+#define __BUILD_NUM 2 // due to beta builders
+
+#define __STRINGIFY_(x) #x
+#define __STRINGIFY(x) __STRINGIFY_(x)
+
+#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM
+
+#define __VERSION_STRING __STRINGIFY(__FILEVERSION_STRING_DOTS)
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM)
+
+#define __SHORT_DESC "Extended UserInfo module for Miranda-IM. Provides interface to edit all contact information."
+#define __DESC "Gives extended ability to edit information about your contacts locally. "\
+ "It does not matter what information your contact gives about himself. "\
+ "If you know more you can add more."
+#define __AUTHOR "DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol"
+#define __AUTHOREMAIL "deathaxe@web.de"
+#define __COPYRIGHT " 2006-2009 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol"
+#define __AUTHORWEB "http://nightly.miranda.im/" __STRINGIFY(__UPDATER_DOWNLOAD_ID)
+
+#define __UPDATER_DOWNLOAD_ID 2537
+#define __PLUGIN_DISPLAY_NAME "UserInfoEx"
+#define __PLUGIN_FILENAME "uinfoex.dll"
+ \ No newline at end of file