summaryrefslogtreecommitdiff
path: root/plugins/SmileyAdd/src/smileyroutines.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-07-24 06:41:19 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-07-24 06:41:19 +0000
commita33833212f040272fc6c97047c8cb335b6f5405a (patch)
treed778dfd7187571182d9a1333a72cc941b60f7e3d /plugins/SmileyAdd/src/smileyroutines.cpp
parentec5a78dd895f86e9077caf1aacd3e49b48dffbcf (diff)
SimpleAR, SimpleStatusMsg, SmileyAdd, SpellChecker: changed folder structure
git-svn-id: http://svn.miranda-ng.org/main/trunk@1149 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/SmileyAdd/src/smileyroutines.cpp')
-rw-r--r--plugins/SmileyAdd/src/smileyroutines.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/plugins/SmileyAdd/src/smileyroutines.cpp b/plugins/SmileyAdd/src/smileyroutines.cpp
new file mode 100644
index 0000000000..ba136d7ed5
--- /dev/null
+++ b/plugins/SmileyAdd/src/smileyroutines.cpp
@@ -0,0 +1,610 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy
+Copyright (C) 2003 - 2004 Rein-Peter de Boer
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "general.h"
+#include "smileyroutines.h"
+#include "SmileyBase.h"
+#include "options.h"
+
+#include <richole.h>
+#include <tom.h>
+
+ISmileyBase* CreateSmileyObject(SmileyType* sml);
+ISmileyBase* CreateAniSmileyObject(SmileyType* sml, COLORREF clr, bool ishpp);
+
+
+
+bool g_HiddenTextSupported = true;
+
+
+// {8CC497C0-A1DF-11CE-8098-00AA0047BE5D}
+const GUID IID_ITextDocument =
+{ 0x8CC497C0, 0xA1DF, 0x11CE, { 0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D } };
+
+void LookupAllSmileys(SmileyPackType* smileyPack, SmileyPackCType* smileyCPack, const TCHAR* lpstrText,
+ SmileysQueueType& smllist, const bool firstOnly)
+{
+ if (lpstrText == NULL || *lpstrText == 0) return;
+
+ SmileyPackType::SmileyLookupType* sml = smileyPack ? smileyPack->GetSmileyLookup() : NULL;
+ SmileyPackCType::SmileyLookupType* smlc = smileyCPack ? &smileyCPack->GetSmileyLookup() : NULL;
+
+ // Precompute number of smileys
+ int smlszo = sml ? sml->getCount() : 0;
+ int smlszc = smlc ? smlc->getCount() : 0;
+ int smlsz = smlszo + smlszc;
+
+ if (smlsz == 0) return;
+
+ // All possible smileys
+ SmileyLookup::SmileyLocVecType* smileys = new SmileyLookup::SmileyLocVecType [smlsz];
+
+ // Find all possible smileys
+ bkstring tmpstr(lpstrText);
+ int i = 0;
+
+ if (sml)
+ {
+ for (int j=0; j<sml->getCount(); j++)
+ {
+ (*sml)[j].find(tmpstr, smileys[i], false);
+ i++;
+ }
+ }
+
+ if (smlc)
+ {
+ for (int j=0; j<smlc->getCount(); j++)
+ {
+ (*smlc)[j].find(tmpstr, smileys[i], false);
+ i++;
+ }
+ }
+
+ int* csmlit = (int*)alloca(smlsz * sizeof(int));
+ if (csmlit == NULL) return;
+ memset(csmlit, 0, smlsz * sizeof(int));
+
+ long numCharsSoFar = 0;
+ bkstring::size_type smloff = 0;
+
+ for (;;)
+ {
+ int firstSml = -1;
+ int firstSmlRef = -1;
+ SmileyLookup::SmileyLocVecType* smlf = NULL;
+
+ for (int csml=0; csml<smlsz; csml++)
+ {
+ SmileyLookup::SmileyLocVecType& smlv = smileys[csml];
+
+ int tsml;
+ for (tsml = csmlit[csml]; tsml < smlv.getCount(); tsml++)
+ {
+ if (smlv[tsml].pos >= smloff)
+ {
+ if (firstSmlRef == -1 || smlv[tsml].pos < (*smlf)[firstSmlRef].pos ||
+ (smlv[tsml].pos == (*smlf)[firstSmlRef].pos && smlv[tsml].len > (*smlf)[firstSmlRef].len))
+ {
+ firstSmlRef = tsml;
+ firstSml = csml;
+ smlf = &smileys[csml];
+ }
+ break;
+ }
+ }
+ csmlit[csml] = tsml;
+ }
+
+ // Check if smiley found
+ if (firstSml != -1)
+ {
+ ReplaceSmileyType *dat = new ReplaceSmileyType;
+
+ const TCHAR* textToSearch = lpstrText + smloff;
+ const TCHAR* textSmlStart = lpstrText + (*smlf)[firstSmlRef].pos;
+ const TCHAR* textSmlEnd = textSmlStart + (*smlf)[firstSmlRef].len;
+
+ // check if leading space exist
+ const TCHAR* prech = _tcsdec(textToSearch, textSmlStart);
+ dat->ldspace = prech != NULL ? _istspace(*prech) != 0 : smloff == 0;
+
+ // check if trailing space exist
+ dat->trspace = *textSmlEnd == 0 || _istspace(*textSmlEnd);
+
+ // compute text location in RichEdit
+ dat->loc.cpMin = (long)_tcsnccnt(textToSearch, (*smlf)[firstSmlRef].pos - smloff) + numCharsSoFar;
+ dat->loc.cpMax = numCharsSoFar = (long)_tcsnccnt(textSmlStart, (*smlf)[firstSmlRef].len) + dat->loc.cpMin;
+
+ if (!opt.EnforceSpaces || (dat->ldspace && dat->trspace))
+ {
+ dat->ldspace |= !opt.SurroundSmileyWithSpaces;
+ dat->trspace |= !opt.SurroundSmileyWithSpaces;
+
+ if (firstSml < smlszo)
+ {
+ dat->sml = smileyPack->GetSmiley((*sml)[firstSml].GetIndex());
+ dat->smlc = NULL;
+ }
+ else
+ {
+ dat->smlc = smileyCPack->GetSmiley((*smlc)[firstSml-smlszo].GetIndex());
+ dat->sml = NULL;
+ }
+
+ if (dat->sml != NULL || dat->smlc != NULL)
+ {
+ // First smiley found record it
+ smllist.insert(dat);
+ if (firstOnly) break;
+ }
+ else
+ delete dat;
+ }
+ else
+ delete dat;
+
+ // Advance string pointer to search for the next smiley
+ smloff = (*smlf)[firstSmlRef].pos + (*smlf)[firstSmlRef].len;
+ csmlit[firstSml]++;
+ }
+ else
+ // Nothing to parse exit
+ break;
+ }
+ delete[] smileys;
+}
+
+
+void FindSmileyInText(SmileyPackType* smp, const TCHAR* str,
+ unsigned& first, unsigned& size, SmileyType** sml)
+{
+ SmileysQueueType smllist;
+ LookupAllSmileys(smp, NULL, str, smllist, true);
+ if (smllist.getCount() == 0)
+ {
+ size = 0;
+ *sml = NULL;
+ }
+ else
+ {
+ first = smllist[0].loc.cpMin;
+ size = smllist[0].loc.cpMax - smllist[0].loc.cpMin;
+ *sml = smllist[0].sml;
+ }
+}
+
+
+SmileyType* FindButtonSmiley(SmileyPackType* smp)
+{
+ unsigned start, size;
+ SmileyType* sml;
+
+ FindSmileyInText(smp, smp->GetButtonSmiley(), start, size, &sml);
+
+ return sml;
+}
+
+void UpdateSelection(CHARRANGE& sel, int pos, int dif)
+{
+ if (sel.cpMax == sel.cpMin)
+ {
+ if (sel.cpMax < LONG_MAX && sel.cpMax > pos)
+ {
+ sel.cpMax += dif;
+ sel.cpMin += dif;
+ }
+ }
+ else
+ {
+ if (sel.cpMax >= pos && sel.cpMax < LONG_MAX) sel.cpMax += dif;
+ if (sel.cpMin > pos) sel.cpMin += dif;
+ }
+}
+
+void ReplaceSmileys(HWND hwnd, SmileyPackType* smp, SmileyPackCType* smcp, const CHARRANGE& sel,
+ bool useHidden, bool ignoreLast, bool unFreeze)
+{
+/*
+ LARGE_INTEGER freq, strt, end;
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&strt);
+*/
+ IRichEditOle* RichEditOle;
+ if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0)
+ return;
+
+ ITextDocument* TextDocument;
+ if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK)
+ {
+ RichEditOle->Release();
+ return;
+ }
+
+ long cnt;
+ if (smp == NULL && smcp == NULL)
+ {
+ if (unFreeze) TextDocument->Unfreeze(&cnt);
+ TextDocument->Release();
+ RichEditOle->Release();
+ return;
+ }
+
+ // retrieve text range
+ ITextRange* TextRange;
+ if (TextDocument->Range(sel.cpMin, sel.cpMax, &TextRange) != S_OK)
+ {
+ TextDocument->Release();
+ RichEditOle->Release();
+ return;
+ }
+
+ // retrieve text to parse for smileys
+ BSTR btxt = 0;
+ if (TextRange->GetText(&btxt) != S_OK)
+ {
+ TextRange->Release();
+ TextDocument->Release();
+ RichEditOle->Release();
+ return;
+ }
+
+ TextRange->Release();
+
+ SmileysQueueType smllist;
+
+
+ LookupAllSmileys(smp, smcp, W2T_SM(btxt), smllist, false);
+
+ SysFreeString(btxt);
+
+ if (smllist.getCount() != 0)
+ {
+ // disable screen updates
+ TextDocument->Freeze(&cnt);
+
+ TCHAR classname[20];
+ GetClassName(hwnd, classname, SIZEOF(classname));
+ bool ishpp = (_tcsncmp(classname, _T("THppRichEdit"), 12) == 0);
+
+ SetRichCallback(hwnd, NULL, false, true);
+
+ bool rdo = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) != 0;
+ if (rdo) SendMessage(hwnd, EM_SETREADONLY, FALSE, 0);
+
+ ITextSelection* TextSelection;
+ TextDocument->GetSelection(&TextSelection);
+
+ ITextFont *TextFont;
+ TextSelection->GetFont(&TextFont);
+
+ //save selection
+ CHARRANGE oldSel;
+ TextSelection->GetStart(&oldSel.cpMin);
+ TextSelection->GetEnd(&oldSel.cpMax);
+
+ CHARFORMAT2 chf;
+
+ chf.cbSize = sizeof(chf);
+ chf.dwMask = CFM_ALL2;
+
+ // Determine background color
+ // This logic trying to minimize number of background color changes
+ static COLORREF bkgColor = GetSysColor(COLOR_WINDOW);
+// if (!insemf)
+// {
+ COLORREF bkgColorPv = (COLORREF)SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, bkgColor);
+ if (bkgColorPv != bkgColor)
+ {
+ bkgColor = bkgColorPv;
+ SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, bkgColor);
+ }
+// }
+
+ HDC hdc = GetDC(hwnd);
+ int sclX = GetDeviceCaps(hdc, LOGPIXELSX);
+ int sclY = GetDeviceCaps(hdc, LOGPIXELSY);
+
+ unsigned numBTBSm = 0;
+
+ BSTR spaceb = SysAllocString(L" ");
+
+ // Replace smileys specified in the list in RichEdit
+ for (int j = smllist.getCount(); j--; )
+ {
+ CHARRANGE& smlpos = smllist[j].loc;
+ if (ignoreLast && oldSel.cpMax == smlpos.cpMax) continue;
+
+ smlpos.cpMin += sel.cpMin;
+ smlpos.cpMax += sel.cpMin;
+
+ // Find all back to back smileys and for propper hidden text detection
+ if ( numBTBSm == 0 )
+ {
+ CHARRANGE lastPos = smlpos;
+ for (int jn = j; jn--; )
+ {
+ if (jn != j && smllist[jn].loc.cpMax != lastPos.cpMin)
+ break;
+
+ ++numBTBSm;
+ lastPos.cpMin = smllist[jn].loc.cpMin;
+ }
+ TextSelection->SetRange(lastPos.cpMin, lastPos.cpMax);
+ long hid;
+ TextFont->GetHidden(&hid);
+ if (hid == tomFalse) numBTBSm = 0;
+ }
+ if ( numBTBSm != 0 )
+ {
+ --numBTBSm;
+ continue;
+ }
+
+ SmileyType* sml = smllist[j].sml;
+ SmileyCType* smlc = smllist[j].smlc;
+ if (sml == NULL && smlc == NULL) continue;
+
+ // Select text analyze
+ TextSelection->SetRange(smlpos.cpMin, smlpos.cpMax);
+
+ BSTR btxt = NULL;
+
+ if (smlc == NULL && sml->IsText())
+ {
+ btxt = SysAllocString(T2W_SM(sml->GetToolText().c_str()));
+ TextSelection->SetText(btxt);
+ }
+ else
+ {
+ TextSelection->GetText(&btxt);
+
+ // Get font properties
+ SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
+
+ //do not look for smileys in hyperlinks
+ if ((chf.dwEffects & (CFE_LINK | CFE_HIDDEN)) != 0) continue;
+
+ SIZE osize;
+ if (sml)
+ sml->GetSize(osize);
+ else
+ smlc->GetSize(osize);
+
+ if (osize.cx == 0 || osize.cy == 0) continue;
+
+ int sizeX, sizeY;
+ if (opt.ScaleToTextheight)
+ {
+ sizeY = CalculateTextHeight(hdc, &chf);
+ sizeX = osize.cx * sizeY / osize.cy;
+
+ int dx = osize.cx - sizeX;
+ sizeX += dx & 1;
+
+ int dy = osize.cy - sizeY;
+ sizeY += dy & 1;
+ }
+ else
+ {
+ sizeX = osize.cx;
+ sizeY = osize.cy;
+ }
+
+ if (smlc != NULL && opt.MaxCustomSmileySize && (unsigned)sizeY > opt.MaxCustomSmileySize)
+ {
+ sizeY = opt.MaxCustomSmileySize;
+ sizeX = osize.cx * sizeY / osize.cy;
+
+ int dx = osize.cx - sizeX;
+ sizeX += dx & 1;
+
+ int dy = osize.cy - opt.MaxCustomSmileySize;
+ sizeY += dy & 1;
+ }
+
+ if (opt.MinSmileySize && (unsigned)sizeY < opt.MinSmileySize)
+ {
+ sizeY = opt.MinSmileySize;
+ sizeX = osize.cx * sizeY / osize.cy;
+
+ int dx = osize.cx - sizeX;
+ sizeX += dx & 1;
+
+ int dy = osize.cy - opt.MinSmileySize;
+ sizeY += dy & 1;
+ }
+
+ // Convert pixel to HIMETRIC
+ SIZEL sizehm;
+ sizehm.cx = (2540 * (sizeX+1) + (sclX >> 1)) / sclX;
+ sizehm.cy = (2540 * (sizeY+1) + (sclY >> 1)) / sclY;
+
+ // If font does not have designated background use control background
+ if (chf.dwEffects & CFE_AUTOBACKCOLOR) chf.crBackColor = bkgColor;
+
+ // insert space after
+ if (!smllist[j].trspace && useHidden)
+ {
+ TextSelection->SetStart(smlpos.cpMax);
+ TextSelection->TypeText(spaceb);
+ UpdateSelection(oldSel, smlpos.cpMax , 1);
+
+ // Restore selection
+ TextSelection->SetRange(smlpos.cpMin, smlpos.cpMax);
+ }
+
+ if (g_HiddenTextSupported && useHidden)
+ {
+ TextFont->SetHidden(tomTrue);
+ TextSelection->SetEnd(smlpos.cpMin);
+ UpdateSelection(oldSel, smlpos.cpMin , 1);
+ }
+ else
+ {
+ UpdateSelection(oldSel, smlpos.cpMin, -(int)SysStringLen(btxt)+1);
+ }
+
+ ISmileyBase* smileyBase = CreateAniSmileyObject(smlc ? smlc : sml, chf.crBackColor, ishpp);
+ if (smileyBase == NULL) continue;
+
+ smileyBase->SetExtent(DVASPECT_CONTENT, &sizehm);
+ smileyBase->SetHint(W2T_SM(btxt));
+
+ smileyBase->SetPosition(hwnd, NULL);
+
+ // Get the RichEdit container site
+ IOleClientSite *pOleClientSite;
+ RichEditOle->GetClientSite(&pOleClientSite);
+
+ // Now Add the object to the RichEdit
+ REOBJECT reobject = { 0 };
+
+ reobject.cbStruct = sizeof(REOBJECT);
+ reobject.cp = REO_CP_SELECTION;
+ reobject.dvaspect = DVASPECT_CONTENT;
+ reobject.poleobj = smileyBase;
+ reobject.polesite = pOleClientSite;
+ reobject.dwFlags = REO_BELOWBASELINE | REO_BLANK;
+ reobject.dwUser = (DWORD)smileyBase;
+
+ // Insert the bitmap at the current location in the richedit control
+ RichEditOle->InsertObject(&reobject);
+
+ smileyBase->Release();
+
+ // insert space before
+ if (!smllist[j].ldspace && useHidden)
+ {
+ TextSelection->SetRange(smlpos.cpMin, smlpos.cpMin);
+ TextSelection->TypeText(spaceb);
+ UpdateSelection(oldSel, smlpos.cpMin , 1);
+ }
+ }
+ SysFreeString(btxt);
+ }
+ SysFreeString(spaceb);
+
+ TextSelection->SetRange(oldSel.cpMin, oldSel.cpMax);
+ if (rdo) SendMessage(hwnd, EM_SETREADONLY, TRUE, 0);
+
+ TextFont->Release();
+ TextSelection->Release();
+
+ ReleaseDC(hwnd, hdc);
+
+ TextDocument->Unfreeze(&cnt);
+ if (cnt == 0) UpdateWindow(hwnd);
+ }
+
+ if (unFreeze)
+ {
+ TextDocument->Unfreeze(&cnt);
+ if (cnt == 0) UpdateWindow(hwnd);
+ }
+
+ TextDocument->Release();
+ RichEditOle->Release();
+
+/*
+ QueryPerformanceCounter(&end);
+ unsigned dif = (end.QuadPart - strt.QuadPart)/(freq.QuadPart/1000);
+ TCHAR mess[300];
+ wsprintf(mess, _T("Time elapsed: %u"), dif);
+ MessageBox(NULL, mess, _T(""), MB_OK);
+*/
+}
+
+
+void ReplaceSmileysWithText(HWND hwnd, CHARRANGE& sel, bool keepFrozen)
+{
+ IRichEditOle* RichEditOle;
+ if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0)
+ return;
+
+ ITextDocument* TextDocument;
+ if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK)
+ {
+ RichEditOle->Release();
+ return;
+ }
+
+ // retrieve text range
+ ITextRange* TextRange;
+ if (TextDocument->Range(0, 0, &TextRange) != S_OK)
+ {
+ TextDocument->Release();
+ RichEditOle->Release();
+ return;
+ }
+
+ long cnt;
+ TextDocument->Freeze(&cnt);
+
+ bool rdo = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) != 0;
+ if (rdo) SendMessage(hwnd, EM_SETREADONLY, FALSE, 0);
+
+ CHARRANGE oldSel;
+ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&oldSel);
+
+ int objectCount = RichEditOle->GetObjectCount();
+ for (int i = objectCount - 1; i >= 0; i--)
+ {
+ REOBJECT reObj = {0};
+ reObj.cbStruct = sizeof(REOBJECT);
+
+ HRESULT hr = RichEditOle->GetObject(i, &reObj, REO_GETOBJ_POLEOBJ);
+ if (FAILED(hr)) continue;
+
+ if (reObj.cp < sel.cpMin)
+ {
+ reObj.poleobj->Release();
+ break;
+ }
+
+ ISmileyBase *igsc = NULL;
+ if (reObj.cp < sel.cpMax && reObj.clsid == CLSID_NULL)
+ reObj.poleobj->QueryInterface(IID_ISmileyAddSmiley, (void**) &igsc);
+
+ reObj.poleobj->Release();
+ if (igsc == NULL) continue;
+
+ TextRange->SetRange(reObj.cp, reObj.cp + 1);
+
+ BSTR bstr = NULL;
+ igsc->GetTooltip(&bstr);
+ TextRange->SetText(bstr);
+
+ unsigned int len = SysStringLen(bstr);
+ UpdateSelection(oldSel, reObj.cp, len-1);
+ UpdateSelection(sel, reObj.cp, len-1);
+
+ SysFreeString(bstr);
+
+ igsc->Release();
+ }
+
+ SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&oldSel);
+ if (rdo) SendMessage(hwnd, EM_SETREADONLY, TRUE, 0);
+ if (!keepFrozen) TextDocument->Unfreeze(&cnt);
+
+ TextRange->Release();
+ TextDocument->Release();
+ RichEditOle->Release();
+}