From a15e5552aeeb16bc71ec8b98061a1dd3f7d6bd4a Mon Sep 17 00:00:00 2001 From: Rozhuk Ivan Date: Wed, 26 Nov 2014 20:15:48 +0000 Subject: Tipper: code cleanup git-svn-id: http://svn.miranda-ng.org/main/trunk@11105 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/TipperYM/src/popwin.cpp | 3369 +++++++++++++++++++-------------------- plugins/TipperYM/src/subst.cpp | 1702 ++++++++++---------- 2 files changed, 2533 insertions(+), 2538 deletions(-) (limited to 'plugins/TipperYM') diff --git a/plugins/TipperYM/src/popwin.cpp b/plugins/TipperYM/src/popwin.cpp index 192a451d0d..caea08e7fa 100644 --- a/plugins/TipperYM/src/popwin.cpp +++ b/plugins/TipperYM/src/popwin.cpp @@ -1,1687 +1,1682 @@ -/* -Copyright (C) 2006-2007 Scott Ellis -Copyright (C) 2007-2011 Jan Holub - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "common.h" - -__inline void AddRow(PopupWindowData *pwd, TCHAR *swzLabel, TCHAR *swzValue, char *szProto, bool bParseSmileys, bool bNewline, bool bLineAbove, bool bIsTitle = false, HICON hIcon = NULL) -{ - pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData) * (pwd->iRowCount + 1)); - pwd->rows[pwd->iRowCount].swzLabel = swzLabel ? mir_tstrdup(swzLabel) : NULL; - pwd->rows[pwd->iRowCount].swzValue = swzValue ? mir_tstrdup(swzValue) : NULL; - pwd->rows[pwd->iRowCount].spi = bParseSmileys ? Smileys_PreParse(swzValue, (int)_tcslen(swzValue), szProto) : NULL; - pwd->rows[pwd->iRowCount].bValueNewline = bNewline; - pwd->rows[pwd->iRowCount].bLineAbove = bLineAbove; - pwd->rows[pwd->iRowCount].bIsTitle = bIsTitle; - pwd->rows[pwd->iRowCount].hIcon = hIcon; - pwd->iRowCount++; -} - -LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - PopupWindowData *pwd = (PopupWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch(uMsg) { - case WM_CREATE: - { - CREATESTRUCT *cs = (CREATESTRUCT *)lParam; - pwd = (PopupWindowData *)mir_alloc(sizeof(PopupWindowData)); - memset(pwd, 0, sizeof(PopupWindowData)); - pwd->clcit = *(CLCINFOTIPEX *)cs->lpCreateParams; - pwd->iIconIndex = -1; - pwd->hpenBorder = CreatePen(PS_SOLID, 1, opt.bBorder ? opt.colBorder : opt.colBg); - pwd->hpenDivider = CreatePen(PS_SOLID, 1, opt.colDivider); - pwd->iTrans = (int)(opt.iOpacity / 100.0 * 255); - - // load icons order - for (int i = 0; i < EXICONS_COUNT; i++) - pwd->bIsIconVisible[opt.exIconsOrder[i]] = opt.exIconsVis[i] ? true : false; - - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pwd); - - // work around bug hiding tips - GetCursorPos(&pwd->ptCursorStartPos); - SetTimer(hwnd, ID_TIMER_CHECKMOUSE, CHECKMOUSE_ELAPSE, 0); - - //Register copy menu hotkey (CTRL+C) - pwd->iHotkeyId = GlobalAddAtom(_T("Tipper")); - RegisterHotKey(hwnd, pwd->iHotkeyId, MOD_CONTROL, 0x43); - - if (pwd->clcit.szProto) { - pwd->bIsTextTip = false; - pwd->iIndent = opt.iTextIndent; - pwd->iSidebarWidth = opt.iSidebarWidth; - - if (ServiceExists(MS_PROTO_GETACCOUNT)) { - PROTOACCOUNT *pa = ProtoGetAccount(pwd->clcit.szProto); - if (pa) - _tcscpy(pwd->swzTitle, pa->tszAccountName); - } - - if (_tcslen(pwd->swzTitle) == 0) - a2t(pwd->clcit.szProto, pwd->swzTitle, TITLE_TEXT_LEN); - - if (CallService(MS_PROTO_ISACCOUNTLOCKED, 0, (LPARAM)pwd->clcit.szProto)) - mir_sntprintf(pwd->swzTitle, SIZEOF(pwd->swzTitle), TranslateT("%s (locked)"), pwd->swzTitle); - - // protocol status - WORD wStatus = (WORD)CallProtoService(pwd->clcit.szProto, PS_GETSTATUS, 0, 0); - - // get status icon - if (pwd->bIsIconVisible[0]) { - pwd->extraIcons[0].hIcon = LoadSkinnedProtoIcon(pwd->clcit.szProto, wStatus); - pwd->extraIcons[0].bDestroy = false; - } - - // get activity icon - if (pwd->bIsIconVisible[2]) { - pwd->extraIcons[2].hIcon = GetJabberActivityIcon(0, pwd->clcit.szProto); - pwd->extraIcons[2].bDestroy = false; - } - - // uid info - TCHAR swzUid[256], swzUidName[256]; - if (Uid(0, pwd->clcit.szProto, swzUid, 256) && UidName(pwd->clcit.szProto, swzUidName, 253)) { - _tcscat(swzUidName, _T(": ")); - AddRow(pwd, swzUidName, swzUid, NULL, false, false, false); - } - - // logon info - TCHAR swzLogon[64]; - if (TimestampToTimeDifference(NULL, pwd->clcit.szProto, "LogonTS", swzLogon, 59)) { - TCHAR ago[96]; - mir_sntprintf(ago, SIZEOF(ago), TranslateT("%s ago"), swzLogon); - AddRow(pwd, TranslateT("Log on:"), ago, NULL, false, false, false); - } - - // number of unread emails - TCHAR swzEmailCount[64]; - if (ProtoServiceExists(pwd->clcit.szProto, PS_GETUNREADEMAILCOUNT)) { - int iCount = (int)ProtoCallService(pwd->clcit.szProto, PS_GETUNREADEMAILCOUNT, 0, 0); - if (iCount > 0) { - _itot(iCount, swzEmailCount, 10); - AddRow(pwd, TranslateT("Unread emails:"), swzEmailCount, NULL, false, false, false); - } - } - - TCHAR *swzText = pcli->pfnGetStatusModeDescription(wStatus, 0); - if (swzText) - AddRow(pwd, TranslateT("Status:"), swzText, NULL, false, false, false); - - if (wStatus >= ID_STATUS_ONLINE && wStatus <= ID_STATUS_OUTTOLUNCH) { - // status message - TCHAR *swzText = GetProtoStatusMessage(pwd->clcit.szProto, wStatus); - if (swzText) { - StripBBCodesInPlace(swzText); - AddRow(pwd, TranslateT("Status message:"), swzText, pwd->clcit.szProto, true, true, true); - mir_free(swzText); - } - - // jabber mood or icq xstatus - TCHAR *swzAdvTitle = GetJabberAdvStatusText(pwd->clcit.szProto, "mood", "title"); - if (swzAdvTitle) { - StripBBCodesInPlace(swzAdvTitle); - AddRow(pwd, TranslateT("Mood:"), swzAdvTitle, pwd->clcit.szProto, true, false, true); - - TCHAR *swzAdvText = GetJabberAdvStatusText(pwd->clcit.szProto, "mood", "text"); - if (swzAdvText) { - StripBBCodesInPlace(swzAdvText); - AddRow(pwd, _T(""), swzAdvText, pwd->clcit.szProto, true, true, false); - mir_free(swzAdvText); - } - } - else { - if (db_get_b(0, pwd->clcit.szProto, "XStatusId", 0)) { - // xstatus title - swzAdvTitle = GetProtoExtraStatusTitle(pwd->clcit.szProto); - if (swzAdvTitle) { - StripBBCodesInPlace(swzAdvTitle); - AddRow(pwd, TranslateT("xStatus:"), swzAdvTitle, pwd->clcit.szProto, true, false, true); - } - - // xstatus message - TCHAR *swzAdvText = GetProtoExtraStatusMessage(pwd->clcit.szProto); - if (swzAdvText) { - StripBBCodesInPlace(swzAdvText); - AddRow(pwd, _T(""), swzAdvText, pwd->clcit.szProto, true, true, false); - mir_free(swzAdvText); - } - } - } - - if (swzAdvTitle) { - mir_free(swzAdvTitle); - // get advanced status icon - if (pwd->bIsIconVisible[1]) { - pwd->extraIcons[1].hIcon = (HICON)CallProtoService(pwd->clcit.szProto, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); - pwd->extraIcons[1].bDestroy = false; - } - } - - // jabber activity - TCHAR *swzActTitle = GetJabberAdvStatusText(pwd->clcit.szProto, "activity", "title"); - if (swzActTitle) { - StripBBCodesInPlace(swzActTitle); - AddRow(pwd, TranslateT("Activity:"), swzActTitle, pwd->clcit.szProto, true, false, true); - mir_free(swzActTitle); - } - - TCHAR *swzActText = GetJabberAdvStatusText(pwd->clcit.szProto, "activity", "text"); - if (swzActText) { - StripBBCodesInPlace(swzActText); - AddRow(pwd, _T(""), swzActText, pwd->clcit.szProto, true, true, false); - mir_free(swzActText); - } - - // listening to - TCHAR *swzListening = GetListeningTo(pwd->clcit.szProto); - if (swzListening) { - StripBBCodesInPlace(swzListening); - AddRow(pwd, TranslateT("Listening to:"), swzListening, NULL, false, true, true); - mir_free(swzListening); - } - } - } - else if (pwd->clcit.swzText) { - pwd->bIsTextTip = true; - pwd->iIndent = 0; - pwd->iSidebarWidth = 0; - - RECT rc = pwd->clcit.rcItem; - bool mirandaTrayTip = ((rc.right - rc.left) == 20) && ((rc.bottom - rc.top) == 20) ? true : false; - - if (mirandaTrayTip && !opt.bTraytip) { - MyDestroyWindow(hwnd); - return 0; - } - - if (mirandaTrayTip && opt.bHandleByTipper) { // extended tray tooltip - pwd->bIsTrayTip = true; - pwd->iIndent = opt.iTextIndent; - pwd->iSidebarWidth = opt.iSidebarWidth; - - SendMessage(hwnd, PUM_REFRESHTRAYTIP, 0, 0); - - if (opt.bExpandTraytip) - SetTimer(hwnd, ID_TIMER_TRAYTIP, opt.iExpandTime, 0); - } - else { - TCHAR buff[2048], *swzText = pwd->clcit.swzText; - size_t iBuffPos, i = 0, iSize = _tcslen(pwd->clcit.swzText); - bool bTopMessage = false; - - while (i < iSize && swzText[i] != _T('<')) { - iBuffPos = 0; - while (swzText[i] != _T('\n') && swzText[i] != _T('\r') && i < iSize && iBuffPos < 2048) { - if (swzText[i] != _T('\t')) - buff[iBuffPos++] = swzText[i]; - i++; - } - - buff[iBuffPos] = 0; - - if (iBuffPos) { - AddRow(pwd, _T(""), buff, NULL, false, true, false); - bTopMessage = true; - } - - while (i < iSize && (swzText[i] == _T('\n') || swzText[i] == _T('\r'))) - i++; - } - - // parse bold bits into labels and the rest into items - while (i < iSize) { - while (i + 2 < iSize && (swzText[i] != _T('<') || swzText[i + 1] != _T('b') || swzText[i + 2] != _T('>'))) - i++; - - i += 3; - - iBuffPos = 0; - while (i + 3 < iSize && iBuffPos < 2048 && (swzText[i] != _T('<') - || swzText[i + 1] != _T('/') || swzText[i + 2] != _T('b') || swzText[i + 3] != _T('>'))) { - if (swzText[i] != _T('\t')) - buff[iBuffPos++] = swzText[i]; - i++; - } - - i += 4; - - buff[iBuffPos] = 0; - - if (iBuffPos) { - pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData)* (pwd->iRowCount + 1)); - pwd->rows[pwd->iRowCount].bValueNewline = false; - pwd->rows[pwd->iRowCount].swzLabel = mir_tstrdup(buff); - if (pwd->iRowCount == 1 && bTopMessage) - pwd->rows[pwd->iRowCount].bLineAbove = true; - else - pwd->rows[pwd->iRowCount].bLineAbove = false; - - iBuffPos = 0; - while (i < iSize && iBuffPos < 2048 && swzText[i] != _T('\n')) { - if (swzText[i] != _T('\t') && swzText[i] != _T('\r')) - buff[iBuffPos++] = swzText[i]; - i++; - } - buff[iBuffPos] = 0; - - pwd->rows[pwd->iRowCount].swzValue = mir_tstrdup(buff); - pwd->rows[pwd->iRowCount].spi = NULL; - pwd->iRowCount++; - } - - i++; - } - - if (pwd->iRowCount == 0) { - // single item - pwd->iRowCount = 1; - pwd->rows = (RowData *)mir_alloc(sizeof(RowData)); - pwd->rows[0].bLineAbove = pwd->rows[0].bValueNewline = false; - pwd->rows[0].swzLabel = 0; - pwd->rows[0].swzValue = swzText; - pwd->rows[0].spi = NULL; - } - } - } - else { - pwd->bIsTextTip = false; - pwd->iIndent = opt.iTextIndent; - pwd->iSidebarWidth = opt.iSidebarWidth; - pwd->hContact = (MCONTACT)pwd->clcit.hItem; - pwd->iIconIndex = (int)CallService(MS_CLIST_GETCONTACTICON, pwd->hContact, 0); - - // don't use stored status message - if (!opt.bWaitForContent) - db_unset(pwd->hContact, MODULE, "TempStatusMsg"); - - TCHAR *swzNick = pcli->pfnGetContactDisplayName(pwd->hContact, 0); - _tcsncpy(pwd->swzTitle, swzNick, TITLE_TEXT_LEN); - - char *szProto = GetContactProto(pwd->hContact); - pwd->spiTitle = Smileys_PreParse(pwd->swzTitle, -1, szProto); - - // get extra icons - DBVARIANT dbv = { 0 }; - int i = 0; - - if (szProto) { - // status icon - if (pwd->bIsIconVisible[0]) { - for (i = 0; opt.exIconsOrder[i] != 0; i++); - pwd->extraIcons[i].hIcon = ImageList_GetIcon((HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0), pwd->iIconIndex, 0); - pwd->extraIcons[i].bDestroy = true; - } - - // xstatus icon - if (pwd->bIsIconVisible[1]) { - for (i = 0; opt.exIconsOrder[i] != 1; i++); - int iXstatus = db_get_b(pwd->hContact, szProto, "XStatusId", 0); - if (iXstatus) { - char szIconProto[64]; - if (strcmp(szProto, META_PROTO) != 0) - strcpy(szIconProto, szProto); - else if (!db_get_s(pwd->hContact, szProto, "XStatusProto", &dbv)) { - strcpy(szIconProto, dbv.pszVal); - db_free(&dbv); - } - - pwd->extraIcons[i].hIcon = (HICON)CallProtoService(szIconProto, PS_GETCUSTOMSTATUSICON, (WPARAM)iXstatus, LR_SHARED); - pwd->extraIcons[i].bDestroy = false; - } - } - - // activity icon - if (pwd->bIsIconVisible[2]) { - for (i = 0; opt.exIconsOrder[i] != 2; i++); - pwd->extraIcons[i].hIcon = GetJabberActivityIcon(pwd->hContact, szProto); - pwd->extraIcons[i].bDestroy = false; - } - - // gender icon - if (pwd->bIsIconVisible[3]) { - for (i = 0; opt.exIconsOrder[i] != 3; i++); - int iGender = db_get_b(pwd->hContact, "UserInfo", "Gender", 0); - if (iGender == 0) - iGender = db_get_b(pwd->hContact, szProto, "Gender", 0); - - if (iGender == GEN_FEMALE) - pwd->extraIcons[i].hIcon = Skin_GetIcon("gender_female"); - else if (iGender == GEN_MALE) - pwd->extraIcons[i].hIcon = Skin_GetIcon("gender_male"); - pwd->extraIcons[i].bDestroy = false; - } - - // flags icon - if (pwd->bIsIconVisible[4]) { - for (i = 0; opt.exIconsOrder[i] != 4; i++); - - INT_PTR iCountry = CallService(MS_FLAGS_DETECTCONTACTORIGINCOUNTRY, pwd->hContact, 0); - if (iCountry == CALLSERVICE_NOTFOUND) - iCountry = CallService(MS_FLAGS_GETCONTACTORIGINCOUNTRY, pwd->hContact, 0); - - if (iCountry != CALLSERVICE_NOTFOUND && iCountry != 0 && iCountry != CTRY_UNKNOWN && iCountry != CTRY_ERROR) { - pwd->extraIcons[i].hIcon = LoadFlagIcon(iCountry); - pwd->extraIcons[i].bDestroy = false; - } - } - - // fingerprint icon - if (pwd->bIsIconVisible[5]) { - for (i = 0; opt.exIconsOrder[i] != 5; i++); - if (ServiceExists(MS_FP_GETCLIENTICONT)) { - if (!db_get_ts(pwd->hContact, szProto, "MirVer", &dbv)) { - pwd->extraIcons[i].hIcon = Finger_GetClientIcon(dbv.ptszVal, 0); - pwd->extraIcons[i].bDestroy = true; - db_free(&dbv); - } - } - } - - //request xstatus details - if (opt.bRetrieveXstatus) - if (!db_get_b(0, szProto, "XStatusAuto", 1) && ProtoServiceExists(szProto, PS_ICQ_REQUESTCUSTOMSTATUS)) - CallProtoService(szProto, PS_ICQ_REQUESTCUSTOMSTATUS, pwd->hContact, 0); - } - - SendMessage(hwnd, PUM_REFRESH_VALUES, FALSE, 0); - } - - SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); - SendMessage(hwnd, PUM_CALCPOS, 0, 0); - - // transparency - SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); - - if (opt.bDropShadow && opt.skinMode == SM_COLORFILL) - SetClassLongPtr(hwnd, GCL_STYLE, CS_DROPSHADOW); - else - SetClassLongPtr(hwnd, GCL_STYLE, 0); - - if (!skin.bNeedLayerUpdate) - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_ALPHA); - - if (opt.showEffect) - SetTimer(hwnd, ID_TIMER_ANIMATE, ANIM_ELAPSE, 0); - - ShowWindow(hwnd, SW_SHOWNOACTIVATE); - InvalidateRect(hwnd, 0, FALSE); - - // since tipper win is topmost, this should put it at top of topmost windows - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - } - return 0; - - case WM_ERASEBKGND: - if (!skin.bNeedLayerUpdate) { - HDC hdc = (HDC)wParam; - RECT rc; - GetClientRect(hwnd, &rc); - - BitBlt(hdc, 0, 0, skin.iWidth, skin.iHeight, skin.hdc, 0, 0, SRCCOPY); - - // border - if (opt.bBorder) { - HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); - HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenBorder); - - int h = 0; - if (opt.bRound) { - int v; - int w = 14; - h = (rc.right - rc.left) > (w * 2) ? w : (rc.right - rc.left); - v = (rc.bottom - rc.top) > (w * 2) ? w : (rc.bottom - rc.top); - h = (h < v) ? h : v; - } - - RoundRect(hdc, 0, 0, (rc.right - rc.left), (rc.bottom - rc.top), h, h); - - SelectObject(hdc, hOldBrush); - SelectObject(hdc, hOldPen); - } - } - return TRUE; - - case WM_PAINT: - { - RECT r, r2; - PAINTSTRUCT ps; - BeginPaint(hwnd, &ps); - HDC hdc = skin.bNeedLayerUpdate ? skin.hdc : ps.hdc; - - GetClientRect(hwnd, &r); - r2 = r; - HFONT hOldFont = (HFONT)GetCurrentObject(hdc,OBJ_FONT); - - // text background - SetBkMode(hdc, TRANSPARENT); - - BLENDFUNCTION blend; - blend.BlendOp = AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = 255; - blend.AlphaFormat = AC_SRC_ALPHA; - - // avatar - if (!pwd->bIsTextTip && opt.avatarLayout != PAV_NONE && pwd->iAvatarHeight) { - RECT rcAvatar; - rcAvatar.top = opt.iOuterAvatarPadding; - - if (opt.avatarLayout == PAV_LEFT) { - rcAvatar.left = r.left + opt.iOuterAvatarPadding; - rcAvatar.right = rcAvatar.left + pwd->iRealAvatarWidth; - r2.left += pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); // padding re-added for text - } - else if (opt.avatarLayout == PAV_RIGHT) { - rcAvatar.right = r.right - opt.iOuterAvatarPadding; - rcAvatar.left = rcAvatar.right - pwd->iRealAvatarWidth; - r2.right -= pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); - } - - rcAvatar.bottom = rcAvatar.top + pwd->iRealAvatarHeight; - - AVATARCACHEENTRY *ace = 0; - if (pwd->hContact) - ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, pwd->hContact, 0); - else - ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); - - if (ace && ace->hbmPic && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { - ResizeBitmap rb = { 0 }; - rb.size = sizeof(rb); - rb.max_width = pwd->iRealAvatarWidth; - rb.max_height = pwd->iRealAvatarHeight; - rb.fit = RESIZEBITMAP_STRETCH | RESIZEBITMAP_KEEP_PROPORTIONS; - rb.hBmp = ace->hbmPic; - HBITMAP hbmpAvatar = (HBITMAP)CallService(MS_IMG_RESIZE, (WPARAM)&rb, 0); - - if (hbmpAvatar) { - HRGN hrgnAvatar = 0; - if (opt.bAvatarRound) { - hrgnAvatar = CreateRoundRectRgn(rcAvatar.left, rcAvatar.top, rcAvatar.right + 1, rcAvatar.bottom + 1, 9, 9); - SelectClipRgn(hdc, hrgnAvatar); - } - - BITMAP bm; - GetObject(hbmpAvatar, sizeof(bm), &bm); - HDC hdcMem = CreateCompatibleDC(hdc); - SelectObject(hdcMem, hbmpAvatar); - - blend.SourceConstantAlpha = (BYTE)(opt.iAvatarOpacity / 100.0 * 255); - AlphaBlend(hdc, rcAvatar.left, rcAvatar.top, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, blend); - blend.SourceConstantAlpha = 255; - - if (opt.bAvatarBorder) { - SaveAlpha(&rcAvatar); - HBRUSH hbrBorder = CreateSolidBrush(opt.colAvatarBorder); - if (opt.bAvatarRound) - FrameRgn(hdc, hrgnAvatar, hbrBorder, 1, 1); - else - FrameRect(hdc, &rcAvatar, hbrBorder); - - DeleteObject(hbrBorder); - RestoreAlpha(&rcAvatar, (BYTE)(opt.iAvatarOpacity / 100.0 * 255)); - } - - if (hrgnAvatar) { - SelectClipRgn(hdc, 0); - DeleteObject(hrgnAvatar); - } - - if (hbmpAvatar != ace->hbmPic) - DeleteObject(hbmpAvatar); - - DeleteDC(hdcMem); - } - } - } - - RECT tr; - tr.left = r2.left + opt.iPadding + opt.iTitleIndent; - tr.right = r2.right - opt.iPadding; - tr.top = 0; - tr.bottom = opt.iPadding; - - if (!pwd->bIsTextTip) { - if (opt.titleIconLayout != PTL_NOICON) { - // draw icons - int iIconX, iIconY; - iIconY = opt.iPadding + opt.iTextPadding; - - if (opt.titleIconLayout == PTL_RIGHTICON) - iIconX = r2.right - 16 - opt.iPadding; - else - iIconX = r2.left + opt.iPadding; - - for (int i = 0; i < EXICONS_COUNT; i++) { - if (pwd->extraIcons[i].hIcon) { - DrawIconExAlpha(hdc, iIconX, iIconY, pwd->extraIcons[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); - iIconY += 20; - } - } - } - - // title text - if (opt.bShowTitle) { - if (hFontTitle) SelectObject(hdc, (HGDIOBJ)hFontTitle); - SetTextColor(hdc, opt.colTitle); - tr.top = opt.iPadding; - tr.bottom = tr.top + pwd->iTitleHeight - opt.iPadding; - UINT uTextFormat = DT_TOP | DT_LEFT | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX; - DrawTextExt(hdc, pwd->swzTitle, -1, &tr, uTextFormat, NULL, pwd->spiTitle); - } - } - - // values - pwd->iTextHeight = 0; - bool bIconPainted, bUseRect = true; - int iRowHeight; - for (int i = 0; i < pwd->iRowCount; i++) { - tr.top = tr.bottom; - bUseRect = (tr.top + opt.iTextPadding >= pwd->iAvatarHeight); - bIconPainted = false; - if (bUseRect) { - if (pwd->rows[i].bLineAbove) { - HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenDivider); - tr.top += opt.iTextPadding; - RECT rec; - SetRect(&rec, r.left + opt.iPadding + pwd->iIndent, tr.top, r.right - opt.iPadding, tr.top + 1); - SaveAlpha(&rec); - Rectangle(hdc, rec.left, rec.top, rec.right, rec.bottom); - RestoreAlpha(&rec); - SelectObject(hdc, hOldPen); - } - - tr.left = r.left + opt.iPadding + pwd->iIndent; - if (pwd->rows[i].bValueNewline) - tr.right = r.right - opt.iPadding; - else - tr.right = r.left + opt.iPadding + pwd->iIndent + pwd->iLabelWidth; - } - else { - if (pwd->rows[i].bLineAbove) { - HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenDivider); - tr.top += opt.iTextPadding; - RECT rec; - SetRect(&rec, r2.left + opt.iPadding + pwd->iIndent, tr.top, r2.right - opt.iPadding, tr.top + 1); - SaveAlpha(&rec); - Rectangle(hdc, rec.left, rec.top, rec.right, rec.bottom); - RestoreAlpha(&rec); - SelectObject(hdc, hOldPen); - } - - tr.left = r2.left + opt.iPadding + pwd->iIndent; - if (pwd->rows[i].bValueNewline) - tr.right = r2.right - opt.iPadding; - else - tr.right = r2.left + opt.iPadding + pwd->iIndent + pwd->iLabelWidth; - } - - if (pwd->rows[i].bValueNewline) - iRowHeight = pwd->rows[i].iLabelHeight; - else - iRowHeight = max(pwd->rows[i].iLabelHeight, pwd->rows[i].iValueHeight); - - if (pwd->rows[i].iLabelHeight) { - tr.top += opt.iTextPadding; - tr.bottom = tr.top + iRowHeight; - - if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { - if (hFontTrayTitle) SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); - SetTextColor(hdc, opt.colTrayTitle); - } - else { - if (hFontLabels) SelectObject(hdc, (HGDIOBJ)hFontLabels); - SetTextColor(hdc, opt.colLabel); - } - - // status icon in tray tooltip - if (opt.titleIconLayout != PTL_NOICON && pwd->bIsTrayTip && pwd->rows[i].hIcon) { - DrawIconExAlpha(hdc, opt.iPadding, tr.top + (pwd->rows[i].iLabelHeight - 16) / 2, pwd->rows[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); - bIconPainted = true; - } - - DrawTextAlpha(hdc, pwd->rows[i].swzLabel, -1, &tr, opt.iLabelValign | ((opt.iLabelHalign == DT_RIGHT && !pwd->rows[i].bValueNewline) ? DT_RIGHT : DT_LEFT) | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); - if (pwd->rows[i].bValueNewline) - tr.top = tr.bottom; - } - else tr.bottom = tr.top; - - if (pwd->rows[i].bValueNewline) - iRowHeight = pwd->rows[i].iValueHeight; - - if (hFontValues) - SelectObject(hdc, (HGDIOBJ)hFontValues); - SetTextColor(hdc, opt.colValue); - if (bUseRect) { - tr.left = r.left + opt.iPadding + pwd->iIndent; - if (!pwd->rows[i].bValueNewline) - tr.left += pwd->iLabelWidth + opt.iValueIndent; - - tr.right = r.right - opt.iPadding; - } - else { - tr.left = r2.left + opt.iPadding + pwd->iIndent; - if (!pwd->rows[i].bValueNewline) - tr.left += pwd->iLabelWidth + opt.iValueIndent; - - tr.right = r2.right - opt.iPadding; - } - - if (pwd->rows[i].iValueHeight) { - if (pwd->rows[i].bValueNewline || !pwd->rows[i].iLabelHeight) tr.top += opt.iTextPadding; - if (pwd->rows[i].iLabelHeight > pwd->rows[i].iValueHeight && pwd->bIsTextTip&& pwd->rows[i].bIsTitle) - tr.top = tr.bottom - pwd->rows[i].iValueHeight - 2; - else - tr.bottom = tr.top + iRowHeight; - - if (opt.titleIconLayout != PTL_NOICON && pwd->bIsTrayTip && pwd->rows[i].hIcon && !bIconPainted) - DrawIconExAlpha(hdc, opt.iPadding, tr.top + (pwd->rows[i].iValueHeight - 16) / 2, pwd->rows[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); - - UINT uFormat = opt.iValueValign | opt.iValueHalign | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX; - DrawTextExt(hdc, pwd->rows[i].swzValue, -1, &tr, uFormat, NULL, pwd->rows[i].spi); - } - } - - PremultipleChannels(); - - if (opt.showEffect == PSE_NONE) { - if (skin.bNeedLayerUpdate) { - POINT ptSrc = { 0, 0 }; - SIZE szTip = { r.right - r.left, r.bottom - r.top }; - blend.SourceConstantAlpha = pwd->iTrans; - UpdateLayeredWindow(hwnd, NULL, NULL, &szTip, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); - - if (opt.bAeroGlass && MyDwmEnableBlurBehindWindow) { - DWM_BLURBEHIND bb = { 0 }; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.fEnable = TRUE; - bb.hRgnBlur = CreateOpaqueRgn(25, true); - MyDwmEnableBlurBehindWindow(hwnd, &bb); - } - } - else SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iTrans, LWA_ALPHA); - } - - SelectObject(hdc, hOldFont); - EndPaint(hwnd, &ps); - pwd->bIsPainted = true; - } - return 0; - - case WM_HOTKEY: - if (LOWORD(lParam) == MOD_CONTROL && HIWORD(lParam) == 0x43) { // CTRL+C - if (pwd->iRowCount == 0) - return 0; - - ShowWindow(hwnd, SW_HIDE); - HMENU hMenu = CreatePopupMenu(); - if (!hMenu) - return 0; - - HICON hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ITEM_ALL), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); - if (!hIcon) { - DestroyMenu(hMenu); - return 0; - } - - ICONINFO iconInfo; - GetIconInfo(hIcon, &iconInfo); - HBITMAP hbmpAllItems = iconInfo.hbmColor; - DestroyIcon(hIcon); - - AppendMenu(hMenu, MF_STRING, COPYMENU_ALLITEMS_LABELS, LPGENT("Copy all items with labels")); - AppendMenu(hMenu, MF_STRING, COPYMENU_ALLITEMS, LPGENT("Copy all items")); - if (pwd->clcit.szProto || pwd->hContact) - AppendMenu(hMenu, MF_STRING, COPYMENU_AVATAR, LPGENT("Copy avatar")); - AppendMenu(hMenu, MF_SEPARATOR, 2000, 0); - TranslateMenu(hMenu); - - SetMenuItemBitmaps(hMenu, COPYMENU_ALLITEMS_LABELS, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); - SetMenuItemBitmaps(hMenu, COPYMENU_ALLITEMS, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); - SetMenuItemBitmaps(hMenu, COPYMENU_AVATAR, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); - - hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ITEM), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); - if (!hIcon) { - DeleteObject(hbmpAllItems); - DestroyMenu(hMenu); - return 0; - } - - GetIconInfo(hIcon, &iconInfo); - HBITMAP hbmpItem = iconInfo.hbmColor; - DestroyIcon(hIcon); - - for (int i = 0; i < pwd->iRowCount; i++) { - if (pwd->rows[i].swzValue) { - TCHAR buff[128]; - int iLen = (int)_tcslen(pwd->rows[i].swzValue); - if (iLen) { - if (iLen > MAX_VALUE_LEN) { - _tcsncpy(buff, pwd->rows[i].swzValue, MAX_VALUE_LEN); - buff[MAX_VALUE_LEN] = 0; - _tcscat(buff, _T("...")); - } - else _tcscpy(buff, pwd->rows[i].swzValue); - - AppendMenu(hMenu, MF_STRING, i + 1, buff); // first id = 1, because no select have id = 0 - SetMenuItemBitmaps(hMenu, i + 1, MF_BYCOMMAND, hbmpItem, hbmpItem); - } - else AppendMenu(hMenu, MF_SEPARATOR, 0, 0); - } - } - - POINT pt; - GetCursorPos(&pt); - SetForegroundWindow(hwnd); - int iSelItem = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, 0); - DeleteObject(hbmpAllItems); - DeleteObject(hbmpItem); - DestroyMenu(hMenu); - - if (iSelItem == 0) - return 0; // no item was selected - - if (OpenClipboard(NULL)) { - EmptyClipboard(); - if (iSelItem == COPYMENU_AVATAR) { // copy avatar - AVATARCACHEENTRY *ace = 0; - if (pwd->hContact) - ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, pwd->hContact, 0); - else - ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); - if (ace && ace->hbmPic && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { - HDC hdc = GetDC(hwnd); - HDC hdcSrc = CreateCompatibleDC(hdc); - HDC hdcDes = CreateCompatibleDC(hdc); - HBITMAP hbmAvr = CreateCompatibleBitmap(hdc, ace->bmWidth, ace->bmHeight); - SelectObject(hdcSrc, ace->hbmPic); - SelectObject(hdcDes, hbmAvr); - BitBlt(hdcDes, 0, 0, ace->bmWidth, ace->bmHeight, hdcSrc, 0, 0, SRCCOPY); - SetClipboardData(CF_BITMAP, hbmAvr); - ReleaseDC(hwnd, hdc); - DeleteDC(hdcSrc); - DeleteDC(hdcDes); - } - } - else { // copy text - HGLOBAL hClipboardData = GlobalAlloc(GMEM_DDESHARE, 4096); - TCHAR *pchData = (TCHAR *)GlobalLock(hClipboardData); - pchData[0] = 0; - if (iSelItem == COPYMENU_ALLITEMS_LABELS) { // copy all items with labels - for (int i = 0; i < pwd->iRowCount; i++) { - if ((pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) || (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0])) { - if (pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) { - _tcscat(pchData, pwd->rows[i].swzLabel); - _tcscat(pchData, _T(" ")); - } - else _tcscat(pchData, TranslateT(": ")); - - if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) - _tcscat(pchData, pwd->rows[i].swzValue); - else - _tcscat(pchData, TranslateT("")); - - _tcscat(pchData, _T("\r\n")); - } - } - } - else if (iSelItem == COPYMENU_ALLITEMS) { // copy all items - for (int i = 0; i < pwd->iRowCount; i++) { - if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) { - _tcscat(pchData, pwd->rows[i].swzValue); - _tcscat(pchData, _T("\r\n")); - } - } - } - // single row - else _tcscpy(pchData, pwd->rows[iSelItem - 1].swzValue); - - GlobalUnlock(hClipboardData); - SetClipboardData(CF_UNICODETEXT, hClipboardData); - } - - CloseClipboard(); - } - } - break; - - case PUM_FADEOUTWINDOW: - // kill timers - KillTimer(hwnd, ID_TIMER_ANIMATE); - KillTimer(hwnd, ID_TIMER_CHECKMOUSE); - KillTimer(hwnd, ID_TIMER_TRAYTIP); - - if (opt.showEffect != PSE_NONE) { - if (skin.bNeedLayerUpdate) { - POINT ptSrc = { 0, 0 }; - SIZE sz = { pwd->rcWindow.right - pwd->rcWindow.left, pwd->rcWindow.bottom - pwd->rcWindow.top }; - - BLENDFUNCTION blend; - blend.BlendOp = AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = pwd->iTrans; - blend.AlphaFormat = AC_SRC_ALPHA; - - while (blend.SourceConstantAlpha != 0) { - UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); - blend.SourceConstantAlpha = max(blend.SourceConstantAlpha - opt.iAnimateSpeed, 0); - Sleep(ANIM_ELAPSE); - } - } - else { - int iTrans = pwd->iTrans; - while (iTrans != 0) { - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), iTrans, LWA_ALPHA); - iTrans = max(iTrans - opt.iAnimateSpeed, 0); - Sleep(ANIM_ELAPSE); - } - } - } - break; - - case WM_DESTROY: - ShowWindow(hwnd, SW_HIDE); - - // unregister hotkey - UnregisterHotKey(hwnd, pwd->iHotkeyId); - - DeleteObject(pwd->hpenBorder); - DeleteObject(pwd->hpenDivider); - - if (pwd->hrgnAeroGlass) - DeleteObject(pwd->hrgnAeroGlass); - - Smileys_FreeParse(pwd->spiTitle); - - for (int i = 0; i < pwd->iRowCount; i++) { - mir_free(pwd->rows[i].swzLabel); - mir_free(pwd->rows[i].swzValue); - Smileys_FreeParse(pwd->rows[i].spi); - } - - if (pwd->rows) - mir_free(pwd->rows); - pwd->rows = NULL; - - // destroy icons - for (int i = 0; i < EXICONS_COUNT; i++) { - if (pwd->extraIcons[i].hIcon == NULL) - continue; - - if (pwd->extraIcons[i].bDestroy) - DestroyIcon(pwd->extraIcons[i].hIcon); - else - Skin_ReleaseIcon(pwd->extraIcons[i].hIcon); - } - - if (pwd->clcit.swzText) { - mir_free(pwd->clcit.swzText); - pwd->clcit.swzText = NULL; - } - - mir_free(pwd); - pwd = NULL; - - if (skin.colSavedBits) mir_free(skin.colSavedBits); - if (skin.hBitmap)DeleteObject(skin.hBitmap); - if (skin.hdc) DeleteDC(skin.hdc); - skin.colSavedBits = NULL; - skin.hBitmap = NULL; - skin.hdc = NULL; - - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - break; - - case WM_TIMER: - if (wParam == ID_TIMER_ANIMATE) { - pwd->iAnimStep++; - if (pwd->iAnimStep == ANIM_STEPS) - KillTimer(hwnd, ID_TIMER_ANIMATE); - - SendMessage(hwnd, PUM_UPDATERGN, 1, 0); - } - else if (wParam == ID_TIMER_CHECKMOUSE) { - // workaround for tips that just won't go away - POINT pt; - GetCursorPos(&pt); - - // mouse has moved beyond tollerance - if (abs(pt.x - pwd->ptCursorStartPos.x) > opt.iMouseTollerance || abs(pt.y - pwd->ptCursorStartPos.y) > opt.iMouseTollerance) - PostMPMessage(MUM_DELETEPOPUP, 0, 0); - } - else if (wParam == ID_TIMER_TRAYTIP) { - KillTimer(hwnd, ID_TIMER_TRAYTIP); - SendMessage(hwnd, PUM_EXPANDTRAYTIP, 0, 0); - } - - break; - - case PUM_SETSTATUSTEXT: - if (pwd && wParam == pwd->hContact) { - db_set_ts(pwd->hContact, MODULE, "TempStatusMsg", (TCHAR *)lParam); - pwd->bIsPainted = false; - pwd->bNeedRefresh = true; - SendMessage(hwnd, PUM_REFRESH_VALUES, TRUE, 0); - InvalidateRect(hwnd, 0, TRUE); - } - - if (lParam) mir_free((void *)lParam); - return TRUE; - - case PUM_SHOWXSTATUS: - if (pwd && wParam == pwd->hContact) { - // in case we have retrieve xstatus - pwd->bIsPainted = false; - SendMessage(hwnd, PUM_REFRESH_VALUES, TRUE, 0); - InvalidateRect(hwnd, 0, TRUE); - } - return TRUE; - - case PUM_SETAVATAR: - if (pwd && wParam == pwd->hContact) { - pwd->bIsPainted = false; - SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); - SendMessage(hwnd, PUM_CALCPOS, 0, 0); - InvalidateRect(hwnd, 0, TRUE); - } - return TRUE; - - case PUM_EXPANDTRAYTIP: - pwd->bIsPainted = false; - if (skin.bNeedLayerUpdate) { - RECT r = pwd->rcWindow; - POINT ptSrc = {0, 0}; - SIZE sz = {r.right - r.left, r.bottom - r.top}; - - BLENDFUNCTION blend; - blend.BlendOp = AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = 0; - blend.AlphaFormat = AC_SRC_ALPHA; - - UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); - } - else SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 0, LWA_ALPHA); - - SendMessage(hwnd, PUM_REFRESHTRAYTIP, 1, 0); - SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); - SendMessage(hwnd, PUM_CALCPOS, 0, 0); - InvalidateRect(hwnd, 0, TRUE); - - if (opt.showEffect) { - KillTimer(hwnd, ID_TIMER_ANIMATE); - SetTimer(hwnd, ID_TIMER_ANIMATE, ANIM_ELAPSE, 0); - pwd->iAnimStep = 0; - pwd->iCurrentTrans = 0; - } - return TRUE; - - case PUM_REFRESH_VALUES: - if (pwd && pwd->clcit.szProto == 0 && !pwd->bIsTextTip) { - for (int i = 0; i < pwd->iRowCount; i++) { - mir_free(pwd->rows[i].swzLabel); - mir_free(pwd->rows[i].swzValue); - Smileys_FreeParse(pwd->rows[i].spi); - } - - if (pwd->rows) { - mir_free(pwd->rows); - pwd->rows = 0; - } - pwd->iRowCount = 0; - - DIListNode *node = opt.diList; - TCHAR buff_label[LABEL_LEN], buff[VALUE_LEN]; - while (node) { - if (node->di.bIsVisible && CheckContactType(pwd->hContact, node->di)) { - if (GetLabelText(pwd->hContact, node->di, buff_label, LABEL_LEN) && GetValueText(pwd->hContact, node->di, buff, VALUE_LEN)) { - if (node->di.bLineAbove // we have a line above - && pwd->iRowCount > 0 // and we're not the first row - && pwd->rows[pwd->iRowCount - 1].bLineAbove // and above us there's a line above - && pwd->rows[pwd->iRowCount - 1].swzLabel[0] == 0 // with no label - && pwd->rows[pwd->iRowCount - 1].swzValue[0] == 0) // and no value - { - // overwrite item above - pwd->iRowCount--; - mir_free(pwd->rows[pwd->iRowCount].swzLabel); - mir_free(pwd->rows[pwd->iRowCount].swzValue); - Smileys_FreeParse(pwd->rows[pwd->iRowCount].spi); //prevent possible mem leak - } - else pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData)* (pwd->iRowCount + 1)); - - char *szProto = GetContactProto(pwd->hContact); - - pwd->rows[pwd->iRowCount].swzLabel = mir_tstrdup(buff_label); - pwd->rows[pwd->iRowCount].swzValue = mir_tstrdup(buff); - pwd->rows[pwd->iRowCount].spi = Smileys_PreParse(buff, (int)_tcslen(buff), szProto); - pwd->rows[pwd->iRowCount].bValueNewline = node->di.bValueNewline; - pwd->rows[pwd->iRowCount].bLineAbove = node->di.bLineAbove; - pwd->iRowCount++; - } - } - node = node->next; - } - - // if the last item is just a divider, remove it - if (pwd->iRowCount > 0 - && pwd->rows[pwd->iRowCount - 1].bLineAbove // and above us there's a line above - && pwd->rows[pwd->iRowCount - 1].swzLabel[0] == 0 // with no label - && pwd->rows[pwd->iRowCount - 1].swzValue[0] == 0) // and no value - { - pwd->iRowCount--; - mir_free(pwd->rows[pwd->iRowCount].swzLabel); - mir_free(pwd->rows[pwd->iRowCount].swzValue); - Smileys_FreeParse(pwd->rows[pwd->iRowCount].spi); //prevent possible mem leak - - if (pwd->iRowCount == 0) { - mir_free(pwd->rows); - pwd->rows = 0; - } - } - - if (wParam == TRUE) { - SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); - SendMessage(hwnd, PUM_CALCPOS, 0, 0); - } - } - return TRUE; - - case PUM_GETHEIGHT: - { - int *pHeight = (int *)wParam; - HDC hdc = GetDC(hwnd); - SIZE sz; - RECT rc, smr; - rc.top = rc.left = 0; - rc.right = opt.iWinWidth; - int iWidth = opt.iPadding; - int iWinAvatarHeight = 0; - bool bStatusMsg = false; - HFONT hOldFont = (HFONT)GetCurrentObject(hdc,OBJ_FONT); - - // avatar height - pwd->iAvatarHeight = 0; - if (!pwd->bIsTextTip && opt.avatarLayout != PAV_NONE && ServiceExists(MS_AV_GETAVATARBITMAP)) { - AVATARCACHEENTRY *ace = 0; - if (pwd->hContact) ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)pwd->hContact, 0); - else ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); - - if (ace && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { - if (opt.bOriginalAvatarSize && max(ace->bmWidth, ace->bmHeight) <= opt.iAvatarSize) { - pwd->iRealAvatarHeight = ace->bmHeight; - pwd->iRealAvatarWidth = ace->bmWidth; - } - else if (ace->bmHeight >= ace->bmWidth) { - pwd->iRealAvatarHeight = opt.iAvatarSize; - pwd->iRealAvatarWidth = (int)(opt.iAvatarSize * (ace->bmWidth / (double)ace->bmHeight)); - } - else { - pwd->iRealAvatarHeight = (int)(opt.iAvatarSize * (ace->bmHeight / (double)ace->bmWidth)); - pwd->iRealAvatarWidth = opt.iAvatarSize; - } - - pwd->iAvatarHeight = opt.iOuterAvatarPadding + opt.iInnerAvatarPadding + pwd->iRealAvatarHeight; - iWinAvatarHeight = 2 * opt.iOuterAvatarPadding + pwd->iRealAvatarHeight; - iWidth += pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); - } - } - - // titlebar height - if (!pwd->bIsTextTip && pwd->swzTitle && opt.bShowTitle) { - smr.top = smr.bottom = 0; - smr.left = rc.left + opt.iPadding + pwd->iIndent; - smr.right = rc.right; - - if (pwd->iRealAvatarWidth > 0) - smr.right -= pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding; - else - smr.right -= opt.iPadding; - - if (hFontTitle) SelectObject(hdc, (HGDIOBJ)hFontTitle); - DrawTextExt(hdc, pwd->swzTitle, -1, &smr, DT_CALCRECT | DT_LEFT | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, NULL, pwd->spiTitle); - - iWidth += opt.iPadding + opt.iTitleIndent + smr.right; - pwd->iTitleHeight = opt.iPadding + smr.bottom; - } - else pwd->iTitleHeight = opt.iPadding; - - // icon height - int i, iCount = 0; - if (pwd->hContact || pwd->clcit.szProto) { - for(i = 0; i < EXICONS_COUNT; i++) { - if ((INT_PTR)pwd->extraIcons[i].hIcon == CALLSERVICE_NOTFOUND) - pwd->extraIcons[i].hIcon = 0; - - if (pwd->extraIcons[i].hIcon) - iCount++; - } - } - pwd->iIconsHeight = (iCount * 20) + 20; - - // text height - pwd->iTextHeight = pwd->iLabelWidth = 0; - // iterate once to find max label width for items with label and value on same line, but don't consider width of labels on a new line - for (i = 0; i < pwd->iRowCount; i++) { - if (pwd->rows[i].swzLabel && !pwd->rows[i].bValueNewline) { - if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { - if (hFontTrayTitle) - SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); - } - else if (hFontLabels) - SelectObject(hdc, (HGDIOBJ)hFontLabels); - - GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, (int)_tcslen(pwd->rows[i].swzLabel), &sz); - if (sz.cx > pwd->iLabelWidth) - pwd->iLabelWidth = sz.cx; - } - } - - for (i = 0; i < pwd->iRowCount; i++) { - if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { - if (hFontTrayTitle) - SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); - } - else if (hFontLabels) - SelectObject(hdc, (HGDIOBJ)hFontLabels); - - if (pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) - GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, (int)_tcslen(pwd->rows[i].swzLabel), &sz); - else - sz.cy = sz.cx = 0; - - // save so we don't have to recalculate - pwd->rows[i].iLabelHeight = sz.cy; - - smr.top = smr.bottom = 0; - smr.left = rc.left + opt.iPadding + pwd->iIndent; - smr.right = rc.right; - if (hFontValues) SelectObject(hdc, (HGDIOBJ)hFontValues); - if (pwd->iTitleHeight + pwd->iTextHeight + opt.iTextPadding < pwd->iAvatarHeight) - smr.right -= pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding; - else - smr.right -= opt.iPadding; - - if (!pwd->rows[i].bValueNewline) - smr.right -= pwd->iLabelWidth + opt.iValueIndent; - - if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) { - if (!bStatusMsg && opt.bGetNewStatusMsg) { - if (!_tcscmp(pwd->rows[i].swzValue, _T("%sys:status_msg%"))) - bStatusMsg = true; - } - - DrawTextExt(hdc, pwd->rows[i].swzValue, -1, &smr, DT_CALCRECT | DT_LEFT | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, NULL, pwd->rows[i].spi); - } - else smr.left = smr.right = 0; - - // save so we don't have to recalculate - pwd->rows[i].iValueHeight = smr.bottom; - - pwd->rows[i].iTotalHeight = (pwd->rows[i].bLineAbove ? opt.iTextPadding : 0); - if (pwd->rows[i].bValueNewline) { - if (sz.cy) pwd->rows[i].iTotalHeight += sz.cy + opt.iTextPadding; - if (smr.bottom) pwd->rows[i].iTotalHeight += smr.bottom + opt.iTextPadding; - } - else { - int maxheight = max(sz.cy, smr.bottom); - if (maxheight) pwd->rows[i].iTotalHeight += maxheight + opt.iTextPadding; - } - - // only consider this item's width, and include it's height, if it doesn't make the window too big - if (max(pwd->iTitleHeight + pwd->iTextHeight + opt.iPadding + pwd->rows[i].iTotalHeight, pwd->iAvatarHeight) <= opt.iWinMaxHeight || pwd->bIsTrayTip) { - if (iWidth < opt.iWinWidth) { - int wid = opt.iPadding + pwd->iIndent + (pwd->rows[i].bValueNewline ? max(sz.cx, smr.right - smr.left) : pwd->iLabelWidth + opt.iValueIndent + (smr.right - smr.left)); - if (pwd->iTitleHeight + pwd->iTextHeight + opt.iTextPadding < pwd->iAvatarHeight) - iWidth = max(iWidth, wid + pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding); - else - iWidth = max(iWidth, wid + opt.iPadding); - } - - pwd->iTextHeight += pwd->rows[i].iTotalHeight; - } - } - - SelectObject(hdc, hOldFont); - ReleaseDC(hwnd, hdc); - - int iHeight = max(pwd->iTitleHeight + pwd->iTextHeight + opt.iPadding, iWinAvatarHeight); - iHeight = max(pwd->iIconsHeight, iHeight); - if (bStatusMsg) iHeight += 50; - - if (iHeight < opt.iMinHeight) iHeight = opt.iMinHeight; - // ignore minwidth for text tips - if (!pwd->bIsTextTip && iWidth < opt.iMinWidth) iWidth = opt.iMinWidth; - - // ignore maxheight for tray tip - if (!pwd->bIsTrayTip && iHeight > opt.iWinMaxHeight) iHeight = opt.iWinMaxHeight; - if (iWidth > opt.iWinWidth) iWidth = opt.iWinWidth; - - CreateSkinBitmap(iWidth, iHeight, pwd->bIsTextTip && !pwd->bIsTrayTip); - - GetWindowRect(hwnd, &rc); - if (rc.right - rc.left != iWidth || rc.bottom - rc.top != iHeight) - { - SetWindowPos(hwnd, 0, 0, 0, iWidth, iHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); - GetWindowRect(hwnd, &pwd->rcWindow); - SendMessage(hwnd, PUM_UPDATERGN, 0, 0); - InvalidateRect(hwnd, 0, TRUE); - } - - if (pHeight) - *pHeight = iHeight; - } - return TRUE; - - case PUM_UPDATERGN: - { - HRGN hRgn; - RECT r = pwd->rcWindow; - int v, h; - int w = 11; - - r.right -= r.left; - r.left = 0; - r.bottom -= r.top; - r.top = 0; - - if (opt.showEffect == PSE_FADE && wParam == 1) { - if (skin.bNeedLayerUpdate) { - POINT ptSrc = { 0, 0 }; - SIZE sz = { r.right - r.left, r.bottom - r.top }; - - BLENDFUNCTION blend; - blend.BlendOp = AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = pwd->iCurrentTrans; - blend.AlphaFormat = AC_SRC_ALPHA; - - pwd->iCurrentTrans += opt.iAnimateSpeed; - if (pwd->iCurrentTrans > pwd->iTrans) { - pwd->iCurrentTrans = pwd->iTrans; - pwd->iAnimStep = ANIM_STEPS; - } - - UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); - - } - else { - pwd->iCurrentTrans += opt.iAnimateSpeed; - if (pwd->iCurrentTrans > pwd->iTrans) { - pwd->iCurrentTrans = pwd->iTrans; - pwd->iAnimStep = ANIM_STEPS; - } - - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iCurrentTrans, LWA_ALPHA); - } - } - else if (opt.showEffect == PSE_ANIMATE && pwd->bIsPainted) { - if (pwd->iAnimStep <= (ANIM_STEPS / opt.iAnimateSpeed)) { - float frac = 1.0f - pwd->iAnimStep / ((float)ANIM_STEPS / opt.iAnimateSpeed); - int wi = r.right, hi = r.bottom; - - r.left += (int)(wi / 2.0f * frac + 0.5f); - r.right -= (int)(wi / 2.0f * frac + 0.5f); - r.top += (int)(hi / 2.0f * frac + 0.5f); - r.bottom -= (int)(hi / 2.0f * frac + 0.5f); - } - else pwd->iAnimStep = ANIM_STEPS; - - if (skin.bNeedLayerUpdate) { - RECT r2 = pwd->rcWindow; - POINT ptPos = { r.left + r2.left, r.top + r2.top }; - POINT ptSrc = { r.left, r.top }; - - SIZE sz = { r.right - r.left, r.bottom - r.top }; - - BLENDFUNCTION blend; - blend.BlendOp = AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = pwd->iTrans; - blend.AlphaFormat = AC_SRC_ALPHA; - - UpdateLayeredWindow(hwnd, NULL, &ptPos, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); - } - } - - if (!skin.bNeedLayerUpdate) { - // round corners - if (opt.bRound) { - h = (r.right - r.left) > (w * 2) ? w : (r.right - r.left); - v = (r.bottom - r.top) > (w * 2) ? w : (r.bottom - r.top); - h = (h < v) ? h : v; - } - else h = 0; - - hRgn = CreateRoundRectRgn(r.left, r.top, r.right + 1, r.bottom + 1, h, h); - SetWindowRgn(hwnd, hRgn, FALSE); - - if (opt.showEffect == PSE_ANIMATE && pwd->iAnimStep == 1) - SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iTrans, LWA_ALPHA); - } - else { - // aero glass (vista+) - if (opt.bAeroGlass && MyDwmEnableBlurBehindWindow && pwd->iAnimStep > 5) { - if (pwd->hrgnAeroGlass) { - DeleteObject(pwd->hrgnAeroGlass); - pwd->hrgnAeroGlass = 0; - } - - pwd->hrgnAeroGlass = CreateOpaqueRgn(25, true); - - if (pwd->hrgnAeroGlass) { - DWM_BLURBEHIND bb = { 0 }; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.fEnable = TRUE; - bb.hRgnBlur = pwd->hrgnAeroGlass; - MyDwmEnableBlurBehindWindow(hwnd, &bb); - } - } - } - } - return TRUE; - - case PUM_CALCPOS: - { - RECT rcWork, rc; - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, FALSE); - - HMONITOR hMon = MonitorFromPoint(pwd->clcit.ptCursor, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcWork = mi.rcWork; - - GetWindowRect(hwnd, &rc); - - int x = 0, y = 0, iWidth = (rc.right - rc.left), iHeight = (rc.bottom - rc.top); - - switch (opt.pos) { - case PP_BOTTOMRIGHT: - x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); // cursor size is too large - use small icon size - y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); - break; - case PP_BOTTOMLEFT: - x = pwd->clcit.ptCursor.x - iWidth - GetSystemMetrics(SM_CXSMICON); - y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); - break; - case PP_TOPRIGHT: - x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); - y = pwd->clcit.ptCursor.y - iHeight - GetSystemMetrics(SM_CYSMICON); - break; - case PP_TOPLEFT: - x = pwd->clcit.ptCursor.x - iWidth - GetSystemMetrics(SM_CXSMICON); - y = pwd->clcit.ptCursor.y - iHeight - GetSystemMetrics(SM_CYSMICON); - break; - } - - - if (x + iWidth + 8 > rcWork.right) - x = rcWork.right - iWidth - 8; - if (x - 8 < rcWork.left) - x = rcWork.left + 8; - - if (pwd->bAllowReposition || !pwd->bNeedRefresh) { - if (y + iHeight > rcWork.bottom) { - y = pwd->clcit.ptCursor.y - iHeight - 8; - pwd->bAllowReposition = true; - } - - if (y - 8 < rcWork.top) { - y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); - pwd->bAllowReposition = true; - } - } - - SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); - GetWindowRect(hwnd, &pwd->rcWindow); - } - return TRUE; - - case PUM_REFRESHTRAYTIP: - for (int i = 0; i < pwd->iRowCount; i++) { - mir_free(pwd->rows[i].swzLabel); - mir_free(pwd->rows[i].swzValue); - Smileys_FreeParse(pwd->rows[i].spi); - } - - if (pwd->rows) { - mir_free(pwd->rows); - pwd->rows = NULL; - } - - pwd->iRowCount = 0; - - DWORD dwItems = (wParam == 0) ? opt.iFirstItems : opt.iSecondItems; - bool bFirstItem = true; - TCHAR buff[64]; - - int oldOrder = -1, iProtoCount = 0; - PROTOACCOUNT **accs; - ProtoEnumAccounts(&iProtoCount, &accs); - - for (int j = 0; j < iProtoCount; j++) { - PROTOACCOUNT *pa = NULL; - for (int i = 0; i < iProtoCount; i++) - if (accs[i]->iOrder > oldOrder && (pa == NULL || accs[i]->iOrder < pa->iOrder)) - pa = accs[i]; - - if (pa == NULL) - continue; - - oldOrder = pa->iOrder; - - WORD wStatus = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0); - if (opt.bHideOffline && wStatus == ID_STATUS_OFFLINE) - continue; - - if (!IsAccountEnabled(pa) || !IsTrayProto(pa->tszAccountName, (BOOL)wParam)) - continue; - - if (dwItems & TRAYTIP_NUMCONTACTS) { - int iCount = 0, iCountOnline = 0; - for (MCONTACT hContact = db_find_first(pa->szModuleName); hContact; hContact = db_find_next(hContact, pa->szModuleName)) { - if (db_get_w(hContact, pa->szModuleName, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) - iCountOnline++; - iCount++; - } - mir_sntprintf(buff, 64, _T("(%d/%d)"), iCountOnline, iCount); - } - else buff[0] = 0; - - TCHAR swzProto[256]; - _tcscpy(swzProto, pa->tszAccountName); - if (dwItems & TRAYTIP_LOCKSTATUS) - if (CallService(MS_PROTO_ISACCOUNTLOCKED, 0, (LPARAM)pa->szModuleName)) - mir_sntprintf(swzProto, SIZEOF(swzProto), TranslateT("%s (locked)"), pa->tszAccountName); - - AddRow(pwd, swzProto, buff, NULL, false, false, !bFirstItem, true, LoadSkinnedProtoIcon(pa->szModuleName, wStatus)); - bFirstItem = false; - - if (dwItems & TRAYTIP_LOGON) { - if (TimestampToTimeDifference(NULL, pa->szModuleName, "LogonTS", buff, 59)) { - TCHAR ago[96]; - mir_sntprintf(ago, SIZEOF(ago), TranslateT("%s ago"), buff); - AddRow(pwd, TranslateT("Log on:"), ago, NULL, false, false, false); - } - } - - if (dwItems & TRAYTIP_UNREAD_EMAILS && ProtoServiceExists(pa->szModuleName, PS_GETUNREADEMAILCOUNT)) { - int iCount = (int)ProtoCallService(pa->szModuleName, PS_GETUNREADEMAILCOUNT, 0, 0); - if (iCount > 0) { - _itot(iCount, buff, 10); - AddRow(pwd, TranslateT("Unread emails:"), buff, NULL, false, false, false); - } - } - - if (dwItems & TRAYTIP_STATUS) { - TCHAR *swzText = pcli->pfnGetStatusModeDescription(wStatus, 0); - if (swzText) - AddRow(pwd, TranslateT("Status:"), swzText, NULL, false, false, false); - } - - if (wStatus >= ID_STATUS_ONLINE && wStatus <= ID_STATUS_OUTTOLUNCH) { - if (dwItems & TRAYTIP_STATUS_MSG) { - TCHAR *swzText = GetProtoStatusMessage(pa->szModuleName, wStatus); - if (swzText) { - StripBBCodesInPlace(swzText); - AddRow(pwd, TranslateT("Status message:"), swzText, pa->szModuleName, true, true, false); - mir_free(swzText); - } - } - - if (dwItems & TRAYTIP_EXTRA_STATUS) { - // jabber mood or icq xstatus - TCHAR *swzAdvTitle = GetJabberAdvStatusText(pa->szModuleName, "mood", "title"); - if (swzAdvTitle) { - StripBBCodesInPlace(swzAdvTitle); - AddRow(pwd, TranslateT("Mood:"), swzAdvTitle, pa->szModuleName, true, false, false); - mir_free(swzAdvTitle); - - TCHAR *swzAdvText = GetJabberAdvStatusText(pa->szModuleName, "mood", "text"); - if (swzAdvText) { - StripBBCodesInPlace(swzAdvText); - AddRow(pwd, _T(""), swzAdvText, pa->szModuleName, true, true, false); - mir_free(swzAdvText); - } - } - else { - if (db_get_b(0, pa->szModuleName, "XStatusId", 0)) { - // xstatus title - swzAdvTitle = GetProtoExtraStatusTitle(pa->szModuleName); - if (swzAdvTitle) { - StripBBCodesInPlace(swzAdvTitle); - AddRow(pwd, TranslateT("xStatus:"), swzAdvTitle, pa->szModuleName, true, false, false); - mir_free(swzAdvTitle); - } - - // xstatus message - TCHAR *swzAdvText = GetProtoExtraStatusMessage(pa->szModuleName); - if (swzAdvText) { - StripBBCodesInPlace(swzAdvText); - AddRow(pwd, _T(""), swzAdvText, pa->szModuleName, true, true, false); - mir_free(swzAdvText); - } - } - } - - TCHAR *swzActTitle = GetJabberAdvStatusText(pa->szModuleName, "activity", "title"); - if (swzActTitle) { - StripBBCodesInPlace(swzActTitle); - AddRow(pwd, TranslateT("Activity:"), swzActTitle, pa->szModuleName, true, false, false); - mir_free(swzActTitle); - } - - TCHAR *swzActText = GetJabberAdvStatusText(pa->szModuleName, "activity", "text"); - if (swzActText) { - StripBBCodesInPlace(swzActText); - AddRow(pwd, _T(""), swzActText, pa->szModuleName, true, true, false); - mir_free(swzActText); - } - } - - if (dwItems & TRAYTIP_LISTENINGTO) { - TCHAR *swzListening = GetListeningTo(pa->szModuleName); - if (swzListening) { - StripBBCodesInPlace(swzListening); - AddRow(pwd, TranslateT("Listening to:"), swzListening, NULL, false, true, false); - mir_free(swzListening); - } - } - } - } - - if (dwItems & TRAYTIP_FAVCONTACTS) { - if (db_get_dw(0, MODULE, "FavouriteContactsCount", 0)) { - TCHAR swzName[256]; - TCHAR swzStatus[256]; - bool bTitlePainted = false; - int iCount = 0, iCountOnline = 0; - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - if (db_get_b(hContact, MODULE, "FavouriteContact", 0)) { - char *proto = GetContactProto(hContact); - if (proto == NULL) - continue; - - WORD wStatus = db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE); - WordToStatusDesc(hContact, proto, "Status", swzStatus, 256); - - if (wStatus != ID_STATUS_OFFLINE) - iCountOnline++; - - iCount++; - - if (!(opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE && wStatus == ID_STATUS_OFFLINE)) { - if (!bTitlePainted) { - AddRow(pwd, TranslateT("Fav. contacts"), NULL, NULL, false, false, !bFirstItem, true, NULL); - bFirstItem = false; - bTitlePainted = true; - } - - TCHAR *swzNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, hContact, GCDNF_TCHAR); - if (opt.iFavoriteContFlags & FAVCONT_APPEND_PROTO) { - TCHAR *swzProto = a2t(proto); - mir_sntprintf(swzName, 256, _T("%s (%s)"), swzNick, swzProto); - mir_free(swzProto); - } - else _tcscpy(swzName, swzNick); - - AddRow(pwd, swzName, swzStatus, NULL, false, false, false); - } - } - } - - int index = pwd->iRowCount - 1; - if (opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE) - index -= iCountOnline; - else - index -= iCount; - - if (index >= 0 && (dwItems & TRAYTIP_NUMCONTACTS) && !((opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE) && iCountOnline == 0)) { - mir_sntprintf(buff, 64, _T("(%d/%d)"), iCountOnline, iCount); - pwd->rows[index].swzValue = mir_tstrdup(buff); - } - } - } - - if (dwItems & TRAYTIP_MIRANDA_UPTIME) { - if (TimestampToTimeDifference(NULL, MODULE, "MirandaStartTS", buff, 64)) { - AddRow(pwd, TranslateT("Other"), _T(""), NULL, false, false, !bFirstItem, true, NULL); - AddRow(pwd, TranslateT("Miranda uptime:"), buff, NULL, false, false, false); - } - } - - if (dwItems & TRAYTIP_CLIST_EVENT && pwd->clcit.swzText) { - TCHAR *pchBr = _tcschr(pwd->clcit.swzText, '\n'); - TCHAR *pchBold = _tcsstr(pwd->clcit.swzText, _T("")); - - if (!pchBold || pchBold != pwd->clcit.swzText) { - TCHAR swzText[256]; - _tcscpy(swzText, pwd->clcit.swzText); - if (pchBr) swzText[pchBr - pwd->clcit.swzText] = 0; - AddRow(pwd, swzText, _T(""), NULL, false, true, false, true, LoadSkinnedIcon(SKINICON_OTHER_FILLEDBLOB)); - } - } - - return TRUE; - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} +/* +Copyright (C) 2006-2007 Scott Ellis +Copyright (C) 2007-2011 Jan Holub + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "common.h" + +__inline void AddRow(PopupWindowData *pwd, TCHAR *swzLabel, TCHAR *swzValue, char *szProto, bool bParseSmileys, bool bNewline, bool bLineAbove, bool bIsTitle = false, HICON hIcon = NULL) +{ + pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData) * (pwd->iRowCount + 1)); + pwd->rows[pwd->iRowCount].swzLabel = swzLabel ? mir_tstrdup(swzLabel) : NULL; + pwd->rows[pwd->iRowCount].swzValue = swzValue ? mir_tstrdup(swzValue) : NULL; + pwd->rows[pwd->iRowCount].spi = bParseSmileys ? Smileys_PreParse(swzValue, (int)_tcslen(swzValue), szProto) : NULL; + pwd->rows[pwd->iRowCount].bValueNewline = bNewline; + pwd->rows[pwd->iRowCount].bLineAbove = bLineAbove; + pwd->rows[pwd->iRowCount].bIsTitle = bIsTitle; + pwd->rows[pwd->iRowCount].hIcon = hIcon; + pwd->iRowCount++; +} + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PopupWindowData *pwd = (PopupWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + switch(uMsg) { + case WM_CREATE: + { + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + pwd = (PopupWindowData *)mir_alloc(sizeof(PopupWindowData)); + memset(pwd, 0, sizeof(PopupWindowData)); + pwd->clcit = *(CLCINFOTIPEX *)cs->lpCreateParams; + pwd->iIconIndex = -1; + pwd->hpenBorder = CreatePen(PS_SOLID, 1, opt.bBorder ? opt.colBorder : opt.colBg); + pwd->hpenDivider = CreatePen(PS_SOLID, 1, opt.colDivider); + pwd->iTrans = (int)(opt.iOpacity / 100.0 * 255); + + // load icons order + for (int i = 0; i < EXICONS_COUNT; i++) + pwd->bIsIconVisible[opt.exIconsOrder[i]] = opt.exIconsVis[i] ? true : false; + + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pwd); + + // work around bug hiding tips + GetCursorPos(&pwd->ptCursorStartPos); + SetTimer(hwnd, ID_TIMER_CHECKMOUSE, CHECKMOUSE_ELAPSE, 0); + + //Register copy menu hotkey (CTRL+C) + pwd->iHotkeyId = GlobalAddAtom(_T("Tipper")); + RegisterHotKey(hwnd, pwd->iHotkeyId, MOD_CONTROL, 0x43); + + if (pwd->clcit.szProto) { + pwd->bIsTextTip = false; + pwd->iIndent = opt.iTextIndent; + pwd->iSidebarWidth = opt.iSidebarWidth; + + if (ServiceExists(MS_PROTO_GETACCOUNT)) { + PROTOACCOUNT *pa = ProtoGetAccount(pwd->clcit.szProto); + if (pa) + _tcscpy(pwd->swzTitle, pa->tszAccountName); + } + + if (_tcslen(pwd->swzTitle) == 0) + a2t(pwd->clcit.szProto, pwd->swzTitle, TITLE_TEXT_LEN); + + if (CallService(MS_PROTO_ISACCOUNTLOCKED, 0, (LPARAM)pwd->clcit.szProto)) + mir_sntprintf(pwd->swzTitle, SIZEOF(pwd->swzTitle), TranslateT("%s (locked)"), pwd->swzTitle); + + // protocol status + WORD wStatus = (WORD)CallProtoService(pwd->clcit.szProto, PS_GETSTATUS, 0, 0); + + // get status icon + if (pwd->bIsIconVisible[0]) { + pwd->extraIcons[0].hIcon = LoadSkinnedProtoIcon(pwd->clcit.szProto, wStatus); + pwd->extraIcons[0].bDestroy = false; + } + + // get activity icon + if (pwd->bIsIconVisible[2]) { + pwd->extraIcons[2].hIcon = GetJabberActivityIcon(0, pwd->clcit.szProto); + pwd->extraIcons[2].bDestroy = false; + } + + // uid info + TCHAR swzUid[256], swzUidName[256]; + if (Uid(0, pwd->clcit.szProto, swzUid, 256) && UidName(pwd->clcit.szProto, swzUidName, 253)) { + _tcscat(swzUidName, _T(": ")); + AddRow(pwd, swzUidName, swzUid, NULL, false, false, false); + } + + // logon info + TCHAR swzLogon[64]; + if (TimestampToTimeDifference(NULL, pwd->clcit.szProto, "LogonTS", swzLogon, 59)) { + TCHAR ago[96]; + mir_sntprintf(ago, SIZEOF(ago), TranslateT("%s ago"), swzLogon); + AddRow(pwd, TranslateT("Log on:"), ago, NULL, false, false, false); + } + + // number of unread emails + TCHAR swzEmailCount[64]; + if (ProtoServiceExists(pwd->clcit.szProto, PS_GETUNREADEMAILCOUNT)) { + int iCount = (int)ProtoCallService(pwd->clcit.szProto, PS_GETUNREADEMAILCOUNT, 0, 0); + if (iCount > 0) { + _itot(iCount, swzEmailCount, 10); + AddRow(pwd, TranslateT("Unread emails:"), swzEmailCount, NULL, false, false, false); + } + } + + TCHAR *swzText = pcli->pfnGetStatusModeDescription(wStatus, 0); + if (swzText) + AddRow(pwd, TranslateT("Status:"), swzText, NULL, false, false, false); + + if (wStatus >= ID_STATUS_ONLINE && wStatus <= ID_STATUS_OUTTOLUNCH) { + // status message + TCHAR *swzText = GetProtoStatusMessage(pwd->clcit.szProto, wStatus); + if (swzText) { + StripBBCodesInPlace(swzText); + AddRow(pwd, TranslateT("Status message:"), swzText, pwd->clcit.szProto, true, true, true); + mir_free(swzText); + } + + // jabber mood or icq xstatus + TCHAR *swzAdvTitle = GetJabberAdvStatusText(pwd->clcit.szProto, "mood", "title"); + if (swzAdvTitle) { + StripBBCodesInPlace(swzAdvTitle); + AddRow(pwd, TranslateT("Mood:"), swzAdvTitle, pwd->clcit.szProto, true, false, true); + + TCHAR *swzAdvText = GetJabberAdvStatusText(pwd->clcit.szProto, "mood", "text"); + if (swzAdvText) { + StripBBCodesInPlace(swzAdvText); + AddRow(pwd, _T(""), swzAdvText, pwd->clcit.szProto, true, true, false); + mir_free(swzAdvText); + } + } + else { + if (db_get_b(0, pwd->clcit.szProto, "XStatusId", 0)) { + // xstatus title + swzAdvTitle = GetProtoExtraStatusTitle(pwd->clcit.szProto); + if (swzAdvTitle) { + StripBBCodesInPlace(swzAdvTitle); + AddRow(pwd, TranslateT("xStatus:"), swzAdvTitle, pwd->clcit.szProto, true, false, true); + } + + // xstatus message + TCHAR *swzAdvText = GetProtoExtraStatusMessage(pwd->clcit.szProto); + if (swzAdvText) { + StripBBCodesInPlace(swzAdvText); + AddRow(pwd, _T(""), swzAdvText, pwd->clcit.szProto, true, true, false); + mir_free(swzAdvText); + } + } + } + + if (swzAdvTitle) { + mir_free(swzAdvTitle); + // get advanced status icon + if (pwd->bIsIconVisible[1]) { + pwd->extraIcons[1].hIcon = (HICON)CallProtoService(pwd->clcit.szProto, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); + pwd->extraIcons[1].bDestroy = false; + } + } + + // jabber activity + TCHAR *swzActTitle = GetJabberAdvStatusText(pwd->clcit.szProto, "activity", "title"); + if (swzActTitle) { + StripBBCodesInPlace(swzActTitle); + AddRow(pwd, TranslateT("Activity:"), swzActTitle, pwd->clcit.szProto, true, false, true); + mir_free(swzActTitle); + } + + TCHAR *swzActText = GetJabberAdvStatusText(pwd->clcit.szProto, "activity", "text"); + if (swzActText) { + StripBBCodesInPlace(swzActText); + AddRow(pwd, _T(""), swzActText, pwd->clcit.szProto, true, true, false); + mir_free(swzActText); + } + + // listening to + TCHAR *swzListening = GetListeningTo(pwd->clcit.szProto); + if (swzListening) { + StripBBCodesInPlace(swzListening); + AddRow(pwd, TranslateT("Listening to:"), swzListening, NULL, false, true, true); + mir_free(swzListening); + } + } + } + else if (pwd->clcit.swzText) { + pwd->bIsTextTip = true; + pwd->iIndent = 0; + pwd->iSidebarWidth = 0; + + RECT rc = pwd->clcit.rcItem; + bool mirandaTrayTip = ((rc.right - rc.left) == 20) && ((rc.bottom - rc.top) == 20) ? true : false; + + if (mirandaTrayTip && !opt.bTraytip) { + MyDestroyWindow(hwnd); + return 0; + } + + if (mirandaTrayTip && opt.bHandleByTipper) { // extended tray tooltip + pwd->bIsTrayTip = true; + pwd->iIndent = opt.iTextIndent; + pwd->iSidebarWidth = opt.iSidebarWidth; + + SendMessage(hwnd, PUM_REFRESHTRAYTIP, 0, 0); + + if (opt.bExpandTraytip) + SetTimer(hwnd, ID_TIMER_TRAYTIP, opt.iExpandTime, 0); + } + else { + TCHAR buff[2048], *swzText = pwd->clcit.swzText; + size_t iBuffPos, i = 0, iSize = _tcslen(pwd->clcit.swzText); + bool bTopMessage = false; + + while (i < iSize && swzText[i] != _T('<')) { + iBuffPos = 0; + while (swzText[i] != _T('\n') && swzText[i] != _T('\r') && i < iSize && iBuffPos < 2048) { + if (swzText[i] != _T('\t')) + buff[iBuffPos++] = swzText[i]; + i++; + } + + buff[iBuffPos] = 0; + + if (iBuffPos) { + AddRow(pwd, _T(""), buff, NULL, false, true, false); + bTopMessage = true; + } + + while (i < iSize && (swzText[i] == _T('\n') || swzText[i] == _T('\r'))) + i++; + } + + // parse bold bits into labels and the rest into items + while (i < iSize) { + while (i + 2 < iSize && (swzText[i] != _T('<') || swzText[i + 1] != _T('b') || swzText[i + 2] != _T('>'))) + i++; + + i += 3; + + iBuffPos = 0; + while (i + 3 < iSize && iBuffPos < 2048 && (swzText[i] != _T('<') + || swzText[i + 1] != _T('/') || swzText[i + 2] != _T('b') || swzText[i + 3] != _T('>'))) { + if (swzText[i] != _T('\t')) + buff[iBuffPos++] = swzText[i]; + i++; + } + + i += 4; + + buff[iBuffPos] = 0; + + if (iBuffPos) { + pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData)* (pwd->iRowCount + 1)); + pwd->rows[pwd->iRowCount].bValueNewline = false; + pwd->rows[pwd->iRowCount].swzLabel = mir_tstrdup(buff); + if (pwd->iRowCount == 1 && bTopMessage) + pwd->rows[pwd->iRowCount].bLineAbove = true; + else + pwd->rows[pwd->iRowCount].bLineAbove = false; + + iBuffPos = 0; + while (i < iSize && iBuffPos < 2048 && swzText[i] != _T('\n')) { + if (swzText[i] != _T('\t') && swzText[i] != _T('\r')) + buff[iBuffPos++] = swzText[i]; + i++; + } + buff[iBuffPos] = 0; + + pwd->rows[pwd->iRowCount].swzValue = mir_tstrdup(buff); + pwd->rows[pwd->iRowCount].spi = NULL; + pwd->iRowCount++; + } + + i++; + } + + if (pwd->iRowCount == 0) { + // single item + pwd->iRowCount = 1; + pwd->rows = (RowData *)mir_alloc(sizeof(RowData)); + pwd->rows[0].bLineAbove = pwd->rows[0].bValueNewline = false; + pwd->rows[0].swzLabel = 0; + pwd->rows[0].swzValue = swzText; + pwd->rows[0].spi = NULL; + } + } + } + else { + pwd->bIsTextTip = false; + pwd->iIndent = opt.iTextIndent; + pwd->iSidebarWidth = opt.iSidebarWidth; + pwd->hContact = (MCONTACT)pwd->clcit.hItem; + pwd->iIconIndex = (int)CallService(MS_CLIST_GETCONTACTICON, pwd->hContact, 0); + + // don't use stored status message + if (!opt.bWaitForContent) + db_unset(pwd->hContact, MODULE, "TempStatusMsg"); + + TCHAR *swzNick = pcli->pfnGetContactDisplayName(pwd->hContact, 0); + _tcsncpy(pwd->swzTitle, swzNick, TITLE_TEXT_LEN); + + char *szProto = GetContactProto(pwd->hContact); + pwd->spiTitle = Smileys_PreParse(pwd->swzTitle, -1, szProto); + + // get extra icons + DBVARIANT dbv = { 0 }; + int i = 0; + + if (szProto) { + // status icon + if (pwd->bIsIconVisible[0]) { + for (i = 0; opt.exIconsOrder[i] != 0; i++); + pwd->extraIcons[i].hIcon = ImageList_GetIcon((HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0), pwd->iIconIndex, 0); + pwd->extraIcons[i].bDestroy = true; + } + + // xstatus icon + if (pwd->bIsIconVisible[1]) { + for (i = 0; opt.exIconsOrder[i] != 1; i++); + int iXstatus = db_get_b(pwd->hContact, szProto, "XStatusId", 0); + if (iXstatus) { + char szIconProto[64]; + if (strcmp(szProto, META_PROTO) != 0) + strcpy(szIconProto, szProto); + else if (!db_get_s(pwd->hContact, szProto, "XStatusProto", &dbv)) { + strcpy(szIconProto, dbv.pszVal); + db_free(&dbv); + } + + pwd->extraIcons[i].hIcon = (HICON)CallProtoService(szIconProto, PS_GETCUSTOMSTATUSICON, (WPARAM)iXstatus, LR_SHARED); + pwd->extraIcons[i].bDestroy = false; + } + } + + // activity icon + if (pwd->bIsIconVisible[2]) { + for (i = 0; opt.exIconsOrder[i] != 2; i++); + pwd->extraIcons[i].hIcon = GetJabberActivityIcon(pwd->hContact, szProto); + pwd->extraIcons[i].bDestroy = false; + } + + // gender icon + if (pwd->bIsIconVisible[3]) { + for (i = 0; opt.exIconsOrder[i] != 3; i++); + int iGender = db_get_b(pwd->hContact, "UserInfo", "Gender", 0); + if (iGender == 0) + iGender = db_get_b(pwd->hContact, szProto, "Gender", 0); + + if (iGender == GEN_FEMALE) + pwd->extraIcons[i].hIcon = Skin_GetIcon("gender_female"); + else if (iGender == GEN_MALE) + pwd->extraIcons[i].hIcon = Skin_GetIcon("gender_male"); + pwd->extraIcons[i].bDestroy = false; + } + + // flags icon + if (pwd->bIsIconVisible[4]) { + for (i = 0; opt.exIconsOrder[i] != 4; i++); + + INT_PTR iCountry = CallService(MS_FLAGS_DETECTCONTACTORIGINCOUNTRY, pwd->hContact, 0); + if (iCountry == CALLSERVICE_NOTFOUND) + iCountry = CallService(MS_FLAGS_GETCONTACTORIGINCOUNTRY, pwd->hContact, 0); + + if (iCountry != CALLSERVICE_NOTFOUND && iCountry != 0 && iCountry != CTRY_UNKNOWN && iCountry != CTRY_ERROR) { + pwd->extraIcons[i].hIcon = LoadFlagIcon(iCountry); + pwd->extraIcons[i].bDestroy = false; + } + } + + // fingerprint icon + if (pwd->bIsIconVisible[5]) { + for (i = 0; opt.exIconsOrder[i] != 5; i++); + if (ServiceExists(MS_FP_GETCLIENTICONT)) { + if (!db_get_ts(pwd->hContact, szProto, "MirVer", &dbv)) { + pwd->extraIcons[i].hIcon = Finger_GetClientIcon(dbv.ptszVal, 0); + pwd->extraIcons[i].bDestroy = true; + db_free(&dbv); + } + } + } + + //request xstatus details + if (opt.bRetrieveXstatus) + if (!db_get_b(0, szProto, "XStatusAuto", 1) && ProtoServiceExists(szProto, PS_ICQ_REQUESTCUSTOMSTATUS)) + CallProtoService(szProto, PS_ICQ_REQUESTCUSTOMSTATUS, pwd->hContact, 0); + } + + SendMessage(hwnd, PUM_REFRESH_VALUES, FALSE, 0); + } + + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + + // transparency + SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); + + if (opt.bDropShadow && opt.skinMode == SM_COLORFILL) + SetClassLongPtr(hwnd, GCL_STYLE, CS_DROPSHADOW); + else + SetClassLongPtr(hwnd, GCL_STYLE, 0); + + if (!skin.bNeedLayerUpdate) + SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_ALPHA); + + if (opt.showEffect) + SetTimer(hwnd, ID_TIMER_ANIMATE, ANIM_ELAPSE, 0); + + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + InvalidateRect(hwnd, 0, FALSE); + + // since tipper win is topmost, this should put it at top of topmost windows + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + return 0; + + case WM_ERASEBKGND: + if (!skin.bNeedLayerUpdate) { + HDC hdc = (HDC)wParam; + RECT rc; + GetClientRect(hwnd, &rc); + + BitBlt(hdc, 0, 0, skin.iWidth, skin.iHeight, skin.hdc, 0, 0, SRCCOPY); + + // border + if (opt.bBorder) { + HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenBorder); + + int h = 0; + if (opt.bRound) { + int v; + int w = 14; + h = (rc.right - rc.left) > (w * 2) ? w : (rc.right - rc.left); + v = (rc.bottom - rc.top) > (w * 2) ? w : (rc.bottom - rc.top); + h = (h < v) ? h : v; + } + + RoundRect(hdc, 0, 0, (rc.right - rc.left), (rc.bottom - rc.top), h, h); + + SelectObject(hdc, hOldBrush); + SelectObject(hdc, hOldPen); + } + } + return TRUE; + + case WM_PAINT: + { + RECT r, r2; + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + HDC hdc = skin.bNeedLayerUpdate ? skin.hdc : ps.hdc; + + GetClientRect(hwnd, &r); + r2 = r; + HFONT hOldFont = (HFONT)GetCurrentObject(hdc,OBJ_FONT); + + // text background + SetBkMode(hdc, TRANSPARENT); + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = 255; + blend.AlphaFormat = AC_SRC_ALPHA; + + // avatar + if (!pwd->bIsTextTip && opt.avatarLayout != PAV_NONE && pwd->iAvatarHeight) { + RECT rcAvatar; + rcAvatar.top = opt.iOuterAvatarPadding; + + if (opt.avatarLayout == PAV_LEFT) { + rcAvatar.left = r.left + opt.iOuterAvatarPadding; + rcAvatar.right = rcAvatar.left + pwd->iRealAvatarWidth; + r2.left += pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); // padding re-added for text + } + else if (opt.avatarLayout == PAV_RIGHT) { + rcAvatar.right = r.right - opt.iOuterAvatarPadding; + rcAvatar.left = rcAvatar.right - pwd->iRealAvatarWidth; + r2.right -= pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); + } + + rcAvatar.bottom = rcAvatar.top + pwd->iRealAvatarHeight; + + AVATARCACHEENTRY *ace = 0; + if (pwd->hContact) + ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, pwd->hContact, 0); + else + ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); + + if (ace && ace->hbmPic && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { + ResizeBitmap rb = { 0 }; + rb.size = sizeof(rb); + rb.max_width = pwd->iRealAvatarWidth; + rb.max_height = pwd->iRealAvatarHeight; + rb.fit = RESIZEBITMAP_STRETCH | RESIZEBITMAP_KEEP_PROPORTIONS; + rb.hBmp = ace->hbmPic; + HBITMAP hbmpAvatar = (HBITMAP)CallService(MS_IMG_RESIZE, (WPARAM)&rb, 0); + + if (hbmpAvatar) { + HRGN hrgnAvatar = 0; + if (opt.bAvatarRound) { + hrgnAvatar = CreateRoundRectRgn(rcAvatar.left, rcAvatar.top, rcAvatar.right + 1, rcAvatar.bottom + 1, 9, 9); + SelectClipRgn(hdc, hrgnAvatar); + } + + BITMAP bm; + GetObject(hbmpAvatar, sizeof(bm), &bm); + HDC hdcMem = CreateCompatibleDC(hdc); + SelectObject(hdcMem, hbmpAvatar); + + blend.SourceConstantAlpha = (BYTE)(opt.iAvatarOpacity / 100.0 * 255); + AlphaBlend(hdc, rcAvatar.left, rcAvatar.top, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, blend); + blend.SourceConstantAlpha = 255; + + if (opt.bAvatarBorder) { + SaveAlpha(&rcAvatar); + HBRUSH hbrBorder = CreateSolidBrush(opt.colAvatarBorder); + if (opt.bAvatarRound) + FrameRgn(hdc, hrgnAvatar, hbrBorder, 1, 1); + else + FrameRect(hdc, &rcAvatar, hbrBorder); + + DeleteObject(hbrBorder); + RestoreAlpha(&rcAvatar, (BYTE)(opt.iAvatarOpacity / 100.0 * 255)); + } + + if (hrgnAvatar) { + SelectClipRgn(hdc, 0); + DeleteObject(hrgnAvatar); + } + + if (hbmpAvatar != ace->hbmPic) + DeleteObject(hbmpAvatar); + + DeleteDC(hdcMem); + } + } + } + + RECT tr; + tr.left = r2.left + opt.iPadding + opt.iTitleIndent; + tr.right = r2.right - opt.iPadding; + tr.top = 0; + tr.bottom = opt.iPadding; + + if (!pwd->bIsTextTip) { + if (opt.titleIconLayout != PTL_NOICON) { + // draw icons + int iIconX, iIconY; + iIconY = opt.iPadding + opt.iTextPadding; + + if (opt.titleIconLayout == PTL_RIGHTICON) + iIconX = r2.right - 16 - opt.iPadding; + else + iIconX = r2.left + opt.iPadding; + + for (int i = 0; i < EXICONS_COUNT; i++) { + if (pwd->extraIcons[i].hIcon) { + DrawIconExAlpha(hdc, iIconX, iIconY, pwd->extraIcons[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); + iIconY += 20; + } + } + } + + // title text + if (opt.bShowTitle) { + if (hFontTitle) SelectObject(hdc, (HGDIOBJ)hFontTitle); + SetTextColor(hdc, opt.colTitle); + tr.top = opt.iPadding; + tr.bottom = tr.top + pwd->iTitleHeight - opt.iPadding; + UINT uTextFormat = DT_TOP | DT_LEFT | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX; + DrawTextExt(hdc, pwd->swzTitle, -1, &tr, uTextFormat, NULL, pwd->spiTitle); + } + } + + // values + pwd->iTextHeight = 0; + bool bIconPainted, bUseRect = true; + int iRowHeight; + for (int i = 0; i < pwd->iRowCount; i++) { + tr.top = tr.bottom; + bUseRect = (tr.top + opt.iTextPadding >= pwd->iAvatarHeight); + bIconPainted = false; + if (bUseRect) { + if (pwd->rows[i].bLineAbove) { + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenDivider); + tr.top += opt.iTextPadding; + RECT rec; + SetRect(&rec, r.left + opt.iPadding + pwd->iIndent, tr.top, r.right - opt.iPadding, tr.top + 1); + SaveAlpha(&rec); + Rectangle(hdc, rec.left, rec.top, rec.right, rec.bottom); + RestoreAlpha(&rec); + SelectObject(hdc, hOldPen); + } + + tr.left = r.left + opt.iPadding + pwd->iIndent; + if (pwd->rows[i].bValueNewline) + tr.right = r.right - opt.iPadding; + else + tr.right = r.left + opt.iPadding + pwd->iIndent + pwd->iLabelWidth; + } + else { + if (pwd->rows[i].bLineAbove) { + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->hpenDivider); + tr.top += opt.iTextPadding; + RECT rec; + SetRect(&rec, r2.left + opt.iPadding + pwd->iIndent, tr.top, r2.right - opt.iPadding, tr.top + 1); + SaveAlpha(&rec); + Rectangle(hdc, rec.left, rec.top, rec.right, rec.bottom); + RestoreAlpha(&rec); + SelectObject(hdc, hOldPen); + } + + tr.left = r2.left + opt.iPadding + pwd->iIndent; + if (pwd->rows[i].bValueNewline) + tr.right = r2.right - opt.iPadding; + else + tr.right = r2.left + opt.iPadding + pwd->iIndent + pwd->iLabelWidth; + } + + if (pwd->rows[i].bValueNewline) + iRowHeight = pwd->rows[i].iLabelHeight; + else + iRowHeight = max(pwd->rows[i].iLabelHeight, pwd->rows[i].iValueHeight); + + if (pwd->rows[i].iLabelHeight) { + tr.top += opt.iTextPadding; + tr.bottom = tr.top + iRowHeight; + + if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { + if (hFontTrayTitle) SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); + SetTextColor(hdc, opt.colTrayTitle); + } + else { + if (hFontLabels) SelectObject(hdc, (HGDIOBJ)hFontLabels); + SetTextColor(hdc, opt.colLabel); + } + + // status icon in tray tooltip + if (opt.titleIconLayout != PTL_NOICON && pwd->bIsTrayTip && pwd->rows[i].hIcon) { + DrawIconExAlpha(hdc, opt.iPadding, tr.top + (pwd->rows[i].iLabelHeight - 16) / 2, pwd->rows[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); + bIconPainted = true; + } + + DrawTextAlpha(hdc, pwd->rows[i].swzLabel, -1, &tr, opt.iLabelValign | ((opt.iLabelHalign == DT_RIGHT && !pwd->rows[i].bValueNewline) ? DT_RIGHT : DT_LEFT) | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); + if (pwd->rows[i].bValueNewline) + tr.top = tr.bottom; + } + else tr.bottom = tr.top; + + if (pwd->rows[i].bValueNewline) + iRowHeight = pwd->rows[i].iValueHeight; + + if (hFontValues) + SelectObject(hdc, (HGDIOBJ)hFontValues); + SetTextColor(hdc, opt.colValue); + if (bUseRect) { + tr.left = r.left + opt.iPadding + pwd->iIndent; + if (!pwd->rows[i].bValueNewline) + tr.left += pwd->iLabelWidth + opt.iValueIndent; + + tr.right = r.right - opt.iPadding; + } + else { + tr.left = r2.left + opt.iPadding + pwd->iIndent; + if (!pwd->rows[i].bValueNewline) + tr.left += pwd->iLabelWidth + opt.iValueIndent; + + tr.right = r2.right - opt.iPadding; + } + + if (pwd->rows[i].iValueHeight) { + if (pwd->rows[i].bValueNewline || !pwd->rows[i].iLabelHeight) tr.top += opt.iTextPadding; + if (pwd->rows[i].iLabelHeight > pwd->rows[i].iValueHeight && pwd->bIsTextTip&& pwd->rows[i].bIsTitle) + tr.top = tr.bottom - pwd->rows[i].iValueHeight - 2; + else + tr.bottom = tr.top + iRowHeight; + + if (opt.titleIconLayout != PTL_NOICON && pwd->bIsTrayTip && pwd->rows[i].hIcon && !bIconPainted) + DrawIconExAlpha(hdc, opt.iPadding, tr.top + (pwd->rows[i].iValueHeight - 16) / 2, pwd->rows[i].hIcon, 16, 16, 0, NULL, DI_NORMAL, false); + + UINT uFormat = opt.iValueValign | opt.iValueHalign | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX; + DrawTextExt(hdc, pwd->rows[i].swzValue, -1, &tr, uFormat, NULL, pwd->rows[i].spi); + } + } + + PremultipleChannels(); + + if (opt.showEffect == PSE_NONE) { + if (skin.bNeedLayerUpdate) { + POINT ptSrc = { 0, 0 }; + SIZE szTip = { r.right - r.left, r.bottom - r.top }; + blend.SourceConstantAlpha = pwd->iTrans; + UpdateLayeredWindow(hwnd, NULL, NULL, &szTip, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); + + if (opt.bAeroGlass && MyDwmEnableBlurBehindWindow) { + DWM_BLURBEHIND bb = { 0 }; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.fEnable = TRUE; + bb.hRgnBlur = CreateOpaqueRgn(25, true); + MyDwmEnableBlurBehindWindow(hwnd, &bb); + } + } + else SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iTrans, LWA_ALPHA); + } + + SelectObject(hdc, hOldFont); + EndPaint(hwnd, &ps); + pwd->bIsPainted = true; + } + return 0; + + case WM_HOTKEY: + if (LOWORD(lParam) == MOD_CONTROL && HIWORD(lParam) == 0x43) { // CTRL+C + if (pwd->iRowCount == 0) + return 0; + + ShowWindow(hwnd, SW_HIDE); + HMENU hMenu = CreatePopupMenu(); + if (!hMenu) + return 0; + + HICON hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ITEM_ALL), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); + if (!hIcon) { + DestroyMenu(hMenu); + return 0; + } + + ICONINFO iconInfo; + GetIconInfo(hIcon, &iconInfo); + HBITMAP hbmpAllItems = iconInfo.hbmColor; + DestroyIcon(hIcon); + + AppendMenu(hMenu, MF_STRING, COPYMENU_ALLITEMS_LABELS, LPGENT("Copy all items with labels")); + AppendMenu(hMenu, MF_STRING, COPYMENU_ALLITEMS, LPGENT("Copy all items")); + if (pwd->clcit.szProto || pwd->hContact) + AppendMenu(hMenu, MF_STRING, COPYMENU_AVATAR, LPGENT("Copy avatar")); + AppendMenu(hMenu, MF_SEPARATOR, 2000, 0); + TranslateMenu(hMenu); + + SetMenuItemBitmaps(hMenu, COPYMENU_ALLITEMS_LABELS, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); + SetMenuItemBitmaps(hMenu, COPYMENU_ALLITEMS, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); + SetMenuItemBitmaps(hMenu, COPYMENU_AVATAR, MF_BYCOMMAND, hbmpAllItems, hbmpAllItems); + + hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ITEM), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); + if (!hIcon) { + DeleteObject(hbmpAllItems); + DestroyMenu(hMenu); + return 0; + } + + GetIconInfo(hIcon, &iconInfo); + HBITMAP hbmpItem = iconInfo.hbmColor; + DestroyIcon(hIcon); + + for (int i = 0; i < pwd->iRowCount; i++) { + if (pwd->rows[i].swzValue) { + TCHAR buff[128]; + int iLen = (int)_tcslen(pwd->rows[i].swzValue); + if (iLen) { + if (iLen > MAX_VALUE_LEN) { + _tcsncpy(buff, pwd->rows[i].swzValue, MAX_VALUE_LEN); + buff[MAX_VALUE_LEN] = 0; + _tcscat(buff, _T("...")); + } + else _tcscpy(buff, pwd->rows[i].swzValue); + + AppendMenu(hMenu, MF_STRING, i + 1, buff); // first id = 1, because no select have id = 0 + SetMenuItemBitmaps(hMenu, i + 1, MF_BYCOMMAND, hbmpItem, hbmpItem); + } + else AppendMenu(hMenu, MF_SEPARATOR, 0, 0); + } + } + + POINT pt; + GetCursorPos(&pt); + SetForegroundWindow(hwnd); + int iSelItem = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, 0); + DeleteObject(hbmpAllItems); + DeleteObject(hbmpItem); + DestroyMenu(hMenu); + + if (iSelItem == 0) + return 0; // no item was selected + + if (OpenClipboard(NULL)) { + EmptyClipboard(); + if (iSelItem == COPYMENU_AVATAR) { // copy avatar + AVATARCACHEENTRY *ace = 0; + if (pwd->hContact) + ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, pwd->hContact, 0); + else + ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); + if (ace && ace->hbmPic && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { + HDC hdc = GetDC(hwnd); + HDC hdcSrc = CreateCompatibleDC(hdc); + HDC hdcDes = CreateCompatibleDC(hdc); + HBITMAP hbmAvr = CreateCompatibleBitmap(hdc, ace->bmWidth, ace->bmHeight); + SelectObject(hdcSrc, ace->hbmPic); + SelectObject(hdcDes, hbmAvr); + BitBlt(hdcDes, 0, 0, ace->bmWidth, ace->bmHeight, hdcSrc, 0, 0, SRCCOPY); + SetClipboardData(CF_BITMAP, hbmAvr); + ReleaseDC(hwnd, hdc); + DeleteDC(hdcSrc); + DeleteDC(hdcDes); + } + } + else { // copy text + HGLOBAL hClipboardData = GlobalAlloc(GMEM_DDESHARE, 4096); + TCHAR *pchData = (TCHAR *)GlobalLock(hClipboardData); + pchData[0] = 0; + if (iSelItem == COPYMENU_ALLITEMS_LABELS) { // copy all items with labels + for (int i = 0; i < pwd->iRowCount; i++) { + if ((pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) || (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0])) { + if (pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) { + _tcscat(pchData, pwd->rows[i].swzLabel); + _tcscat(pchData, _T(" ")); + } + else _tcscat(pchData, TranslateT(": ")); + + if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) + _tcscat(pchData, pwd->rows[i].swzValue); + else + _tcscat(pchData, TranslateT("")); + + _tcscat(pchData, _T("\r\n")); + } + } + } + else if (iSelItem == COPYMENU_ALLITEMS) { // copy all items + for (int i = 0; i < pwd->iRowCount; i++) { + if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) { + _tcscat(pchData, pwd->rows[i].swzValue); + _tcscat(pchData, _T("\r\n")); + } + } + } + // single row + else _tcscpy(pchData, pwd->rows[iSelItem - 1].swzValue); + + GlobalUnlock(hClipboardData); + SetClipboardData(CF_UNICODETEXT, hClipboardData); + } + + CloseClipboard(); + } + } + break; + + case PUM_FADEOUTWINDOW: + // kill timers + KillTimer(hwnd, ID_TIMER_ANIMATE); + KillTimer(hwnd, ID_TIMER_CHECKMOUSE); + KillTimer(hwnd, ID_TIMER_TRAYTIP); + + if (opt.showEffect != PSE_NONE) { + if (skin.bNeedLayerUpdate) { + POINT ptSrc = { 0, 0 }; + SIZE sz = { pwd->rcWindow.right - pwd->rcWindow.left, pwd->rcWindow.bottom - pwd->rcWindow.top }; + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = pwd->iTrans; + blend.AlphaFormat = AC_SRC_ALPHA; + + while (blend.SourceConstantAlpha != 0) { + UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); + blend.SourceConstantAlpha = max(blend.SourceConstantAlpha - opt.iAnimateSpeed, 0); + Sleep(ANIM_ELAPSE); + } + } + else { + int iTrans = pwd->iTrans; + while (iTrans != 0) { + SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), iTrans, LWA_ALPHA); + iTrans = max(iTrans - opt.iAnimateSpeed, 0); + Sleep(ANIM_ELAPSE); + } + } + } + break; + + case WM_DESTROY: + ShowWindow(hwnd, SW_HIDE); + + mir_free(skin.colSavedBits); + if (skin.hBitmap) + DeleteObject(skin.hBitmap); + if (skin.hdc) + DeleteDC(skin.hdc); + skin.colSavedBits = NULL; + skin.hBitmap = NULL; + skin.hdc = NULL; + + if (pwd == NULL) + break; + // unregister hotkey + UnregisterHotKey(hwnd, pwd->iHotkeyId); + + DeleteObject(pwd->hpenBorder); + DeleteObject(pwd->hpenDivider); + + if (pwd->hrgnAeroGlass) + DeleteObject(pwd->hrgnAeroGlass); + + Smileys_FreeParse(pwd->spiTitle); + + for (int i = 0; i < pwd->iRowCount && pwd->rows != NULL; i++) { + mir_free(pwd->rows[i].swzLabel); + mir_free(pwd->rows[i].swzValue); + Smileys_FreeParse(pwd->rows[i].spi); + } + mir_free(pwd->rows); + + // destroy icons + for (int i = 0; i < EXICONS_COUNT; i++) { + if (pwd->extraIcons[i].hIcon == NULL) + continue; + + if (pwd->extraIcons[i].bDestroy) + DestroyIcon(pwd->extraIcons[i].hIcon); + else + Skin_ReleaseIcon(pwd->extraIcons[i].hIcon); + } + + mir_free(pwd->clcit.swzText); + mir_free(pwd); + pwd = NULL; + + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + break; + + case WM_TIMER: + switch (wParam) { + case ID_TIMER_ANIMATE: + pwd->iAnimStep ++; + if (pwd->iAnimStep == ANIM_STEPS) + KillTimer(hwnd, ID_TIMER_ANIMATE); + + SendMessage(hwnd, PUM_UPDATERGN, 1, 0); + break; + case ID_TIMER_CHECKMOUSE: + { + // workaround for tips that just won't go away + POINT pt; + + GetCursorPos(&pt); + // mouse has moved beyond tollerance + if (abs(pt.x - pwd->ptCursorStartPos.x) > opt.iMouseTollerance || abs(pt.y - pwd->ptCursorStartPos.y) > opt.iMouseTollerance) + PostMPMessage(MUM_DELETEPOPUP, 0, 0); + } + break; + case ID_TIMER_TRAYTIP: + KillTimer(hwnd, ID_TIMER_TRAYTIP); + SendMessage(hwnd, PUM_EXPANDTRAYTIP, 0, 0); + break; + } + break; + + case PUM_SETSTATUSTEXT: + if (pwd && wParam == pwd->hContact) { + db_set_ts(pwd->hContact, MODULE, "TempStatusMsg", (TCHAR *)lParam); + pwd->bIsPainted = false; + pwd->bNeedRefresh = true; + SendMessage(hwnd, PUM_REFRESH_VALUES, TRUE, 0); + InvalidateRect(hwnd, 0, TRUE); + } + mir_free((void *)lParam); + return TRUE; + + case PUM_SHOWXSTATUS: + if (pwd && wParam == pwd->hContact) { + // in case we have retrieve xstatus + pwd->bIsPainted = false; + SendMessage(hwnd, PUM_REFRESH_VALUES, TRUE, 0); + InvalidateRect(hwnd, 0, TRUE); + } + return TRUE; + + case PUM_SETAVATAR: + if (pwd && wParam == pwd->hContact) { + pwd->bIsPainted = false; + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + } + return TRUE; + + case PUM_EXPANDTRAYTIP: + pwd->bIsPainted = false; + if (skin.bNeedLayerUpdate) { + RECT r = pwd->rcWindow; + POINT ptSrc = {0, 0}; + SIZE sz = {r.right - r.left, r.bottom - r.top}; + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = 0; + blend.AlphaFormat = AC_SRC_ALPHA; + + UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); + } + else SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 0, LWA_ALPHA); + + SendMessage(hwnd, PUM_REFRESHTRAYTIP, 1, 0); + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + + if (opt.showEffect) { + KillTimer(hwnd, ID_TIMER_ANIMATE); + SetTimer(hwnd, ID_TIMER_ANIMATE, ANIM_ELAPSE, 0); + pwd->iAnimStep = 0; + pwd->iCurrentTrans = 0; + } + return TRUE; + + case PUM_REFRESH_VALUES: + if (pwd && pwd->clcit.szProto == 0 && !pwd->bIsTextTip) { + for (int i = 0; i < pwd->iRowCount; i++) { + mir_free(pwd->rows[i].swzLabel); + mir_free(pwd->rows[i].swzValue); + Smileys_FreeParse(pwd->rows[i].spi); + } + + mir_free(pwd->rows); + pwd->rows = NULL; + pwd->iRowCount = 0; + + DIListNode *node = opt.diList; + TCHAR buff_label[LABEL_LEN], buff[VALUE_LEN]; + while (node) { + if (node->di.bIsVisible && CheckContactType(pwd->hContact, node->di)) { + if (GetLabelText(pwd->hContact, node->di, buff_label, LABEL_LEN) && GetValueText(pwd->hContact, node->di, buff, VALUE_LEN)) { + if (node->di.bLineAbove // we have a line above + && pwd->iRowCount > 0 // and we're not the first row + && pwd->rows[pwd->iRowCount - 1].bLineAbove // and above us there's a line above + && pwd->rows[pwd->iRowCount - 1].swzLabel[0] == 0 // with no label + && pwd->rows[pwd->iRowCount - 1].swzValue[0] == 0) // and no value + { + // overwrite item above + pwd->iRowCount--; + mir_free(pwd->rows[pwd->iRowCount].swzLabel); + mir_free(pwd->rows[pwd->iRowCount].swzValue); + Smileys_FreeParse(pwd->rows[pwd->iRowCount].spi); //prevent possible mem leak + } + else pwd->rows = (RowData *)mir_realloc(pwd->rows, sizeof(RowData)* (pwd->iRowCount + 1)); + + char *szProto = GetContactProto(pwd->hContact); + + pwd->rows[pwd->iRowCount].swzLabel = mir_tstrdup(buff_label); + pwd->rows[pwd->iRowCount].swzValue = mir_tstrdup(buff); + pwd->rows[pwd->iRowCount].spi = Smileys_PreParse(buff, (int)_tcslen(buff), szProto); + pwd->rows[pwd->iRowCount].bValueNewline = node->di.bValueNewline; + pwd->rows[pwd->iRowCount].bLineAbove = node->di.bLineAbove; + pwd->iRowCount++; + } + } + node = node->next; + } + + // if the last item is just a divider, remove it + if (pwd->iRowCount > 0 + && pwd->rows[pwd->iRowCount - 1].bLineAbove // and above us there's a line above + && pwd->rows[pwd->iRowCount - 1].swzLabel[0] == 0 // with no label + && pwd->rows[pwd->iRowCount - 1].swzValue[0] == 0) // and no value + { + pwd->iRowCount--; + mir_free(pwd->rows[pwd->iRowCount].swzLabel); + mir_free(pwd->rows[pwd->iRowCount].swzValue); + Smileys_FreeParse(pwd->rows[pwd->iRowCount].spi); //prevent possible mem leak + + if (pwd->iRowCount == 0) { + mir_free(pwd->rows); + pwd->rows = NULL; + } + } + + if (wParam == TRUE) { + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + } + } + return TRUE; + + case PUM_GETHEIGHT: + { + int *pHeight = (int *)wParam; + HDC hdc = GetDC(hwnd); + SIZE sz; + RECT rc, smr; + rc.top = rc.left = 0; + rc.right = opt.iWinWidth; + int iWidth = opt.iPadding; + int iWinAvatarHeight = 0; + bool bStatusMsg = false; + HFONT hOldFont = (HFONT)GetCurrentObject(hdc,OBJ_FONT); + + // avatar height + pwd->iAvatarHeight = 0; + if (!pwd->bIsTextTip && opt.avatarLayout != PAV_NONE && ServiceExists(MS_AV_GETAVATARBITMAP)) { + AVATARCACHEENTRY *ace = 0; + if (pwd->hContact) ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)pwd->hContact, 0); + else ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.szProto); + + if (ace && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { + if (opt.bOriginalAvatarSize && max(ace->bmWidth, ace->bmHeight) <= opt.iAvatarSize) { + pwd->iRealAvatarHeight = ace->bmHeight; + pwd->iRealAvatarWidth = ace->bmWidth; + } + else if (ace->bmHeight >= ace->bmWidth) { + pwd->iRealAvatarHeight = opt.iAvatarSize; + pwd->iRealAvatarWidth = (int)(opt.iAvatarSize * (ace->bmWidth / (double)ace->bmHeight)); + } + else { + pwd->iRealAvatarHeight = (int)(opt.iAvatarSize * (ace->bmHeight / (double)ace->bmWidth)); + pwd->iRealAvatarWidth = opt.iAvatarSize; + } + + pwd->iAvatarHeight = opt.iOuterAvatarPadding + opt.iInnerAvatarPadding + pwd->iRealAvatarHeight; + iWinAvatarHeight = 2 * opt.iOuterAvatarPadding + pwd->iRealAvatarHeight; + iWidth += pwd->iRealAvatarWidth + (opt.iOuterAvatarPadding + opt.iInnerAvatarPadding - opt.iPadding); + } + } + + // titlebar height + if (!pwd->bIsTextTip && pwd->swzTitle && opt.bShowTitle) { + smr.top = smr.bottom = 0; + smr.left = rc.left + opt.iPadding + pwd->iIndent; + smr.right = rc.right; + + if (pwd->iRealAvatarWidth > 0) + smr.right -= pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding; + else + smr.right -= opt.iPadding; + + if (hFontTitle) SelectObject(hdc, (HGDIOBJ)hFontTitle); + DrawTextExt(hdc, pwd->swzTitle, -1, &smr, DT_CALCRECT | DT_LEFT | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, NULL, pwd->spiTitle); + + iWidth += opt.iPadding + opt.iTitleIndent + smr.right; + pwd->iTitleHeight = opt.iPadding + smr.bottom; + } + else pwd->iTitleHeight = opt.iPadding; + + // icon height + int i, iCount = 0; + if (pwd->hContact || pwd->clcit.szProto) { + for(i = 0; i < EXICONS_COUNT; i++) { + if ((INT_PTR)pwd->extraIcons[i].hIcon == CALLSERVICE_NOTFOUND) + pwd->extraIcons[i].hIcon = 0; + + if (pwd->extraIcons[i].hIcon) + iCount++; + } + } + pwd->iIconsHeight = (iCount * 20) + 20; + + // text height + pwd->iTextHeight = pwd->iLabelWidth = 0; + // iterate once to find max label width for items with label and value on same line, but don't consider width of labels on a new line + for (i = 0; i < pwd->iRowCount; i++) { + if (pwd->rows[i].swzLabel && !pwd->rows[i].bValueNewline) { + if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { + if (hFontTrayTitle) + SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); + } + else if (hFontLabels) + SelectObject(hdc, (HGDIOBJ)hFontLabels); + + GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, (int)_tcslen(pwd->rows[i].swzLabel), &sz); + if (sz.cx > pwd->iLabelWidth) + pwd->iLabelWidth = sz.cx; + } + } + + for (i = 0; i < pwd->iRowCount; i++) { + if (pwd->bIsTrayTip && pwd->rows[i].bIsTitle) { + if (hFontTrayTitle) + SelectObject(hdc, (HGDIOBJ)hFontTrayTitle); + } + else if (hFontLabels) + SelectObject(hdc, (HGDIOBJ)hFontLabels); + + if (pwd->rows[i].swzLabel && pwd->rows[i].swzLabel[0]) + GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, (int)_tcslen(pwd->rows[i].swzLabel), &sz); + else + sz.cy = sz.cx = 0; + + // save so we don't have to recalculate + pwd->rows[i].iLabelHeight = sz.cy; + + smr.top = smr.bottom = 0; + smr.left = rc.left + opt.iPadding + pwd->iIndent; + smr.right = rc.right; + if (hFontValues) SelectObject(hdc, (HGDIOBJ)hFontValues); + if (pwd->iTitleHeight + pwd->iTextHeight + opt.iTextPadding < pwd->iAvatarHeight) + smr.right -= pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding; + else + smr.right -= opt.iPadding; + + if (!pwd->rows[i].bValueNewline) + smr.right -= pwd->iLabelWidth + opt.iValueIndent; + + if (pwd->rows[i].swzValue && pwd->rows[i].swzValue[0]) { + if (!bStatusMsg && opt.bGetNewStatusMsg) { + if (!_tcscmp(pwd->rows[i].swzValue, _T("%sys:status_msg%"))) + bStatusMsg = true; + } + + DrawTextExt(hdc, pwd->rows[i].swzValue, -1, &smr, DT_CALCRECT | DT_LEFT | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, NULL, pwd->rows[i].spi); + } + else smr.left = smr.right = 0; + + // save so we don't have to recalculate + pwd->rows[i].iValueHeight = smr.bottom; + + pwd->rows[i].iTotalHeight = (pwd->rows[i].bLineAbove ? opt.iTextPadding : 0); + if (pwd->rows[i].bValueNewline) { + if (sz.cy) pwd->rows[i].iTotalHeight += sz.cy + opt.iTextPadding; + if (smr.bottom) pwd->rows[i].iTotalHeight += smr.bottom + opt.iTextPadding; + } + else { + int maxheight = max(sz.cy, smr.bottom); + if (maxheight) pwd->rows[i].iTotalHeight += maxheight + opt.iTextPadding; + } + + // only consider this item's width, and include it's height, if it doesn't make the window too big + if (max(pwd->iTitleHeight + pwd->iTextHeight + opt.iPadding + pwd->rows[i].iTotalHeight, pwd->iAvatarHeight) <= opt.iWinMaxHeight || pwd->bIsTrayTip) { + if (iWidth < opt.iWinWidth) { + int wid = opt.iPadding + pwd->iIndent + (pwd->rows[i].bValueNewline ? max(sz.cx, smr.right - smr.left) : pwd->iLabelWidth + opt.iValueIndent + (smr.right - smr.left)); + if (pwd->iTitleHeight + pwd->iTextHeight + opt.iTextPadding < pwd->iAvatarHeight) + iWidth = max(iWidth, wid + pwd->iRealAvatarWidth + opt.iOuterAvatarPadding + opt.iInnerAvatarPadding); + else + iWidth = max(iWidth, wid + opt.iPadding); + } + + pwd->iTextHeight += pwd->rows[i].iTotalHeight; + } + } + + SelectObject(hdc, hOldFont); + ReleaseDC(hwnd, hdc); + + int iHeight = max(pwd->iTitleHeight + pwd->iTextHeight + opt.iPadding, iWinAvatarHeight); + iHeight = max(pwd->iIconsHeight, iHeight); + if (bStatusMsg) iHeight += 50; + + if (iHeight < opt.iMinHeight) iHeight = opt.iMinHeight; + // ignore minwidth for text tips + if (!pwd->bIsTextTip && iWidth < opt.iMinWidth) iWidth = opt.iMinWidth; + + // ignore maxheight for tray tip + if (!pwd->bIsTrayTip && iHeight > opt.iWinMaxHeight) iHeight = opt.iWinMaxHeight; + if (iWidth > opt.iWinWidth) iWidth = opt.iWinWidth; + + CreateSkinBitmap(iWidth, iHeight, pwd->bIsTextTip && !pwd->bIsTrayTip); + + GetWindowRect(hwnd, &rc); + if (rc.right - rc.left != iWidth || rc.bottom - rc.top != iHeight) + { + SetWindowPos(hwnd, 0, 0, 0, iWidth, iHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + GetWindowRect(hwnd, &pwd->rcWindow); + SendMessage(hwnd, PUM_UPDATERGN, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + } + + if (pHeight) + *pHeight = iHeight; + } + return TRUE; + + case PUM_UPDATERGN: + { + HRGN hRgn; + RECT r = pwd->rcWindow; + int v, h; + int w = 11; + + r.right -= r.left; + r.left = 0; + r.bottom -= r.top; + r.top = 0; + + if (opt.showEffect == PSE_FADE && wParam == 1) { + if (skin.bNeedLayerUpdate) { + POINT ptSrc = { 0, 0 }; + SIZE sz = { r.right - r.left, r.bottom - r.top }; + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = pwd->iCurrentTrans; + blend.AlphaFormat = AC_SRC_ALPHA; + + pwd->iCurrentTrans += opt.iAnimateSpeed; + if (pwd->iCurrentTrans > pwd->iTrans) { + pwd->iCurrentTrans = pwd->iTrans; + pwd->iAnimStep = ANIM_STEPS; + } + + UpdateLayeredWindow(hwnd, NULL, NULL, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); + + } + else { + pwd->iCurrentTrans += opt.iAnimateSpeed; + if (pwd->iCurrentTrans > pwd->iTrans) { + pwd->iCurrentTrans = pwd->iTrans; + pwd->iAnimStep = ANIM_STEPS; + } + + SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iCurrentTrans, LWA_ALPHA); + } + } + else if (opt.showEffect == PSE_ANIMATE && pwd->bIsPainted) { + if (pwd->iAnimStep <= (ANIM_STEPS / opt.iAnimateSpeed)) { + float frac = 1.0f - pwd->iAnimStep / ((float)ANIM_STEPS / opt.iAnimateSpeed); + int wi = r.right, hi = r.bottom; + + r.left += (int)(wi / 2.0f * frac + 0.5f); + r.right -= (int)(wi / 2.0f * frac + 0.5f); + r.top += (int)(hi / 2.0f * frac + 0.5f); + r.bottom -= (int)(hi / 2.0f * frac + 0.5f); + } + else pwd->iAnimStep = ANIM_STEPS; + + if (skin.bNeedLayerUpdate) { + RECT r2 = pwd->rcWindow; + POINT ptPos = { r.left + r2.left, r.top + r2.top }; + POINT ptSrc = { r.left, r.top }; + + SIZE sz = { r.right - r.left, r.bottom - r.top }; + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = pwd->iTrans; + blend.AlphaFormat = AC_SRC_ALPHA; + + UpdateLayeredWindow(hwnd, NULL, &ptPos, &sz, skin.hdc, &ptSrc, 0xffffffff, &blend, LWA_ALPHA); + } + } + + if (!skin.bNeedLayerUpdate) { + // round corners + if (opt.bRound) { + h = (r.right - r.left) > (w * 2) ? w : (r.right - r.left); + v = (r.bottom - r.top) > (w * 2) ? w : (r.bottom - r.top); + h = (h < v) ? h : v; + } + else h = 0; + + hRgn = CreateRoundRectRgn(r.left, r.top, r.right + 1, r.bottom + 1, h, h); + SetWindowRgn(hwnd, hRgn, FALSE); + + if (opt.showEffect == PSE_ANIMATE && pwd->iAnimStep == 1) + SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), pwd->iTrans, LWA_ALPHA); + } + else { + // aero glass (vista+) + if (opt.bAeroGlass && MyDwmEnableBlurBehindWindow && pwd->iAnimStep > 5) { + if (pwd->hrgnAeroGlass) { + DeleteObject(pwd->hrgnAeroGlass); + pwd->hrgnAeroGlass = 0; + } + + pwd->hrgnAeroGlass = CreateOpaqueRgn(25, true); + + if (pwd->hrgnAeroGlass) { + DWM_BLURBEHIND bb = { 0 }; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.fEnable = TRUE; + bb.hRgnBlur = pwd->hrgnAeroGlass; + MyDwmEnableBlurBehindWindow(hwnd, &bb); + } + } + } + } + return TRUE; + + case PUM_CALCPOS: + { + RECT rcWork, rc; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, FALSE); + + HMONITOR hMon = MonitorFromPoint(pwd->clcit.ptCursor, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMon, &mi)) + rcWork = mi.rcWork; + + GetWindowRect(hwnd, &rc); + + int x = 0, y = 0, iWidth = (rc.right - rc.left), iHeight = (rc.bottom - rc.top); + + switch (opt.pos) { + case PP_BOTTOMRIGHT: + x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); // cursor size is too large - use small icon size + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + break; + case PP_BOTTOMLEFT: + x = pwd->clcit.ptCursor.x - iWidth - GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + break; + case PP_TOPRIGHT: + x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y - iHeight - GetSystemMetrics(SM_CYSMICON); + break; + case PP_TOPLEFT: + x = pwd->clcit.ptCursor.x - iWidth - GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y - iHeight - GetSystemMetrics(SM_CYSMICON); + break; + } + + + if (x + iWidth + 8 > rcWork.right) + x = rcWork.right - iWidth - 8; + if (x - 8 < rcWork.left) + x = rcWork.left + 8; + + if (pwd->bAllowReposition || !pwd->bNeedRefresh) { + if (y + iHeight > rcWork.bottom) { + y = pwd->clcit.ptCursor.y - iHeight - 8; + pwd->bAllowReposition = true; + } + + if (y - 8 < rcWork.top) { + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + pwd->bAllowReposition = true; + } + } + + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); + GetWindowRect(hwnd, &pwd->rcWindow); + } + return TRUE; + + case PUM_REFRESHTRAYTIP: + for (int i = 0; i < pwd->iRowCount; i++) { + mir_free(pwd->rows[i].swzLabel); + mir_free(pwd->rows[i].swzValue); + Smileys_FreeParse(pwd->rows[i].spi); + } + + + mir_free(pwd->rows); + pwd->rows = NULL; + pwd->iRowCount = 0; + + DWORD dwItems = (wParam == 0) ? opt.iFirstItems : opt.iSecondItems; + bool bFirstItem = true; + TCHAR buff[64]; + + int oldOrder = -1, iProtoCount = 0; + PROTOACCOUNT **accs; + ProtoEnumAccounts(&iProtoCount, &accs); + + for (int j = 0; j < iProtoCount; j++) { + PROTOACCOUNT *pa = NULL; + for (int i = 0; i < iProtoCount; i++) + if (accs[i]->iOrder > oldOrder && (pa == NULL || accs[i]->iOrder < pa->iOrder)) + pa = accs[i]; + + if (pa == NULL) + continue; + + oldOrder = pa->iOrder; + + WORD wStatus = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0); + if (opt.bHideOffline && wStatus == ID_STATUS_OFFLINE) + continue; + + if (!IsAccountEnabled(pa) || !IsTrayProto(pa->tszAccountName, (BOOL)wParam)) + continue; + + if (dwItems & TRAYTIP_NUMCONTACTS) { + int iCount = 0, iCountOnline = 0; + for (MCONTACT hContact = db_find_first(pa->szModuleName); hContact; hContact = db_find_next(hContact, pa->szModuleName)) { + if (db_get_w(hContact, pa->szModuleName, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) + iCountOnline++; + iCount++; + } + mir_sntprintf(buff, 64, _T("(%d/%d)"), iCountOnline, iCount); + } + else buff[0] = 0; + + TCHAR swzProto[256]; + _tcscpy(swzProto, pa->tszAccountName); + if (dwItems & TRAYTIP_LOCKSTATUS) + if (CallService(MS_PROTO_ISACCOUNTLOCKED, 0, (LPARAM)pa->szModuleName)) + mir_sntprintf(swzProto, SIZEOF(swzProto), TranslateT("%s (locked)"), pa->tszAccountName); + + AddRow(pwd, swzProto, buff, NULL, false, false, !bFirstItem, true, LoadSkinnedProtoIcon(pa->szModuleName, wStatus)); + bFirstItem = false; + + if (dwItems & TRAYTIP_LOGON) { + if (TimestampToTimeDifference(NULL, pa->szModuleName, "LogonTS", buff, 59)) { + TCHAR ago[96]; + mir_sntprintf(ago, SIZEOF(ago), TranslateT("%s ago"), buff); + AddRow(pwd, TranslateT("Log on:"), ago, NULL, false, false, false); + } + } + + if (dwItems & TRAYTIP_UNREAD_EMAILS && ProtoServiceExists(pa->szModuleName, PS_GETUNREADEMAILCOUNT)) { + int iCount = (int)ProtoCallService(pa->szModuleName, PS_GETUNREADEMAILCOUNT, 0, 0); + if (iCount > 0) { + _itot(iCount, buff, 10); + AddRow(pwd, TranslateT("Unread emails:"), buff, NULL, false, false, false); + } + } + + if (dwItems & TRAYTIP_STATUS) { + TCHAR *swzText = pcli->pfnGetStatusModeDescription(wStatus, 0); + if (swzText) + AddRow(pwd, TranslateT("Status:"), swzText, NULL, false, false, false); + } + + if (wStatus >= ID_STATUS_ONLINE && wStatus <= ID_STATUS_OUTTOLUNCH) { + if (dwItems & TRAYTIP_STATUS_MSG) { + TCHAR *swzText = GetProtoStatusMessage(pa->szModuleName, wStatus); + if (swzText) { + StripBBCodesInPlace(swzText); + AddRow(pwd, TranslateT("Status message:"), swzText, pa->szModuleName, true, true, false); + mir_free(swzText); + } + } + + if (dwItems & TRAYTIP_EXTRA_STATUS) { + // jabber mood or icq xstatus + TCHAR *swzAdvTitle = GetJabberAdvStatusText(pa->szModuleName, "mood", "title"); + if (swzAdvTitle) { + StripBBCodesInPlace(swzAdvTitle); + AddRow(pwd, TranslateT("Mood:"), swzAdvTitle, pa->szModuleName, true, false, false); + mir_free(swzAdvTitle); + + TCHAR *swzAdvText = GetJabberAdvStatusText(pa->szModuleName, "mood", "text"); + if (swzAdvText) { + StripBBCodesInPlace(swzAdvText); + AddRow(pwd, _T(""), swzAdvText, pa->szModuleName, true, true, false); + mir_free(swzAdvText); + } + } + else { + if (db_get_b(0, pa->szModuleName, "XStatusId", 0)) { + // xstatus title + swzAdvTitle = GetProtoExtraStatusTitle(pa->szModuleName); + if (swzAdvTitle) { + StripBBCodesInPlace(swzAdvTitle); + AddRow(pwd, TranslateT("xStatus:"), swzAdvTitle, pa->szModuleName, true, false, false); + mir_free(swzAdvTitle); + } + + // xstatus message + TCHAR *swzAdvText = GetProtoExtraStatusMessage(pa->szModuleName); + if (swzAdvText) { + StripBBCodesInPlace(swzAdvText); + AddRow(pwd, _T(""), swzAdvText, pa->szModuleName, true, true, false); + mir_free(swzAdvText); + } + } + } + + TCHAR *swzActTitle = GetJabberAdvStatusText(pa->szModuleName, "activity", "title"); + if (swzActTitle) { + StripBBCodesInPlace(swzActTitle); + AddRow(pwd, TranslateT("Activity:"), swzActTitle, pa->szModuleName, true, false, false); + mir_free(swzActTitle); + } + + TCHAR *swzActText = GetJabberAdvStatusText(pa->szModuleName, "activity", "text"); + if (swzActText) { + StripBBCodesInPlace(swzActText); + AddRow(pwd, _T(""), swzActText, pa->szModuleName, true, true, false); + mir_free(swzActText); + } + } + + if (dwItems & TRAYTIP_LISTENINGTO) { + TCHAR *swzListening = GetListeningTo(pa->szModuleName); + if (swzListening) { + StripBBCodesInPlace(swzListening); + AddRow(pwd, TranslateT("Listening to:"), swzListening, NULL, false, true, false); + mir_free(swzListening); + } + } + } + } + + if (dwItems & TRAYTIP_FAVCONTACTS) { + if (db_get_dw(0, MODULE, "FavouriteContactsCount", 0)) { + TCHAR swzName[256]; + TCHAR swzStatus[256]; + bool bTitlePainted = false; + int iCount = 0, iCountOnline = 0; + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + if (db_get_b(hContact, MODULE, "FavouriteContact", 0)) { + char *proto = GetContactProto(hContact); + if (proto == NULL) + continue; + + WORD wStatus = db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE); + WordToStatusDesc(hContact, proto, "Status", swzStatus, 256); + + if (wStatus != ID_STATUS_OFFLINE) + iCountOnline++; + + iCount++; + + if (!(opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE && wStatus == ID_STATUS_OFFLINE)) { + if (!bTitlePainted) { + AddRow(pwd, TranslateT("Fav. contacts"), NULL, NULL, false, false, !bFirstItem, true, NULL); + bFirstItem = false; + bTitlePainted = true; + } + + TCHAR *swzNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, hContact, GCDNF_TCHAR); + if (opt.iFavoriteContFlags & FAVCONT_APPEND_PROTO) { + TCHAR *swzProto = a2t(proto); + mir_sntprintf(swzName, 256, _T("%s (%s)"), swzNick, swzProto); + mir_free(swzProto); + } + else _tcscpy(swzName, swzNick); + + AddRow(pwd, swzName, swzStatus, NULL, false, false, false); + } + } + } + + int index = pwd->iRowCount - 1; + if (opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE) + index -= iCountOnline; + else + index -= iCount; + + if (index >= 0 && (dwItems & TRAYTIP_NUMCONTACTS) && !((opt.iFavoriteContFlags & FAVCONT_HIDE_OFFLINE) && iCountOnline == 0)) { + mir_sntprintf(buff, 64, _T("(%d/%d)"), iCountOnline, iCount); + pwd->rows[index].swzValue = mir_tstrdup(buff); + } + } + } + + if (dwItems & TRAYTIP_MIRANDA_UPTIME) { + if (TimestampToTimeDifference(NULL, MODULE, "MirandaStartTS", buff, 64)) { + AddRow(pwd, TranslateT("Other"), _T(""), NULL, false, false, !bFirstItem, true, NULL); + AddRow(pwd, TranslateT("Miranda uptime:"), buff, NULL, false, false, false); + } + } + + if (dwItems & TRAYTIP_CLIST_EVENT && pwd->clcit.swzText) { + TCHAR *pchBr = _tcschr(pwd->clcit.swzText, '\n'); + TCHAR *pchBold = _tcsstr(pwd->clcit.swzText, _T("")); + + if (!pchBold || pchBold != pwd->clcit.swzText) { + TCHAR swzText[256]; + _tcscpy(swzText, pwd->clcit.swzText); + if (pchBr) swzText[pchBr - pwd->clcit.swzText] = 0; + AddRow(pwd, swzText, _T(""), NULL, false, true, false, true, LoadSkinnedIcon(SKINICON_OTHER_FILLEDBLOB)); + } + } + + return TRUE; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/plugins/TipperYM/src/subst.cpp b/plugins/TipperYM/src/subst.cpp index 01f3344305..97559c11da 100644 --- a/plugins/TipperYM/src/subst.cpp +++ b/plugins/TipperYM/src/subst.cpp @@ -1,852 +1,852 @@ -/* -Copyright (C) 2006-2007 Scott Ellis -Copyright (C) 2007-2011 Jan Holub - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "common.h" - -bool DBGetContactSettingAsString(MCONTACT hContact, const char *szModuleName, const char *szSettingName, TCHAR *buff, int bufflen) -{ - DBVARIANT dbv; - buff[0] = 0; - - if (!szModuleName || !szSettingName) - return false; - - if (!db_get(hContact, szModuleName, szSettingName, &dbv)) { - switch (dbv.type) { - case DBVT_BYTE: - _itot(dbv.bVal, buff, 10); - break; - case DBVT_WORD: - _ltot(dbv.wVal, buff, 10); - break; - case DBVT_DWORD: - _ltot(dbv.dVal, buff, 10); - break; - case DBVT_ASCIIZ: - if (dbv.pszVal) a2t(dbv.pszVal, buff, bufflen); - buff[bufflen - 1] = 0; - break; - case DBVT_UTF8: - if (dbv.pszVal) utf2t(dbv.pszVal, buff, bufflen); - buff[bufflen - 1] = 0; - break; - - case DBVT_WCHAR: - if (dbv.pwszVal) wcsncpy(buff, dbv.pwszVal, bufflen); - buff[bufflen - 1] = 0; - break; - } - - db_free(&dbv); - } - - return buff[0] ? true : false; -} - -bool CheckContactType(MCONTACT hContact, const DISPLAYITEM &di) -{ - if (di.type == DIT_ALL) - return true; - - char *szProto = GetContactProto(hContact); - if (szProto) { - if (db_get_b(hContact, szProto, "ChatRoom", 0) != 0) - return di.type == DIT_CHATS; - else - return di.type == DIT_CONTACTS; - } - - return false; -} - -void StripBBCodesInPlace(TCHAR *swzText) -{ - if (!db_get_b(0, MODULE, "StripBBCodes", 1)) - return; - - if (swzText == 0) - return; - - size_t iRead = 0, iWrite = 0; - size_t iLen = _tcslen(swzText); - - while(iRead <= iLen) { // copy terminating null too - while (iRead <= iLen && swzText[iRead] != '[') { - if (swzText[iRead] != swzText[iWrite]) swzText[iWrite] = swzText[iRead]; - iRead++; iWrite++; - } - - if (iRead > iLen) - break; - - if (iLen - iRead >= 3 && (_tcsnicmp(swzText + iRead, _T("[b]"), 3) == 0 || _tcsnicmp(swzText + iRead, _T("[i]"), 3) == 0)) - iRead += 3; - else if (iLen - iRead >= 4 && (_tcsnicmp(swzText + iRead, _T("[/b]"), 4) == 0 || _tcsnicmp(swzText + iRead, _T("[/i]"), 4) == 0)) - iRead += 4; - else if (iLen - iRead >= 6 && (_tcsnicmp(swzText + iRead, _T("[color"), 6) == 0)) { - while (iRead < iLen && swzText[iRead] != ']') iRead++; - iRead++;// skip the ']' - } - else if (iLen - iRead >= 8 && (_tcsnicmp(swzText + iRead, _T("[/color]"), 8) == 0)) - iRead += 8; - else if (iLen - iRead >= 5 && (_tcsnicmp(swzText + iRead, _T("[size"), 5) == 0)) { - while (iRead < iLen && swzText[iRead] != ']') iRead++; - iRead++;// skip the ']' - } - else if (iLen - iRead >= 7 && (_tcsnicmp(swzText + iRead, _T("[/size]"), 7) == 0)) - iRead += 7; - else { - if (swzText[iRead] != swzText[iWrite]) swzText[iWrite] = swzText[iRead]; - iRead++; iWrite++; - } - } -} - -DWORD LastMessageTimestamp(MCONTACT hContact, bool received) -{ - for (HANDLE hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { - DBEVENTINFO dbei = { sizeof(dbei) }; - db_event_get(hDbEvent, &dbei); - if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT) == received) - return dbei.timestamp; - } - - return 0; -} - -void FormatTimestamp(DWORD ts, char *szFormat, TCHAR *buff, int bufflen) -{ - TCHAR swzForm[16]; - DBTIMETOSTRINGT dbt = {0}; - dbt.cbDest = bufflen; - dbt.szDest = buff; - a2t(szFormat, swzForm, 16); - dbt.szFormat = swzForm; - CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); -} - -bool Uid(MCONTACT hContact, char *szProto, TCHAR *buff, int bufflen) -{ - char *tmpProto = (hContact) ? tmpProto = GetContactProto(hContact) : szProto; - if (tmpProto) { - char *szUid = (char*)CallProtoService(tmpProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); - if (szUid && (INT_PTR)szUid != CALLSERVICE_NOTFOUND) - return DBGetContactSettingAsString(hContact, tmpProto, szUid, buff, bufflen); - } - - return false; -} - -bool UidName(char *szProto, TCHAR *buff, int bufflen) -{ - if (szProto) { - char *szUidName = (char*)CallProtoService(szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); - if (szUidName && (INT_PTR)szUidName != CALLSERVICE_NOTFOUND) { - a2t(szUidName, buff, bufflen); - return true; - } - } - return false; -} - -TCHAR *GetLastMessageText(MCONTACT hContact, bool received) -{ - for (HANDLE hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { - DBEVENTINFO dbei = { sizeof(dbei) }; - db_event_get(hDbEvent, &dbei); - if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT) == received) { - dbei.pBlob = (BYTE *)alloca(dbei.cbBlob); - db_event_get(hDbEvent, &dbei); - if (dbei.cbBlob == 0 || dbei.pBlob == 0) - return 0; - - TCHAR *buff = DbGetEventTextT( &dbei, CP_ACP ); - TCHAR *swzMsg = mir_tstrdup(buff); - mir_free(buff); - - StripBBCodesInPlace(swzMsg); - return swzMsg; - } - } - - return 0; -} - -bool CanRetrieveStatusMsg(MCONTACT hContact, char *szProto) -{ - if (opt.bGetNewStatusMsg) { - int iFlags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0); - WORD wStatus = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); - if ((CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND) && (iFlags & Proto_Status2Flag(wStatus))) { - iFlags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & (PF1_VISLIST | PF1_INVISLIST); - if (opt.bDisableIfInvisible && iFlags) { - int iVisMode = db_get_w(hContact, szProto, "ApparentMode", 0); - int wProtoStatus = CallProtoService(szProto, PS_GETSTATUS, 0, 0); - if ((iVisMode == ID_STATUS_OFFLINE) || (wProtoStatus == ID_STATUS_INVISIBLE && iVisMode != ID_STATUS_ONLINE)) - return false; - return true; - } - return true; - } - } - - return false; -} - -TCHAR* GetStatusMessageText(MCONTACT hContact) -{ - TCHAR *swzMsg = 0; - DBVARIANT dbv; - - char *szProto = GetContactProto(hContact); - if (szProto) { - if (!strcmp(szProto, META_PROTO)) - hContact = db_mc_getMostOnline(hContact); - else { - WORD wStatus = (int)CallProtoService(szProto, PS_GETSTATUS, 0, 0); - if (wStatus == ID_STATUS_OFFLINE) - return NULL; - - if (!db_get_ts(hContact, MODULE, "TempStatusMsg", &dbv)) { - if (_tcslen(dbv.ptszVal) != 0) - swzMsg = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - } - } - - if (!swzMsg) { - if (CanRetrieveStatusMsg(hContact, szProto)) - if (CallContactService(hContact, PSS_GETAWAYMSG, 0, 0)) - return NULL; - - if (!db_get_ts(hContact, "CList", "StatusMsg", &dbv)) { - if (dbv.ptszVal && _tcslen(dbv.ptszVal) != 0) - swzMsg = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - } - } - } - - if (swzMsg) - StripBBCodesInPlace(swzMsg); - - return swzMsg; -} - -bool GetSysSubstText(MCONTACT hContact, TCHAR *swzRawSpec, TCHAR *buff, int bufflen) -{ - bool recv = false; - - if (!_tcscmp(swzRawSpec, _T("uid"))) - return Uid(hContact, 0, buff, bufflen); - - if (!_tcscmp(swzRawSpec, _T("proto"))) { - char *szProto = GetContactProto(hContact); - if (szProto) { - a2t(szProto, buff, bufflen); - return true; - } - } - else if (!_tcscmp(swzRawSpec, _T("account"))) { - char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEACCOUNT, hContact, 0); - if ((INT_PTR)szProto == CALLSERVICE_NOTFOUND) { - return GetSysSubstText(hContact, _T("proto"), buff, bufflen); - } - else if (szProto) { - PROTOACCOUNT *pa = ProtoGetAccount(szProto); - if (pa && pa->tszAccountName) { - _tcsncpy(buff, pa->tszAccountName, bufflen); - return true; - } - else - return GetSysSubstText(hContact, _T("proto"), buff, bufflen); - } - } - else if (!_tcscmp(swzRawSpec, _T("time"))) { - if (tmi.printDateTime && !tmi.printDateTimeByContact(hContact, _T("t"), buff, bufflen, TZF_KNOWNONLY)) - return true; - } - else if (!_tcscmp(swzRawSpec, _T("uidname"))) { - char *szProto = GetContactProto(hContact); - return UidName(szProto, buff, bufflen); - } - else if (!_tcscmp(swzRawSpec, _T("status_msg"))) { - TCHAR *swzMsg = GetStatusMessageText(hContact); - if (swzMsg) { - _tcsncpy(buff, swzMsg, bufflen); - mir_free(swzMsg); - return true; - } - } - else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg"))) || !_tcscmp(swzRawSpec, _T("last_msg_out"))) { - TCHAR *swzMsg = GetLastMessageText(hContact, recv); - if (swzMsg) { - _tcsncpy(buff, swzMsg, bufflen); - mir_free(swzMsg); - return true; - } - } - else if (!_tcscmp(swzRawSpec, _T("meta_subname"))) { - // get contact list name of active subcontact - MCONTACT hSubContact = db_mc_getMostOnline(hContact); - if (!hSubContact) - return false; - - TCHAR *swzNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, GCDNF_TCHAR); - if (swzNick) _tcsncpy(buff, swzNick, bufflen); - return true; - } - else if (!_tcscmp(swzRawSpec, _T("meta_subuid"))) { - MCONTACT hSubContact = db_mc_getMostOnline(hContact); - if (!hSubContact || (INT_PTR)hSubContact == CALLSERVICE_NOTFOUND) - return false; - return Uid(hSubContact, 0, buff, bufflen); - } - else if (!_tcscmp(swzRawSpec, _T("meta_subproto"))) { - // get protocol of active subcontact - MCONTACT hSubContact = db_mc_getMostOnline(hContact); - if (!hSubContact || (INT_PTR)hSubContact == CALLSERVICE_NOTFOUND) - return false; - return GetSysSubstText(hSubContact, _T("account"), buff, bufflen); - } - else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_time"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_time"))) { - DWORD ts = LastMessageTimestamp(hContact, recv); - if (ts == 0) return false; - FormatTimestamp(ts, "t", buff, bufflen); - return true; - } - else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_date"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_date"))) { - DWORD ts = LastMessageTimestamp(hContact, recv); - if (ts == 0) return false; - FormatTimestamp(ts, "d", buff, bufflen); - return true; - } - else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_reltime"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_reltime"))) { - DWORD ts = LastMessageTimestamp(hContact, recv); - if (ts == 0) return false; - DWORD t = (DWORD)time(0); - DWORD diff = (t - ts); - int d = (diff / 60 / 60 / 24); - int h = (diff - d * 60 * 60 * 24) / 60 / 60; - int m = (diff - d * 60 * 60 * 24 - h * 60 * 60) / 60; - if (d > 0) mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); - else if (h > 0) mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); - else mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); - return true; - } - else if (!_tcscmp(swzRawSpec, _T("msg_count_all")) || !_tcscmp(swzRawSpec, _T("msg_count_out")) || !_tcscmp(swzRawSpec, _T("msg_count_in"))) { - DWORD dwCountOut, dwCountIn; - DWORD dwMetaCountOut = 0, dwMetaCountIn = 0; - DWORD dwLastTs, dwNewTs, dwRecountTs; - DWORD dwTime, dwDiff; - int iNumber = 1; - MCONTACT hTmpContact = hContact; - - char *szProto = GetContactProto(hContact); - if (szProto && !strcmp(szProto, META_PROTO)) { - iNumber = db_mc_getSubCount(hContact); - hTmpContact = db_mc_getSub(hContact, 0); - } - - for (int i = 0; i < iNumber; i++) { - if (i > 0) - hTmpContact = db_mc_getSub(hContact, i); - dwRecountTs = db_get_dw(hTmpContact, MODULE, "LastCountTS", 0); - dwTime = (DWORD)time(0); - dwDiff = (dwTime - dwRecountTs); - if (dwDiff > (60 * 60 * 24 * 3)) { - db_set_dw(hTmpContact, MODULE, "LastCountTS", dwTime); - dwCountOut = dwCountIn = dwLastTs = 0; - } - else { - dwCountOut = db_get_dw(hTmpContact, MODULE, "MsgCountOut", 0); - dwCountIn = db_get_dw(hTmpContact, MODULE, "MsgCountIn", 0); - dwLastTs = db_get_dw(hTmpContact, MODULE, "LastMsgTS", 0); - } - - dwNewTs = dwLastTs; - - HANDLE dbe = db_event_last(hTmpContact); - while (dbe != NULL) { - DBEVENTINFO dbei = { sizeof(dbei) }; - if (!db_event_get(dbe, &dbei)) { - if (dbei.eventType == EVENTTYPE_MESSAGE) { - dwNewTs = max(dwNewTs, dbei.timestamp); - if (dbei.timestamp > dwLastTs) { - if (dbei.flags & DBEF_SENT) dwCountOut++; - else dwCountIn++; - } - else break; - } - } - dbe = db_event_prev(hTmpContact, dbe); - } - - if (dwNewTs > dwLastTs) { - db_set_dw(hTmpContact, MODULE, "MsgCountOut", dwCountOut); - db_set_dw(hTmpContact, MODULE, "MsgCountIn", dwCountIn); - db_set_dw(hTmpContact, MODULE, "LastMsgTS", dwNewTs); - } - - dwMetaCountOut += dwCountOut; - dwMetaCountIn += dwCountIn; - } - - if (!_tcscmp(swzRawSpec, _T("msg_count_out"))) - mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountOut); - else if (!_tcscmp(swzRawSpec, _T("msg_count_in"))) - mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountIn); - else - mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountOut + dwMetaCountIn); - return true; - } - - return false; -} - -bool GetSubstText(MCONTACT hContact, const DISPLAYSUBST &ds, TCHAR *buff, int bufflen) -{ - TranslateFunc *transFunc = 0; - for (int i = 0; i < iTransFuncsCount; i++) - if (translations[i].id == (DWORD)ds.iTranslateFuncId) { - transFunc = translations[i].transFunc; - break; - } - - if (!transFunc) - return false; - - switch (ds.type) { - case DVT_DB: - return transFunc(hContact, ds.szModuleName, ds.szSettingName, buff, bufflen) != 0; - case DVT_PROTODB: - char *szProto = GetContactProto(hContact); - if (szProto) { - if (transFunc(hContact, szProto, ds.szSettingName, buff, bufflen) != 0) - return true; - return transFunc(hContact, "UserInfo", ds.szSettingName, buff, bufflen) != 0; - } - break; - } - return false; -} - -bool GetRawSubstText(MCONTACT hContact, char *szRawSpec, TCHAR *buff, int bufflen) -{ - size_t lenght = strlen(szRawSpec); - for (size_t i = 0; i < lenght; i++) { - if (szRawSpec[i] == '/') { - szRawSpec[i] = 0; - if (strlen(szRawSpec) == 0) { - char *szProto = GetContactProto(hContact); - if (szProto) { - if (translations[0].transFunc(hContact, szProto, &szRawSpec[i + 1], buff, bufflen) != 0) - return true; - return translations[0].transFunc(hContact, "UserInfo", &szRawSpec[i + 1], buff, bufflen) != 0; - } - return false; - } - return translations[0].transFunc(hContact, szRawSpec, &szRawSpec[i + 1], buff, bufflen) != 0; - } - } - return false; -} - -bool ApplySubst(MCONTACT hContact, const TCHAR *swzSource, bool parseTipperVarsFirst, TCHAR *swzDest, int iDestLen) -{ - // hack - allow empty strings before passing to variables (note - zero length strings return false after this) - if (swzDest && swzSource && _tcslen(swzSource) == 0) { - swzDest[0] = 0; - return true; - } - - // pass to variables plugin if available - TCHAR *swzVarSrc; - if (parseTipperVarsFirst) - swzVarSrc = mir_tstrdup(swzSource); - else - swzVarSrc = variables_parsedup((TCHAR *)swzSource, 0, hContact); - - size_t iSourceLen = _tcslen(swzVarSrc); - size_t si = 0, di = 0, v = 0; - - TCHAR swzVName[LABEL_LEN]; - TCHAR swzRep[VALUE_LEN], swzAlt[VALUE_LEN]; - while (si < iSourceLen && di < (size_t)iDestLen - 1) { - if (swzVarSrc[si] == _T('%')) { - si++; - v = 0; - while (si < iSourceLen && v < LABEL_LEN) { - if (swzVarSrc[si] == _T('%')) - break; - - swzVName[v] = swzVarSrc[si]; - v++; si++; - } - - if (v == 0) // bSubst len is 0 - just a % symbol - swzDest[di] = _T('%'); - else if (si < iSourceLen) // we found end % - { - swzVName[v] = 0; - - bool bAltSubst = false; - bool bSubst = false; - - // apply only to specific protocols - TCHAR *p = _tcsrchr(swzVName, _T('^')); // use last '^', so if you want a ^ in swzAlt text, you can just put a '^' on the end - if (p) { - *p = 0; - p++; - if (*p) { - char *cp = GetContactProto(hContact); - if (cp != NULL) { - PROTOACCOUNT *acc = ProtoGetAccount(cp); - if (acc != NULL) { - cp = acc->szProtoName; - } - } - - if (cp == NULL) - goto empty; - - bool negate = false; - if (*p == _T('!')) { - p++; - if (*p == 0) goto error; - negate = true; - } - - char sproto[256]; - bool spec = false; - int len; - - TCHAR *last = _tcsrchr(p, _T(',')); - if (!last) last = p; - - while (p <= last + 1) { - len = (int)_tcscspn(p, _T(",")); - t2a(p, sproto, len); - sproto[len] = 0; - p += len + 1; - - if (_stricmp(cp, sproto) == 0) { - spec = true; - break; - } - } - - if (negate ? spec : !spec) - goto empty; - } - } - - // get alternate text, if bSubst fails - swzAlt[0] = 0; - p = _tcschr(swzVName, _T('|')); // use first '|' - so you can use the '|' symbol in swzAlt text - if (p) { - *p = 0; // clip swzAlt from swzVName - p++; - if (_tcslen(p) > 4 && _tcsncmp(p, _T("raw:"), 4) == 0) { // raw db substitution - char raw_spec[LABEL_LEN]; - p += 4; - t2a(p, raw_spec, LABEL_LEN); - GetRawSubstText(hContact, raw_spec, swzAlt, VALUE_LEN); - } - else if (_tcslen(p) > 4 && _tcsncmp(p, _T("sys:"), 4) == 0) { // 'system' substitution - p += 4; - GetSysSubstText(hContact, p, swzAlt, VALUE_LEN); - } - else { - // see if we can find the bSubst - DSListNode *ds_node = opt.dsList; - while (ds_node) { - if (_tcscmp(ds_node->ds.swzName, p) == 0) - break; - - ds_node = ds_node->next; - } - - if (ds_node) - GetSubstText(hContact, ds_node->ds, swzAlt, VALUE_LEN); - else { - _tcsncpy(swzAlt, p, VALUE_LEN); - bAltSubst = true; - } - } - swzAlt[VALUE_LEN - 1] = 0; - if (_tcslen(swzAlt) != 0) - bAltSubst = true; - } - - // get bSubst text - if (v > 4 && _tcsncmp(swzVName, _T("raw:"), 4) == 0) // raw db substitution - { - char raw_spec[LABEL_LEN]; - t2a(&swzVName[4], raw_spec, LABEL_LEN); - bSubst = GetRawSubstText(hContact, raw_spec, swzRep, VALUE_LEN); - } - else if (v > 4 && _tcsncmp(swzVName, _T("sys:"), 4) == 0) // 'system' substitution - { - bSubst = GetSysSubstText(hContact, &swzVName[4], swzRep, VALUE_LEN); - } - else { - // see if we can find the bSubst - DSListNode *ds_node = opt.dsList; - while (ds_node) { - if (_tcscmp(ds_node->ds.swzName, swzVName) == 0) - break; - - ds_node = ds_node->next; - } - - if (!ds_node) - goto error; // no such bSubst - - bSubst = GetSubstText(hContact, ds_node->ds, swzRep, VALUE_LEN); - } - - if (bSubst) { - size_t rep_len = _tcslen(swzRep); - _tcsncpy(&swzDest[di], swzRep, min(rep_len, iDestLen - di)); - di += rep_len - 1; // -1 because we inc at bottom of loop - } - else if (bAltSubst) { - size_t alt_len = _tcslen(swzAlt); - _tcsncpy(&swzDest[di], swzAlt, min(alt_len, iDestLen - di)); - di += alt_len - 1; // -1 because we inc at bottom of loop - } - else goto empty; // empty value - } - else // no end % - error - goto error; - } - else swzDest[di] = swzVarSrc[si]; - - si++; - di++; - } - - mir_free(swzVarSrc); - swzDest[di] = 0; - - if (parseTipperVarsFirst) { - swzVarSrc = variables_parsedup((TCHAR *)swzDest, 0, hContact); - _tcscpy(swzDest, swzVarSrc); - mir_free(swzVarSrc); - } - - - // check for a 'blank' string - just spaces etc - for (si = 0; si <= di; si++) { - if (swzDest[si] != 0 && swzDest[si] != _T(' ') && swzDest[si] != _T('\t') && swzDest[si] != _T('\r') && swzDest[si] != _T('\n')) - return true; - } - - return false; - -empty: - mir_free(swzVarSrc); - return false; - -error: - swzDest[0] = _T('*'); - swzDest[1] = 0; - mir_free(swzVarSrc); - return true; -} - -bool GetLabelText(MCONTACT hContact, const DISPLAYITEM &di, TCHAR *buff, int bufflen) -{ - return ApplySubst(hContact, di.swzLabel, false, buff, bufflen); -} - -bool GetValueText(MCONTACT hContact, const DISPLAYITEM &di, TCHAR *buff, int bufflen) -{ - return ApplySubst(hContact, di.swzValue, di.bParseTipperVarsFirst, buff, bufflen); -} - -void TruncateString(TCHAR *swzText) -{ - if (swzText && opt.iLimitCharCount > 3) { - if ((int)_tcslen(swzText) > opt.iLimitCharCount) { - swzText[opt.iLimitCharCount - 3] = 0; - _tcscat(swzText, _T("...")); - } - } -} - -TCHAR *GetProtoStatusMessage(char *szProto, WORD wStatus) -{ - if (!szProto || wStatus == ID_STATUS_OFFLINE) - return NULL; - - // check if protocol supports status message for status - int flags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0); - if (!(flags & Proto_Status2Flag(wStatus))) - return NULL; - - TCHAR *swzText = (TCHAR *)CallProtoService(szProto, PS_GETMYAWAYMSG, 0, SGMA_TCHAR); - if ((INT_PTR)swzText == CALLSERVICE_NOTFOUND) - swzText = (TCHAR*)CallService(MS_AWAYMSG_GETSTATUSMSGT, wStatus, 0); - - else if (swzText == NULL) { - // try to use service without SGMA_TCHAR - char *tmpMsg = (char *)CallProtoService(szProto, PS_GETMYAWAYMSG, 0, 0); - if (tmpMsg && (INT_PTR)tmpMsg != CALLSERVICE_NOTFOUND) { - swzText = mir_a2t(tmpMsg); - mir_free(tmpMsg); - } - } - - - if (swzText && !swzText[0]) { - mir_free(swzText); - swzText = NULL; - } - - if (swzText && opt.bLimitMsg) - TruncateString(swzText); - - return swzText; -} - -TCHAR *GetProtoExtraStatusTitle(char *szProto) -{ - DBVARIANT dbv; - TCHAR *swzText = NULL; - - if (!szProto) - return NULL; - - if (!db_get_ts(0, szProto, "XStatusName", &dbv)) { - if (_tcslen(dbv.ptszVal) != 0) - swzText = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - } - - if (!swzText) { - TCHAR buff[256]; - if (EmptyXStatusToDefaultName(0, szProto, 0, buff, 256)) - swzText = mir_tstrdup(buff); - } - - if (opt.bLimitMsg) - TruncateString(swzText); - - return swzText; -} - -TCHAR *GetProtoExtraStatusMessage(char *szProto) -{ - if (!szProto) - return NULL; - - TCHAR *swzText = NULL; - DBVARIANT dbv; - if (!db_get_ts(0, szProto, "XStatusMsg", &dbv)) { - if (_tcslen(dbv.ptszVal) != 0) - swzText = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - - if (ServiceExists(MS_VARS_FORMATSTRING)) { - MCONTACT hContact = db_find_first(); - char *proto = GetContactProto(hContact); - while (!proto) { - hContact = db_find_next(hContact); - if (hContact) - proto = GetContactProto(hContact); - else { - hContact = NULL; - break; - } - } - - TCHAR *tszParsed = variables_parse(swzText, NULL, hContact); - if (tszParsed) - replaceStrT(swzText, tszParsed); - } - } - - if (opt.bLimitMsg) - TruncateString(swzText); - - return swzText; -} - -TCHAR *GetListeningTo(char *szProto) -{ - DBVARIANT dbv; - TCHAR *swzText = NULL; - - if (!szProto) - return NULL; - - if (!db_get_ts(0, szProto, "ListeningTo", &dbv)) { - if (_tcslen(dbv.ptszVal) != 0) - swzText = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - } - - if (opt.bLimitMsg) - TruncateString(swzText); - - return swzText; -} - -TCHAR *GetJabberAdvStatusText(char *szProto, const char *szSlot, const char *szValue) -{ - DBVARIANT dbv; - TCHAR *swzText = NULL; - - if (!szProto) - return NULL; - - char szSetting[128]; - mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", szProto, szSlot, szValue); - if (!db_get_ts(0, "AdvStatus", szSetting, &dbv)) { - if (_tcslen(dbv.ptszVal) != 0) - swzText = mir_tstrdup(dbv.ptszVal); - db_free(&dbv); - } - - if (opt.bLimitMsg) - TruncateString(swzText); - - return swzText; -} - -HICON GetJabberActivityIcon(MCONTACT hContact, char *szProto) -{ - DBVARIANT dbv; - HICON hIcon = NULL; - - if (!szProto) - return NULL; - - char szSetting[128]; - mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", szProto, "activity", "icon"); - if (!db_get_s(hContact, "AdvStatus", szSetting, &dbv)) { - hIcon = Skin_GetIcon(dbv.pszVal); - db_free(&dbv); - } - - return hIcon; +/* +Copyright (C) 2006-2007 Scott Ellis +Copyright (C) 2007-2011 Jan Holub + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "common.h" + +bool DBGetContactSettingAsString(MCONTACT hContact, const char *szModuleName, const char *szSettingName, TCHAR *buff, int bufflen) +{ + DBVARIANT dbv; + buff[0] = 0; + + if (!szModuleName || !szSettingName) + return false; + + if (!db_get(hContact, szModuleName, szSettingName, &dbv)) { + switch (dbv.type) { + case DBVT_BYTE: + _itot(dbv.bVal, buff, 10); + break; + case DBVT_WORD: + _ltot(dbv.wVal, buff, 10); + break; + case DBVT_DWORD: + _ltot(dbv.dVal, buff, 10); + break; + case DBVT_ASCIIZ: + if (dbv.pszVal) a2t(dbv.pszVal, buff, bufflen); + buff[bufflen - 1] = 0; + break; + case DBVT_UTF8: + if (dbv.pszVal) utf2t(dbv.pszVal, buff, bufflen); + buff[bufflen - 1] = 0; + break; + + case DBVT_WCHAR: + if (dbv.pwszVal) wcsncpy(buff, dbv.pwszVal, bufflen); + buff[bufflen - 1] = 0; + break; + } + + db_free(&dbv); + } + + return buff[0] ? true : false; +} + +bool CheckContactType(MCONTACT hContact, const DISPLAYITEM &di) +{ + if (di.type == DIT_ALL) + return true; + + char *szProto = GetContactProto(hContact); + if (szProto) { + if (db_get_b(hContact, szProto, "ChatRoom", 0) != 0) + return di.type == DIT_CHATS; + else + return di.type == DIT_CONTACTS; + } + + return false; +} + +void StripBBCodesInPlace(TCHAR *swzText) +{ + if (!db_get_b(0, MODULE, "StripBBCodes", 1)) + return; + + if (swzText == 0) + return; + + size_t iRead = 0, iWrite = 0; + size_t iLen = _tcslen(swzText); + + while(iRead <= iLen) { // copy terminating null too + while (iRead <= iLen && swzText[iRead] != '[') { + if (swzText[iRead] != swzText[iWrite]) swzText[iWrite] = swzText[iRead]; + iRead++; iWrite++; + } + + if (iRead > iLen) + break; + + if (iLen - iRead >= 3 && (_tcsnicmp(swzText + iRead, _T("[b]"), 3) == 0 || _tcsnicmp(swzText + iRead, _T("[i]"), 3) == 0)) + iRead += 3; + else if (iLen - iRead >= 4 && (_tcsnicmp(swzText + iRead, _T("[/b]"), 4) == 0 || _tcsnicmp(swzText + iRead, _T("[/i]"), 4) == 0)) + iRead += 4; + else if (iLen - iRead >= 6 && (_tcsnicmp(swzText + iRead, _T("[color"), 6) == 0)) { + while (iRead < iLen && swzText[iRead] != ']') iRead++; + iRead++;// skip the ']' + } + else if (iLen - iRead >= 8 && (_tcsnicmp(swzText + iRead, _T("[/color]"), 8) == 0)) + iRead += 8; + else if (iLen - iRead >= 5 && (_tcsnicmp(swzText + iRead, _T("[size"), 5) == 0)) { + while (iRead < iLen && swzText[iRead] != ']') iRead++; + iRead++;// skip the ']' + } + else if (iLen - iRead >= 7 && (_tcsnicmp(swzText + iRead, _T("[/size]"), 7) == 0)) + iRead += 7; + else { + if (swzText[iRead] != swzText[iWrite]) swzText[iWrite] = swzText[iRead]; + iRead++; iWrite++; + } + } +} + +DWORD LastMessageTimestamp(MCONTACT hContact, bool received) +{ + for (HANDLE hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get(hDbEvent, &dbei); + if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT) == received) + return dbei.timestamp; + } + + return 0; +} + +void FormatTimestamp(DWORD ts, char *szFormat, TCHAR *buff, int bufflen) +{ + TCHAR swzForm[16]; + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + a2t(szFormat, swzForm, 16); + dbt.szFormat = swzForm; + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); +} + +bool Uid(MCONTACT hContact, char *szProto, TCHAR *buff, int bufflen) +{ + char *tmpProto = (hContact) ? tmpProto = GetContactProto(hContact) : szProto; + if (tmpProto) { + char *szUid = (char*)CallProtoService(tmpProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); + if (szUid && (INT_PTR)szUid != CALLSERVICE_NOTFOUND) + return DBGetContactSettingAsString(hContact, tmpProto, szUid, buff, bufflen); + } + + return false; +} + +bool UidName(char *szProto, TCHAR *buff, int bufflen) +{ + if (szProto) { + char *szUidName = (char*)CallProtoService(szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); + if (szUidName && (INT_PTR)szUidName != CALLSERVICE_NOTFOUND) { + a2t(szUidName, buff, bufflen); + return true; + } + } + return false; +} + +TCHAR *GetLastMessageText(MCONTACT hContact, bool received) +{ + for (HANDLE hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get(hDbEvent, &dbei); + if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT) == received) { + dbei.pBlob = (BYTE *)alloca(dbei.cbBlob); + db_event_get(hDbEvent, &dbei); + if (dbei.cbBlob == 0 || dbei.pBlob == 0) + return 0; + + TCHAR *buff = DbGetEventTextT( &dbei, CP_ACP ); + TCHAR *swzMsg = mir_tstrdup(buff); + mir_free(buff); + + StripBBCodesInPlace(swzMsg); + return swzMsg; + } + } + + return 0; +} + +bool CanRetrieveStatusMsg(MCONTACT hContact, char *szProto) +{ + if (opt.bGetNewStatusMsg) { + int iFlags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0); + WORD wStatus = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); + if ((CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND) && (iFlags & Proto_Status2Flag(wStatus))) { + iFlags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & (PF1_VISLIST | PF1_INVISLIST); + if (opt.bDisableIfInvisible && iFlags) { + int iVisMode = db_get_w(hContact, szProto, "ApparentMode", 0); + int wProtoStatus = CallProtoService(szProto, PS_GETSTATUS, 0, 0); + if ((iVisMode == ID_STATUS_OFFLINE) || (wProtoStatus == ID_STATUS_INVISIBLE && iVisMode != ID_STATUS_ONLINE)) + return false; + return true; + } + return true; + } + } + + return false; +} + +TCHAR* GetStatusMessageText(MCONTACT hContact) +{ + TCHAR *swzMsg = NULL; + DBVARIANT dbv; + + char *szProto = GetContactProto(hContact); + if (szProto) { + if (!strcmp(szProto, META_PROTO)) + hContact = db_mc_getMostOnline(hContact); + else { + WORD wStatus = (int)CallProtoService(szProto, PS_GETSTATUS, 0, 0); + if (wStatus == ID_STATUS_OFFLINE) + return NULL; + + if (!db_get_ts(hContact, MODULE, "TempStatusMsg", &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzMsg = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + } + } + + if (!swzMsg) { + if (CanRetrieveStatusMsg(hContact, szProto)) + if (CallContactService(hContact, PSS_GETAWAYMSG, 0, 0)) + return NULL; + + if (!db_get_ts(hContact, "CList", "StatusMsg", &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzMsg = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + } + } + } + + if (swzMsg) + StripBBCodesInPlace(swzMsg); + + return swzMsg; +} + +bool GetSysSubstText(MCONTACT hContact, TCHAR *swzRawSpec, TCHAR *buff, int bufflen) +{ + bool recv = false; + + if (!_tcscmp(swzRawSpec, _T("uid"))) + return Uid(hContact, 0, buff, bufflen); + + if (!_tcscmp(swzRawSpec, _T("proto"))) { + char *szProto = GetContactProto(hContact); + if (szProto) { + a2t(szProto, buff, bufflen); + return true; + } + } + else if (!_tcscmp(swzRawSpec, _T("account"))) { + char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEACCOUNT, hContact, 0); + if ((INT_PTR)szProto == CALLSERVICE_NOTFOUND) { + return GetSysSubstText(hContact, _T("proto"), buff, bufflen); + } + else if (szProto) { + PROTOACCOUNT *pa = ProtoGetAccount(szProto); + if (pa && pa->tszAccountName) { + _tcsncpy(buff, pa->tszAccountName, bufflen); + return true; + } + else + return GetSysSubstText(hContact, _T("proto"), buff, bufflen); + } + } + else if (!_tcscmp(swzRawSpec, _T("time"))) { + if (tmi.printDateTime && !tmi.printDateTimeByContact(hContact, _T("t"), buff, bufflen, TZF_KNOWNONLY)) + return true; + } + else if (!_tcscmp(swzRawSpec, _T("uidname"))) { + char *szProto = GetContactProto(hContact); + return UidName(szProto, buff, bufflen); + } + else if (!_tcscmp(swzRawSpec, _T("status_msg"))) { + TCHAR *swzMsg = GetStatusMessageText(hContact); + if (swzMsg) { + _tcsncpy(buff, swzMsg, bufflen); + mir_free(swzMsg); + return true; + } + } + else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg"))) || !_tcscmp(swzRawSpec, _T("last_msg_out"))) { + TCHAR *swzMsg = GetLastMessageText(hContact, recv); + if (swzMsg) { + _tcsncpy(buff, swzMsg, bufflen); + mir_free(swzMsg); + return true; + } + } + else if (!_tcscmp(swzRawSpec, _T("meta_subname"))) { + // get contact list name of active subcontact + MCONTACT hSubContact = db_mc_getMostOnline(hContact); + if (!hSubContact) + return false; + + TCHAR *swzNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, GCDNF_TCHAR); + if (swzNick) _tcsncpy(buff, swzNick, bufflen); + return true; + } + else if (!_tcscmp(swzRawSpec, _T("meta_subuid"))) { + MCONTACT hSubContact = db_mc_getMostOnline(hContact); + if (!hSubContact || (INT_PTR)hSubContact == CALLSERVICE_NOTFOUND) + return false; + return Uid(hSubContact, 0, buff, bufflen); + } + else if (!_tcscmp(swzRawSpec, _T("meta_subproto"))) { + // get protocol of active subcontact + MCONTACT hSubContact = db_mc_getMostOnline(hContact); + if (!hSubContact || (INT_PTR)hSubContact == CALLSERVICE_NOTFOUND) + return false; + return GetSysSubstText(hSubContact, _T("account"), buff, bufflen); + } + else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_time"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_time"))) { + DWORD ts = LastMessageTimestamp(hContact, recv); + if (ts == 0) return false; + FormatTimestamp(ts, "t", buff, bufflen); + return true; + } + else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_date"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_date"))) { + DWORD ts = LastMessageTimestamp(hContact, recv); + if (ts == 0) return false; + FormatTimestamp(ts, "d", buff, bufflen); + return true; + } + else if ((recv = !_tcscmp(swzRawSpec, _T("last_msg_reltime"))) || !_tcscmp(swzRawSpec, _T("last_msg_out_reltime"))) { + DWORD ts = LastMessageTimestamp(hContact, recv); + if (ts == 0) return false; + DWORD t = (DWORD)time(0); + DWORD diff = (t - ts); + int d = (diff / 60 / 60 / 24); + int h = (diff - d * 60 * 60 * 24) / 60 / 60; + int m = (diff - d * 60 * 60 * 24 - h * 60 * 60) / 60; + if (d > 0) mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); + else if (h > 0) mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); + else mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); + return true; + } + else if (!_tcscmp(swzRawSpec, _T("msg_count_all")) || !_tcscmp(swzRawSpec, _T("msg_count_out")) || !_tcscmp(swzRawSpec, _T("msg_count_in"))) { + DWORD dwCountOut, dwCountIn; + DWORD dwMetaCountOut = 0, dwMetaCountIn = 0; + DWORD dwLastTs, dwNewTs, dwRecountTs; + DWORD dwTime, dwDiff; + int iNumber = 1; + MCONTACT hTmpContact = hContact; + + char *szProto = GetContactProto(hContact); + if (szProto && !strcmp(szProto, META_PROTO)) { + iNumber = db_mc_getSubCount(hContact); + hTmpContact = db_mc_getSub(hContact, 0); + } + + for (int i = 0; i < iNumber; i++) { + if (i > 0) + hTmpContact = db_mc_getSub(hContact, i); + dwRecountTs = db_get_dw(hTmpContact, MODULE, "LastCountTS", 0); + dwTime = (DWORD)time(0); + dwDiff = (dwTime - dwRecountTs); + if (dwDiff > (60 * 60 * 24 * 3)) { + db_set_dw(hTmpContact, MODULE, "LastCountTS", dwTime); + dwCountOut = dwCountIn = dwLastTs = 0; + } + else { + dwCountOut = db_get_dw(hTmpContact, MODULE, "MsgCountOut", 0); + dwCountIn = db_get_dw(hTmpContact, MODULE, "MsgCountIn", 0); + dwLastTs = db_get_dw(hTmpContact, MODULE, "LastMsgTS", 0); + } + + dwNewTs = dwLastTs; + + HANDLE dbe = db_event_last(hTmpContact); + while (dbe != NULL) { + DBEVENTINFO dbei = { sizeof(dbei) }; + if (!db_event_get(dbe, &dbei)) { + if (dbei.eventType == EVENTTYPE_MESSAGE) { + dwNewTs = max(dwNewTs, dbei.timestamp); + if (dbei.timestamp > dwLastTs) { + if (dbei.flags & DBEF_SENT) dwCountOut++; + else dwCountIn++; + } + else break; + } + } + dbe = db_event_prev(hTmpContact, dbe); + } + + if (dwNewTs > dwLastTs) { + db_set_dw(hTmpContact, MODULE, "MsgCountOut", dwCountOut); + db_set_dw(hTmpContact, MODULE, "MsgCountIn", dwCountIn); + db_set_dw(hTmpContact, MODULE, "LastMsgTS", dwNewTs); + } + + dwMetaCountOut += dwCountOut; + dwMetaCountIn += dwCountIn; + } + + if (!_tcscmp(swzRawSpec, _T("msg_count_out"))) + mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountOut); + else if (!_tcscmp(swzRawSpec, _T("msg_count_in"))) + mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountIn); + else + mir_sntprintf(buff, bufflen, _T("%d"), dwMetaCountOut + dwMetaCountIn); + return true; + } + + return false; +} + +bool GetSubstText(MCONTACT hContact, const DISPLAYSUBST &ds, TCHAR *buff, int bufflen) +{ + TranslateFunc *transFunc = 0; + for (int i = 0; i < iTransFuncsCount; i++) + if (translations[i].id == (DWORD)ds.iTranslateFuncId) { + transFunc = translations[i].transFunc; + break; + } + + if (!transFunc) + return false; + + switch (ds.type) { + case DVT_DB: + return transFunc(hContact, ds.szModuleName, ds.szSettingName, buff, bufflen) != 0; + case DVT_PROTODB: + char *szProto = GetContactProto(hContact); + if (szProto) { + if (transFunc(hContact, szProto, ds.szSettingName, buff, bufflen) != 0) + return true; + return transFunc(hContact, "UserInfo", ds.szSettingName, buff, bufflen) != 0; + } + break; + } + return false; +} + +bool GetRawSubstText(MCONTACT hContact, char *szRawSpec, TCHAR *buff, int bufflen) +{ + size_t lenght = strlen(szRawSpec); + for (size_t i = 0; i < lenght; i++) { + if (szRawSpec[i] == '/') { + szRawSpec[i] = 0; + if (strlen(szRawSpec) == 0) { + char *szProto = GetContactProto(hContact); + if (szProto) { + if (translations[0].transFunc(hContact, szProto, &szRawSpec[i + 1], buff, bufflen) != 0) + return true; + return translations[0].transFunc(hContact, "UserInfo", &szRawSpec[i + 1], buff, bufflen) != 0; + } + return false; + } + return translations[0].transFunc(hContact, szRawSpec, &szRawSpec[i + 1], buff, bufflen) != 0; + } + } + return false; +} + +bool ApplySubst(MCONTACT hContact, const TCHAR *swzSource, bool parseTipperVarsFirst, TCHAR *swzDest, int iDestLen) +{ + // hack - allow empty strings before passing to variables (note - zero length strings return false after this) + if (swzDest && swzSource && _tcslen(swzSource) == 0) { + swzDest[0] = 0; + return true; + } + + // pass to variables plugin if available + TCHAR *swzVarSrc; + if (parseTipperVarsFirst) + swzVarSrc = mir_tstrdup(swzSource); + else + swzVarSrc = variables_parsedup((TCHAR *)swzSource, 0, hContact); + + size_t iSourceLen = _tcslen(swzVarSrc); + size_t si = 0, di = 0, v = 0; + + TCHAR swzVName[LABEL_LEN]; + TCHAR swzRep[VALUE_LEN], swzAlt[VALUE_LEN]; + while (si < iSourceLen && di < (size_t)iDestLen - 1) { + if (swzVarSrc[si] == _T('%')) { + si++; + v = 0; + while (si < iSourceLen && v < LABEL_LEN) { + if (swzVarSrc[si] == _T('%')) + break; + + swzVName[v] = swzVarSrc[si]; + v++; si++; + } + + if (v == 0) // bSubst len is 0 - just a % symbol + swzDest[di] = _T('%'); + else if (si < iSourceLen) // we found end % + { + swzVName[v] = 0; + + bool bAltSubst = false; + bool bSubst = false; + + // apply only to specific protocols + TCHAR *p = _tcsrchr(swzVName, _T('^')); // use last '^', so if you want a ^ in swzAlt text, you can just put a '^' on the end + if (p) { + *p = 0; + p++; + if (*p) { + char *cp = GetContactProto(hContact); + if (cp != NULL) { + PROTOACCOUNT *acc = ProtoGetAccount(cp); + if (acc != NULL) { + cp = acc->szProtoName; + } + } + + if (cp == NULL) + goto empty; + + bool negate = false; + if (*p == _T('!')) { + p++; + if (*p == 0) goto error; + negate = true; + } + + char sproto[256]; + bool spec = false; + int len; + + TCHAR *last = _tcsrchr(p, _T(',')); + if (!last) last = p; + + while (p <= last + 1) { + len = (int)_tcscspn(p, _T(",")); + t2a(p, sproto, len); + sproto[len] = 0; + p += len + 1; + + if (_stricmp(cp, sproto) == 0) { + spec = true; + break; + } + } + + if (negate ? spec : !spec) + goto empty; + } + } + + // get alternate text, if bSubst fails + swzAlt[0] = 0; + p = _tcschr(swzVName, _T('|')); // use first '|' - so you can use the '|' symbol in swzAlt text + if (p) { + *p = 0; // clip swzAlt from swzVName + p++; + if (_tcslen(p) > 4 && _tcsncmp(p, _T("raw:"), 4) == 0) { // raw db substitution + char raw_spec[LABEL_LEN]; + p += 4; + t2a(p, raw_spec, LABEL_LEN); + GetRawSubstText(hContact, raw_spec, swzAlt, VALUE_LEN); + } + else if (_tcslen(p) > 4 && _tcsncmp(p, _T("sys:"), 4) == 0) { // 'system' substitution + p += 4; + GetSysSubstText(hContact, p, swzAlt, VALUE_LEN); + } + else { + // see if we can find the bSubst + DSListNode *ds_node = opt.dsList; + while (ds_node) { + if (_tcscmp(ds_node->ds.swzName, p) == 0) + break; + + ds_node = ds_node->next; + } + + if (ds_node) + GetSubstText(hContact, ds_node->ds, swzAlt, VALUE_LEN); + else { + _tcsncpy(swzAlt, p, VALUE_LEN); + bAltSubst = true; + } + } + swzAlt[VALUE_LEN - 1] = 0; + if (_tcslen(swzAlt) != 0) + bAltSubst = true; + } + + // get bSubst text + if (v > 4 && _tcsncmp(swzVName, _T("raw:"), 4) == 0) // raw db substitution + { + char raw_spec[LABEL_LEN]; + t2a(&swzVName[4], raw_spec, LABEL_LEN); + bSubst = GetRawSubstText(hContact, raw_spec, swzRep, VALUE_LEN); + } + else if (v > 4 && _tcsncmp(swzVName, _T("sys:"), 4) == 0) // 'system' substitution + { + bSubst = GetSysSubstText(hContact, &swzVName[4], swzRep, VALUE_LEN); + } + else { + // see if we can find the bSubst + DSListNode *ds_node = opt.dsList; + while (ds_node) { + if (_tcscmp(ds_node->ds.swzName, swzVName) == 0) + break; + + ds_node = ds_node->next; + } + + if (!ds_node) + goto error; // no such bSubst + + bSubst = GetSubstText(hContact, ds_node->ds, swzRep, VALUE_LEN); + } + + if (bSubst) { + size_t rep_len = _tcslen(swzRep); + _tcsncpy(&swzDest[di], swzRep, min(rep_len, iDestLen - di)); + di += rep_len - 1; // -1 because we inc at bottom of loop + } + else if (bAltSubst) { + size_t alt_len = _tcslen(swzAlt); + _tcsncpy(&swzDest[di], swzAlt, min(alt_len, iDestLen - di)); + di += alt_len - 1; // -1 because we inc at bottom of loop + } + else goto empty; // empty value + } + else // no end % - error + goto error; + } + else swzDest[di] = swzVarSrc[si]; + + si++; + di++; + } + + mir_free(swzVarSrc); + swzDest[di] = 0; + + if (parseTipperVarsFirst) { + swzVarSrc = variables_parsedup((TCHAR *)swzDest, 0, hContact); + _tcscpy(swzDest, swzVarSrc); + mir_free(swzVarSrc); + } + + + // check for a 'blank' string - just spaces etc + for (si = 0; si <= di; si++) { + if (swzDest[si] != 0 && swzDest[si] != _T(' ') && swzDest[si] != _T('\t') && swzDest[si] != _T('\r') && swzDest[si] != _T('\n')) + return true; + } + + return false; + +empty: + mir_free(swzVarSrc); + return false; + +error: + swzDest[0] = _T('*'); + swzDest[1] = 0; + mir_free(swzVarSrc); + return true; +} + +bool GetLabelText(MCONTACT hContact, const DISPLAYITEM &di, TCHAR *buff, int bufflen) +{ + return ApplySubst(hContact, di.swzLabel, false, buff, bufflen); +} + +bool GetValueText(MCONTACT hContact, const DISPLAYITEM &di, TCHAR *buff, int bufflen) +{ + return ApplySubst(hContact, di.swzValue, di.bParseTipperVarsFirst, buff, bufflen); +} + +void TruncateString(TCHAR *swzText) +{ + if (swzText && opt.iLimitCharCount > 3) { + if ((int)_tcslen(swzText) > opt.iLimitCharCount) { + swzText[opt.iLimitCharCount - 3] = 0; + _tcscat(swzText, _T("...")); + } + } +} + +TCHAR *GetProtoStatusMessage(char *szProto, WORD wStatus) +{ + if (!szProto || wStatus == ID_STATUS_OFFLINE) + return NULL; + + // check if protocol supports status message for status + int flags = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_3, 0); + if (!(flags & Proto_Status2Flag(wStatus))) + return NULL; + + TCHAR *swzText = (TCHAR *)CallProtoService(szProto, PS_GETMYAWAYMSG, 0, SGMA_TCHAR); + if ((INT_PTR)swzText == CALLSERVICE_NOTFOUND) + swzText = (TCHAR*)CallService(MS_AWAYMSG_GETSTATUSMSGT, wStatus, 0); + + else if (swzText == NULL) { + // try to use service without SGMA_TCHAR + char *tmpMsg = (char *)CallProtoService(szProto, PS_GETMYAWAYMSG, 0, 0); + if (tmpMsg && (INT_PTR)tmpMsg != CALLSERVICE_NOTFOUND) { + swzText = mir_a2t(tmpMsg); + mir_free(tmpMsg); + } + } + + + if (swzText && !swzText[0]) { + mir_free(swzText); + swzText = NULL; + } + + if (swzText && opt.bLimitMsg) + TruncateString(swzText); + + return swzText; +} + +TCHAR *GetProtoExtraStatusTitle(char *szProto) +{ + DBVARIANT dbv; + TCHAR *swzText = NULL; + + if (!szProto) + return NULL; + + if (!db_get_ts(0, szProto, "XStatusName", &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzText = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + } + + if (!swzText) { + TCHAR buff[256]; + if (EmptyXStatusToDefaultName(0, szProto, 0, buff, 256)) + swzText = mir_tstrdup(buff); + } + + if (opt.bLimitMsg) + TruncateString(swzText); + + return swzText; +} + +TCHAR *GetProtoExtraStatusMessage(char *szProto) +{ + if (!szProto) + return NULL; + + TCHAR *swzText = NULL; + DBVARIANT dbv; + if (!db_get_ts(0, szProto, "XStatusMsg", &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzText = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + + if (ServiceExists(MS_VARS_FORMATSTRING)) { + MCONTACT hContact = db_find_first(); + char *proto = GetContactProto(hContact); + while (!proto) { + hContact = db_find_next(hContact); + if (hContact) + proto = GetContactProto(hContact); + else { + hContact = NULL; + break; + } + } + + TCHAR *tszParsed = variables_parse(swzText, NULL, hContact); + if (tszParsed) + replaceStrT(swzText, tszParsed); + } + } + + if (opt.bLimitMsg) + TruncateString(swzText); + + return swzText; +} + +TCHAR *GetListeningTo(char *szProto) +{ + DBVARIANT dbv; + TCHAR *swzText = NULL; + + if (!szProto) + return NULL; + + if (!db_get_ts(0, szProto, "ListeningTo", &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzText = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + } + + if (opt.bLimitMsg) + TruncateString(swzText); + + return swzText; +} + +TCHAR *GetJabberAdvStatusText(char *szProto, const char *szSlot, const char *szValue) +{ + DBVARIANT dbv; + TCHAR *swzText = NULL; + + if (!szProto) + return NULL; + + char szSetting[128]; + mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", szProto, szSlot, szValue); + if (!db_get_ts(0, "AdvStatus", szSetting, &dbv)) { + if (_tcslen(dbv.ptszVal) != 0) + swzText = mir_tstrdup(dbv.ptszVal); + db_free(&dbv); + } + + if (opt.bLimitMsg) + TruncateString(swzText); + + return swzText; +} + +HICON GetJabberActivityIcon(MCONTACT hContact, char *szProto) +{ + DBVARIANT dbv; + HICON hIcon = NULL; + + if (!szProto) + return NULL; + + char szSetting[128]; + mir_snprintf(szSetting, SIZEOF(szSetting), "%s/%s/%s", szProto, "activity", "icon"); + if (!db_get_s(hContact, "AdvStatus", szSetting, &dbv)) { + hIcon = Skin_GetIcon(dbv.pszVal); + db_free(&dbv); + } + + return hIcon; } \ No newline at end of file -- cgit v1.2.3