summaryrefslogtreecommitdiff
path: root/src/mir_app/src/clistcontacts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir_app/src/clistcontacts.cpp')
-rw-r--r--src/mir_app/src/clistcontacts.cpp466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/mir_app/src/clistcontacts.cpp b/src/mir_app/src/clistcontacts.cpp
new file mode 100644
index 0000000000..5caa21315c
--- /dev/null
+++ b/src/mir_app/src/clistcontacts.cpp
@@ -0,0 +1,466 @@
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static wchar_t* nameOrderDescr[] =
+{
+ LPGENW("My custom name (not movable)"),
+ LPGENW("Nick"),
+ LPGENW("FirstName"),
+ LPGENW("E-mail"),
+ LPGENW("LastName"),
+ LPGENW("Username"),
+ LPGENW("FirstName LastName"),
+ LPGENW("LastName FirstName"),
+ LPGENW("'(Unknown contact)' (not movable)")
+};
+
+uint8_t nameOrder[_countof(nameOrderDescr)];
+
+static wchar_t* ProcessDatabaseValueDefault(MCONTACT hContact, const char *szProto, const char *szSetting)
+{
+ DBVARIANT dbv;
+ if (db_get(hContact, szProto, szSetting, &dbv))
+ return nullptr;
+
+ wchar_t *ret;
+ wchar_t buf[40];
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ ret = _itow(dbv.bVal, buf, 10);
+ break;
+ case DBVT_WORD:
+ ret = _itow(dbv.wVal, buf, 10);
+ break;
+ case DBVT_DWORD:
+ ret = _itow(dbv.dVal, buf, 10);
+ break;
+ case DBVT_BLOB:
+ if (dbv.cpbVal == 8)
+ ret = _i64tow(*(__int64*)dbv.pbVal, buf, 10);
+ else
+ ret = bin2hexW(dbv.pbVal, min(int(dbv.cpbVal), 19), buf);
+ break;
+ case DBVT_ASCIIZ:
+ ret = mir_a2u(dbv.pszVal);
+ break;
+ case DBVT_UTF8:
+ ret = mir_utf8decodeW(dbv.pszVal);
+ break;
+ case DBVT_WCHAR:
+ return dbv.pwszVal; // no need to free dbv
+ default:
+ ret = nullptr;
+ }
+
+ db_free(&dbv);
+ return mir_wstrdup(ret);
+}
+
+MIR_APP_DLL(wchar_t*) Contact::GetInfo(int type, MCONTACT hContact, const char *szProto)
+{
+ if (hContact == 0 && szProto == nullptr)
+ return nullptr;
+
+ if (szProto == nullptr)
+ if (auto *pa = Proto_GetContactAccount(hContact))
+ szProto = pa->szModuleName;
+
+ if (szProto == nullptr)
+ return nullptr;
+
+ wchar_t *res;
+ DBVARIANT dbv;
+ switch (type) {
+ case CNF_FIRSTNAME: return ProcessDatabaseValueDefault(hContact, szProto, "FirstName");
+ case CNF_LASTNAME: return ProcessDatabaseValueDefault(hContact, szProto, "LastName");
+ case CNF_NICK: return ProcessDatabaseValueDefault(hContact, szProto, "Nick");
+ case CNF_EMAIL: return ProcessDatabaseValueDefault(hContact, szProto, "e-mail");
+ case CNF_CITY: return ProcessDatabaseValueDefault(hContact, szProto, "City");
+ case CNF_STATE: return ProcessDatabaseValueDefault(hContact, szProto, "State");
+ case CNF_PHONE: return ProcessDatabaseValueDefault(hContact, szProto, "Phone");
+ case CNF_HOMEPAGE: return ProcessDatabaseValueDefault(hContact, szProto, "Homepage");
+ case CNF_ABOUT: return ProcessDatabaseValueDefault(hContact, szProto, "About");
+ case CNF_AGE: return ProcessDatabaseValueDefault(hContact, szProto, "Age");
+ case CNF_GENDER: return ProcessDatabaseValueDefault(hContact, szProto, "Gender");
+ case CNF_FAX: return ProcessDatabaseValueDefault(hContact, szProto, "Fax");
+ case CNF_CELLULAR: return ProcessDatabaseValueDefault(hContact, szProto, "Cellular");
+ case CNF_BIRTHDAY: return ProcessDatabaseValueDefault(hContact, szProto, "BirthDay");
+ case CNF_BIRTHMONTH: return ProcessDatabaseValueDefault(hContact, szProto, "BirthMonth");
+ case CNF_BIRTHYEAR: return ProcessDatabaseValueDefault(hContact, szProto, "BirthYear");
+ case CNF_STREET: return ProcessDatabaseValueDefault(hContact, szProto, "Street");
+ case CNF_ZIP: return ProcessDatabaseValueDefault(hContact, szProto, "ZIP");
+ case CNF_LANGUAGE1: return ProcessDatabaseValueDefault(hContact, szProto, "Language1");
+ case CNF_LANGUAGE2: return ProcessDatabaseValueDefault(hContact, szProto, "Language2");
+ case CNF_LANGUAGE3: return ProcessDatabaseValueDefault(hContact, szProto, "Language3");
+ case CNF_CONAME: return ProcessDatabaseValueDefault(hContact, szProto, "Company");
+ case CNF_CODEPT: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyDepartment");
+ case CNF_COPOSITION: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyPosition");
+ case CNF_COSTREET: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyStreet");
+ case CNF_COCITY: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyCity");
+ case CNF_COSTATE: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyState");
+ case CNF_COZIP: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyZIP");
+ case CNF_COHOMEPAGE: return ProcessDatabaseValueDefault(hContact, szProto, "CompanyHomepage");
+ case CNF_CUSTOMNICK:
+ if (hContact)
+ return ProcessDatabaseValueDefault(hContact, "CList", "MyHandle");
+ return ProcessDatabaseValueDefault(0, szProto, "MyHandle");
+
+ case CNF_COUNTRY:
+ case CNF_COCOUNTRY:
+ if (!db_get_ws(hContact, szProto, type == CNF_COUNTRY ? "CountryName" : "CompanyCountryName", &dbv))
+ return dbv.pwszVal;
+
+ if (!db_get(hContact, szProto, type == CNF_COUNTRY ? "Country" : "CompanyCountry", &dbv)) {
+ if (dbv.type == DBVT_WORD) {
+ int countryCount;
+ struct CountryListEntry *countries;
+ CallService(MS_UTILS_GETCOUNTRYLIST, (WPARAM)&countryCount, (LPARAM)&countries);
+ for (int i = 0; i < countryCount; i++)
+ if (countries[i].id == dbv.wVal)
+ return mir_a2u(countries[i].szName);
+ }
+ else {
+ db_free(&dbv);
+ return ProcessDatabaseValueDefault(hContact, szProto, type == CNF_COUNTRY ? "Country" : "CompanyCountry");
+ }
+ }
+ break;
+
+ case CNF_FIRSTLAST:
+ if (!db_get_ws(hContact, szProto, "FirstName", &dbv)) {
+ DBVARIANT dbv2;
+ if (!db_get_ws(hContact, szProto, "LastName", &dbv2)) {
+ size_t len = mir_wstrlen(dbv.pwszVal) + mir_wstrlen(dbv2.pwszVal) + 2;
+ wchar_t *buf = (wchar_t*)mir_alloc(sizeof(wchar_t)*len);
+ if (buf != nullptr)
+ mir_wstrcat(mir_wstrcat(mir_wstrcpy(buf, dbv.pwszVal), L" "), dbv2.pwszVal);
+ db_free(&dbv);
+ db_free(&dbv2);
+ return buf;
+ }
+ return dbv.pwszVal;
+ }
+ break;
+
+ case CNF_UNIQUEID:
+ if (db_mc_isMeta(hContact)) {
+ wchar_t buf[40];
+ _itow(hContact, buf, 10);
+ return mir_wstrdup(buf);
+ }
+
+ if (auto *uid = Proto_GetUniqueId(szProto))
+ return ProcessDatabaseValueDefault(hContact, szProto, uid);
+ break;
+
+ case CNF_DISPLAYUID:
+ if (res = ProcessDatabaseValueDefault(hContact, szProto, "display_uid"))
+ return res;
+
+ if (auto *uid = Proto_GetUniqueId(szProto))
+ return ProcessDatabaseValueDefault(hContact, szProto, uid);
+ break;
+
+ case CNF_DISPLAYNC:
+ case CNF_DISPLAY:
+ for (auto &it : nameOrder) {
+ switch (it) {
+ case 0: // custom name
+ // make sure we aren't in CNF_DISPLAYNC mode
+ // don't get custom name for nullptr contact
+ if (type == CNF_DISPLAY)
+ if (res = ProcessDatabaseValueDefault(hContact, (hContact != 0) ? "CList" : szProto, "MyHandle"))
+ return res;
+ break;
+
+ case 1:
+ if (res = ProcessDatabaseValueDefault(hContact, szProto, "Nick")) // nick
+ return res;
+ break;
+ case 2:
+ if (res = ProcessDatabaseValueDefault(hContact, szProto, "FirstName")) // First Name
+ return res;
+ break;
+ case 3:
+ if (res = ProcessDatabaseValueDefault(hContact, szProto, "e-mail")) // E-mail
+ return res;
+ break;
+ case 4:
+ if (res = ProcessDatabaseValueDefault(hContact, szProto, "LastName")) // Last Name
+ return res;
+ break;
+
+ case 5: // Unique id
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ if (auto *uid = Proto_GetUniqueId(szProto)) {
+ if (!db_get_ws(hContact, szProto, uid, &dbv)) {
+ if (dbv.type == DBVT_BYTE || dbv.type == DBVT_WORD || dbv.type == DBVT_DWORD) {
+ long value = (dbv.type == DBVT_BYTE) ? dbv.bVal : (dbv.type == DBVT_WORD ? dbv.wVal : dbv.dVal);
+ wchar_t buf[40];
+ _ltow(value, buf, 10);
+ return mir_wstrdup(buf);
+ }
+ return dbv.pwszVal;
+ }
+ }
+ break;
+
+ case 6: // first + last name
+ case 7: // last + first name
+ if (!db_get_ws(hContact, szProto, it == 6 ? "FirstName" : "LastName", &dbv)) {
+ DBVARIANT dbv2;
+ if (!db_get_ws(hContact, szProto, it == 6 ? "LastName" : "FirstName", &dbv2)) {
+ size_t len = mir_wstrlen(dbv.pwszVal) + mir_wstrlen(dbv2.pwszVal) + 2;
+ wchar_t *buf = (wchar_t*)mir_alloc(sizeof(wchar_t)*len);
+ if (buf != nullptr)
+ mir_wstrcat(mir_wstrcat(mir_wstrcpy(buf, dbv.pwszVal), L" "), dbv2.pwszVal);
+
+ db_free(&dbv);
+ db_free(&dbv2);
+ return buf;
+ }
+ db_free(&dbv);
+ }
+ break;
+
+ case 8:
+ return mir_wstrdup(TranslateT("'(Unknown contact)'"));
+ }
+ }
+ break;
+
+ case CNF_MYNOTES:
+ return ProcessDatabaseValueDefault(hContact, "UserInfo", "MyNotes");
+
+ case CNF_TIMEZONE:
+ HANDLE hTz = TimeZone_CreateByContact(hContact, nullptr, TZF_KNOWNONLY);
+ if (hTz) {
+ LPTIME_ZONE_INFORMATION tzi = TimeZone_GetInfo(hTz);
+ int offset = tzi->Bias + tzi->StandardBias;
+
+ char str[80];
+ mir_snprintf(str, offset ? "UTC%+d:%02d" : "UTC", offset / -60, abs(offset % 60));
+ return mir_a2u(str);
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+MIR_APP_DLL(int) Contact::GetStatus(MCONTACT hContact, const char *szProto)
+{
+ if (hContact == 0)
+ return ID_STATUS_OFFLINE;
+
+ if (szProto == nullptr)
+ szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr)
+ return ID_STATUS_OFFLINE;
+
+ return db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Contact::IsGroupChat(MCONTACT hContact, const char *szProto)
+{
+ if (szProto == nullptr) {
+ szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr)
+ return false;
+ }
+
+ return db_get_b(hContact, szProto, "ChatRoom");
+}
+
+MIR_APP_DLL(void) Contact::SetBirthday(MCONTACT hContact, int dd, int mm, int yy)
+{
+ if (auto *szProto = Proto_GetBaseAccountName(hContact)) {
+ db_set_w(hContact, szProto, "BirthMonth", mm);
+ db_set_w(hContact, szProto, "BirthDay", dd);
+
+ if (yy) {
+ db_set_w(hContact, szProto, "BirthYear", yy);
+
+ SYSTEMTIME sToday = {};
+ GetLocalTime(&sToday);
+ int nAge = sToday.wYear - yy;
+ if (sToday.wMonth < mm || (sToday.wMonth == mm && sToday.wDay < dd))
+ nAge--;
+ if (nAge)
+ db_set_w(hContact, szProto, "Age", nAge);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Contact::IsHidden(MCONTACT hContact)
+{
+ return db_get_b(hContact, "CList", "Hidden") != 0;
+}
+
+MIR_APP_DLL(void) Contact::Hide(MCONTACT hContact, bool bHidden)
+{
+ if (bHidden)
+ db_set_b(hContact, "CList", "Hidden", 1);
+ else
+ db_unset(hContact, "CList", "Hidden");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Contact::IsReadonly(MCONTACT hContact)
+{
+ return db_get_b(hContact, "CList", "ReadOnly") != 0;
+}
+
+MIR_APP_DLL(void) Contact::Readonly(MCONTACT hContact, bool bReadOnly)
+{
+ if (bReadOnly)
+ db_set_b(hContact, "CList", "ReadOnly", 1);
+ else
+ db_unset(hContact, "CList", "ReadOnly");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Contact::OnList(MCONTACT hContact)
+{
+ return db_get_b(hContact, "CList", "NotOnList", 0) == 0;
+}
+
+MIR_APP_DLL(void) Contact::PutOnList(MCONTACT hContact)
+{
+ db_unset(hContact, "CList", "NotOnList");
+}
+
+MIR_APP_DLL(void) Contact::RemoveFromList(MCONTACT hContact)
+{
+ db_set_b(hContact, "CList", "NotOnList", 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Options dialog
+
+class CContactOptsDlg : public CDlgBase
+{
+ CCtrlTreeView m_nameOrder;
+
+public:
+ CContactOptsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_CONTACT),
+ m_nameOrder(this, IDC_NAMEORDER)
+ {
+ m_nameOrder.SetFlags(MTREE_DND);
+ m_nameOrder.OnBeginDrag = BCallback(this, &CContactOptsDlg::OnBeginDrag);
+ m_nameOrder.OnEndDrag = BCallback(this, &CContactOptsDlg::OnEndDrag);
+ }
+
+ bool OnInitDialog() override
+ {
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
+ for (int i = 0; i < _countof(nameOrderDescr); i++) {
+ tvis.item.lParam = nameOrder[i];
+ tvis.item.pszText = TranslateW(nameOrderDescr[nameOrder[i]]);
+ m_nameOrder.InsertItem(&tvis);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ TVITEMEX tvi;
+ tvi.hItem = m_nameOrder.GetRoot();
+ int i = 0;
+ while (tvi.hItem != nullptr) {
+ tvi.mask = TVIF_PARAM | TVIF_HANDLE;
+ m_nameOrder.GetItem(&tvi);
+ nameOrder[i++] = (uint8_t)tvi.lParam;
+ tvi.hItem = m_nameOrder.GetNextSibling(tvi.hItem);
+ }
+ db_set_blob(0, "Contact", "NameOrder", nameOrder, _countof(nameOrderDescr));
+ g_clistApi.pfnInvalidateDisplayNameCacheEntry(INVALID_CONTACT_ID);
+ return true;
+ }
+
+ bool OnBeginDrag(CCtrlTreeView::TEventInfo *evt)
+ {
+ LPNMTREEVIEW pNotify = evt->nmtv;
+ return (pNotify->itemNew.lParam != 0 && pNotify->itemNew.lParam != _countof(nameOrderDescr)-1);
+ }
+
+ bool OnEndDrag(CCtrlTreeView::TEventInfo *evt)
+ {
+ auto &hti = *evt->ntvhi;
+
+ // do not allow to move selection over the first item
+ if (hti.flags & TVHT_ABOVE)
+ return false;
+
+ // do not allow to move selection below the last item either
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hti.hItem;
+ m_nameOrder.GetItem(&tvi);
+ if (tvi.lParam == _countof(nameOrderDescr) - 1)
+ return false;
+
+ return true;
+ }
+};
+
+static int ContactOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -1000000000;
+ odp.szGroup.a = LPGEN("Contact list");
+ odp.szTitle.a = LPGEN("Contact names");
+ odp.pDialog = new CContactOptsDlg();
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+
+ return PopupOptionsInit(wParam);
+}
+
+int LoadContactsModule(void)
+{
+ for (uint8_t i = 0; i < _countof(nameOrder); i++)
+ nameOrder[i] = i;
+
+ DBVARIANT dbv;
+ if (!db_get(0, "Contact", "NameOrder", &dbv)) {
+ memcpy(nameOrder, dbv.pbVal, dbv.cpbVal);
+ db_free(&dbv);
+ }
+
+ HookEvent(ME_OPT_INITIALISE, ContactOptInit);
+ return 0;
+}