From 171e81205e357e0d54283a63997ed58ff97d54a9 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy <watcherhd@gmail.com> Date: Tue, 24 Jul 2012 11:48:31 +0000 Subject: UserInfoEx, Variables: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1160 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- .../UserInfoEx/src/Flags/svc_countrylistext.cpp | 326 ++++ plugins/UserInfoEx/src/Flags/svc_countrylistext.h | 39 + plugins/UserInfoEx/src/Flags/svc_flags.cpp | 765 ++++++++ plugins/UserInfoEx/src/Flags/svc_flags.h | 102 + plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp | 456 +++++ plugins/UserInfoEx/src/Flags/svc_flagsicons.h | 43 + plugins/UserInfoEx/src/classMAnnivDate.cpp | 836 +++++++++ plugins/UserInfoEx/src/classMAnnivDate.h | 132 ++ plugins/UserInfoEx/src/classMTime.cpp | 468 +++++ plugins/UserInfoEx/src/classMTime.h | 125 ++ plugins/UserInfoEx/src/classPsTree.cpp | 993 ++++++++++ plugins/UserInfoEx/src/classPsTreeItem.cpp | 697 +++++++ plugins/UserInfoEx/src/commonheaders.cpp | 180 ++ plugins/UserInfoEx/src/commonheaders.h | 234 +++ plugins/UserInfoEx/src/ctrl_annivedit.cpp | 636 +++++++ plugins/UserInfoEx/src/ctrl_annivedit.h | 104 + plugins/UserInfoEx/src/ctrl_base.cpp | 301 +++ plugins/UserInfoEx/src/ctrl_base.h | 238 +++ plugins/UserInfoEx/src/ctrl_button.cpp | 708 +++++++ plugins/UserInfoEx/src/ctrl_button.h | 36 + plugins/UserInfoEx/src/ctrl_combo.cpp | 277 +++ plugins/UserInfoEx/src/ctrl_combo.h | 80 + plugins/UserInfoEx/src/ctrl_contact.cpp | 1539 +++++++++++++++ plugins/UserInfoEx/src/ctrl_contact.h | 86 + plugins/UserInfoEx/src/ctrl_edit.cpp | 381 ++++ plugins/UserInfoEx/src/ctrl_edit.h | 80 + plugins/UserInfoEx/src/ctrl_tzcombo.cpp | 306 +++ plugins/UserInfoEx/src/ctrl_tzcombo.h | 69 + plugins/UserInfoEx/src/dlg_anniversarylist.cpp | 1120 +++++++++++ plugins/UserInfoEx/src/dlg_anniversarylist.h | 43 + plugins/UserInfoEx/src/dlg_msgbox.cpp | 856 +++++++++ plugins/UserInfoEx/src/dlg_msgbox.h | 103 + plugins/UserInfoEx/src/dlg_propsheet.cpp | 1867 ++++++++++++++++++ plugins/UserInfoEx/src/dlg_propsheet.h | 281 +++ .../src/ex_import/classExImContactBase.cpp | 581 ++++++ .../src/ex_import/classExImContactBase.h | 103 + .../src/ex_import/classExImContactXML.cpp | 1141 +++++++++++ .../UserInfoEx/src/ex_import/classExImContactXML.h | 103 + .../UserInfoEx/src/ex_import/dlg_ExImModules.cpp | 464 +++++ plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h | 39 + .../src/ex_import/dlg_ExImOpenSaveFile.cpp | 371 ++++ .../src/ex_import/dlg_ExImOpenSaveFile.h | 39 + .../UserInfoEx/src/ex_import/dlg_ExImProgress.cpp | 244 +++ .../UserInfoEx/src/ex_import/dlg_ExImProgress.h | 56 + plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h | 371 ++++ plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp | 547 ++++++ plugins/UserInfoEx/src/ex_import/svc_ExImINI.h | 39 + plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp | 1364 ++++++++++++++ plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h | 103 + plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp | 453 +++++ plugins/UserInfoEx/src/ex_import/svc_ExImXML.h | 75 + plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp | 382 ++++ plugins/UserInfoEx/src/ex_import/svc_ExImport.h | 62 + plugins/UserInfoEx/src/ex_import/tinystr.cpp | 143 ++ plugins/UserInfoEx/src/ex_import/tinystr.h | 335 ++++ plugins/UserInfoEx/src/ex_import/tinyxml.cpp | 1978 ++++++++++++++++++++ plugins/UserInfoEx/src/ex_import/tinyxml.h | 1599 ++++++++++++++++ plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp | 76 + plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp | 1613 ++++++++++++++++ plugins/UserInfoEx/src/init.cpp | 315 ++++ plugins/UserInfoEx/src/mir_contactqueue.cpp | 425 +++++ plugins/UserInfoEx/src/mir_contactqueue.h | 221 +++ plugins/UserInfoEx/src/mir_db.cpp | 1334 +++++++++++++ plugins/UserInfoEx/src/mir_db.h | 229 +++ plugins/UserInfoEx/src/mir_icolib.cpp | 401 ++++ plugins/UserInfoEx/src/mir_icolib.h | 148 ++ plugins/UserInfoEx/src/mir_menuitems.cpp | 651 +++++++ plugins/UserInfoEx/src/mir_menuitems.h | 47 + plugins/UserInfoEx/src/mir_string.cpp | 167 ++ plugins/UserInfoEx/src/mir_string.h | 88 + plugins/UserInfoEx/src/psp_about.cpp | 124 ++ plugins/UserInfoEx/src/psp_anniversary.cpp | 347 ++++ plugins/UserInfoEx/src/psp_base.cpp | 111 ++ plugins/UserInfoEx/src/psp_base.h | 51 + plugins/UserInfoEx/src/psp_company.cpp | 76 + plugins/UserInfoEx/src/psp_contact.cpp | 347 ++++ plugins/UserInfoEx/src/psp_general.cpp | 211 +++ plugins/UserInfoEx/src/psp_options.cpp | 1570 ++++++++++++++++ plugins/UserInfoEx/src/psp_options.h | 43 + plugins/UserInfoEx/src/psp_origin.cpp | 176 ++ plugins/UserInfoEx/src/psp_profile.cpp | 1464 +++++++++++++++ plugins/UserInfoEx/src/resdefines.h | 5 + plugins/UserInfoEx/src/resource.h | 333 ++++ plugins/UserInfoEx/src/svc_avatar.cpp | 247 +++ plugins/UserInfoEx/src/svc_avatar.h | 41 + plugins/UserInfoEx/src/svc_constants.cpp | 436 +++++ plugins/UserInfoEx/src/svc_constants.h | 195 ++ plugins/UserInfoEx/src/svc_contactinfo.cpp | 798 ++++++++ plugins/UserInfoEx/src/svc_contactinfo.h | 36 + plugins/UserInfoEx/src/svc_email.cpp | 409 ++++ plugins/UserInfoEx/src/svc_email.h | 39 + plugins/UserInfoEx/src/svc_gender.cpp | 284 +++ plugins/UserInfoEx/src/svc_gender.h | 40 + plugins/UserInfoEx/src/svc_homepage.cpp | 337 ++++ plugins/UserInfoEx/src/svc_homepage.h | 38 + plugins/UserInfoEx/src/svc_phone.cpp | 309 +++ plugins/UserInfoEx/src/svc_phone.h | 38 + plugins/UserInfoEx/src/svc_refreshci.cpp | 936 +++++++++ plugins/UserInfoEx/src/svc_refreshci.h | 36 + plugins/UserInfoEx/src/svc_reminder.cpp | 1216 ++++++++++++ plugins/UserInfoEx/src/svc_reminder.h | 117 ++ plugins/UserInfoEx/src/svc_timezone.cpp | 83 + plugins/UserInfoEx/src/svc_timezone.h | 51 + plugins/UserInfoEx/src/svc_timezone_old.cpp | 645 +++++++ plugins/UserInfoEx/src/svc_timezone_old.h | 102 + plugins/UserInfoEx/src/version.h | 51 + 106 files changed, 42156 insertions(+) create mode 100644 plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp create mode 100644 plugins/UserInfoEx/src/Flags/svc_countrylistext.h create mode 100644 plugins/UserInfoEx/src/Flags/svc_flags.cpp create mode 100644 plugins/UserInfoEx/src/Flags/svc_flags.h create mode 100644 plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp create mode 100644 plugins/UserInfoEx/src/Flags/svc_flagsicons.h create mode 100644 plugins/UserInfoEx/src/classMAnnivDate.cpp create mode 100644 plugins/UserInfoEx/src/classMAnnivDate.h create mode 100644 plugins/UserInfoEx/src/classMTime.cpp create mode 100644 plugins/UserInfoEx/src/classMTime.h create mode 100644 plugins/UserInfoEx/src/classPsTree.cpp create mode 100644 plugins/UserInfoEx/src/classPsTreeItem.cpp create mode 100644 plugins/UserInfoEx/src/commonheaders.cpp create mode 100644 plugins/UserInfoEx/src/commonheaders.h create mode 100644 plugins/UserInfoEx/src/ctrl_annivedit.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_annivedit.h create mode 100644 plugins/UserInfoEx/src/ctrl_base.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_base.h create mode 100644 plugins/UserInfoEx/src/ctrl_button.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_button.h create mode 100644 plugins/UserInfoEx/src/ctrl_combo.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_combo.h create mode 100644 plugins/UserInfoEx/src/ctrl_contact.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_contact.h create mode 100644 plugins/UserInfoEx/src/ctrl_edit.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_edit.h create mode 100644 plugins/UserInfoEx/src/ctrl_tzcombo.cpp create mode 100644 plugins/UserInfoEx/src/ctrl_tzcombo.h create mode 100644 plugins/UserInfoEx/src/dlg_anniversarylist.cpp create mode 100644 plugins/UserInfoEx/src/dlg_anniversarylist.h create mode 100644 plugins/UserInfoEx/src/dlg_msgbox.cpp create mode 100644 plugins/UserInfoEx/src/dlg_msgbox.h create mode 100644 plugins/UserInfoEx/src/dlg_propsheet.cpp create mode 100644 plugins/UserInfoEx/src/dlg_propsheet.h create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactBase.h create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactXML.h create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h create mode 100644 plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImINI.h create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImXML.h create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImport.h create mode 100644 plugins/UserInfoEx/src/ex_import/tinystr.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/tinystr.h create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxml.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxml.h create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp create mode 100644 plugins/UserInfoEx/src/init.cpp create mode 100644 plugins/UserInfoEx/src/mir_contactqueue.cpp create mode 100644 plugins/UserInfoEx/src/mir_contactqueue.h create mode 100644 plugins/UserInfoEx/src/mir_db.cpp create mode 100644 plugins/UserInfoEx/src/mir_db.h create mode 100644 plugins/UserInfoEx/src/mir_icolib.cpp create mode 100644 plugins/UserInfoEx/src/mir_icolib.h create mode 100644 plugins/UserInfoEx/src/mir_menuitems.cpp create mode 100644 plugins/UserInfoEx/src/mir_menuitems.h create mode 100644 plugins/UserInfoEx/src/mir_string.cpp create mode 100644 plugins/UserInfoEx/src/mir_string.h create mode 100644 plugins/UserInfoEx/src/psp_about.cpp create mode 100644 plugins/UserInfoEx/src/psp_anniversary.cpp create mode 100644 plugins/UserInfoEx/src/psp_base.cpp create mode 100644 plugins/UserInfoEx/src/psp_base.h create mode 100644 plugins/UserInfoEx/src/psp_company.cpp create mode 100644 plugins/UserInfoEx/src/psp_contact.cpp create mode 100644 plugins/UserInfoEx/src/psp_general.cpp create mode 100644 plugins/UserInfoEx/src/psp_options.cpp create mode 100644 plugins/UserInfoEx/src/psp_options.h create mode 100644 plugins/UserInfoEx/src/psp_origin.cpp create mode 100644 plugins/UserInfoEx/src/psp_profile.cpp create mode 100644 plugins/UserInfoEx/src/resdefines.h create mode 100644 plugins/UserInfoEx/src/resource.h create mode 100644 plugins/UserInfoEx/src/svc_avatar.cpp create mode 100644 plugins/UserInfoEx/src/svc_avatar.h create mode 100644 plugins/UserInfoEx/src/svc_constants.cpp create mode 100644 plugins/UserInfoEx/src/svc_constants.h create mode 100644 plugins/UserInfoEx/src/svc_contactinfo.cpp create mode 100644 plugins/UserInfoEx/src/svc_contactinfo.h create mode 100644 plugins/UserInfoEx/src/svc_email.cpp create mode 100644 plugins/UserInfoEx/src/svc_email.h create mode 100644 plugins/UserInfoEx/src/svc_gender.cpp create mode 100644 plugins/UserInfoEx/src/svc_gender.h create mode 100644 plugins/UserInfoEx/src/svc_homepage.cpp create mode 100644 plugins/UserInfoEx/src/svc_homepage.h create mode 100644 plugins/UserInfoEx/src/svc_phone.cpp create mode 100644 plugins/UserInfoEx/src/svc_phone.h create mode 100644 plugins/UserInfoEx/src/svc_refreshci.cpp create mode 100644 plugins/UserInfoEx/src/svc_refreshci.h create mode 100644 plugins/UserInfoEx/src/svc_reminder.cpp create mode 100644 plugins/UserInfoEx/src/svc_reminder.h create mode 100644 plugins/UserInfoEx/src/svc_timezone.cpp create mode 100644 plugins/UserInfoEx/src/svc_timezone.h create mode 100644 plugins/UserInfoEx/src/svc_timezone_old.cpp create mode 100644 plugins/UserInfoEx/src/svc_timezone_old.h create mode 100644 plugins/UserInfoEx/src/version.h (limited to 'plugins/UserInfoEx/src') 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 }, // L�we + { 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 }, // Sch�tze + { 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. + // © -- 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 ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 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 -- cgit v1.2.3