summaryrefslogtreecommitdiff
path: root/utils/mir_smileys.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mir_smileys.cpp')
-rw-r--r--utils/mir_smileys.cpp479
1 files changed, 479 insertions, 0 deletions
diff --git a/utils/mir_smileys.cpp b/utils/mir_smileys.cpp
new file mode 100644
index 0000000000..bce1cc75ee
--- /dev/null
+++ b/utils/mir_smileys.cpp
@@ -0,0 +1,479 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+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 "mir_smileys.h"
+#include "utf8_helpers.h"
+
+#include <richedit.h>
+#include <m_smileyadd.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_clui.h>
+#include <m_database.h>
+#include <commctrl.h>
+#include <m_skin_eng.h>
+#include <tchar.h>
+
+// Prototypes
+
+#define TEXT_PIECE_TYPE_TEXT 0
+#define TEXT_PIECE_TYPE_SMILEY 1
+typedef struct
+{
+ int type;
+ int len;
+ union
+ {
+ struct
+ {
+ int start_pos;
+ };
+ struct
+ {
+ HICON smiley;
+ int smiley_width;
+ int smiley_height;
+ };
+ };
+}
+TextPiece;
+
+SortedList * ReplaceSmileys(const TCHAR *text, int text_size, const char *protocol, int *max_smiley_height);
+void DrawTextSmiley(HDC hdcMem, RECT free_rc, const TCHAR *szText, int len, SortedList *plText, UINT uTextFormat, int max_smiley_height);
+void DestroySmileyList(SortedList* p_list);
+SIZE GetTextSize(HDC hdcMem, const TCHAR *szText, SortedList *plText, UINT uTextFormat, int max_smiley_height);
+
+// Functions
+
+int InitContactListSmileys()
+{
+ // Register smiley category
+ if (ServiceExists(MS_SMILEYADD_REGISTERCATEGORY))
+ {
+ SMADD_REGCAT rc;
+
+ rc.cbSize = sizeof(rc);
+ rc.name = "clist";
+ rc.dispname = Translate("Contact List smileys");
+
+ CallService(MS_SMILEYADD_REGISTERCATEGORY, 0, (LPARAM)&rc);
+ }
+
+ return 0;
+}
+
+SmileysParseInfo Smileys_PreParse(const TCHAR* lpString, int nCount, const char *protocol)
+{
+ SmileysParseInfo info = (SmileysParseInfo)mir_calloc(sizeof(_SmileysParseInfo));
+
+ info->pieces = ReplaceSmileys(lpString, nCount, protocol, &info->max_height);
+
+ return info;
+}
+
+void Smileys_FreeParse(SmileysParseInfo parseInfo)
+{
+ if (parseInfo != NULL)
+ {
+ if (parseInfo->pieces != NULL)
+ DestroySmileyList(parseInfo->pieces);
+
+ mir_free(parseInfo);
+ }
+}
+
+int skin_DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat)
+{
+ if ((uFormat & DT_CALCRECT) == 0 && ServiceExists(MS_SKINENG_ALPHATEXTOUT))
+ {
+ COLORREF color = SetTextColor(hDC, 0);
+ SetTextColor(hDC, color);
+
+ return AlphaText(hDC, lpString, nCount, lpRect, uFormat, color);
+ }
+
+ return DrawText(hDC, lpString, nCount, lpRect, uFormat);
+}
+
+int skin_DrawIconEx(HDC hdc, int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth,
+ UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
+{
+ if (ServiceExists(MS_SKINENG_DRAWICONEXFIX))
+ return mod_DrawIconEx_helper(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags);
+ else
+ return DrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags);
+}
+
+
+
+// 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, const char *protocol, SmileysParseInfo parseInfo)
+{
+ SmileysParseInfo info;
+ int ret;
+
+ if (nCount < 0)
+ nCount = (int)mir_tstrlen(lpString);
+
+ // Get parse info
+ if (parseInfo == NULL)
+ info = Smileys_PreParse(lpString, nCount, protocol);
+ else
+ info = parseInfo;
+
+ if (uFormat & DT_CALCRECT)
+ {
+ SIZE text_size = GetTextSize(hDC, lpString, info->pieces, uFormat, info->max_height);
+
+ lpRect->bottom = min(lpRect->bottom, lpRect->top + 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;
+ }
+
+ ret = text_size.cy;
+ }
+ else
+ {
+ // Clipping rgn
+ HRGN oldRgn = CreateRectRgn(0, 0, 1, 1);
+ if (GetClipRgn(hDC, oldRgn) != 1)
+ {
+ DeleteObject(oldRgn);
+ oldRgn = NULL;
+ }
+
+ HRGN rgn = CreateRectRgnIndirect(lpRect);
+ ExtSelectClipRgn(hDC, rgn, RGN_AND);
+
+ // Draw
+ if (info->pieces == NULL)
+ {
+ ret = skin_DrawText(hDC, lpString, nCount, lpRect, uFormat);
+ }
+ else
+ {
+ RECT rc = *lpRect;
+
+ SIZE text_size = GetTextSize(hDC, lpString, info->pieces, uFormat, info->max_height);
+
+ 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;
+ }
+
+ ret = text_size.cy;
+
+ DrawTextSmiley(hDC, rc, lpString, nCount, info->pieces, uFormat, info->max_height);
+ }
+
+ // Clipping rgn
+ SelectClipRgn(hDC, oldRgn);
+ DeleteObject(rgn);
+ if (oldRgn) DeleteObject(oldRgn);
+ }
+
+
+ // Free parse info
+ if (parseInfo == NULL)
+ Smileys_FreeParse(info);
+
+ return ret;
+}
+
+
+
+SIZE GetTextSize(HDC hdcMem, const TCHAR *szText, SortedList *plText, UINT uTextFormat, int max_smiley_height)
+{
+ SIZE text_size;
+
+ if (szText == NULL) {
+ text_size.cy = 0;
+ text_size.cx = 0;
+ }
+ else {
+ RECT text_rc = { 0, 0, 0x7FFFFFFF, 0x7FFFFFFF };
+
+ // Always need cy...
+ DrawText(hdcMem, szText, -1, &text_rc, DT_CALCRECT | uTextFormat);
+ text_size.cy = text_rc.bottom - text_rc.top;
+
+ if (plText == NULL)
+ text_size.cx = text_rc.right - text_rc.left;
+ else {
+ if (!(uTextFormat & DT_RESIZE_SMILEYS))
+ text_size.cy = max(text_size.cy, max_smiley_height);
+
+ text_size.cx = 0;
+
+ // See each item of list
+ for (int i = 0; i < plText->realCount; i++) {
+ TextPiece *piece = (TextPiece *)plText->items[i];
+
+ if (piece->type == TEXT_PIECE_TYPE_TEXT) {
+ RECT rc = { 0, 0, 0x7FFFFFFF, 0x7FFFFFFF };
+
+ DrawText(hdcMem, &szText[piece->start_pos], piece->len, &rc, DT_CALCRECT | uTextFormat);
+ text_size.cx = text_size.cx + rc.right - rc.left;
+ }
+ else {
+ double factor;
+
+ if ((uTextFormat & DT_RESIZE_SMILEYS) && piece->smiley_height > text_size.cy)
+ factor = text_size.cy / (double)piece->smiley_height;
+ else
+ factor = 1;
+
+ text_size.cx = text_size.cx + (LONG)(factor * piece->smiley_width);
+ }
+ }
+ }
+ }
+
+ return text_size;
+}
+
+void DrawTextSmiley(HDC hdcMem, RECT free_rc, const TCHAR *szText, int len, SortedList *plText, UINT uTextFormat, int max_smiley_height)
+{
+ if (szText == NULL)
+ return;
+
+ uTextFormat &= ~DT_RIGHT;
+
+ // Draw list
+ int i;
+ int pos_x = 0;
+ int row_height, text_height;
+ RECT tmp_rc = free_rc;
+
+ if (uTextFormat & DT_RTLREADING)
+ i = plText->realCount - 1;
+ else
+ i = 0;
+
+ // Get real height of the line
+ text_height = skin_DrawText(hdcMem, _T("A"), 1, &tmp_rc, DT_CALCRECT | uTextFormat);
+ if (uTextFormat & DT_RESIZE_SMILEYS)
+ row_height = text_height;
+ else
+ row_height = max(text_height, max_smiley_height);
+
+ // Just draw ellipsis
+ if (free_rc.right <= free_rc.left)
+ {
+ skin_DrawText(hdcMem, _T("..."), 3, &free_rc, uTextFormat & ~DT_END_ELLIPSIS);
+ }
+ else
+ {
+ // Draw text and smileys
+ for (; i < plText->realCount && i >= 0 && pos_x < free_rc.right - free_rc.left && len > 0; i += (uTextFormat & DT_RTLREADING ? -1 : 1))
+ {
+ TextPiece *piece = (TextPiece *)plText->items[i];
+ RECT text_rc = free_rc;
+
+ if (uTextFormat & DT_RTLREADING)
+ text_rc.right -= pos_x;
+ else
+ text_rc.left += pos_x;
+
+ if (piece->type == TEXT_PIECE_TYPE_TEXT)
+ {
+ text_rc.top += (row_height - text_height) >> 1;
+
+ tmp_rc = text_rc;
+ tmp_rc.right += 50;
+ skin_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 (uTextFormat & DT_RTLREADING)
+ text_rc.left = max(text_rc.left, text_rc.right - (tmp_rc.right - tmp_rc.left));
+
+ skin_DrawText(hdcMem, &szText[piece->start_pos], min(len, piece->len), &text_rc, uTextFormat);
+ len -= piece->len;
+ }
+ else
+ {
+ double factor;
+
+ if (len < piece->len)
+ {
+ len = 0;
+ }
+ else
+ {
+ len -= piece->len;
+
+ if ((uTextFormat & DT_RESIZE_SMILEYS) && piece->smiley_height > row_height)
+ {
+ factor = row_height / (double)piece->smiley_height;
+ }
+ else
+ {
+ factor = 1;
+ }
+
+ if (uTextFormat & DT_RTLREADING)
+ text_rc.left = max(text_rc.right - (LONG)(piece->smiley_width * factor), text_rc.left);
+
+ if ((LONG)(piece->smiley_width * factor) <= text_rc.right - text_rc.left)
+ {
+ text_rc.top += (row_height - (LONG)(piece->smiley_height * factor)) >> 1;
+
+ skin_DrawIconEx(hdcMem, text_rc.left, text_rc.top, piece->smiley,
+ (LONG)(piece->smiley_width * factor), (LONG)(piece->smiley_height * factor), 0, NULL, DI_NORMAL);
+ }
+ else
+ {
+ text_rc.top += (row_height - text_height) >> 1;
+ skin_DrawText(hdcMem, _T("..."), 3, &text_rc, uTextFormat);
+ }
+
+ pos_x += (LONG)(piece->smiley_width * factor);
+ }
+ }
+ }
+ }
+}
+
+
+void DestroySmileyList(SortedList* p_list)
+{
+ if (p_list == NULL)
+ return;
+
+ if (p_list->items != NULL)
+ {
+ int i;
+ for (i = 0; i < p_list->realCount; i++)
+ {
+ TextPiece *piece = (TextPiece *)p_list->items[i];
+
+ if (piece != NULL)
+ {
+ if (piece->type == TEXT_PIECE_TYPE_SMILEY)
+ DestroyIcon(piece->smiley);
+
+ mir_free(piece);
+ }
+ }
+ }
+
+ List_Destroy(p_list);
+}
+
+
+
+// Generete the list of smileys / text to be drawn
+SortedList * ReplaceSmileys(const TCHAR *text, int text_size, const char *protocol, int *max_smiley_height)
+{
+ *max_smiley_height = 0;
+
+ if (text[0] == '\0' || !ServiceExists(MS_SMILEYADD_BATCHPARSE))
+ return NULL;
+
+ // Parse it!
+ SMADD_BATCHPARSE2 sp = { sizeof(sp) };
+ sp.Protocolname = protocol;
+ sp.str = (TCHAR*)text;
+ sp.flag = SAFL_TCHAR;
+ SMADD_BATCHPARSERES *spres = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp);
+ if (spres == NULL)
+ // Did not find a simley
+ return NULL;
+
+ // Lets add smileys
+ SortedList *plText = List_Create(0, 10);
+
+ const TCHAR *next_text_pos = text;
+ const TCHAR *last_text_pos = _tcsninc(text, text_size);
+
+ for (unsigned int i = 0; i < sp.numSmileys; i++) {
+ TCHAR* start = _tcsninc(text, spres[i].startChar);
+ TCHAR* end = _tcsninc(start, spres[i].size);
+
+ if (spres[i].hIcon != NULL) { // For defective smileypacks
+ // Add text
+ if (start > next_text_pos) {
+ TextPiece *piece = (TextPiece *)mir_calloc(sizeof(TextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = next_text_pos - text;
+ piece->len = start - next_text_pos;
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ // Add smiley
+ {
+ BITMAP bm;
+ ICONINFO icon;
+ TextPiece *piece = (TextPiece *)mir_calloc(sizeof(TextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_SMILEY;
+ piece->len = end - start;
+ piece->smiley = spres[i].hIcon;
+
+ piece->smiley_width = 16;
+ piece->smiley_height = 16;
+ if (GetIconInfo(piece->smiley, &icon))
+ {
+ 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);
+ }
+
+ next_text_pos = end;
+ }
+ }
+
+ // Add rest of text
+ if (last_text_pos > next_text_pos)
+ {
+ TextPiece *piece = (TextPiece *)mir_calloc(sizeof(TextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = next_text_pos - text;
+ piece->len = last_text_pos - next_text_pos;
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spres);
+
+ return plText;
+}