From f4ce2b5c214cce406dbd7a73dc7f35ae409546ad Mon Sep 17 00:00:00 2001 From: Tobias Weimer Date: Sun, 12 Jul 2015 14:10:16 +0000 Subject: Clist NG: Commit of CList NG by silvercircle from https://github.com/silvercircle/miranda-ng This is based on clist_nicer and Anti-Grain Geometry: http://www.antigrain.com/ This is the first version that actually compiles. Do NOT use it in production environment! git-svn-id: http://svn.miranda-ng.org/main/trunk@14543 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Clist_ng/SRC/clc.cpp | 934 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 934 insertions(+) create mode 100644 plugins/Clist_ng/SRC/clc.cpp (limited to 'plugins/Clist_ng/SRC/clc.cpp') diff --git a/plugins/Clist_ng/SRC/clc.cpp b/plugins/Clist_ng/SRC/clc.cpp new file mode 100644 index 0000000000..1ec6eb5bb8 --- /dev/null +++ b/plugins/Clist_ng/SRC/clc.cpp @@ -0,0 +1,934 @@ +/* + * astyle --force-indent=tab=4 --brackets=linux --indent-switches + * --pad=oper --one-line=keep-blocks --unpad=paren + * + * Miranda IM: the free IM client for Microsoft* Windows* + * + * Copyright 2000-2010 Miranda ICQ/IM project, + * all portions of this codebase are copyrighted to the people + * listed in contributors.txt. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * you should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * part of clist_nicer plugin for Miranda. + * + * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors + * + * $Id: clc.cpp 138 2010-11-01 10:51:15Z silvercircle $ + * + */ + +#include +#include +#include +#include "../coolsb/coolscroll.h" + +/* + * static function pointers for Clist interface + */ +LRESULT (CALLBACK* CLC::saveContactListControlWndProc) (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) = 0; + +bool CLC::fHottrackDone = false, CLC::fInPaint = false; +int CLC::iHottrackItem = 0; +HANDLE CLC::hSettingsChanged = 0, CLC::hDBEvent = 0; +HIMAGELIST CLC::hClistImages = 0; +unsigned CLC::uNrAvatars = 0; + +TDisplayProfile CLC::dsp_default = {0}; + +int CLC::_status2onlineness[] = { + 100, // ID_STATUS_ONLINE + 30, // ID_STATUS_AWAY + 40, // ID_STATUS_DND + 10, // ID_STATUS_NA + 60, // ID_STATUS_OCCUPIED + 110, // ID_STATUS_FREECHAT + 5, // ID_STATUS_INVISIBLE + 50, // ID_STATUS_ONTHEPHONE + 20 // ID_STATUS_OUTTOLUNCH +}; + +extern HANDLE hExtraImageApplying; +extern FRAMEWND *wndFrameCLC; + +extern int during_sizing; + +HANDLE hSoundHook = 0, hIcoLibChanged = 0, hSvc_GetContactStatusMsg = 0; + +HMENU BuildGroupPopupMenu(struct ClcGroup* group) +{ + return Menu_BuildSubGroupMenu(group); +} + +int AvatarChanged(WPARAM wParam, LPARAM lParam) +{ + pcli->pfnClcBroadcast(INTM_AVATARCHANGED, wParam, lParam); + return 0; +} + +int __forceinline __strcmp(const char * src, const char * dst) +{ + int ret = 0 ; + + while (!(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) + ++src, ++dst; + return(ret); +} + +static int ClcEventAdded(WPARAM hContact, LPARAM lParam) +{ + DWORD new_freq = 0; + cfg::dat.t_now = time(NULL); + + if (hContact && lParam) { + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get((MEVENT)lParam, &dbei); + if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { + DWORD firstTime = cfg::getDword(hContact, "CList", "mf_firstEvent", 0); + DWORD count = cfg::getDword(hContact, "CList", "mf_count", 0); + count++; + new_freq = count ? (dbei.timestamp - firstTime) / count : 0x7fffffff; + cfg::writeDword(hContact, "CList", "mf_freq", new_freq); + cfg::writeDword(hContact, "CList", "mf_count", count); + int iEntry = cfg::getCache(hContact, NULL); + if (iEntry >= 0 && iEntry < cfg::nextCacheEntry) { + cfg::eCache[iEntry].dwLastMsgTime = dbei.timestamp; + if (new_freq) + cfg::eCache[iEntry].msgFrequency = new_freq; + pcli->pfnClcBroadcast(INTM_FORCESORT, 0, 1); + } + } + } + return 0; +} + +int ClcSoundHook(WPARAM wParam, LPARAM lParam) +{ + return 0; +} + +int CLC::SettingChanged(WPARAM hContact, LPARAM lParam) +{ + char *szProto = NULL; + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; + + if (hContact) { + if (!__strcmp(cws->szModule, "CList")) { + if (!__strcmp(cws->szSetting, "StatusMsg")) + SendMessage(pcli->hwndContactTree, INTM_STATUSMSGCHANGED, hContact, lParam); + } + else if (!__strcmp(cws->szModule, "UserInfo")) { + if (!__strcmp(cws->szSetting, "Timezone") || !__strcmp(cws->szSetting, "TzName")) + ReloadExtraInfo(hContact); + } + else if (hContact != 0 && (szProto = GetContactProto(hContact)) != NULL) { + if (!__strcmp(cws->szModule, "Protocol") && !__strcmp(cws->szSetting, "p")) { + char *szProto_s; + pcli->pfnClcBroadcast(INTM_PROTOCHANGED, hContact, lParam); + if (cws->value.type == DBVT_DELETED) + szProto_s = NULL; + else + szProto_s = cws->value.pszVal; + pcli->pfnChangeContactIcon(hContact, IconFromStatusMode(szProto_s, szProto_s == NULL ? ID_STATUS_OFFLINE : cfg::getWord(hContact, szProto_s, "Status", ID_STATUS_OFFLINE), hContact, NULL), 0); + } + // something is being written to a protocol module + if (!__strcmp(szProto, cws->szModule)) { + // was a unique setting key written? + pcli->pfnInvalidateDisplayNameCacheEntry(hContact); + if (!__strcmp(cws->szSetting, "Status")) { + if (!cfg::getByte(hContact, "CList", "Hidden", 0)) { + if (cfg::getByte("CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT)) { + // User's state is changing, and we are hideOffline-ing + if (cws->value.wVal == ID_STATUS_OFFLINE) { + pcli->pfnChangeContactIcon(hContact, IconFromStatusMode(cws->szModule, cws->value.wVal, hContact, NULL), 0); + CallService(MS_CLUI_CONTACTDELETED, hContact, 0); + return 0; + } + pcli->pfnChangeContactIcon(hContact, IconFromStatusMode(cws->szModule, cws->value.wVal, hContact, NULL), 1); + } + pcli->pfnChangeContactIcon(hContact, IconFromStatusMode(cws->szModule, cws->value.wVal, hContact, NULL), 0); + } + SendMessage(pcli->hwndContactTree, INTM_STATUSCHANGED, hContact, lParam); + return 0; + } else if (strstr("YMsg|StatusDescr|XStatusMsg", cws->szSetting)) + SendMessage(pcli->hwndContactTree, INTM_STATUSMSGCHANGED, hContact, lParam); + else if (strstr(cws->szSetting, "XStatus")) + SendMessage(pcli->hwndContactTree, INTM_XSTATUSCHANGED, hContact, lParam); + else if (!__strcmp(cws->szSetting, "Timezone") || !__strcmp(cws->szSetting, "TzName")) + ReloadExtraInfo(hContact); + else if (!__strcmp(cws->szSetting, "MirVer")) + NotifyEventHooks(hExtraImageApplying, hContact, 0); + + if (cfg::dat.bMetaAvail && !__strcmp(szProto, cfg::dat.szMetaName)) { + if ((lstrlenA(cws->szSetting) > 6 && !strncmp(cws->szSetting, "Status", 6)) || strstr("Default,ForceSend,Nick", cws->szSetting)) + pcli->pfnClcBroadcast(INTM_NAMEORDERCHANGED, hContact, lParam); + } + } + if (cfg::dat.bMetaAvail && cfg::dat.bMetaEnabled && !__strcmp(cws->szModule, cfg::dat.szMetaName) && !__strcmp(cws->szSetting, "IsSubcontact")) + pcli->pfnClcBroadcast(INTM_HIDDENCHANGED, hContact, lParam); + } + } else if (hContact == 0 && !__strcmp(cws->szModule, cfg::dat.szMetaName)) { + BYTE bMetaEnabled = cfg::getByte(cfg::dat.szMetaName, "Enabled", 1); + if (bMetaEnabled != (BYTE)cfg::dat.bMetaEnabled) { + cfg::dat.bMetaEnabled = bMetaEnabled; + pcli->pfnClcBroadcast(CLM_AUTOREBUILD, 0, 0); + } + } else if (hContact == 0 && !__strcmp(cws->szModule, "Skin")) { + if (!__strcmp(cws->szSetting, "UseSound")) { + if (hSoundHook) { + UnhookEvent(hSoundHook); + hSoundHook = 0; + } + cfg::dat.soundsOff = cfg::getByte(cws->szModule, cws->szSetting, 0) ? 0 : 1; + if (cfg::dat.soundsOff && hSoundHook == 0) + hSoundHook = HookEvent(ME_SKIN_PLAYINGSOUND, ClcSoundHook); + CheckDlgButton(pcli->hwndContactList, IDC_TBSOUND, cfg::dat.soundsOff ? BST_UNCHECKED : BST_CHECKED); + CLUI::setButtonStates(pcli->hwndContactList); + } + } else if (szProto == NULL && hContact == 0) { + if (!__strcmp(cws->szSetting, "XStatusId")) + CluiProtocolStatusChanged(0, cws->szModule); + return 0; + } + return 0; +} + +static int ClcModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + return 0; +} + +int CLC::preshutdown(WPARAM wParam, LPARAM lParam) +{ + SFL_Destroy(); + cfg::shutDown = TRUE; + if (hSvc_GetContactStatusMsg) + DestroyServiceFunction(hSvc_GetContactStatusMsg); + UnhookEvent(hSettingsChanged); + UnhookEvent(hDBEvent); + if (hIcoLibChanged) + UnhookEvent(hIcoLibChanged); + return 0; +} + +int CLC::shutDown(WPARAM wParam, LPARAM lParam) +{ + if (cfg::dat.hIconInvisible) + DestroyIcon(cfg::dat.hIconInvisible); + if (cfg::dat.hIconVisible) + DestroyIcon(cfg::dat.hIconVisible); + if (cfg::dat.hIconChatactive) + DestroyIcon(cfg::dat.hIconChatactive); + + DeleteObject(cfg::dat.hBrushCLCBk); + DeleteObject(cfg::dat.hBrushCLCGroupsBk); + DeleteObject(cfg::dat.hBrushAvatarBorder); + DestroyMenu(cfg::dat.hMenuNotify); + ClearIcons(1); + SFL_UnregisterWindowClass(); + if (cfg::eCache) { + int i; + + for (i = 0; i < cfg::nextCacheEntry; i++) { + if (cfg::eCache[i].statusMsg) + free(cfg::eCache[i].statusMsg); + if (cfg::eCache[i].status_item) { + TStatusItem *item = cfg::eCache[i].status_item; + int j; + + free(cfg::eCache[i].status_item); + cfg::eCache[i].status_item = 0; + for (j = i; j < cfg::nextCacheEntry; j++) { // avoid duplicate free()'ing status item pointers (there are references from sub to master contacts, so compare the pointers... + if (cfg::eCache[j].status_item == item) + cfg::eCache[j].status_item = 0; + } + } + } + free(cfg::eCache); + cfg::eCache = NULL; + } + Skin::Unload(); + DeleteCriticalSection(&cfg::cachecs); + return 0; +} + +int CLC::loadModule(void) +{ + CLC::hClistImages = (HIMAGELIST) CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0); + + HookEvent(ME_SYSTEM_MODULESLOADED, ClcModulesLoaded); + hSettingsChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, CLC::SettingChanged); + hDBEvent = HookEvent(ME_DB_EVENT_ADDED, ClcEventAdded); + HookEvent(ME_OPT_INITIALISE, ClcOptInit); + HookEvent(ME_SYSTEM_SHUTDOWN, preshutdown); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// clist_nicer+ control window procedure + +LRESULT CALLBACK CLC::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ClcData *dat; + BOOL frameHasTitlebar = FALSE; + + if (wndFrameCLC) + frameHasTitlebar = wndFrameCLC->TitleBar.ShowTitleBar; + + dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0); + if (msg >= CLM_FIRST && msg < CLM_LAST) + return ProcessExternalMessages(hwnd, dat, msg, wParam, lParam); + + switch (msg) { + case WM_NCCREATE: + dat = (struct ClcData *)mir_alloc(sizeof(struct ClcData)); + memset(dat, 0, sizeof(struct ClcData)); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); + dat->ph = new CLCPaintHelper(hwnd, dat, 0, 0, 0, 0, 0); + RowHeight::Init(dat); + break; + case WM_CREATE: + dat->forceScroll = 0; + dat->lastRepaint = 0; + dat->needsResort = false; + dat->himlExtraColumns = CLUI::hExtraImages; + dat->hwndParent = GetParent(hwnd); + dat->lastSort = GetTickCount(); + dat->ph = new CLCPaintHelper(hwnd, dat, 0, 0, 0, 0, 0); + { + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + if (cs->lpCreateParams == (LPVOID)0xff00ff00) { + dat->bisEmbedded = FALSE; + dat->bHideSubcontacts = TRUE; + cfg::clcdat = dat; + if (cfg::dat.bShowLocalTime) + SetTimer(hwnd, TIMERID_REFRESH, 65000, NULL); + } else + dat->bisEmbedded = TRUE; + } + break; + case WM_SIZE: + pcli->pfnEndRename(hwnd, dat, 1); + KillTimer(hwnd, TIMERID_INFOTIP); + KillTimer(hwnd, TIMERID_RENAME); + pcli->pfnRecalcScrollBar(hwnd, dat); +LBL_Def: + return DefWindowProc(hwnd, msg, wParam, lParam); + + case WM_NCCALCSIZE: { + return FrameNCCalcSize(hwnd, DefWindowProc, wParam, lParam, frameHasTitlebar); + } + + /* + * scroll bar handling + */ + case WM_NCPAINT: + return FrameNCPaint(hwnd, DefWindowProc, wParam, lParam, frameHasTitlebar); + case INTM_SCROLLBARCHANGED: + if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CONTACTLIST) { + if (!cfg::getByte("CLC", "NoVScrollBar", 0)) { + CoolSB_SetupScrollBar(hwnd); + ShowScrollBar(hwnd, SB_VERT, FALSE); + } + else + pcli->pfnRecalcScrollBar(hwnd, dat); + } + break; + case INTM_GROUPCHANGED: { + ClcContact *contact; + BYTE iExtraImage[MAXEXTRACOLUMNS]; + BYTE flags = 0; + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + memset(iExtraImage, 0xFF, sizeof(iExtraImage)); + else { + CopyMemory(iExtraImage, contact->iExtraImage, sizeof(iExtraImage)); + flags = contact->flags; + } + pcli->pfnDeleteItemFromTree(hwnd, (MCONTACT)wParam); + if (GetWindowLong(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !CLVM_GetContactHiddenStatus(wParam, NULL, dat)) { + NMCLISTCONTROL nm; + pcli->pfnAddContactToTree(hwnd, dat, wParam, 1, 1); + if (findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) { + CopyMemory(contact->iExtraImage, iExtraImage, sizeof(iExtraImage)); + if (flags & CONTACTF_CHECKED) + contact->flags |= CONTACTF_CHECKED; + } + nm.hdr.code = CLN_CONTACTMOVED; + nm.hdr.hwndFrom = hwnd; + nm.hdr.idFrom = GetDlgCtrlID(hwnd); + nm.flags = 0; + nm.hItem = (HANDLE) wParam; + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) &nm); + } + dat->needsResort = true; + PostMessage(hwnd, INTM_SORTCLC, 0, 1); + goto LBL_Def; + } + + case INTM_ICONCHANGED: { + struct ClcContact *contact = NULL; + struct ClcGroup *group = NULL; + int recalcScrollBar = 0, shouldShow; + WORD status = ID_STATUS_OFFLINE; + char *szProto; + int contactRemoved = 0; + DWORD hSelItem = 0; + struct ClcContact *selcontact = NULL; + + szProto = GetContactProto(wParam); + if (szProto == NULL) + status = ID_STATUS_OFFLINE; + else + status = cfg::getWord(wParam, szProto, "Status", ID_STATUS_OFFLINE); + + shouldShow = (GetWindowLong(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !CLVM_GetContactHiddenStatus(wParam, szProto, dat)) && ((cfg::dat.bFilterEffective ? TRUE : !pcli->pfnIsHiddenMode(dat, status)) || CallService(MS_CLIST_GETCONTACTICON, wParam, 0) != lParam);// XXX CLVM changed - this means an offline msg is flashing, so the contact should be shown + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, &group, NULL)) { + if (shouldShow && CallService(MS_DB_CONTACT_IS, wParam, 0)) { + if (dat->selection >= 0 && pcli->pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1) + hSelItem = (MCONTACT)pcli->pfnContactToHItem(selcontact); + pcli->pfnAddContactToTree(hwnd, dat, wParam, 0, 0); + recalcScrollBar = 1; + findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL); + if (contact) { + contact->iImage = (WORD) lParam; + pcli->pfnNotifyNewContact(hwnd, wParam); + } + } + } else { + //item in list already + DWORD style = GetWindowLong(hwnd, GWL_STYLE); + if (contact->iImage == (WORD) lParam) + break; + if (!shouldShow && !(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline || cfg::dat.bFilterEffective)) { // CLVM changed + if (dat->selection >= 0 && pcli->pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1) + hSelItem = (MCONTACT)pcli->pfnContactToHItem(selcontact); + pcli->pfnRemoveItemFromGroup(hwnd, group, contact, 0); + contactRemoved = TRUE; + recalcScrollBar = 1; + } else { + contact->iImage = (WORD) lParam; + if (!pcli->pfnIsHiddenMode(dat, status)) + contact->flags |= CONTACTF_ONLINE; + else + contact->flags &= ~CONTACTF_ONLINE; + } + } + if (hSelItem) { + struct ClcGroup *selgroup; + if (pcli->pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL)) + dat->selection = pcli->pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*) & selgroup->cl, selcontact)); + else + dat->selection = -1; + } + dat->needsResort = true; + PostMessage(hwnd, INTM_SORTCLC, 0, recalcScrollBar); + PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)(contactRemoved ? 0 : wParam)); + if (recalcScrollBar) + pcli->pfnRecalcScrollBar(hwnd, dat); + goto LBL_Def; + } + case INTM_METACHANGED: { + struct ClcContact *contact; + if (!pcli->pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) + break; + if (contact->bIsMeta && cfg::dat.bMetaAvail) { + contact->hSubContact = (MCONTACT) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) contact->hContact, 0); + contact->metaProto = GetContactProto(contact->hSubContact); + contact->iImage = CallService(MS_CLIST_GETCONTACTICON, (WPARAM) contact->hSubContact, 0); + if (contact->extraCacheEntry >= 0 && contact->extraCacheEntry < cfg::nextCacheEntry) { + int subIndex = cfg::getCache(contact->hSubContact, contact->metaProto); + cfg::eCache[contact->extraCacheEntry].proto_status_item = GetProtocolStatusItem(contact->metaProto); + if (subIndex >= 0 && subIndex <= cfg::nextCacheEntry) { + cfg::eCache[contact->extraCacheEntry].status_item = cfg::eCache[subIndex].status_item; + CopyMemory(cfg::eCache[contact->extraCacheEntry].iExtraImage, cfg::eCache[subIndex].iExtraImage, MAXEXTRACOLUMNS); + cfg::eCache[contact->extraCacheEntry].iExtraValid = cfg::eCache[subIndex].iExtraValid; + } + } + } + SendMessage(hwnd, INTM_NAMEORDERCHANGED, wParam, lParam); + goto LBL_Def; + } + case INTM_METACHANGEDEVENT: { + struct ClcContact *contact; + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + break; + if (lParam == 0) + pcli->pfnInitAutoRebuild(hwnd); + goto LBL_Def; + } + case INTM_NAMECHANGED: { + struct ClcContact *contact; + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + break; + lstrcpyn(contact->szText, pcli->pfnGetContactDisplayName(wParam, 0), safe_sizeof(contact->szText)); + RTL_DetectAndSet(contact, 0); + dat->needsResort = true; + PostMessage(hwnd, INTM_SORTCLC, 0, 0); + goto LBL_Def; + } + + case INTM_AVATARCHANGED: { + struct avatarCacheEntry *cEntry = (struct avatarCacheEntry *)lParam; + struct ClcContact *contact = NULL; + + if (wParam == 0) { + cfg::dat.bForceRefetchOnPaint = TRUE; + RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); + cfg::dat.bForceRefetchOnPaint = FALSE; + goto LBL_Def; + } + + if (!findItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) + return 0; + contact->ace = cEntry; + if (cEntry == NULL) + contact->cFlags &= ~ECF_AVATAR; + else { + if (cfg::dat.dwFlags & CLUI_FRAME_AVATARS) + contact->cFlags = (contact->dwDFlags & ECF_HIDEAVATAR ? contact->cFlags & ~ECF_AVATAR : contact->cFlags | ECF_AVATAR); + else + contact->cFlags = (contact->dwDFlags & ECF_FORCEAVATAR ? contact->cFlags | ECF_AVATAR : contact->cFlags & ~ECF_AVATAR); + } + PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)contact->hContact); + + goto LBL_Def; + } + case INTM_STATUSMSGCHANGED: { + struct ClcContact *contact = NULL; + int index = -1; + char *szProto = NULL; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + index = cfg::getCache(wParam, NULL); + else { + index = contact->extraCacheEntry; + szProto = contact->proto; + } + GetCachedStatusMsg(index, szProto); + PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)(contact ? contact->hContact : 0)); + goto LBL_Def; + } + case INTM_STATUSCHANGED: { + struct ClcContact *contact = NULL; + WORD wStatus; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + break; + + wStatus = cfg::getWord(wParam, contact->proto, "Status", ID_STATUS_OFFLINE); + if (cfg::dat.bNoOfflineAvatars && wStatus != ID_STATUS_OFFLINE && contact->wStatus == ID_STATUS_OFFLINE) { + contact->wStatus = wStatus; + if (contact->ace == 0) + LoadAvatarForContact(contact); + } + contact->wStatus = wStatus; + goto LBL_Def; + } + case INTM_PROTOCHANGED: { + struct ClcContact *contact = NULL; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + break; + contact->proto = GetContactProto(wParam); + CallService(MS_CLIST_INVALIDATEDISPLAYNAME, wParam, 0); + lstrcpyn(contact->szText, pcli->pfnGetContactDisplayName(wParam, 0), safe_sizeof(contact->szText)); + RTL_DetectAndSet(contact, 0); + dat->needsResort = TRUE; + PostMessage(hwnd, INTM_SORTCLC, 0, 0); + goto LBL_Def; + } + + case INTM_INVALIDATE: + if (!dat->bNeedPaint) { + KillTimer(hwnd, TIMERID_PAINT); + SetTimer(hwnd, TIMERID_PAINT, 100, NULL); + dat->bNeedPaint = TRUE; + } + + if (lParam && !dat->bisEmbedded) { + struct ClcContact *contact = NULL; + + if (findItem(hwnd, dat, (HANDLE)lParam, &contact, NULL, 0)) { + if (contact && contact->extraCacheEntry >= 0 && contact->extraCacheEntry < cfg::nextCacheEntry && cfg::eCache[contact->extraCacheEntry].floater) + FLT_Update(dat, contact); + } + } + goto LBL_Def; + + case INTM_INVALIDATECONTACT: { + struct ClcContact *contact = 0; + struct ClcGroup *group = 0; + int iItem; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, &group, NULL)) + break; + + if (contact == 0 || group == 0) + break; + + iItem = pcli->pfnGetRowsPriorTo(&dat->list, group, List_IndexOf((SortedList*) & group->cl, contact)); + pcli->pfnInvalidateItem(hwnd, dat, iItem); + goto LBL_Def; + } + case INTM_FORCESORT: + dat->needsResort = TRUE; + return SendMessage(hwnd, INTM_SORTCLC, wParam, lParam); + case INTM_SORTCLC: + if (dat->needsResort) { + pcli->pfnSortCLC(hwnd, dat, TRUE); + dat->needsResort = FALSE; + } + if (lParam) + pcli->pfnRecalcScrollBar(hwnd, dat); + goto LBL_Def; + + case INTM_IDLECHANGED: { + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; + char *szProto; + struct ClcContact *contact = NULL; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) + break; + szProto = (char*)cws->szModule; + if (szProto == NULL) + break; + contact->flags &= ~CONTACTF_IDLE; + if (cfg::getDword(wParam, szProto, "IdleTS", 0)) { + contact->flags |= CONTACTF_IDLE; + } + PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)contact->hContact); + goto LBL_Def; + } + case INTM_XSTATUSCHANGED: { + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; + char *szProto; + struct ClcContact *contact = NULL; + int index; + + szProto = (char *)cws->szModule; + + if (!findItem(hwnd, dat, (HANDLE) wParam, &contact, NULL, NULL)) { + index = cfg::getCache(wParam, szProto); + if (!dat->bisEmbedded && cfg::dat.bMetaAvail && szProto) { // may be a subcontact, forward the xstatus + if (cfg::getByte(wParam, cfg::dat.szMetaName, "IsSubcontact", 0)) { + MCONTACT hMasterContact = cfg::getDword(wParam, cfg::dat.szMetaName, "Handle", 0); + if (hMasterContact && hMasterContact != wParam) // avoid recursive call of settings handler + cfg::writeByte(hMasterContact, cfg::dat.szMetaName, "XStatusId", (BYTE)cfg::getByte(wParam, szProto, "XStatusId", 0)); + break; + } + } + } else { + contact->xStatus = cfg::getByte(wParam, szProto, "XStatusId", 0); + index = contact->extraCacheEntry; + } + if (szProto == NULL) + break; + GetCachedStatusMsg(index, szProto); + PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)(contact ? contact->hContact : 0)); + goto LBL_Def; + } + case WM_PAINT: { + HDC hdc; + PAINTSTRUCT ps; + ULONGLONG ulTick; + hdc = BeginPaint(hwnd, &ps); + if (IsWindowVisible(hwnd) && !CLUI::fInSizing && !cfg::shutDown && dat->ph) { + ulTick = Api::pfnGetTickCount64(); + if(ulTick - dat->lastRepaint > 2000) { + countAvatars(dat); + dat->lastRepaint = ulTick; + } + Paint(hwnd, dat, hdc, &ps.rcPaint); + dat->bNeedPaint = FALSE; + } + EndPaint(hwnd, &ps); + if (dat->selection != dat->oldSelection && !dat->bisEmbedded && CLUI::buttonItems != NULL) { + CLUI::setFrameButtonStates(0); + dat->oldSelection = dat->selection; + } + goto LBL_Def; + } + + case WM_MOUSEWHEEL: + dat->forceScroll = TRUE; + break; + + case WM_TIMER: + if (wParam == TIMERID_PAINT) { + KillTimer(hwnd, TIMERID_PAINT); + InvalidateRect(hwnd, NULL, FALSE); + goto LBL_Def; + } + + if (wParam == TIMERID_REFRESH) { + InvalidateRect(hwnd, NULL, FALSE); + goto LBL_Def; + } + break; + + case WM_LBUTTONDBLCLK: { + struct ClcContact *contact; + DWORD hitFlags; + ReleaseCapture(); + dat->iHotTrack = -1; + pcli->pfnHideInfoTip(hwnd, dat); + KillTimer(hwnd, TIMERID_RENAME); + KillTimer(hwnd, TIMERID_INFOTIP); + dat->szQuickSearch[0] = 0; + dat->selection = HitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), &contact, NULL, &hitFlags); + if (hitFlags & CLCHT_ONITEMEXTRAEX && hwnd == pcli->hwndContactTree && contact != 0) { + int column = hitFlags >> 24; + if (column-- > 0) { + if (contact->type == CLCIT_CONTACT) { + CONTACTINFO ci; + ZeroMemory(&ci,sizeof(CONTACTINFO)); + ci.cbSize = sizeof(CONTACTINFO); + ci.hContact = contact->hContact; + ci.szProto = contact->proto; + + if (column == 0) { + ci.dwFlag = CNF_EMAIL; + if (!CallService(MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci)) { + char buf[4096]; + mir_snprintf(buf, sizeof(buf), "mailto:%s", (LPCSTR)ci.pszVal); + mir_free(ci.pszVal); + ShellExecuteA(hwnd, "open", buf, NULL, NULL, SW_SHOW); + } + return TRUE; + } + if (column == 1) { + ci.dwFlag = CNF_HOMEPAGE; + if (!CallService(MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci)) { + ShellExecuteA(hwnd, "open", (LPCSTR)ci.pszVal, NULL, NULL, SW_SHOW); + mir_free(ci.pszVal); + } + return TRUE; + } + } + } + } + InvalidateRect(hwnd, NULL, FALSE); + if (dat->selection != -1) + pcli->pfnEnsureVisible(hwnd, dat, dat->selection, 0); + if (hitFlags & CLCHT_ONAVATAR && cfg::dat.bDblClkAvatars) { + CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)contact->hContact, 0); + return TRUE; + } + if (hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMLABEL | CLCHT_ONITEMSPACE)) { + UpdateWindow(hwnd); + pcli->pfnDoSelectionDefaultAction(hwnd, dat); + } + return TRUE; + } + case WM_CONTEXTMENU: { + struct ClcContact *contact; + HMENU hMenu = NULL; + POINT pt; + DWORD hitFlags; + + pcli->pfnEndRename(hwnd, dat, 1); + pcli->pfnHideInfoTip(hwnd, dat); + KillTimer(hwnd, TIMERID_RENAME); + KillTimer(hwnd, TIMERID_INFOTIP); + if (GetFocus() != hwnd) + SetFocus(hwnd); + dat->iHotTrack = -1; + dat->szQuickSearch[0] = 0; + pt.x = (short) LOWORD(lParam); + pt.y = (short) HIWORD(lParam); + if (pt.x == -1 && pt.y == -1) { + dat->selection = pcli->pfnGetRowByIndex(dat, dat->selection, &contact, NULL); + if (dat->selection != -1) + pcli->pfnEnsureVisible(hwnd, dat, dat->selection, 0); + pt.x = dat->iconXSpace + 15; + pt.y = RowHeight::getItemTopY(dat, dat->selection) - dat->yScroll + (int)(dat->row_heights[dat->selection] * .7); + hitFlags = dat->selection == -1 ? CLCHT_NOWHERE : CLCHT_ONITEMLABEL; + } else { + ScreenToClient(hwnd, &pt); + dat->selection = HitTest(hwnd, dat, pt.x, pt.y, &contact, NULL, &hitFlags); + } + InvalidateRect(hwnd, NULL, FALSE); + if (dat->selection != -1) + pcli->pfnEnsureVisible(hwnd, dat, dat->selection, 0); + UpdateWindow(hwnd); + + if (dat->selection != -1 && hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMCHECK | CLCHT_ONITEMLABEL)) { + if (contact->type == CLCIT_GROUP) { + HMENU hMenu = Menu_BuildSubGroupMenu(contact->group); + ClientToScreen(hwnd, &pt); + TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, pcli->hwndContactList, NULL); + CheckMenuItem(hMenu, POPUP_GROUPHIDEOFFLINE, contact->group->hideOffline ? MF_CHECKED : MF_UNCHECKED); + DestroyMenu(hMenu); + return 0; + } + else if (contact->type == CLCIT_CONTACT) + hMenu = Menu_BuildContactMenu(contact->hContact); + } else { + //call parent for new group/hide offline menu + PostMessage(GetParent(hwnd), WM_CONTEXTMENU, wParam, lParam); + return 0; + } + if (hMenu != NULL) { + ClientToScreen(hwnd, &pt); + TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL); + DestroyMenu(hMenu); + } + return 0; + } + case WM_COMMAND: + if (LOWORD(wParam) == POPUP_NEWGROUP) + SendMessage(GetParent(hwnd), msg, wParam, lParam); + break; + + case WM_MOUSEMOVE: { + int iOldHT = dat->iHotTrack; + LRESULT result = saveContactListControlWndProc(hwnd, msg, wParam, lParam); + if(dat->iHotTrack != iOldHT) + InvalidateRect(hwnd, 0, FALSE); + return(result); + } + + case WM_NCHITTEST: { + LRESULT lr = SendMessage(GetParent(hwnd), WM_NCHITTEST, wParam, lParam); + if (lr == HTLEFT || lr == HTRIGHT || lr == HTBOTTOM || lr == HTTOP || lr == HTTOPLEFT || lr == HTTOPRIGHT + || lr == HTBOTTOMLEFT || lr == HTBOTTOMRIGHT) + return HTTRANSPARENT; + break; + } + case WM_DESTROY: { + int i; + + if (!dat->bisEmbedded) { + for (i = 0; i < cfg::nextCacheEntry; i++) { + if (cfg::eCache[i].floater && cfg::eCache[i].floater->hwnd) + DestroyWindow(cfg::eCache[i].floater->hwnd); + } + } + RowHeight::Free(dat); + if(dat->ph) { + delete dat->ph; + dat->ph = 0; + } + break; + } + } + + { + LRESULT result = coreCli.pfnContactListControlWndProc(hwnd, msg, wParam, lParam); + return result; + } +} + +int CLC::findItem(HWND hwnd, ClcData *dat, HANDLE hItem, ClcContact **contact, ClcGroup **subgroup, int *isVisible) +{ + int index = 0; + int nowVisible = 1; + ClcGroup *group = &dat->list; + + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.count) { + ClcGroup *tgroup; + group = group->parent; + if (group == NULL) + break; + nowVisible = 1; + for (tgroup = group; tgroup; tgroup = tgroup->parent) { + if (!(group->expanded)) { + nowVisible = 0; break; + } + } + group->scanIndex++; + continue; + } + if (nowVisible) + index++; + if ((IsHContactGroup(hItem) && group->cl.items[group->scanIndex]->type == CLCIT_GROUP && ((UINT_PTR)hItem & ~HCONTACT_ISGROUP) == group->cl.items[group->scanIndex]->groupId) || + (IsHContactContact(hItem) && group->cl.items[group->scanIndex]->type == CLCIT_CONTACT && group->cl.items[group->scanIndex]->hContact == (MCONTACT)hItem) || + (IsHContactInfo(hItem) && group->cl.items[group->scanIndex]->type == CLCIT_INFO && group->cl.items[group->scanIndex]->hContact == (MCONTACT)((UINT_PTR)hItem & ~HCONTACT_ISINFO))) + { + if (isVisible) { + if (!nowVisible) + *isVisible = 0; + else { + int posy = RowHeight::getItemTopY(dat, index + 1); + if (posy < dat->yScroll) + *isVisible = 0; + //if ((index + 1) * dat->rowHeight< dat->yScroll) + // *isVisible = 0; + else { + RECT clRect; + GetClientRect(hwnd, &clRect); + //if (index * dat->rowHeight >= dat->yScroll + clRect.bottom) + if (posy >= dat->yScroll + clRect.bottom) + *isVisible = 0; + else + *isVisible = 1; + } + } + } + if (contact) + *contact = group->cl.items[group->scanIndex]; + if (subgroup) + *subgroup = group; + return 1; + } + if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + nowVisible &= (group->expanded); + continue; + } + group->scanIndex++; + } + + if (isVisible) *isVisible = FALSE; + if (contact) *contact = NULL; + if (subgroup) *subgroup = NULL; + return 0; +} + +void CLC::countAvatars(ClcData *dat) +{ + if(dat->bisEmbedded) + return; + + CLC::uNrAvatars = 0; + ClcGroup* group; + + group = &dat->list; + group->scanIndex = 0; + + while(TRUE) { + if (group->scanIndex==group->cl.count) + { + group=group->parent; + if(group==NULL) + break; // Finished list + group->scanIndex++; + continue; + } + + if(group->cl.items[group->scanIndex]->cFlags & ECF_AVATAR) + CLC::uNrAvatars++; + + if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && (group->cl.items[group->scanIndex]->group->expanded)) { + group=group->cl.items[group->scanIndex]->group; + group->scanIndex=0; + continue; + } + group->scanIndex++; + } +} -- cgit v1.2.3