/* Copyright (C) 2005 Ricardo Pescuma Domenecci Copyright (C) 2007-2010 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 "stdafx.h" #include int InitTipperSmileys() { // Register smiley category if (ServiceExists(MS_SMILEYADD_REGISTERCATEGORY)) { SMADD_REGCAT rc; rc.name = "tipper"; rc.dispname = Translate("Tipper smileys"); CallService(MS_SMILEYADD_REGISTERCATEGORY, 0, (LPARAM)&rc); } return 0; } SMILEYPARSEINFO Smileys_PreParse(LPCTSTR lpString, int nCount, const char *protocol) { if (!(opt.iSmileyAddFlags & SMILEYADD_ENABLE)) return nullptr; if (nCount == -1) nCount = (int)mir_wstrlen(lpString); SMILEYPARSEINFO info = (SMILEYPARSEINFO)mir_calloc(sizeof(tagSMILEYPARSEINFO)); info->pieces = ReplaceSmileys(lpString, nCount, protocol, &info->max_height); if (!info->pieces) { mir_free(info); return nullptr; } return info; } void Smileys_FreeParse(SMILEYPARSEINFO parseInfo) { if (parseInfo != nullptr) { if (parseInfo->pieces != nullptr) DestroySmileyList(parseInfo->pieces); mir_free(parseInfo); } } // Similar to DrawText win32 api function // Pass uFormat | DT_CALCRECT to calc rectangle to be returned by lpRect // parseInfo is optional (pass NULL and it will be calculated and deleted inside function int Smileys_DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat, SMILEYPARSEINFO parseInfo) { if (nCount == -1) nCount = (int)mir_wstrlen(lpString); if (uFormat & DT_CALCRECT) { SIZE text_size = GetTextSize(hDC, lpString, parseInfo, uFormat, (lpRect->right - lpRect->left)); lpRect->bottom = text_size.cy; if (text_size.cx < lpRect->right - lpRect->left) { if (uFormat & DT_RIGHT) lpRect->left = lpRect->right - text_size.cx; else lpRect->right = lpRect->left + text_size.cx; } return text_size.cy; } // Draw if (parseInfo->pieces == nullptr) return DrawText(hDC, lpString, nCount, lpRect, uFormat); RECT rc = *lpRect; SIZE text_size = GetTextSize(hDC, lpString, parseInfo, uFormat, (lpRect->right - lpRect->left)); if (text_size.cx < rc.right - rc.left) { if (uFormat & DT_RIGHT) rc.left = rc.right - text_size.cx; else rc.right = rc.left + text_size.cx; } DrawTextSmiley(hDC, rc, lpString, nCount, parseInfo, uFormat); return text_size.cy; } SIZE GetTextSize(HDC hdcMem, const wchar_t *szText, SMILEYPARSEINFO info, UINT uTextFormat, int max_width) { SIZE text_size = { 0 }; int text_height; int row_count = 0, pos_x = 0; if (szText == nullptr || wcslen(szText) == 0) { text_size.cy = 0; text_size.cx = 0; } else { RECT text_rc = { 0, 0, 2048, 2048 }; if (info->pieces == nullptr) { DrawText(hdcMem, szText, -1, &text_rc, DT_CALCRECT | uTextFormat); text_size.cx = text_rc.right - text_rc.left; text_size.cy = text_rc.bottom - text_rc.top; } else { // Get real height of the line text_height = DrawText(hdcMem, L"A", 1, &text_rc, DT_CALCRECT | uTextFormat); // See each item of list int i; for (i = 0; i < info->pieces->realCount; i++) { TEXTPIECE *piece = (TEXTPIECE *)info->pieces->items[i]; info->row_height[row_count] = max(info->row_height[row_count], text_height); if (piece->type == TEXT_PIECE_TYPE_TEXT) { RECT rc = { 0, 0, 2048, 2048 }; DrawText(hdcMem, szText + piece->start_pos, piece->len, &rc, DT_CALCRECT | uTextFormat); pos_x += (rc.right - rc.left); if (pos_x > max_width) { text_size.cx = max(text_size.cx, pos_x - (rc.right - rc.left)); pos_x = rc.right - rc.left; info->row_height[++row_count] = text_height; } if (szText[piece->start_pos + piece->len - 1] == '\n') { text_size.cx = max(text_size.cx, pos_x); pos_x = 0; info->row_height[++row_count] = 0; } } else { double factor; if (uTextFormat & DT_RESIZE_SMILEYS && piece->smiley_height > text_height) factor = text_height / (double)piece->smiley_height; else factor = 1; info->row_height[row_count] = max(info->row_height[row_count], piece->smiley_height * factor); pos_x += piece->smiley_width * factor; if (pos_x > max_width) { text_size.cx = max(text_size.cx, pos_x - (piece->smiley_width * factor)); pos_x = piece->smiley_width * factor; info->row_height[++row_count] = piece->smiley_height * factor; } } } text_size.cx = max(text_size.cx, pos_x); for (i = 0; i < row_count + 1; i++) text_size.cy += info->row_height[i]; } } return text_size; } void DrawTextSmiley(HDC hdcMem, RECT free_rc, const wchar_t *szText, int len, SMILEYPARSEINFO info, UINT uTextFormat) { if (szText == nullptr) return; uTextFormat &= ~DT_RIGHT; // Draw list int text_height, i, shift; int row_count = 0, pos_x = 0; RECT tmp_rc = free_rc; if (uTextFormat & DT_RTLREADING) i = info->pieces->realCount - 1; else i = 0; // Get real height of the line text_height = DrawText(hdcMem, L"A", 1, &tmp_rc, DT_CALCRECT | uTextFormat); mir_ptr pBits(SaveAlpha(&free_rc)); // Just draw ellipsis if (free_rc.right <= free_rc.left) DrawText(hdcMem, L"...", 3, &free_rc, uTextFormat & ~DT_END_ELLIPSIS); else { // Draw text and smileys RECT text_rc = free_rc; for (; i < info->pieces->realCount && i >= 0 && len > 0; i += (uTextFormat & DT_RTLREADING ? -1 : 1)) { TEXTPIECE *piece = (TEXTPIECE *)info->pieces->items[i]; if (uTextFormat & DT_RTLREADING) text_rc.right = free_rc.right - pos_x; else text_rc.left = free_rc.left + pos_x; if (piece->type == TEXT_PIECE_TYPE_TEXT) { tmp_rc = text_rc; tmp_rc.right = 2048; DrawText(hdcMem, szText + piece->start_pos, min(len, piece->len), &tmp_rc, DT_CALCRECT | (uTextFormat & ~DT_END_ELLIPSIS)); pos_x += (tmp_rc.right - tmp_rc.left); if (pos_x > (free_rc.right - free_rc.left)) { pos_x = tmp_rc.right - tmp_rc.left; text_rc.left = free_rc.left; text_rc.right = free_rc.right; text_rc.top += info->row_height[row_count]; row_count++; } shift = (info->row_height[row_count] - text_height) >> 1; text_rc.top += shift; if (uTextFormat & DT_RTLREADING) text_rc.left = max(text_rc.left, text_rc.right - (tmp_rc.right - tmp_rc.left)); DrawText(hdcMem, szText + piece->start_pos, min(len, piece->len), &text_rc, uTextFormat); len -= piece->len; text_rc.top -= shift; if (szText[piece->start_pos + piece->len - 1] == '\n') { text_rc.left = free_rc.left; text_rc.right = free_rc.right; text_rc.top += info->row_height[row_count]; pos_x = 0; row_count++; } } else { double factor; if (len < piece->len) { len = 0; } else { len -= piece->len; if (uTextFormat & DT_RESIZE_SMILEYS && piece->smiley_height > text_height) factor = text_height / (double)piece->smiley_height; else factor = 1; if (uTextFormat & DT_RTLREADING) text_rc.left = max(text_rc.right - (int)(piece->smiley_width * factor), text_rc.left); pos_x += piece->smiley_width * factor; if (pos_x > (free_rc.right - free_rc.left)) { pos_x = piece->smiley_width * factor; text_rc.left = free_rc.left; text_rc.right = free_rc.right; text_rc.top += info->row_height[row_count]; row_count++; } shift = (info->row_height[row_count] - (LONG)(piece->smiley_height * factor)) >> 1; DrawIconExAlpha(hdcMem, text_rc.left, text_rc.top + shift, piece->smiley, piece->smiley_width * factor, piece->smiley_height * factor, 0, nullptr, DI_NORMAL, true); } } } } RestoreAlpha(&free_rc, pBits); } void DestroySmileyList(SortedList *p_list) { if (p_list == nullptr) return; if (p_list->items != nullptr) { for (int i = 0; i < p_list->realCount; i++) { TEXTPIECE *piece = (TEXTPIECE *)p_list->items[i]; if (piece != nullptr) { if (piece->type == TEXT_PIECE_TYPE_SMILEY) DestroyIcon(piece->smiley); mir_free(piece); //this free the p_list->items[i] } } } List_Destroy(p_list); //this free the p_list->items member mir_free(p_list); //this free the p_list itself (alloc by List_Create) } // Generate the list of smileys / text to be drawn SortedList *ReplaceSmileys(const wchar_t *text, int text_size, const char *protocol, int *max_smiley_height) { *max_smiley_height = 0; if (!text || !text[0] || !ServiceExists(MS_SMILEYADD_BATCHPARSE)) return nullptr; char smileyProto[64]; if (protocol == nullptr) strncpy(smileyProto, "tipper", sizeof(smileyProto) - 1); else if (mir_strcmp(protocol, META_PROTO) == 0) strncpy(smileyProto, "tipper", sizeof(smileyProto) - 1); else strncpy(smileyProto, protocol, sizeof(smileyProto) - 1); // Parse it! SMADD_BATCHPARSE sp = {}; sp.str.w = text; sp.flag = SAFL_UNICODE; sp.Protocolname = (opt.iSmileyAddFlags & SMILEYADD_USEPROTO) ? smileyProto : "tipper"; SMADD_BATCHPARSERES *spres = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp); if (!spres) // Did not find a smiley return nullptr; // Lets add smileys SortedList *plText = List_Create(0, 10); wchar_t *word_start, *word_end; wchar_t *smiley_start, *smiley_end; wchar_t *last_text_pos = _tcsninc(text, text_size); word_start = word_end = (wchar_t *)text; for (unsigned i = 0; i < sp.numSmileys; i++) { // Get smile position smiley_start = _tcsninc(text, spres[i].startChar); smiley_end = _tcsninc(smiley_start, spres[i].size); if (spres[i].hIcon) // For deffective smileypacks { if (opt.iSmileyAddFlags & SMILEYADD_ONLYISOLATED) { if ((smiley_start > text && *(smiley_start - 1) != ' ' && *(smiley_start - 1) != '\n' && *smiley_end != '\r') || (*smiley_end != '\0' && *smiley_end != ' ' && *smiley_end != '\n' && *smiley_end != '\r')) continue; } // Add text while (word_end != smiley_start) { while (word_end[0] != ' ' && word_end[0] != '\n') word_end++; if (word_end > smiley_start) word_end = smiley_start; else word_end++; if (word_end > word_start) { TEXTPIECE *piece = (TEXTPIECE *)mir_calloc(sizeof(TEXTPIECE)); piece->type = TEXT_PIECE_TYPE_TEXT; piece->start_pos = word_start - text; piece->len = word_end - word_start; List_Insert(plText, piece, plText->realCount); word_start = word_end; } } // Add smiley TEXTPIECE *piece = (TEXTPIECE *)mir_calloc(sizeof(TEXTPIECE)); piece->type = TEXT_PIECE_TYPE_SMILEY; piece->len = spres[i].size; piece->smiley = spres[i].hIcon; piece->smiley_width = 16; piece->smiley_height = 16; ICONINFO icon; if (GetIconInfo(piece->smiley, &icon)) { BITMAP bm; if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) { piece->smiley_width = bm.bmWidth; piece->smiley_height = bm.bmHeight; } DeleteObject(icon.hbmMask); DeleteObject(icon.hbmColor); } *max_smiley_height = max(piece->smiley_height, *max_smiley_height); List_Insert(plText, piece, plText->realCount); word_start = word_end = smiley_end; } } // Add rest of the text while (word_end != last_text_pos) { while (word_end[0] && word_end[0] != ' ' && word_end[0] != '\n') word_end++; if (word_end[0]) word_end++; if (word_end > word_start) { TEXTPIECE *piece = (TEXTPIECE *)mir_calloc(sizeof(TEXTPIECE)); piece->type = TEXT_PIECE_TYPE_TEXT; piece->start_pos = word_start - text; piece->len = word_end - word_start; List_Insert(plText, piece, plText->realCount); word_start = word_end; } } CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spres); return plText; } int DrawTextExt(HDC hdc, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat, SMILEYPARSEINFO spi) { if ((opt.iSmileyAddFlags & SMILEYADD_ENABLE) && spi != nullptr) { if (opt.iSmileyAddFlags & SMILEYADD_RESIZE) uFormat |= DT_RESIZE_SMILEYS; return Smileys_DrawText(hdc, lpString, nCount, lpRect, uFormat, spi); } if (uFormat & DT_CALCRECT) return DrawText(hdc, lpString, nCount, lpRect, uFormat); return DrawTextAlpha(hdc, lpString, nCount, lpRect, uFormat); }