/* * 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++; } }