ClcGroup* fnRemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) { int iContact; if ((iContact = List_IndexOf((SortedList*)&group->cl, contact)) == -1) return group; if (contact->type == CLCIT_CONTACT) { if (updateTotalCount) group->totalMembers--; ClcCacheEntry *p = cli.pfnGetCacheEntry(contact->hContact); if (p != NULL) replaceStrT(p->tszGroup, NULL); } cli.pfnFreeContact(group->cl.items[iContact]); mir_free(group->cl.items[iContact]); List_Remove((SortedList*)&group->cl, iContact); if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.count == 0 && group->parent != NULL) for (int i=0; i < group->parent->cl.count; i++) if (group->parent->cl.items[i]->type == CLCIT_GROUP && group->parent->cl.items[i]->groupId == group->groupId) return cli.pfnRemoveItemFromGroup(hwnd, group->parent, group->parent->cl.items[i], 0); return group; }
int RestoreSelection(ClcData *dat, MCONTACT hSelected) { ClcContact *selcontact = NULL; ClcGroup *selgroup = NULL; if (!hSelected || !pcli->pfnFindItem(dat->hWnd, dat, hSelected, &selcontact, &selgroup, NULL)) { dat->selection = -1; return dat->selection; } if (!selcontact->isSubcontact) dat->selection = pcli->pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); else { dat->selection = pcli->pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact->subcontacts)); if (dat->selection != -1) dat->selection += selcontact->isSubcontact; } return dat->selection; }
void fnSortCLC(HWND hwnd, struct ClcData *dat, int useInsertionSort) { ClcContact *selcontact; ClcGroup *group = &dat->list, *selgroup; MCONTACT hSelItem; if (dat->needsResort) { if (cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) == -1) hSelItem = NULL; else hSelItem = (UINT_PTR)cli.pfnContactToHItem(selcontact); group->scanIndex = 0; SortGroup(dat, group, useInsertionSort); for (;;) { if (group->scanIndex == group->cl.count) { group = group->parent; if (group == NULL) break; } else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { group = group->cl.items[group->scanIndex]->group; group->scanIndex = 0; SortGroup(dat, group, useInsertionSort); continue; } group->scanIndex++; } if (hSelItem) if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL)) dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); cli.pfnRecalcScrollBar(hwnd, dat); } dat->needsResort = 0; cli.pfnInvalidateRect(hwnd, NULL, FALSE); }
LRESULT CALLBACK fnContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { ClcGroup *group; ClcContact *contact; DWORD hitFlags; int hit; ClcData *dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0); if (msg >= CLM_FIRST && msg < CLM_LAST) return cli.pfnProcessExternalMessages(hwnd, dat, msg, wParam, lParam); switch (msg) { case WM_CREATE: WindowList_Add(hClcWindowList, hwnd, NULL); cli.pfnRegisterFileDropping(hwnd); if (dat == NULL) { dat = (struct ClcData *) mir_calloc(sizeof(struct ClcData)); SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); } { for (int i = 0; i <= FONTID_MAX; i++) dat->fontInfo[i].changed = 1; } dat->selection = -1; dat->iconXSpace = 20; dat->checkboxSize = 13; dat->dragAutoScrollHeight = 30; dat->iDragItem = -1; dat->iInsertionMark = -1; dat->insertionMarkHitHeight = 5; dat->iHotTrack = -1; dat->infoTipTimeout = db_get_w(NULL, "CLC", "InfoTipHoverTime", 750); dat->extraColumnSpacing = 20; dat->list.cl.increment = 30; dat->needsResort = 1; cli.pfnLoadClcOptions(hwnd, dat, TRUE); if (!IsWindowVisible(hwnd)) SetTimer(hwnd, TIMERID_REBUILDAFTER, 10, NULL); else { cli.pfnRebuildEntireList(hwnd, dat); NMCLISTCONTROL nm; nm.hdr.code = CLN_LISTREBUILT; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); } break; case INTM_SCROLLBARCHANGED: if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CONTACTLIST) { if (dat->noVScrollbar) ShowScrollBar(hwnd, SB_VERT, FALSE); else cli.pfnRecalcScrollBar(hwnd, dat); } break; case INTM_RELOADOPTIONS: cli.pfnLoadClcOptions(hwnd, dat, FALSE); cli.pfnSaveStateAndRebuildList(hwnd, dat); break; case WM_THEMECHANGED: cli.pfnInvalidateRect(hwnd, NULL, FALSE); break; case WM_SIZE: cli.pfnEndRename(hwnd, dat, 1); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); cli.pfnRecalcScrollBar(hwnd, dat); { // creating imagelist containing blue line for highlight RECT rc; GetClientRect(hwnd, &rc); if (rc.right == 0) break; rc.bottom = dat->rowHeight; HDC hdc = GetDC(hwnd); int depth = GetDeviceCaps(hdc, BITSPIXEL); if (depth < 16) depth = 16; HBITMAP hBmp = CreateBitmap(rc.right, rc.bottom, 1, depth, NULL); HBITMAP hBmpMask = CreateBitmap(rc.right, rc.bottom, 1, 1, NULL); HDC hdcMem = CreateCompatibleDC(hdc); HBITMAP hoBmp = (HBITMAP)SelectObject(hdcMem, hBmp); HBRUSH hBrush = CreateSolidBrush(dat->useWindowsColours ? GetSysColor(COLOR_HIGHLIGHT) : dat->selBkColour); FillRect(hdcMem, &rc, hBrush); DeleteObject(hBrush); HBITMAP hoMaskBmp = (HBITMAP)SelectObject(hdcMem, hBmpMask); FillRect(hdcMem, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); SelectObject(hdcMem, hoMaskBmp); SelectObject(hdcMem, hoBmp); DeleteDC(hdcMem); ReleaseDC(hwnd, hdc); if (dat->himlHighlight) ImageList_Destroy(dat->himlHighlight); dat->himlHighlight = ImageList_Create(rc.right, rc.bottom, ILC_COLOR32 | ILC_MASK, 1, 1); ImageList_Add(dat->himlHighlight, hBmp, hBmpMask); DeleteObject(hBmpMask); DeleteObject(hBmp); } break; case WM_SYSCOLORCHANGE: SendMessage(hwnd, WM_SIZE, 0, 0); break; case WM_GETDLGCODE: if (lParam) { MSG *msg = (MSG *)lParam; if (msg->message == WM_KEYDOWN) { if (msg->wParam == VK_TAB) return 0; if (msg->wParam == VK_ESCAPE && dat->hwndRenameEdit == NULL && dat->szQuickSearch[0] == 0) return 0; } if (msg->message == WM_CHAR) { if (msg->wParam == '\t') return 0; if (msg->wParam == 27 && dat->hwndRenameEdit == NULL && dat->szQuickSearch[0] == 0) return 0; } } return DLGC_WANTMESSAGE; case WM_KILLFOCUS: KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); case WM_SETFOCUS: case WM_ENABLE: cli.pfnInvalidateRect(hwnd, NULL, FALSE); break; case WM_GETFONT: return (LRESULT)dat->fontInfo[FONTID_CONTACTS].hFont; case INTM_GROUPSCHANGED: { DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *)lParam; if (dbcws->value.type == DBVT_ASCIIZ || dbcws->value.type == DBVT_UTF8) { int groupId = atoi(dbcws->szSetting) + 1; TCHAR szFullName[512]; int i, eq; //check name of group and ignore message if just being expanded/collapsed if (cli.pfnFindItem(hwnd, dat, groupId | HCONTACT_ISGROUP, &contact, &group, NULL)) { mir_tstrcpy(szFullName, contact->szText); while (group->parent) { for (i = 0; i < group->parent->cl.count; i++) if (group->parent->cl.items[i]->group == group) break; if (i == group->parent->cl.count) { szFullName[0] = '\0'; break; } group = group->parent; size_t nameLen = mir_tstrlen(group->cl.items[i]->szText); if (mir_tstrlen(szFullName) + 1 + nameLen > _countof(szFullName)) { szFullName[0] = '\0'; break; } memmove(szFullName + 1 + nameLen, szFullName, sizeof(TCHAR)*(mir_tstrlen(szFullName) + 1)); memcpy(szFullName, group->cl.items[i]->szText, sizeof(TCHAR)*nameLen); szFullName[nameLen] = '\\'; } if (dbcws->value.type == DBVT_ASCIIZ) { WCHAR* wszGrpName = mir_a2u(dbcws->value.pszVal + 1); eq = !mir_tstrcmp(szFullName, wszGrpName); mir_free(wszGrpName); } else { char* szGrpName = NEWSTR_ALLOCA(dbcws->value.pszVal + 1); WCHAR* wszGrpName; Utf8Decode(szGrpName, &wszGrpName); eq = !mir_tstrcmp(szFullName, wszGrpName); mir_free(wszGrpName); } if (eq && (contact->group->hideOffline != 0) == ((dbcws->value.pszVal[0] & GROUPF_HIDEOFFLINE) != 0)) break; //only expanded has changed: no action reqd } } cli.pfnSaveStateAndRebuildList(hwnd, dat); } break; case INTM_NAMEORDERCHANGED: cli.pfnInitAutoRebuild(hwnd); break; case INTM_CONTACTADDED: cli.pfnAddContactToTree(hwnd, dat, wParam, 1, 1); cli.pfnNotifyNewContact(hwnd, wParam); SortClcByTimer(hwnd); break; case INTM_CONTACTDELETED: cli.pfnDeleteItemFromTree(hwnd, wParam); SortClcByTimer(hwnd); break; case INTM_HIDDENCHANGED: { DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *)lParam; if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN) break; if (dbcws->value.type == DBVT_DELETED || dbcws->value.bVal == 0) { if (cli.pfnFindItem(hwnd, dat, wParam, NULL, NULL, NULL)) break; cli.pfnAddContactToTree(hwnd, dat, wParam, 1, 1); cli.pfnNotifyNewContact(hwnd, wParam); } else cli.pfnDeleteItemFromTree(hwnd, wParam); dat->needsResort = 1; SortClcByTimer(hwnd); } break; case INTM_GROUPCHANGED: { WORD iExtraImage[EXTRA_ICON_COUNT]; BYTE flags = 0; if (!cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) memset(iExtraImage, 0xFF, sizeof(iExtraImage)); else { memcpy(iExtraImage, contact->iExtraImage, sizeof(iExtraImage)); flags = contact->flags; } cli.pfnDeleteItemFromTree(hwnd, wParam); if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !db_get_b(wParam, "CList", "Hidden", 0)) { NMCLISTCONTROL nm; cli.pfnAddContactToTree(hwnd, dat, wParam, 1, 1); if (cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) { memcpy(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 = 1; } } SetTimer(hwnd, TIMERID_REBUILDAFTER, 1, NULL); break; case INTM_ICONCHANGED: { int recalcScrollBar = 0, shouldShow; WORD status; MCONTACT hSelItem = NULL; ClcContact *selcontact = NULL; char *szProto = GetContactProto(wParam); if (szProto == NULL) status = ID_STATUS_OFFLINE; else status = db_get_w(wParam, szProto, "Status", ID_STATUS_OFFLINE); // this means an offline msg is flashing, so the contact should be shown DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); shouldShow = (style & CLS_SHOWHIDDEN || !db_get_b(wParam, "CList", "Hidden", 0)) && (!cli.pfnIsHiddenMode(dat, status) || CallService(MS_CLIST_GETCONTACTICON, wParam, 0) != lParam); contact = NULL; group = NULL; if (!cli.pfnFindItem(hwnd, dat, wParam, &contact, &group, NULL)) { if (shouldShow && CallService(MS_DB_CONTACT_IS, wParam, 0)) { if (dat->selection >= 0 && cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1) hSelItem = (MCONTACT)cli.pfnContactToHItem(selcontact); cli.pfnAddContactToTree(hwnd, dat, wParam, (style & CLS_CONTACTLIST) == 0, 0); recalcScrollBar = 1; cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL); if (contact) { contact->iImage = (WORD)lParam; cli.pfnNotifyNewContact(hwnd, wParam); dat->needsResort = 1; } } } else { // item in list already if (contact->iImage == (WORD)lParam) break; if (!shouldShow && !(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { if (dat->selection >= 0 && cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) != -1) hSelItem = (MCONTACT)cli.pfnContactToHItem(selcontact); cli.pfnRemoveItemFromGroup(hwnd, group, contact, (style & CLS_CONTACTLIST) == 0); recalcScrollBar = 1; } else { contact->iImage = (WORD)lParam; if (!cli.pfnIsHiddenMode(dat, status)) contact->flags |= CONTACTF_ONLINE; else contact->flags &= ~CONTACTF_ONLINE; } dat->needsResort = 1; } if (hSelItem) { ClcGroup *selgroup; if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL)) dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); else dat->selection = -1; } SortClcByTimer(hwnd); } break; case INTM_NAMECHANGED: if (!cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) break; mir_tstrncpy(contact->szText, cli.pfnGetContactDisplayName(wParam, 0), _countof(contact->szText)); dat->needsResort = 1; SortClcByTimer(hwnd); break; case INTM_PROTOCHANGED: if (!cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) break; contact->proto = GetContactProto(wParam); cli.pfnInvalidateDisplayNameCacheEntry(wParam); mir_tstrncpy(contact->szText, cli.pfnGetContactDisplayName(wParam, 0), _countof(contact->szText)); SortClcByTimer(hwnd); break; case INTM_NOTONLISTCHANGED: if (!cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) break; if (contact->type == CLCIT_CONTACT) { DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING *)lParam; if (dbcws->value.type == DBVT_DELETED || dbcws->value.bVal == 0) contact->flags &= ~CONTACTF_NOTONLIST; else contact->flags |= CONTACTF_NOTONLIST; cli.pfnInvalidateRect(hwnd, NULL, FALSE); } break; case INTM_INVALIDATE: cli.pfnInvalidateRect(hwnd, NULL, FALSE); break; case INTM_APPARENTMODECHANGED: if (cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) { char *szProto = GetContactProto(wParam); if (szProto == NULL) break; WORD apparentMode = db_get_w(wParam, szProto, "ApparentMode", 0); contact->flags &= ~(CONTACTF_INVISTO | CONTACTF_VISTO); if (apparentMode == ID_STATUS_OFFLINE) contact->flags |= CONTACTF_INVISTO; else if (apparentMode == ID_STATUS_ONLINE) contact->flags |= CONTACTF_VISTO; else if (apparentMode) contact->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; cli.pfnInvalidateRect(hwnd, NULL, FALSE); } break; case INTM_SETINFOTIPHOVERTIME: dat->infoTipTimeout = wParam; break; case INTM_IDLECHANGED: if (cli.pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) { char *szProto = GetContactProto(wParam); if (szProto == NULL) break; contact->flags &= ~CONTACTF_IDLE; if (db_get_dw(wParam, szProto, "IdleTS", 0)) contact->flags |= CONTACTF_IDLE; cli.pfnInvalidateRect(hwnd, NULL, FALSE); } break; case WM_PRINTCLIENT: cli.pfnPaintClc(hwnd, dat, (HDC)wParam, NULL); break; case WM_NCPAINT: if (wParam != 1) { POINT ptTopLeft = { 0, 0 }; HRGN hClientRgn; ClientToScreen(hwnd, &ptTopLeft); hClientRgn = CreateRectRgn(0, 0, 1, 1); CombineRgn(hClientRgn, (HRGN)wParam, NULL, RGN_COPY); OffsetRgn(hClientRgn, -ptTopLeft.x, -ptTopLeft.y); InvalidateRgn(hwnd, hClientRgn, FALSE); DeleteObject(hClientRgn); UpdateWindow(hwnd); } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); /* we get so many cli.pfnInvalidateRect()'s that there is no point painting, Windows in theory shouldn't queue up WM_PAINTs in this case but it does so we'll just ignore them */ if (IsWindowVisible(hwnd)) cli.pfnPaintClc(hwnd, dat, hdc, &ps.rcPaint); EndPaint(hwnd, &ps); } break; case WM_VSCROLL: cli.pfnEndRename(hwnd, dat, 1); cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); { int desty = dat->yScroll, noSmooth = 0; RECT clRect; GetClientRect(hwnd, &clRect); switch (LOWORD(wParam)) { case SB_LINEUP: desty -= dat->rowHeight; break; case SB_LINEDOWN: desty += dat->rowHeight; break; case SB_PAGEUP: desty -= clRect.bottom - dat->rowHeight; break; case SB_PAGEDOWN: desty += clRect.bottom - dat->rowHeight; break; case SB_BOTTOM: desty = 0x7FFFFFFF; break; case SB_TOP: desty = 0; break; case SB_THUMBTRACK: desty = HIWORD(wParam); noSmooth = 1; break; //noone has more than 4000 contacts, right? default: return 0; } cli.pfnScrollTo(hwnd, dat, desty, noSmooth); } break; case WM_MOUSEWHEEL: cli.pfnEndRename(hwnd, dat, 1); cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); { UINT scrollLines; if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, FALSE)) scrollLines = 3; cli.pfnScrollTo(hwnd, dat, dat->yScroll - (short)HIWORD(wParam) * dat->rowHeight * (signed)scrollLines / WHEEL_DELTA, 0); } return 0; case WM_KEYDOWN: { int selMoved = 0; int changeGroupExpand = 0; int pageSize; cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); if (CallService(MS_CLIST_MENUPROCESSHOTKEY, wParam, MPCF_CONTACTMENU)) break; { RECT clRect; GetClientRect(hwnd, &clRect); pageSize = clRect.bottom / dat->rowHeight; } switch (wParam) { case VK_DOWN: dat->selection++; selMoved = 1; break; case VK_UP: dat->selection--; selMoved = 1; break; case VK_PRIOR: dat->selection -= pageSize; selMoved = 1; break; case VK_NEXT: dat->selection += pageSize; selMoved = 1; break; case VK_HOME: dat->selection = 0; selMoved = 1; break; case VK_END: dat->selection = cli.pfnGetGroupContentsCount(&dat->list, 1) - 1; selMoved = 1; break; case VK_LEFT: changeGroupExpand = 1; break; case VK_RIGHT: changeGroupExpand = 2; break; case VK_RETURN: cli.pfnDoSelectionDefaultAction(hwnd, dat); dat->szQuickSearch[0] = 0; if (dat->filterSearch) cli.pfnSaveStateAndRebuildList(hwnd, dat); return 0; case VK_F2: cli.pfnBeginRenameSelection(hwnd, dat); return 0; case VK_DELETE: cli.pfnDeleteFromContactList(hwnd, dat); return 0; default: { NMKEY nmkey; nmkey.hdr.hwndFrom = hwnd; nmkey.hdr.idFrom = GetDlgCtrlID(hwnd); nmkey.hdr.code = NM_KEYDOWN; nmkey.nVKey = wParam; nmkey.uFlags = HIWORD(lParam); if (SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nmkey)) return 0; } } if (changeGroupExpand) { if (!dat->filterSearch) dat->szQuickSearch[0] = 0; hit = cli.pfnGetRowByIndex(dat, dat->selection, &contact, &group); if (hit != -1) { if (changeGroupExpand == 1 && contact->type == CLCIT_CONTACT) { if (group == &dat->list) return 0; dat->selection = cli.pfnGetRowsPriorTo(&dat->list, group, -1); selMoved = 1; } else { if (contact->type == CLCIT_GROUP) cli.pfnSetGroupExpand(hwnd, dat, contact->group, changeGroupExpand == 2); return 0; } } else return 0; } if (selMoved) { if (!dat->filterSearch) dat->szQuickSearch[0] = 0; if (dat->selection >= cli.pfnGetGroupContentsCount(&dat->list, 1)) dat->selection = cli.pfnGetGroupContentsCount(&dat->list, 1) - 1; if (dat->selection < 0) dat->selection = 0; cli.pfnInvalidateRect(hwnd, NULL, FALSE); cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0); UpdateWindow(hwnd); return 0; } } break; case WM_CHAR: cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); if (wParam == 27) //escape dat->szQuickSearch[0] = 0; else if (wParam == '\b' && dat->szQuickSearch[0]) dat->szQuickSearch[mir_tstrlen(dat->szQuickSearch) - 1] = '\0'; else if (wParam < ' ') break; else if (wParam == ' ' && dat->szQuickSearch[0] == '\0' && GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_CHECKBOXES) { NMCLISTCONTROL nm; if (cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL) == -1) break; if (contact->type != CLCIT_CONTACT) break; contact->flags ^= CONTACTF_CHECKED; if (contact->type == CLCIT_GROUP) cli.pfnSetGroupChildCheckboxes(contact->group, contact->flags & CONTACTF_CHECKED); cli.pfnRecalculateGroupCheckboxes(hwnd, dat); cli.pfnInvalidateRect(hwnd, NULL, FALSE); nm.hdr.code = CLN_CHECKCHANGED; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); } else { TCHAR szNew[2]; szNew[0] = (TCHAR)wParam; szNew[1] = '\0'; if (mir_tstrlen(dat->szQuickSearch) >= _countof(dat->szQuickSearch) - 1) { MessageBeep(MB_OK); break; } mir_tstrcat(dat->szQuickSearch, szNew); } if (dat->filterSearch) cli.pfnSaveStateAndRebuildList(hwnd, dat); if (dat->szQuickSearch[0]) { int index; index = cli.pfnFindRowByText(hwnd, dat, dat->szQuickSearch, 1); if (index != -1) dat->selection = index; else { MessageBeep(MB_OK); dat->szQuickSearch[mir_tstrlen(dat->szQuickSearch) - 1] = '\0'; cli.pfnSaveStateAndRebuildList(hwnd, dat); } cli.pfnInvalidateRect(hwnd, NULL, FALSE); cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0); } else cli.pfnInvalidateRect(hwnd, NULL, FALSE); break; case WM_SYSKEYDOWN: cli.pfnEndRename(hwnd, dat, 1); cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); dat->iHotTrack = -1; cli.pfnInvalidateRect(hwnd, NULL, FALSE); ReleaseCapture(); if (wParam == VK_F10 && GetKeyState(VK_SHIFT) & 0x8000) break; SendMessage(GetParent(hwnd), msg, wParam, lParam); return 0; case WM_TIMER: switch (wParam) { case TIMERID_RENAME: cli.pfnBeginRenameSelection(hwnd, dat); break; case TIMERID_DRAGAUTOSCROLL: cli.pfnScrollTo(hwnd, dat, dat->yScroll + dat->dragAutoScrolling * dat->rowHeight * 2, 0); break; case TIMERID_INFOTIP: { CLCINFOTIP it; RECT clRect; POINT ptClientOffset = { 0 }; KillTimer(hwnd, wParam); GetCursorPos(&it.ptCursor); ScreenToClient(hwnd, &it.ptCursor); if (it.ptCursor.x != dat->ptInfoTip.x || it.ptCursor.y != dat->ptInfoTip.y) break; GetClientRect(hwnd, &clRect); it.rcItem.left = 0; it.rcItem.right = clRect.right; hit = cli.pfnHitTest(hwnd, dat, it.ptCursor.x, it.ptCursor.y, &contact, NULL, NULL); if (hit == -1) break; if (contact->type != CLCIT_GROUP && contact->type != CLCIT_CONTACT) break; ClientToScreen(hwnd, &it.ptCursor); ClientToScreen(hwnd, &ptClientOffset); it.isTreeFocused = GetFocus() == hwnd; it.rcItem.top = cli.pfnGetRowTopY(dat, hit) - dat->yScroll; it.rcItem.bottom = it.rcItem.top + cli.pfnGetRowHeight(dat, hit); OffsetRect(&it.rcItem, ptClientOffset.x, ptClientOffset.y); it.isGroup = contact->type == CLCIT_GROUP; it.hItem = (contact->type == CLCIT_GROUP) ? (HANDLE)contact->groupId : (HANDLE)contact->hContact; it.cbSize = sizeof(it); dat->hInfoTipItem = cli.pfnContactToHItem(contact); NotifyEventHooks(hShowInfoTipEvent, 0, (LPARAM)& it); break; } case TIMERID_REBUILDAFTER: KillTimer(hwnd, TIMERID_REBUILDAFTER); cli.pfnInvalidateRect(hwnd, NULL, FALSE); cli.pfnSaveStateAndRebuildList(hwnd, dat); break; case TIMERID_DELAYEDRESORTCLC: KillTimer(hwnd, TIMERID_DELAYEDRESORTCLC); cli.pfnInvalidateRect(hwnd, NULL, FALSE); cli.pfnSortCLC(hwnd, dat, 1); cli.pfnRecalcScrollBar(hwnd, dat); break; } break; case WM_MBUTTONDOWN: case WM_LBUTTONDOWN: if (GetFocus() != hwnd) SetFocus(hwnd); cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_INFOTIP); KillTimer(hwnd, TIMERID_RENAME); cli.pfnEndRename(hwnd, dat, 1); dat->ptDragStart.x = (short)LOWORD(lParam); dat->ptDragStart.y = (short)HIWORD(lParam); if (!dat->filterSearch) dat->szQuickSearch[0] = 0; hit = cli.pfnHitTest(hwnd, dat, (short)LOWORD(lParam), (short)HIWORD(lParam), &contact, &group, &hitFlags); if (hit != -1) { if (hit == dat->selection && hitFlags & CLCHT_ONITEMLABEL && dat->exStyle & CLS_EX_EDITLABELS) { SetCapture(hwnd); dat->iDragItem = dat->selection; dat->dragStage = DRAGSTAGE_NOTMOVED | DRAGSTAGEF_MAYBERENAME; dat->dragAutoScrolling = 0; break; } } if (hit != -1 && contact->type == CLCIT_GROUP) { if (hitFlags & CLCHT_ONITEMICON) { ClcGroup *selgroup; ClcContact *selcontact; dat->selection = cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, &selgroup); cli.pfnSetGroupExpand(hwnd, dat, contact->group, -1); if (dat->selection != -1) { dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); if (dat->selection == -1) dat->selection = cli.pfnGetRowsPriorTo(&dat->list, contact->group, -1); } cli.pfnInvalidateRect(hwnd, NULL, FALSE); UpdateWindow(hwnd); break; } } if (hit != -1 && hitFlags & CLCHT_ONITEMCHECK) { NMCLISTCONTROL nm; contact->flags ^= CONTACTF_CHECKED; if (contact->type == CLCIT_GROUP) cli.pfnSetGroupChildCheckboxes(contact->group, contact->flags & CONTACTF_CHECKED); cli.pfnRecalculateGroupCheckboxes(hwnd, dat); cli.pfnInvalidateRect(hwnd, NULL, FALSE); nm.hdr.code = CLN_CHECKCHANGED; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&nm); } if (!(hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMLABEL | CLCHT_ONITEMCHECK))) { NMCLISTCONTROL nm; nm.hdr.code = NM_CLICK; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; if (hit == -1) nm.hItem = NULL; else nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); nm.iColumn = hitFlags & CLCHT_ONITEMEXTRA ? HIBYTE(HIWORD(hitFlags)) : -1; nm.pt = dat->ptDragStart; SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); } if (hitFlags & (CLCHT_ONITEMCHECK | CLCHT_ONITEMEXTRA)) break; dat->selection = hit; cli.pfnInvalidateRect(hwnd, NULL, FALSE); if (dat->selection != -1) cli.pfnEnsureVisible(hwnd, dat, hit, 0); UpdateWindow(hwnd); if (dat->selection != -1 && (contact->type == CLCIT_CONTACT || contact->type == CLCIT_GROUP) && !(hitFlags & (CLCHT_ONITEMEXTRA | CLCHT_ONITEMCHECK))) { SetCapture(hwnd); dat->iDragItem = dat->selection; dat->dragStage = DRAGSTAGE_NOTMOVED; dat->dragAutoScrolling = 0; } break; case WM_MOUSEMOVE: if (dat->iDragItem == -1) { int iOldHotTrack = dat->iHotTrack; if (dat->hwndRenameEdit != NULL) break; if (GetKeyState(VK_MENU) & 0x8000 || GetKeyState(VK_F10) & 0x8000) break; dat->iHotTrack = cli.pfnHitTest(hwnd, dat, (short)LOWORD(lParam), (short)HIWORD(lParam), NULL, NULL, NULL); if (iOldHotTrack != dat->iHotTrack) { if (iOldHotTrack == -1) SetCapture(hwnd); else if (dat->iHotTrack == -1) ReleaseCapture(); if (dat->exStyle & CLS_EX_TRACKSELECT) { cli.pfnInvalidateItem(hwnd, dat, iOldHotTrack); cli.pfnInvalidateItem(hwnd, dat, dat->iHotTrack); } cli.pfnHideInfoTip(hwnd, dat); } KillTimer(hwnd, TIMERID_INFOTIP); if (wParam == 0 && dat->hInfoTipItem == NULL) { dat->ptInfoTip.x = (short)LOWORD(lParam); dat->ptInfoTip.y = (short)HIWORD(lParam); SetTimer(hwnd, TIMERID_INFOTIP, dat->infoTipTimeout, NULL); } break; } if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_NOTMOVED && !(dat->exStyle & CLS_EX_DISABLEDRAGDROP)) { if (abs((short)LOWORD(lParam) - dat->ptDragStart.x) >= GetSystemMetrics(SM_CXDRAG) || abs((short)HIWORD(lParam) - dat->ptDragStart.y) >= GetSystemMetrics(SM_CYDRAG)) dat->dragStage = (dat->dragStage & ~DRAGSTAGEM_STAGE) | DRAGSTAGE_ACTIVE; } if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_ACTIVE) { HCURSOR hNewCursor; RECT clRect; POINT pt; int target; GetClientRect(hwnd, &clRect); pt.x = (short)LOWORD(lParam); pt.y = (short)HIWORD(lParam); hNewCursor = LoadCursor(NULL, IDC_NO); cli.pfnInvalidateRect(hwnd, NULL, FALSE); if (dat->dragAutoScrolling) { KillTimer(hwnd, TIMERID_DRAGAUTOSCROLL); dat->dragAutoScrolling = 0; } target = cli.pfnGetDropTargetInformation(hwnd, dat, pt); if (dat->dragStage & DRAGSTAGEF_OUTSIDE && target != DROPTARGET_OUTSIDE) { cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL); NMCLISTCONTROL nm; nm.hdr.code = CLN_DRAGSTOP; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); dat->dragStage &= ~DRAGSTAGEF_OUTSIDE; } switch (target) { case DROPTARGET_ONSELF: case DROPTARGET_ONCONTACT: break; case DROPTARGET_ONGROUP: hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER)); break; case DROPTARGET_INSERTION: hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER)); break; case DROPTARGET_OUTSIDE: { NMCLISTCONTROL nm; if (pt.x >= 0 && pt.x < clRect.right && ((pt.y < 0 && pt.y > -dat->dragAutoScrollHeight) || (pt.y >= clRect.bottom && pt.y < clRect.bottom + dat->dragAutoScrollHeight))) { if (!dat->dragAutoScrolling) { if (pt.y < 0) dat->dragAutoScrolling = -1; else dat->dragAutoScrolling = 1; SetTimer(hwnd, TIMERID_DRAGAUTOSCROLL, dat->scrollTime, NULL); } SendMessage(hwnd, WM_TIMER, TIMERID_DRAGAUTOSCROLL, 0); } dat->dragStage |= DRAGSTAGEF_OUTSIDE; cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL); nm.hdr.code = CLN_DRAGGING; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); nm.pt = pt; if (SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm)) return 0; } break; default: cli.pfnGetRowByIndex(dat, dat->iDragItem, NULL, &group); if (group->parent) hNewCursor = LoadCursor(cli.hInst, MAKEINTRESOURCE(IDC_DROPUSER)); break; } SetCursor(hNewCursor); } break; case WM_LBUTTONUP: if (dat->iDragItem == -1) break; SetCursor((HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR)); if (dat->exStyle & CLS_EX_TRACKSELECT) { dat->iHotTrack = cli.pfnHitTest(hwnd, dat, (short)LOWORD(lParam), (short)HIWORD(lParam), NULL, NULL, NULL); if (dat->iHotTrack == -1) ReleaseCapture(); } else ReleaseCapture(); KillTimer(hwnd, TIMERID_DRAGAUTOSCROLL); if (dat->dragStage == (DRAGSTAGE_NOTMOVED | DRAGSTAGEF_MAYBERENAME)) SetTimer(hwnd, TIMERID_RENAME, GetDoubleClickTime(), NULL); else if ((dat->dragStage & DRAGSTAGEM_STAGE) == DRAGSTAGE_ACTIVE) { POINT pt = { LOWORD(lParam), HIWORD(lParam) }; int target = cli.pfnGetDropTargetInformation(hwnd, dat, pt); switch (target) { case DROPTARGET_ONSELF: case DROPTARGET_ONCONTACT: break; case DROPTARGET_ONGROUP: { ClcContact *contactn, *contacto; cli.pfnGetRowByIndex(dat, dat->selection, &contactn, NULL); cli.pfnGetRowByIndex(dat, dat->iDragItem, &contacto, NULL); if (contacto->type == CLCIT_CONTACT) //dropee is a contact CallService(MS_CLIST_CONTACTCHANGEGROUP, (WPARAM)contacto->hContact, contactn->groupId); else if (contacto->type == CLCIT_GROUP) { //dropee is a group TCHAR szNewName[120]; mir_sntprintf(szNewName, _countof(szNewName), _T("%s\\%s"), cli.pfnGetGroupName(contactn->groupId, NULL), contacto->szText); cli.pfnRenameGroup(contacto->groupId, szNewName); } } break; case DROPTARGET_INSERTION: cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL); { ClcContact *destcontact; ClcGroup *destgroup; if (cli.pfnGetRowByIndex(dat, dat->iInsertionMark, &destcontact, &destgroup) == -1 || destgroup != contact->group->parent) CallService(MS_CLIST_GROUPMOVEBEFORE, contact->groupId, 0); else { if (destcontact->type == CLCIT_GROUP) destgroup = destcontact->group; CallService(MS_CLIST_GROUPMOVEBEFORE, contact->groupId, destgroup->groupId); } } break; case DROPTARGET_OUTSIDE: cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, NULL); { NMCLISTCONTROL nm; nm.hdr.code = CLN_DROPPED; nm.hdr.hwndFrom = hwnd; nm.hdr.idFrom = GetDlgCtrlID(hwnd); nm.flags = 0; nm.hItem = cli.pfnContactToItemHandle(contact, &nm.flags); nm.pt = pt; SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); } break; default: cli.pfnGetRowByIndex(dat, dat->iDragItem, &contact, &group); if (!group->parent) break; if (contact->type == CLCIT_GROUP) { //dropee is a group TCHAR szNewName[120]; mir_tstrncpy(szNewName, contact->szText, _countof(szNewName)); cli.pfnRenameGroup(contact->groupId, szNewName); } else if (contact->type == CLCIT_CONTACT) //dropee is a contact CallService(MS_CLIST_CONTACTCHANGEGROUP, (WPARAM)contact->hContact, 0); } } cli.pfnInvalidateRect(hwnd, NULL, FALSE); dat->iDragItem = -1; dat->iInsertionMark = -1; break; case WM_LBUTTONDBLCLK: ReleaseCapture(); dat->iHotTrack = -1; cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_RENAME); KillTimer(hwnd, TIMERID_INFOTIP); dat->selection = cli.pfnHitTest(hwnd, dat, (short)LOWORD(lParam), (short)HIWORD(lParam), &contact, NULL, &hitFlags); cli.pfnInvalidateRect(hwnd, NULL, FALSE); if (dat->selection != -1) cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0); if (!(hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMLABEL))) break; UpdateWindow(hwnd); cli.pfnDoSelectionDefaultAction(hwnd, dat); dat->szQuickSearch[0] = 0; if (dat->filterSearch) cli.pfnSaveStateAndRebuildList(hwnd, dat); break; case WM_CONTEXTMENU: cli.pfnEndRename(hwnd, dat, 1); cli.pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_RENAME); KillTimer(hwnd, TIMERID_INFOTIP); if (GetFocus() != hwnd) SetFocus(hwnd); dat->iHotTrack = -1; if (!dat->filterSearch) dat->szQuickSearch[0] = 0; { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (pt.x == -1 && pt.y == -1) { dat->selection = cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL); if (dat->selection != -1) cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0); pt.x = dat->iconXSpace + 15; pt.y = cli.pfnGetRowTopY(dat, dat->selection) - dat->yScroll + (int)(cli.pfnGetRowHeight(dat, dat->selection) * .7); hitFlags = (dat->selection == -1) ? CLCHT_NOWHERE : CLCHT_ONITEMLABEL; } else { ScreenToClient(hwnd, &pt); dat->selection = cli.pfnHitTest(hwnd, dat, pt.x, pt.y, &contact, NULL, &hitFlags); } cli.pfnInvalidateRect(hwnd, NULL, FALSE); if (dat->selection != -1) cli.pfnEnsureVisible(hwnd, dat, dat->selection, 0); UpdateWindow(hwnd); if (dat->selection != -1 && hitFlags & (CLCHT_ONITEMICON | CLCHT_ONITEMCHECK | CLCHT_ONITEMLABEL)) { HMENU hMenu; if (contact->type == CLCIT_GROUP) hMenu = cli.pfnBuildGroupPopupMenu(contact->group); else if (contact->type == CLCIT_CONTACT) hMenu = Menu_BuildContactMenu(contact->hContact); else return 0; ClientToScreen(hwnd, &pt); TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL); DestroyMenu(hMenu); } else //call parent for new group/hide offline menu SendMessage(GetParent(hwnd), WM_CONTEXTMENU, wParam, lParam); } return 0; case WM_MEASUREITEM: return Menu_MeasureItem((LPMEASUREITEMSTRUCT)lParam); case WM_DRAWITEM: return Menu_DrawItem((LPDRAWITEMSTRUCT)lParam); case WM_COMMAND: hit = cli.pfnGetRowByIndex(dat, dat->selection, &contact, NULL); if (hit == -1) break; if (contact->type == CLCIT_CONTACT) if (CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)contact->hContact)) break; switch (LOWORD(wParam)) { case POPUP_NEWSUBGROUP: if (contact->type != CLCIT_GROUP) break; SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~CLS_HIDEEMPTYGROUPS); CallService(MS_CLIST_GROUPCREATE, contact->groupId, 0); break; case POPUP_RENAMEGROUP: cli.pfnBeginRenameSelection(hwnd, dat); break; case POPUP_DELETEGROUP: if (contact->type != CLCIT_GROUP) break; CallService(MS_CLIST_GROUPDELETE, contact->groupId, 0); break; case POPUP_GROUPHIDEOFFLINE: if (contact->type != CLCIT_GROUP) break; CallService(MS_CLIST_GROUPSETFLAGS, contact->groupId, MAKELPARAM(contact->group->hideOffline ? 0 : GROUPF_HIDEOFFLINE, GROUPF_HIDEOFFLINE)); break; } break; case WM_DESTROY: cli.pfnHideInfoTip(hwnd, dat); for (int i = 0; i <= FONTID_MAX; i++) if (!dat->fontInfo[i].changed) DeleteObject(dat->fontInfo[i].hFont); if (dat->himlHighlight) ImageList_Destroy(dat->himlHighlight); if (dat->hwndRenameEdit) DestroyWindow(dat->hwndRenameEdit); if (dat->hBmpBackground) DeleteObject(dat->hBmpBackground); cli.pfnFreeGroup(&dat->list); mir_free(dat); cli.pfnUnregisterFileDropping(hwnd); WindowList_Remove(hClcWindowList, hwnd); } return DefWindowProc(hwnd, msg, wParam, lParam); }
LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { ClcContact *contact; ClcGroup *group; BOOL frameHasTitlebar = FALSE; if (wndFrameCLC) frameHasTitlebar = wndFrameCLC->TitleBar.ShowTitleBar; ClcData *dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0); if (msg >= CLM_FIRST && msg < CLM_LAST) return ProcessExternalMessages(hwnd, dat, msg, wParam, lParam); switch (msg) { case WM_CREATE: dat = (struct ClcData *)mir_alloc(sizeof(struct ClcData)); memset(dat, 0, sizeof(struct ClcData)); SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); RowHeight::Init(dat); dat->forceScroll = 0; dat->lastRepaint = 0; dat->hwndParent = GetParent(hwnd); dat->lastSort = GetTickCount(); dat->needsResort = FALSE; { 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_GROUPCHANGED: { WORD iExtraImage[EXTRA_ICON_COUNT]; BYTE flags = 0; if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) memset(iExtraImage, 0xFF, sizeof(iExtraImage)); else { memcpy(iExtraImage, contact->iExtraImage, sizeof(iExtraImage)); flags = contact->flags; } pcli->pfnDeleteItemFromTree(hwnd, wParam); if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !CLVM_GetContactHiddenStatus(wParam, NULL, dat)) { pcli->pfnAddContactToTree(hwnd, dat, wParam, 1, 1); if (FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) { memcpy(contact->iExtraImage, iExtraImage, sizeof(iExtraImage)); if (flags & CONTACTF_CHECKED) contact->flags |= CONTACTF_CHECKED; } NMCLISTCONTROL nm; 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: { int recalcScrollBar = 0; MCONTACT hContact = wParam; WORD status = ID_STATUS_OFFLINE; int contactRemoved = 0; MCONTACT hSelItem = NULL; ClcContact *selcontact = NULL; char *szProto = GetContactProto(hContact); if (szProto == NULL) status = ID_STATUS_OFFLINE; else status = cfg::getWord(hContact, szProto, "Status", ID_STATUS_OFFLINE); int shouldShow = (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SHOWHIDDEN || !CLVM_GetContactHiddenStatus(hContact, szProto, dat)) && ((cfg::dat.bFilterEffective ? TRUE : !pcli->pfnIsHiddenMode(dat, status)) || pcli->pfnGetContactIcon(hContact) != lParam); // XXX CLVM changed - this means an offline msg is flashing, so the contact should be shown if (!FindItem(hwnd, dat, (HANDLE)hContact, &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, hContact, 0, 0); recalcScrollBar = 1; FindItem(hwnd, dat, (HANDLE)hContact, &contact, NULL, NULL); if (contact) { contact->iImage = (WORD)lParam; pcli->pfnNotifyNewContact(hwnd, hContact); } } } else { //item in list already DWORD style = GetWindowLongPtr(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) { 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: if (!pcli->pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) break; if (contact->bIsMeta && !(cfg::dat.dwFlags & CLUI_USEMETAICONS)) { contact->hSubContact = db_mc_getMostOnline(contact->hContact); contact->metaProto = GetContactProto(contact->hSubContact); contact->iImage = pcli->pfnGetContactIcon(contact->hSubContact); if (contact->pExtra) { TExtraCache *pSub = cfg::getCache(contact->hSubContact, contact->metaProto); ClcContact *subContact; if (!pcli->pfnFindItem(hwnd, dat, contact->hSubContact, &subContact, NULL, NULL)) break; contact->pExtra->proto_status_item = GetProtocolStatusItem(contact->metaProto); if (pSub) { contact->pExtra->status_item = pSub->status_item; memcpy(contact->iExtraImage, subContact->iExtraImage, sizeof(contact->iExtraImage)); } } } SendMessage(hwnd, INTM_NAMEORDERCHANGED, wParam, lParam); goto LBL_Def; case INTM_METACHANGEDEVENT: if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) break; if (lParam == 0) pcli->pfnInitAutoRebuild(hwnd); goto LBL_Def; case INTM_NAMECHANGED: ClcContact *contact; if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) break; lstrcpyn(contact->szText, pcli->pfnGetContactDisplayName(wParam, 0), SIZEOF(contact->szText)); RTL_DetectAndSet(contact, 0); dat->needsResort = TRUE; PostMessage(hwnd, INTM_SORTCLC, 0, 0); goto LBL_Def; case INTM_CODEPAGECHANGED: if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) break; contact->codePage = cfg::getDword(wParam, "Tab_SRMsg", "ANSIcodepage", cfg::getDword(wParam, "UserInfo", "ANSIcodepage", CP_ACP)); PostMessage(hwnd, INTM_INVALIDATE, 0, 0); goto LBL_Def; case INTM_AVATARCHANGED: { avatarCacheEntry *cEntry = (struct avatarCacheEntry *)lParam; contact = NULL; if (wParam == 0) { //RemoveFromImgCache(0, cEntry); 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 { DWORD dwFlags; if (contact->pExtra) dwFlags = contact->pExtra->dwDFlags; else dwFlags = cfg::getDword(contact->hContact, "CList", "CLN_Flags", 0); if (cfg::dat.dwFlags & CLUI_FRAME_AVATARS) contact->cFlags = (dwFlags & ECF_HIDEAVATAR ? contact->cFlags & ~ECF_AVATAR : contact->cFlags | ECF_AVATAR); else contact->cFlags = (dwFlags & ECF_FORCEAVATAR ? contact->cFlags | ECF_AVATAR : contact->cFlags & ~ECF_AVATAR); } PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)contact->hContact); } goto LBL_Def; case INTM_STATUSMSGCHANGED: { TExtraCache *p; char *szProto = NULL; if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) p = cfg::getCache(wParam, NULL); else { p = contact->pExtra; szProto = contact->proto; } GetCachedStatusMsg(p, szProto); PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)(contact ? contact->hContact : 0)); } goto LBL_Def; case INTM_STATUSCHANGED: if (FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) { WORD 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 (cfg::dat.bAvatarServiceAvail && contact->ace == NULL) LoadAvatarForContact(contact); } contact->wStatus = wStatus; goto LBL_Def; } break; case INTM_PROTOCHANGED: 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), 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; } goto LBL_Def; case INTM_INVALIDATECONTACT: if (!FindItem(hwnd, dat, (HANDLE)wParam, &contact, &group, NULL)) break; if (contact == 0 || group == 0) break; { int 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: if (FindItem(hwnd, dat, (HANDLE)wParam, &contact, NULL, NULL)) { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; char *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; } break; case INTM_XSTATUSCHANGED: { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; char *szProto = (char *)cws->szModule; MCONTACT hContact = wParam; TExtraCache *p; if (!FindItem(hwnd, dat, (HANDLE)hContact, &contact, NULL, NULL)) { p = cfg::getCache(hContact, szProto); if (!dat->bisEmbedded && szProto) { // may be a subcontact, forward the xstatus MCONTACT hMasterContact = db_mc_getMeta(hContact); if (hMasterContact && hMasterContact != hContact) // avoid recursive call of settings handler cfg::writeByte(hMasterContact, META_PROTO, "XStatusId", (BYTE)cfg::getByte(hContact, szProto, "XStatusId", 0)); break; } } else { contact->xStatus = cfg::getByte(hContact, szProto, "XStatusId", 0); p = contact->pExtra; } if (szProto == NULL) break; if (contact) { if (ProtoServiceExists(szProto, PS_GETADVANCEDSTATUSICON)) { int iconId = ProtoCallService(szProto, PS_GETADVANCEDSTATUSICON, hContact, 0); if (iconId != -1) contact->xStatusIcon = iconId >> 16; } } GetCachedStatusMsg(p, szProto); PostMessage(hwnd, INTM_INVALIDATE, 0, (LPARAM)(contact ? contact->hContact : 0)); } goto LBL_Def; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); if (IsWindowVisible(hwnd) && !during_sizing && !cfg::shutDown) { PaintClc(hwnd, dat, hdc, &ps.rcPaint); dat->bNeedPaint = FALSE; dat->lastRepaint = GetTickCount(); } EndPaint(hwnd, &ps); if (dat->selection != dat->oldSelection && !dat->bisEmbedded && g_ButtonItems != NULL) { SetDBButtonStates(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: ReleaseCapture(); dat->iHotTrack = -1; pcli->pfnHideInfoTip(hwnd, dat); KillTimer(hwnd, TIMERID_RENAME); KillTimer(hwnd, TIMERID_INFOTIP); dat->szQuickSearch[0] = 0; { DWORD hitFlags; dat->selection = HitTest(hwnd, dat, (short) LOWORD(lParam), (short) HIWORD(lParam), &contact, NULL, &hitFlags); if (hitFlags & CLCHT_ONITEMEXTRA) break; 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: { 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)CallService(MS_CLIST_MENUBUILDSUBGROUP, (WPARAM)contact->group, 0); 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 = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) contact->hContact, 0); } 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_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: RowHeight::Free(dat); break; } return saveContactListControlWndProc(hwnd, msg, wParam, lParam); }
LRESULT cli_ProcessExternalMessages(HWND hwnd, ClcData *dat, UINT msg, WPARAM wParam, LPARAM lParam) { ClcContact *contact = NULL; ClcGroup *group = NULL; switch (msg) { case CLM_DELETEITEM: pcli->pfnDeleteItemFromTree(hwnd, wParam); clcSetDelayTimer(TIMERID_DELAYEDRESORTCLC, hwnd, 1); //pcli->pfnSortCLC(hwnd, dat, 1); clcSetDelayTimer(TIMERID_RECALCSCROLLBAR, hwnd, 2); //pcli->pfnRecalcScrollBar(hwnd, dat); return 0; case CLM_AUTOREBUILD: if (dat->force_in_dialog) { pcli->pfnSaveStateAndRebuildList(hwnd, dat); pcli->bAutoRebuild = false; } else clcSetDelayTimer(TIMERID_REBUILDAFTER, hwnd); return 0; case CLM_SETFONT: if (HIWORD(lParam) > FONTID_MODERN_MAX) return 0; dat->fontModernInfo[HIWORD(lParam)].hFont = (HFONT)wParam; dat->fontModernInfo[HIWORD(lParam)].changed = 1; RowHeights_GetMaxRowHeight(dat, hwnd); if (LOWORD(lParam)) CLUI__cliInvalidateRect(hwnd, NULL, FALSE); return 0; case CLM_SETHIDEEMPTYGROUPS: { BOOL old = ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) != 0); if (wParam) SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | CLS_HIDEEMPTYGROUPS); else SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~CLS_HIDEEMPTYGROUPS); BOOL newval = ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) != 0); if (newval != old) pcli->pfnInitAutoRebuild(hwnd); } return 0; case CLM_SETTEXTCOLOR: if (wParam > FONTID_MODERN_MAX) break; dat->fontModernInfo[wParam].colour = lParam; dat->force_in_dialog = TRUE; // Issue 40: option knows nothing about moderns colors // others who know have to set colors from lowest to highest switch (wParam) { case FONTID_CONTACTS: dat->fontModernInfo[FONTID_SECONDLINE].colour = lParam; dat->fontModernInfo[FONTID_THIRDLINE].colour = lParam; dat->fontModernInfo[FONTID_AWAY].colour = lParam; dat->fontModernInfo[FONTID_DND].colour = lParam; dat->fontModernInfo[FONTID_NA].colour = lParam; dat->fontModernInfo[FONTID_OCCUPIED].colour = lParam; dat->fontModernInfo[FONTID_CHAT].colour = lParam; dat->fontModernInfo[FONTID_INVISIBLE].colour = lParam; dat->fontModernInfo[FONTID_PHONE].colour = lParam; dat->fontModernInfo[FONTID_LUNCH].colour = lParam; dat->fontModernInfo[FONTID_CONTACT_TIME].colour = lParam; break; case FONTID_OPENGROUPS: dat->fontModernInfo[FONTID_CLOSEDGROUPS].colour = lParam; break; case FONTID_OPENGROUPCOUNTS: dat->fontModernInfo[FONTID_CLOSEDGROUPCOUNTS].colour = lParam; break; } return 0; case CLM_GETNEXTITEM: { int i = 0; if (wParam != CLGN_ROOT) { if (!pcli->pfnFindItem(hwnd, dat, lParam, &contact, &group, NULL)) return NULL; i = List_IndexOf((SortedList*)&group->cl, contact); if (i < 0) return 0; } switch (wParam) { case CLGN_ROOT: if (dat->list.cl.count) return (LRESULT)pcli->pfnContactToHItem(dat->list.cl.items[0]); else return NULL; case CLGN_CHILD: if (contact->type != CLCIT_GROUP) return NULL; group = contact->group; if (group->cl.count == 0) return NULL; return (LRESULT)pcli->pfnContactToHItem(group->cl.items[0]); case CLGN_PARENT: return group->groupId | HCONTACT_ISGROUP; case CLGN_NEXT: do { if (++i >= group->cl.count) return NULL; } while (group->cl.items[i]->type == CLCIT_DIVIDER); return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); case CLGN_PREVIOUS: do { if (--i < 0) return NULL; } while (group->cl.items[i]->type == CLCIT_DIVIDER); return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); case CLGN_NEXTCONTACT: for (i++; i < group->cl.count; i++) if (group->cl.items[i]->type == CLCIT_CONTACT) break; if (i >= group->cl.count) return NULL; return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); case CLGN_PREVIOUSCONTACT: if (i >= group->cl.count) return NULL; for (i--; i >= 0; i--) if (group->cl.items[i]->type == CLCIT_CONTACT) break; if (i < 0) return NULL; return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); case CLGN_NEXTGROUP: for (i++; i < group->cl.count; i++) if (group->cl.items[i]->type == CLCIT_GROUP) break; if (i >= group->cl.count) return NULL; return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); case CLGN_PREVIOUSGROUP: if (i >= group->cl.count) return NULL; for (i--; i >= 0; i--) if (group->cl.items[i]->type == CLCIT_GROUP) break; if (i < 0) return NULL; return (LRESULT)pcli->pfnContactToHItem(group->cl.items[i]); } } return NULL; case CLM_SELECTITEM: ClcGroup *tgroup; { int index = -1; int mainindex = -1; if (!pcli->pfnFindItem(hwnd, dat, wParam, &contact, &group, NULL)) break; for (tgroup = group; tgroup; tgroup = tgroup->parent) pcli->pfnSetGroupExpand(hwnd, dat, tgroup, 1); if (!contact->isSubcontact) { index = List_IndexOf((SortedList*)&group->cl, contact); mainindex = index; } else { index = List_IndexOf((SortedList*)&group->cl, contact->subcontacts); mainindex = index; index += contact->isSubcontact; } BYTE k = db_get_b(NULL, "CLC", "MetaExpanding", SETTING_METAEXPANDING_DEFAULT); if (k) { for (int i = 0; i < mainindex; i++) { ClcContact *tempCont = group->cl.items[i]; if (tempCont->type == CLCIT_CONTACT && tempCont->SubAllocated && tempCont->SubExpanded) index += tempCont->SubAllocated; } } dat->selection = pcli->pfnGetRowsPriorTo(&dat->list, group, index); pcli->pfnEnsureVisible(hwnd, dat, dat->selection, 0); } return 0; case CLM_SETEXTRAIMAGE: if (LOWORD(lParam) >= dat->extraColumnsCount) return 0; if (!pcli->pfnFindItem(hwnd, dat, wParam, &contact, NULL, NULL)) return 0; contact->iExtraImage[LOWORD(lParam)] = HIWORD(lParam); pcli->pfnInvalidateRect(hwnd, NULL, FALSE); return 0; } return corecli.pfnProcessExternalMessages(hwnd, dat, msg, wParam, lParam); }