summaryrefslogtreecommitdiff
path: root/plugins/SmileyAdd/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/SmileyAdd/src')
-rw-r--r--plugins/SmileyAdd/src/AniSmileyObject.cpp450
-rw-r--r--plugins/SmileyAdd/src/SmileyBase.cpp391
-rw-r--r--plugins/SmileyAdd/src/SmileyBase.h127
-rw-r--r--plugins/SmileyAdd/src/anim.cpp184
-rw-r--r--plugins/SmileyAdd/src/anim.h87
-rw-r--r--plugins/SmileyAdd/src/bkstring.cpp215
-rw-r--r--plugins/SmileyAdd/src/bkstring.h270
-rw-r--r--plugins/SmileyAdd/src/customsmiley.cpp172
-rw-r--r--plugins/SmileyAdd/src/customsmiley.h85
-rw-r--r--plugins/SmileyAdd/src/dlgboxsubclass.cpp568
-rw-r--r--plugins/SmileyAdd/src/download.cpp292
-rw-r--r--plugins/SmileyAdd/src/download.h28
-rw-r--r--plugins/SmileyAdd/src/general.cpp299
-rw-r--r--plugins/SmileyAdd/src/general.h232
-rw-r--r--plugins/SmileyAdd/src/imagecache.cpp775
-rw-r--r--plugins/SmileyAdd/src/imagecache.h166
-rw-r--r--plugins/SmileyAdd/src/main.cpp225
-rw-r--r--plugins/SmileyAdd/src/options.cpp728
-rw-r--r--plugins/SmileyAdd/src/options.h60
-rw-r--r--plugins/SmileyAdd/src/regexp/Matcher.cpp178
-rw-r--r--plugins/SmileyAdd/src/regexp/Matcher.h248
-rw-r--r--plugins/SmileyAdd/src/regexp/Pattern.cpp1709
-rw-r--r--plugins/SmileyAdd/src/regexp/Pattern.h1663
-rw-r--r--plugins/SmileyAdd/src/regexp/WCMatcher.cpp181
-rw-r--r--plugins/SmileyAdd/src/regexp/WCMatcher.h234
-rw-r--r--plugins/SmileyAdd/src/regexp/WCPattern.cpp1747
-rw-r--r--plugins/SmileyAdd/src/regexp/WCPattern.h1663
-rw-r--r--plugins/SmileyAdd/src/regexp/test.cpp38
-rw-r--r--plugins/SmileyAdd/src/resource.h49
-rw-r--r--plugins/SmileyAdd/src/richcall.cpp554
-rw-r--r--plugins/SmileyAdd/src/services.cpp590
-rw-r--r--plugins/SmileyAdd/src/services.h48
-rw-r--r--plugins/SmileyAdd/src/smileyroutines.cpp610
-rw-r--r--plugins/SmileyAdd/src/smileyroutines.h49
-rw-r--r--plugins/SmileyAdd/src/smileys.cpp1146
-rw-r--r--plugins/SmileyAdd/src/smileys.h287
-rw-r--r--plugins/SmileyAdd/src/smltool.cpp785
-rw-r--r--plugins/SmileyAdd/src/smltool.h45
-rw-r--r--plugins/SmileyAdd/src/version.h3
39 files changed, 17181 insertions, 0 deletions
diff --git a/plugins/SmileyAdd/src/AniSmileyObject.cpp b/plugins/SmileyAdd/src/AniSmileyObject.cpp
new file mode 100644
index 0000000000..42a63d3549
--- /dev/null
+++ b/plugins/SmileyAdd/src/AniSmileyObject.cpp
@@ -0,0 +1,450 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "SmileyBase.h"
+#include "m_smileyadd.h"
+#include "smileys.h"
+#include "options.h"
+
+#include <richole.h>
+
+
+class CAniSmileyObject;
+
+static int CompareAniSmiley(const CAniSmileyObject* p1, const CAniSmileyObject* p2)
+{
+ return (int)((char*)p2 - (char*)p1);
+}
+
+static LIST<CAniSmileyObject> regAniSmileys(10, CompareAniSmiley);
+
+static UINT_PTR timerId;
+static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD);
+
+static void CALLBACK sttMainThreadCallback( PVOID )
+{
+ if (timerId == 0xffffffff)
+ timerId = SetTimer(NULL, 0, 100, (TIMERPROC)timerProc);
+}
+
+
+class CAniSmileyObject : public ISmileyBase
+{
+private:
+ typedef enum { animStdOle, animDrctRichEd, animHpp } AnimType;
+
+ POINTL m_rectOrig;
+ SIZEL m_rectExt;
+
+ COLORREF m_bkg;
+
+ SmileyType* m_sml;
+ ImageBase* m_img;
+ unsigned m_nFramePosition;
+
+ long m_counter;
+ unsigned m_richFlags;
+ long m_lastObjNum;
+
+ AnimType m_animtype;
+ bool m_allowAni;
+
+public:
+ CAniSmileyObject(SmileyType* sml, COLORREF clr, bool ishpp)
+ {
+ m_allowAni = false;
+ m_animtype = ishpp ? animHpp : animStdOle;
+ m_bkg = clr;
+
+ m_rectOrig.x = 0;
+ m_rectOrig.y = 0;
+ m_rectExt.cx = 0;
+ m_rectExt.cy = 0;
+
+ m_richFlags = 0;
+ m_lastObjNum = 0;
+
+ m_sml = sml;
+ m_img = NULL;
+ m_nFramePosition = 0;
+ m_counter = 0;
+ }
+
+ ~CAniSmileyObject(void)
+ {
+ UnloadSmiley();
+ }
+
+ void LoadSmiley(void)
+ {
+ if (m_img != NULL) return;
+
+ m_img = m_sml->CreateCachedImage();
+ if (m_img && m_img->IsAnimated() && opt.AnimateDlg)
+ {
+ m_nFramePosition = 0;
+ m_img->SelectFrame(m_nFramePosition);
+ long frtm = m_img->GetFrameDelay();
+ m_counter = frtm / 10 + ((frtm % 10) >= 5);
+
+ regAniSmileys.insert(this);
+ if (timerId == 0)
+ {
+ timerId = 0xffffffff;
+ CallFunctionAsync(sttMainThreadCallback, NULL);
+ }
+ }
+ else
+ m_nFramePosition = m_sml->GetStaticFrame();
+ }
+
+ void UnloadSmiley(void)
+ {
+ regAniSmileys.remove(this);
+
+ if (timerId && (timerId+1) && regAniSmileys.getCount() == 0)
+ {
+ KillTimer(NULL, timerId);
+ timerId = 0;
+ }
+ if (m_img) m_img->Release();
+ m_img = NULL;
+ }
+
+ void GetDrawingProp(void)
+ {
+ if (m_hwnd == NULL) return;
+
+ IRichEditOle* RichEditOle;
+ if (SendMessage(m_hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0)
+ return;
+
+ REOBJECT reObj = {0};
+ reObj.cbStruct = sizeof(REOBJECT);
+
+ HRESULT hr = RichEditOle->GetObject(m_lastObjNum, &reObj, REO_GETOBJ_NO_INTERFACES);
+ if (hr == S_OK && reObj.dwUser == (DWORD)(ISmileyBase*)this && reObj.clsid == CLSID_NULL)
+ {
+ m_richFlags = reObj.dwFlags;
+ }
+ else
+ {
+ long objectCount = RichEditOle->GetObjectCount();
+ for (long i = objectCount; i--; )
+ {
+ HRESULT hr = RichEditOle->GetObject(i, &reObj, REO_GETOBJ_NO_INTERFACES);
+ if (FAILED(hr)) continue;
+
+ if (reObj.dwUser == (DWORD)(ISmileyBase*)this && reObj.clsid == CLSID_NULL)
+ {
+ m_lastObjNum = i;
+ m_richFlags = reObj.dwFlags;
+ break;
+ }
+ }
+ }
+ RichEditOle->Release();
+
+ if ((m_richFlags & REO_SELECTED) == 0)
+ {
+ CHARRANGE sel;
+ SendMessage(m_hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (reObj.cp >= sel.cpMin && reObj.cp < sel.cpMax)
+ m_richFlags |= REO_INVERTEDSELECT;
+ else
+ m_richFlags &= ~REO_INVERTEDSELECT;
+ }
+ }
+
+
+ void DoDirectDraw(HDC hdc)
+ {
+ HBITMAP hBmp = CreateCompatibleBitmap(hdc, m_rectExt.cx, m_rectExt.cy);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HANDLE hOld = SelectObject(hdcMem, hBmp);
+
+ RECT rc;
+ rc.left = m_rectExt.cx - m_sizeExtent.cx;
+ rc.top = m_rectExt.cy - m_sizeExtent.cy;
+ rc.right = rc.left + m_sizeExtent.cx;
+ rc.bottom = rc.top + m_sizeExtent.cy;
+
+ HBRUSH hbr = CreateSolidBrush(m_bkg);
+ RECT frc = { 0, 0, m_rectExt.cx, m_rectExt.cy };
+ FillRect(hdcMem, &frc, hbr);
+ DeleteObject(hbr);
+
+ m_img->DrawInternal(hdcMem, rc.left, rc.top, m_sizeExtent.cx - 1, m_sizeExtent.cy - 1);
+
+ if (m_richFlags & REO_SELECTED)
+ {
+ HBRUSH hbr = CreateSolidBrush(m_bkg ^ 0xFFFFFF);
+ FrameRect(hdcMem, &rc, hbr);
+ DeleteObject(hbr);
+ }
+
+ if (m_richFlags & REO_INVERTEDSELECT)
+ InvertRect(hdcMem, &rc);
+
+ BitBlt(hdc, m_rectOrig.x, m_rectOrig.y, m_rectExt.cx, m_rectExt.cy, hdcMem, 0, 0, SRCCOPY);
+
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hBmp);
+ DeleteDC(hdcMem);
+ }
+
+ void DrawOnRichEdit(void)
+ {
+ HDC hdc = GetDC(m_hwnd);
+ if (RectVisible(hdc, &m_orect))
+ {
+ RECT crct;
+ GetClientRect(m_hwnd, &crct);
+
+ HRGN hrgnOld = CreateRectRgnIndirect(&crct);
+ int res = GetClipRgn(hdc, hrgnOld);
+
+ HRGN hrgn = CreateRectRgnIndirect(&crct);
+ SelectClipRgn(hdc, hrgn);
+ DeleteObject(hrgn);
+
+ DoDirectDraw(hdc);
+
+ SelectClipRgn(hdc, res < 1 ? NULL : hrgnOld);
+ DeleteObject(hrgnOld);
+ }
+ else
+ {
+ m_visible = false;
+ m_allowAni = false;
+ UnloadSmiley();
+ }
+ ReleaseDC(m_hwnd, hdc);
+ }
+
+ void DrawOnHPP(void)
+ {
+ FVCNDATA_NMHDR nmh = {0};
+ nmh.code = NM_FIREVIEWCHANGE;
+ nmh.hwndFrom = m_hwnd;
+
+ nmh.cbSize = sizeof(nmh);
+ nmh.bEvent = FVCN_PREFIRE;
+ nmh.bAction = FVCA_DRAW;
+ nmh.rcRect = m_orect;
+ SendMessage(GetParent(m_hwnd), WM_NOTIFY, (WPARAM)m_hwnd, (LPARAM)&nmh);
+
+ switch (nmh.bAction)
+ {
+ case FVCA_DRAW:
+ // support for pseudo-edit mode and event details
+ m_animtype = m_dirAniAllow ? animDrctRichEd : animStdOle;
+ GetDrawingProp();
+ DrawOnRichEdit();
+ break;
+
+ case FVCA_CUSTOMDRAW:
+ m_rectExt.cy = nmh.rcRect.bottom - nmh.rcRect.top;
+ m_rectExt.cx = nmh.rcRect.right - nmh.rcRect.left;
+ m_rectOrig.x = nmh.rcRect.left;
+ m_rectOrig.y = nmh.rcRect.top;
+
+ m_bkg = nmh.clrBackground;
+
+ DoDirectDraw(nmh.hDC);
+
+ nmh.bEvent = FVCN_POSTFIRE;
+ SendMessage(GetParent(m_hwnd), WM_NOTIFY, (WPARAM)m_hwnd, (LPARAM)&nmh);
+ break;
+
+ case FVCA_SKIPDRAW:
+ break;
+
+ case FVCA_NONE:
+ m_visible = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ void ProcessTimerTick(void)
+ {
+ if (m_visible && m_img && --m_counter <= 0)
+ {
+ m_nFramePosition = m_img->SelectNextFrame(m_nFramePosition);
+ long frtm = m_img->GetFrameDelay();
+ m_counter = frtm / 10 + ((frtm % 10) >= 5);
+
+ switch (m_animtype)
+ {
+ case animStdOle:
+ if (m_allowAni) SendOnViewChange();
+ else
+ {
+ m_visible = false;
+ UnloadSmiley();
+ }
+ m_allowAni = false;
+ break;
+
+ case animDrctRichEd:
+ DrawOnRichEdit();
+ break;
+
+ case animHpp:
+ DrawOnHPP();
+ break;
+ }
+ }
+ }
+
+ void SetPosition(HWND hwnd, LPCRECT lpRect)
+ {
+ ISmileyBase::SetPosition(hwnd, lpRect);
+
+ m_allowAni = m_visible;
+
+ if (m_visible) LoadSmiley();
+ else UnloadSmiley();
+
+ if (lpRect == NULL) return;
+ if (m_animtype == animStdOle)
+ {
+ m_animtype = animDrctRichEd;
+ GetDrawingProp();
+ }
+
+ if (lpRect->top == -1)
+ {
+ m_rectOrig.x = lpRect->left;
+ m_rectOrig.y = lpRect->bottom - m_sizeExtent.cy;
+ m_rectExt.cy = m_sizeExtent.cy;
+ }
+ else if (lpRect->bottom == -1)
+ {
+ m_rectOrig.x = lpRect->left;
+ m_rectOrig.y = lpRect->top;
+ }
+ else
+ {
+ m_rectOrig.x = lpRect->left;
+ m_rectOrig.y = lpRect->top;
+ m_rectExt.cy = lpRect->bottom - lpRect->top;
+ }
+ }
+
+ STDMETHOD(Close)(DWORD dwSaveOption)
+ {
+ m_visible = false;
+ UnloadSmiley();
+
+ return ISmileyBase::Close(dwSaveOption);
+ }
+
+ STDMETHOD(Draw)(DWORD dwAspect, LONG, void*, DVTARGETDEVICE*, HDC,
+ HDC hdc, LPCRECTL pRectBounds, LPCRECTL /* pRectWBounds */,
+ BOOL (__stdcall *)(ULONG_PTR), ULONG_PTR)
+ {
+ if (dwAspect != DVASPECT_CONTENT) return DV_E_DVASPECT;
+ if (pRectBounds == NULL) return E_INVALIDARG;
+
+ LoadSmiley();
+
+ if (m_img == NULL) return E_FAIL;
+
+ m_sizeExtent.cx = pRectBounds->right - pRectBounds->left;
+ m_sizeExtent.cy = pRectBounds->bottom - pRectBounds->top;
+
+ m_rectExt = m_sizeExtent;
+
+ switch (m_animtype)
+ {
+ case animDrctRichEd:
+ {
+ m_rectExt.cy = pRectBounds->bottom - m_rectOrig.y;
+ RECT frc = { 0, 0, m_sizeExtent.cx - 1, m_sizeExtent.cy - 1 };
+
+ HBITMAP hBmp = CreateCompatibleBitmap(hdc, frc.right, frc.bottom);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HANDLE hOld = SelectObject(hdcMem, hBmp);
+
+ HBRUSH hbr = CreateSolidBrush(m_bkg);
+ FillRect(hdcMem, &frc, hbr);
+ DeleteObject(hbr);
+
+ m_img->DrawInternal(hdcMem, 0, 0, frc.right, frc.bottom);
+
+ BitBlt(hdc, pRectBounds->left, pRectBounds->top, frc.right, frc.bottom, hdcMem, 0, 0, SRCCOPY);
+
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hBmp);
+ DeleteDC(hdcMem);
+ }
+ GetDrawingProp();
+ break;
+
+ case animHpp:
+ m_orect = *(LPRECT)pRectBounds;
+
+ default:
+ m_img->DrawInternal(hdc, pRectBounds->left, pRectBounds->top,
+ m_sizeExtent.cx - 1, m_sizeExtent.cy - 1);
+ break;
+ }
+
+ m_allowAni = true;
+ m_visible = true;
+
+ return S_OK;
+ }
+
+ STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL* psizel)
+ {
+ HRESULT hr = ISmileyBase::SetExtent(dwDrawAspect, psizel);
+ if (hr == S_OK) m_rectExt = m_sizeExtent;
+ return hr;
+ }
+};
+
+ISmileyBase* CreateAniSmileyObject(SmileyType* sml, COLORREF clr, bool ishpp)
+{
+ if (!sml->IsValid()) return NULL;
+
+ CAniSmileyObject *obj = new CAniSmileyObject(sml, clr, ishpp);
+ return obj;
+}
+
+static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD)
+{
+ for (int i=0; i<regAniSmileys.getCount(); ++i)
+ regAniSmileys[i]->ProcessTimerTick();
+}
+
+void DestroyAniSmileys(void)
+{
+ if (timerId && (timerId+1))
+ {
+ KillTimer(NULL, timerId);
+ timerId = 0;
+ }
+ regAniSmileys.destroy();
+}
+
diff --git a/plugins/SmileyAdd/src/SmileyBase.cpp b/plugins/SmileyAdd/src/SmileyBase.cpp
new file mode 100644
index 0000000000..28bd767153
--- /dev/null
+++ b/plugins/SmileyAdd/src/SmileyBase.cpp
@@ -0,0 +1,391 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "SmileyBase.h"
+
+#include <olectl.h>
+
+static void HiMetricToPixel(const SIZEL * lpSizeInHiMetric, LPSIZEL lpSizeInPix)
+{
+ HDC hDCScreen = GetDC(NULL);
+ const int nPixelsPerInchX = GetDeviceCaps(hDCScreen, LOGPIXELSX);
+ const int nPixelsPerInchY = GetDeviceCaps(hDCScreen, LOGPIXELSY);
+ ReleaseDC(NULL, hDCScreen);
+
+ lpSizeInPix->cx = (lpSizeInHiMetric->cx * nPixelsPerInchX + (2540/2)) / 2540;
+ lpSizeInPix->cy = (lpSizeInHiMetric->cy * nPixelsPerInchY + (2540/2)) / 2540;
+}
+
+static int CompareISmileyBase(const ISmileyBase* p1, const ISmileyBase* p2)
+{
+ return (int)((char*)p2 - (char*)p1);
+}
+
+static LIST<ISmileyBase> regSmileys(10, CompareISmileyBase);
+
+// {105C56DF-6455-4705-A501-51F1CCFCF688}
+const GUID IID_ISmileyAddSmiley =
+{ 0x105c56df, 0x6455, 0x4705, { 0xa5, 0x1, 0x51, 0xf1, 0xcc, 0xfc, 0xf6, 0x88 } };
+
+// {58B32D03-1BD2-4840-992E-9AE799FD4ADE}
+const GUID IID_ITooltipData =
+{ 0x58b32d03, 0x1bd2, 0x4840, { 0x99, 0x2e, 0x9a, 0xe7, 0x99, 0xfd, 0x4a, 0xde } };
+
+ISmileyBase::ISmileyBase(void)
+{
+ m_spAdviseSink = NULL;
+ m_spClientSite = NULL;
+ m_spAdviseHolder = NULL;
+ m_lRefCount = 1;
+ m_advf = 0;
+ m_smltxt = NULL;
+ m_hwnd = NULL;
+ m_visible = false;
+ m_dirAniAllow = false;
+
+ memset(&m_sizeExtent, 0, sizeof(m_sizeExtent));
+ memset(&m_sizeExtentHiM, 0, sizeof(m_sizeExtentHiM));
+ memset(&m_orect, 0, sizeof(m_orect));
+
+ regSmileys.insert(this);
+}
+
+ISmileyBase::~ISmileyBase(void)
+{
+ free(m_smltxt);
+
+ Close(OLECLOSE_NOSAVE);
+
+ if (m_spClientSite)
+ {
+ m_spClientSite->Release();
+ m_spClientSite = NULL;
+ }
+ if (m_spAdviseHolder)
+ {
+ m_spAdviseHolder->Release();
+ m_spAdviseHolder = NULL;
+ }
+}
+
+void ISmileyBase::OnClose(void)
+{
+ if (m_spAdviseHolder) m_spAdviseHolder->SendOnClose();
+}
+
+void ISmileyBase::SendOnViewChange(void)
+{
+ if (m_spAdviseSink) m_spAdviseSink->OnViewChange(DVASPECT_CONTENT, -1);
+ if (m_advf & ADVF_ONLYONCE)
+ {
+ m_spAdviseSink->Release();
+ m_spAdviseSink = NULL;
+ m_advf = 0;
+ }
+}
+
+bool ISmileyBase::QueryHitPointSpecial(int x, int y, HWND hwnd, TCHAR** smltxt)
+{
+ bool result = m_visible && m_hwnd == hwnd;
+ if (result)
+ {
+ result = x >= m_orect.left && x <= m_orect.right &&
+ y >= m_orect.top && y <= m_orect.bottom;
+ }
+ if (result) *smltxt = m_smltxt;
+ return result;
+}
+
+void ISmileyBase::SetHint(TCHAR* smltxt)
+{
+ m_smltxt = _tcsdup(smltxt);
+}
+
+
+void ISmileyBase::SetPosition(HWND hwnd, LPCRECT lpRect)
+{
+ m_hwnd = hwnd;
+ if (lpRect == NULL || (lpRect->top == -1 && lpRect->bottom == -1))
+ {
+ m_visible = false;
+ }
+ else
+ {
+ m_visible = true;
+ m_dirAniAllow = true;
+ m_orect.left = lpRect->left;
+ m_orect.right = lpRect->left + m_sizeExtent.cx;
+ if (lpRect->top == -1)
+ {
+ m_orect.top = lpRect->bottom - m_sizeExtent.cy;
+ m_orect.bottom = lpRect->bottom;
+ }
+ else if (lpRect->bottom == -1)
+ {
+ m_orect.top = lpRect->top;
+ m_orect.bottom = lpRect->top + m_sizeExtent.cy;;
+ }
+ else
+ {
+ m_orect.top = lpRect->bottom - m_sizeExtent.cy;
+ m_orect.bottom = lpRect->bottom;
+ }
+ }
+}
+
+
+//
+// IUnknown members
+//
+ULONG ISmileyBase::AddRef(void)
+{ return InterlockedIncrement(&m_lRefCount); }
+
+ULONG ISmileyBase::Release(void)
+{
+ LONG count = InterlockedDecrement(&m_lRefCount);
+ if(count == 0)
+ delete this;
+ return count;
+}
+
+
+HRESULT ISmileyBase::QueryInterface(REFIID iid, void ** ppvObject)
+{
+ // check to see what interface has been requested
+ if (ppvObject == NULL) return E_POINTER;
+ if (iid == IID_ISmileyAddSmiley)
+ *ppvObject = this;
+ else if (iid == IID_ITooltipData)
+ *ppvObject = static_cast<ITooltipData*>(this);
+ else if (iid == IID_IViewObject)
+ *ppvObject = static_cast<IViewObject2*>(this);
+ else if (iid == IID_IOleObject)
+ *ppvObject = static_cast<IOleObject*>(this);
+ else if (iid == IID_IUnknown)
+ *ppvObject = this;
+ else if (iid == IID_IViewObject2)
+ *ppvObject = static_cast<IViewObject2*>(this);
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+//
+// IOleObject members
+//
+HRESULT ISmileyBase::SetClientSite(IOleClientSite *pClientSite)
+{
+ if (m_spClientSite != NULL) m_spClientSite->Release();
+ m_spClientSite = pClientSite;
+ if (m_spClientSite != NULL) m_spClientSite->AddRef();
+ return S_OK;
+}
+
+HRESULT ISmileyBase::GetClientSite(IOleClientSite **ppClientSite)
+{
+ if (ppClientSite == NULL) return E_POINTER;
+ *ppClientSite = m_spClientSite;
+ if (m_spClientSite != NULL) m_spClientSite->AddRef();
+ return S_OK;
+}
+
+HRESULT ISmileyBase::SetHostNames(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */)
+{ return S_OK; }
+
+HRESULT ISmileyBase::Close(DWORD /* dwSaveOption */)
+{
+ regSmileys.remove(this);
+
+ if (m_spAdviseSink) m_spAdviseSink->Release();
+ m_spAdviseSink = NULL;
+
+ return S_OK;
+}
+
+HRESULT ISmileyBase::SetMoniker(DWORD /* dwWhichMoniker */, IMoniker* /* pmk */)
+{ return E_NOTIMPL; }
+
+HRESULT ISmileyBase::GetMoniker(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */, IMoniker** /* ppmk */)
+{ return E_NOTIMPL; }
+
+HRESULT ISmileyBase::InitFromData(IDataObject* /* pDataObject */, BOOL /* fCreation */, DWORD /* dwReserved */)
+{ return E_NOTIMPL; }
+
+HRESULT ISmileyBase::GetClipboardData(DWORD /* dwReserved */, IDataObject** /* ppDataObject */)
+{ return E_NOTIMPL; }
+
+HRESULT ISmileyBase::DoVerb(LONG /* iVerb */, LPMSG /* pMsg */, IOleClientSite* /* pActiveSite */, LONG /* lindex */,
+ HWND /* hwndParent */, LPCRECT /* lprcPosRect */)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ISmileyBase::EnumVerbs(IEnumOLEVERB** /*ppEnumOleVerb*/) { return E_NOTIMPL; }
+HRESULT ISmileyBase::Update(void) { return S_OK; }
+HRESULT ISmileyBase::IsUpToDate(void) { return S_OK; }
+
+HRESULT ISmileyBase::GetUserClassID(CLSID *pClsid)
+{
+ if (!pClsid) return E_POINTER;
+ *pClsid = CLSID_NULL;
+ return S_OK;
+}
+
+HRESULT ISmileyBase::GetUserType(DWORD /*dwFormOfType*/, LPOLESTR* /*pszUserType*/)
+{ return E_NOTIMPL; }
+
+HRESULT ISmileyBase::SetExtent(DWORD dwDrawAspect, SIZEL* psizel)
+{
+ if (dwDrawAspect != DVASPECT_CONTENT) return E_FAIL;
+ if (psizel == NULL) return E_POINTER;
+
+ HiMetricToPixel(psizel, &m_sizeExtent);
+ m_sizeExtentHiM = *psizel;
+ return S_OK;
+}
+
+HRESULT ISmileyBase::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
+{
+ if (dwDrawAspect != DVASPECT_CONTENT) return E_FAIL;
+ if (psizel == NULL) return E_POINTER;
+
+ *psizel = m_sizeExtentHiM;
+ return S_OK;
+}
+
+HRESULT ISmileyBase::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
+{
+ HRESULT hr = S_OK;
+ if (m_spAdviseHolder == NULL)
+ hr = CreateOleAdviseHolder(&m_spAdviseHolder);
+ if (SUCCEEDED(hr))
+ hr = m_spAdviseHolder->Advise(pAdvSink, pdwConnection);
+ else
+ m_spAdviseHolder = NULL;
+ return hr;
+}
+
+HRESULT ISmileyBase::Unadvise(DWORD dwConnection)
+{
+ return m_spAdviseHolder ? m_spAdviseHolder->Unadvise(dwConnection) : E_FAIL;
+}
+
+HRESULT ISmileyBase::EnumAdvise(IEnumSTATDATA **ppEnumAdvise)
+{
+ if (ppEnumAdvise == NULL) return E_POINTER;
+ return m_spAdviseHolder ? m_spAdviseHolder->EnumAdvise(ppEnumAdvise) : E_FAIL;
+}
+
+HRESULT ISmileyBase::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
+{
+ if (pdwStatus == NULL) return E_POINTER;
+ if (dwAspect == DVASPECT_CONTENT)
+ {
+ *pdwStatus = OLEMISC_STATIC | OLEMISC_INVISIBLEATRUNTIME |
+ OLEMISC_CANTLINKINSIDE | OLEMISC_NOUIACTIVATE;
+ return S_OK;
+ }
+ else
+ {
+ *pdwStatus = 0;
+ return E_FAIL;
+ }
+}
+
+HRESULT ISmileyBase::SetColorScheme(LOGPALETTE* /* pLogpal */)
+{ return E_NOTIMPL; }
+
+//
+// IViewObject members
+//
+HRESULT ISmileyBase::SetAdvise(DWORD aspect, DWORD advf, IAdviseSink* pAdvSink)
+{
+ if (aspect != DVASPECT_CONTENT) return DV_E_DVASPECT;
+ m_advf = advf;
+ if (m_spAdviseSink) m_spAdviseSink->Release();
+ m_spAdviseSink = pAdvSink;
+ if (advf & ADVF_PRIMEFIRST) SendOnViewChange();
+ return S_OK;
+}
+HRESULT ISmileyBase::GetAdvise(DWORD* /*pAspects*/, DWORD* /*pAdvf*/, IAdviseSink** ppAdvSink)
+{
+ if (!ppAdvSink) return E_POINTER;
+ *ppAdvSink = m_spAdviseSink;
+ if (m_spAdviseSink) m_spAdviseSink->AddRef();
+ return S_OK;
+}
+HRESULT ISmileyBase::Freeze(DWORD, long, void*, DWORD*) { return E_NOTIMPL; }
+HRESULT ISmileyBase::Unfreeze(DWORD) { return E_NOTIMPL; }
+HRESULT ISmileyBase::GetColorSet(DWORD, long, void*, DVTARGETDEVICE*, HDC,
+ LOGPALETTE**) { return E_NOTIMPL; }
+
+//
+// IViewObject2 members
+//
+HRESULT ISmileyBase::GetExtent(DWORD aspect, long, DVTARGETDEVICE*, SIZEL* pSize)
+{
+ if (pSize == NULL) return E_POINTER;
+ if (aspect != DVASPECT_CONTENT) return DV_E_DVASPECT;
+ *pSize = m_sizeExtent;
+ return S_OK;
+}
+
+
+//
+// ITooltipData members
+//
+HRESULT ISmileyBase::SetTooltip(BSTR /* bstrHint */)
+{
+ return S_OK;
+}
+
+HRESULT ISmileyBase::GetTooltip(BSTR *bstrHint)
+{
+ if (bstrHint == NULL) return E_POINTER;
+ *bstrHint = SysAllocString(T2W_SM(m_smltxt));
+ return S_OK;
+}
+
+
+void CloseSmileys(void)
+{
+ for (int i=regSmileys.getCount(); i--;)
+ {
+ regSmileys[i]->OnClose();
+ regSmileys[i]->Close(OLECLOSE_NOSAVE);
+ }
+}
+
+int CheckForTip(int x, int y, HWND hwnd, TCHAR** smltxt)
+{
+ for (int i=0; i<regSmileys.getCount(); ++i)
+ if (regSmileys[i]->QueryHitPointSpecial(x, y, hwnd, smltxt)) return i;
+
+ return -1;
+}
+
+void DestroySmileyBase(void)
+{
+ regSmileys.destroy();
+}
+
diff --git a/plugins/SmileyAdd/src/SmileyBase.h b/plugins/SmileyAdd/src/SmileyBase.h
new file mode 100644
index 0000000000..3cdf244007
--- /dev/null
+++ b/plugins/SmileyAdd/src/SmileyBase.h
@@ -0,0 +1,127 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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/>.
+*/
+
+#pragma once
+#include <oleidl.h>
+#include <tchar.h>
+
+#define OLEIVERB_SETOWNER (-24)
+
+EXTERN_C const IID IID_ITooltipData;
+
+class ITooltipData : public IUnknown
+{
+public:
+ STDMETHOD(SetTooltip) (BSTR bstrHint) PURE;
+ STDMETHOD(GetTooltip) (BSTR * bstrHint) PURE;
+};
+
+
+EXTERN_C const IID IID_ISmileyAddSmiley;
+
+class ISmileyBase :
+ public IOleObject, public IViewObject2, public ITooltipData
+{
+protected:
+ IOleAdviseHolder* m_spAdviseHolder;
+ IAdviseSink* m_spAdviseSink;
+ IOleClientSite* m_spClientSite;
+ DWORD m_advf;
+ LONG m_lRefCount;
+
+ SIZEL m_sizeExtent;
+ SIZEL m_sizeExtentHiM;
+ RECT m_orect;
+
+ TCHAR* m_smltxt;
+ HWND m_hwnd;
+
+ bool m_visible;
+ bool m_dirAniAllow;
+
+public:
+ ISmileyBase(void);
+ virtual ~ISmileyBase(void);
+
+ void OnClose(void);
+ void SendOnViewChange(void);
+
+ bool QueryHitPointSpecial(int x, int y, HWND hwnd, TCHAR** smltxt);
+ void SetHint(TCHAR* smltxt);
+
+ virtual void SetPosition(HWND hwnd, LPCRECT lpRect);
+
+ //
+ // IUnknown members
+ //
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject);
+
+ //
+ // IOleObject members
+ //
+ STDMETHOD(SetClientSite)(IOleClientSite *pClientSite);
+ STDMETHOD(GetClientSite)(IOleClientSite **ppClientSite);
+ STDMETHOD(SetHostNames)(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */);
+ STDMETHOD(Close)(DWORD /* dwSaveOption */);
+ STDMETHOD(SetMoniker)(DWORD /* dwWhichMoniker */, IMoniker* /* pmk */);
+ STDMETHOD(GetMoniker)(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */, IMoniker** /* ppmk */);
+ STDMETHOD(InitFromData)(IDataObject* /* pDataObject */, BOOL /* fCreation */, DWORD /* dwReserved */);
+ STDMETHOD(GetClipboardData)(DWORD /* dwReserved */, IDataObject** /* ppDataObject */);
+ STDMETHOD(DoVerb)(LONG /*iVerb*/, LPMSG /* pMsg */, IOleClientSite* /* pActiveSite */, LONG /* lindex */,
+ HWND /*hwndParent*/, LPCRECT /*lprcPosRect*/);
+ STDMETHOD(EnumVerbs)(IEnumOLEVERB** /*ppEnumOleVerb*/);
+ STDMETHOD(Update)(void);
+ STDMETHOD(IsUpToDate)(void);
+ STDMETHOD(GetUserClassID)(CLSID *pClsid);
+ STDMETHOD(GetUserType)(DWORD /*dwFormOfType*/, LPOLESTR* /*pszUserType*/);
+ STDMETHOD(SetExtent)(DWORD /*dwDrawAspect*/, SIZEL* /*psizel*/);
+ STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL *psizel);
+ STDMETHOD(Advise)(IAdviseSink *pAdvSink, DWORD *pdwConnection);
+ STDMETHOD(Unadvise)(DWORD dwConnection);
+ STDMETHOD(EnumAdvise)(IEnumSTATDATA **ppEnumAdvise);
+ STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus);
+ STDMETHOD(SetColorScheme)(LOGPALETTE* /* pLogpal */);
+
+ //
+ // IViewObject members
+ //
+ STDMETHOD(SetAdvise)(DWORD aspect, DWORD advf, IAdviseSink* pAdvSink);
+ STDMETHOD(GetAdvise)(DWORD* /*pAspects*/, DWORD* /*pAdvf*/, IAdviseSink** ppAdvSink);
+ STDMETHOD(Freeze)(DWORD, long, void*, DWORD*);
+ STDMETHOD(Unfreeze)(DWORD);
+ STDMETHOD(GetColorSet)(DWORD, long, void*, DVTARGETDEVICE*, HDC,
+ LOGPALETTE**);
+
+ //
+ // IViewObject2 members
+ //
+ STDMETHOD(GetExtent)(DWORD aspect, long, DVTARGETDEVICE*, SIZEL* pSize);
+
+ //
+ // ITooltipData members
+ //
+ STDMETHOD(SetTooltip)(BSTR bstrHint);
+ STDMETHOD(GetTooltip)(BSTR * bstrHint);
+
+};
+
+int CheckForTip(int x, int y, HWND hwnd, TCHAR** smltxt);
+void CloseSmileys(void);
+
diff --git a/plugins/SmileyAdd/src/anim.cpp b/plugins/SmileyAdd/src/anim.cpp
new file mode 100644
index 0000000000..5352383821
--- /dev/null
+++ b/plugins/SmileyAdd/src/anim.cpp
@@ -0,0 +1,184 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2006 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "anim.h"
+
+#include <process.h>
+
+Animate::Animate(SmileyType *sml, RECT& rect, HDC hdcMem, HBRUSH hbr, bool clip)
+ : m_sml(sml), m_img(NULL),
+ m_nFramePosition(0), m_sel(false), m_clip(clip),
+ m_offset(0), m_running(false),
+ m_hdcMem(hdcMem), m_hbr(hbr)
+{
+ m_cliprect = rect;
+}
+
+
+Animate::~Animate()
+{
+ if (m_img) m_img->Release();
+}
+
+
+void Animate::StartAnimation(void)
+{
+ m_img = m_sml->CreateCachedImage();
+
+ if (m_img && m_img->IsAnimated())
+ {
+ m_img->SelectFrame(m_nFramePosition);
+ long frtm = m_img->GetFrameDelay();
+ m_counter = frtm / 10 + ((frtm % 10) >= 5);
+ }
+}
+
+
+void Animate::ProcessTimerTick(HWND hwnd)
+{
+ if (m_running && m_img->IsAnimated() && --m_counter <= 0)
+ {
+ m_nFramePosition = m_img->SelectNextFrame(m_nFramePosition);
+
+ long frtm = m_img->GetFrameDelay();
+ m_counter = frtm / 10 + ((frtm % 10) >= 5);
+
+ HDC hdc = GetDC(hwnd);
+ DrawFrame(hdc);
+ ReleaseDC(hwnd, hdc);
+ }
+}
+
+
+void Animate::DrawFrame(HDC hdc)
+{
+ long width = m_cliprect.right - m_cliprect.left;
+ long height = m_cliprect.bottom - m_cliprect.top;
+
+ RECT frc = { 0, 0, width, height };
+ FillRect(m_hdcMem, &frc, m_hbr);
+
+ m_img->Draw(m_hdcMem, frc, m_clip);
+
+ BitBlt(hdc, m_cliprect.left, m_cliprect.top, width, height, m_hdcMem, 0, 0, SRCCOPY);
+
+ if (m_sel)
+ DrawFocusRect(hdc, &m_cliprect);
+}
+
+
+void Animate::Draw(HDC hdc)
+{
+ if (m_running)
+ {
+ m_img->Draw(hdc, m_cliprect, m_clip);
+
+ if (m_sel)
+ DrawFocusRect(hdc, &m_cliprect);
+ }
+}
+
+
+void Animate::SetOffset(int off, int wsize)
+{
+ const int dy = m_offset - off;
+
+ m_cliprect.top += dy;
+ m_cliprect.bottom += dy;
+
+ m_offset = off;
+
+ m_running = m_cliprect.top >= 0 && m_cliprect.top < wsize;
+ if (m_running)
+ {
+ if (m_img == NULL)
+ {
+ StartAnimation();
+ if (m_img == NULL) m_running = false;
+ }
+ }
+ else
+ {
+ if (m_img) m_img->Release();
+ m_img = NULL;
+ }
+}
+
+
+void Animate::SetSel(int x, int y)
+{
+ m_sel = x >= m_cliprect.left && x < m_cliprect.right &&
+ y >= m_cliprect.top && y < m_cliprect.bottom;
+}
+
+
+AnimatedPack::AnimatedPack(HWND hwnd, int wsize, SIZE& sel, COLORREF bkg)
+ : m_AniList(40), m_hwnd(hwnd), m_wsize(wsize)
+{
+ HDC hdc = GetDC(hwnd);
+
+ m_hBmp = CreateCompatibleBitmap(hdc, sel.cx, sel.cy);
+ m_hdcMem = CreateCompatibleDC(hdc);
+ m_hOld = (HBITMAP)SelectObject(m_hdcMem, m_hBmp);
+ m_hbr = CreateSolidBrush(bkg);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+
+AnimatedPack::~AnimatedPack()
+{
+ DeleteObject(m_hbr);
+ SelectObject(m_hdcMem, m_hOld);
+ DeleteObject(m_hBmp);
+ DeleteDC(m_hdcMem);
+}
+
+
+void AnimatedPack::Add(SmileyType *sml, RECT rect, bool clip)
+{
+ m_AniList.insert(new Animate(sml, rect, m_hdcMem, m_hbr, clip));
+}
+
+
+void AnimatedPack::Draw(HDC hdc)
+{
+ for (int i=0; i<m_AniList.getCount(); i++)
+ m_AniList[i].Draw(hdc);
+}
+
+
+void AnimatedPack::SetOffset(int off)
+{
+ for (int i=0; i<m_AniList.getCount(); i++)
+ m_AniList[i].SetOffset(off, m_wsize);
+}
+
+
+void AnimatedPack::SetSel(RECT& rect)
+{
+ for (int i=0; i<m_AniList.getCount(); i++)
+ m_AniList[i].SetSel(rect.left, rect.top);
+}
+
+void AnimatedPack::ProcessTimerTick(HWND hwnd)
+{
+ for (int i=0; i<m_AniList.getCount(); i++)
+ m_AniList[i].ProcessTimerTick(hwnd);
+}
diff --git a/plugins/SmileyAdd/src/anim.h b/plugins/SmileyAdd/src/anim.h
new file mode 100644
index 0000000000..7d81afa689
--- /dev/null
+++ b/plugins/SmileyAdd/src/anim.h
@@ -0,0 +1,87 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2006 - 2011 Boris Krasnovskiy
+
+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/>.
+*/
+
+#ifndef anim_h
+#define anim_h
+
+#include "smileys.h"
+
+class Animate
+{
+private:
+
+ ImageBase *m_img;
+ SmileyType *m_sml;
+
+ HDC m_hdcMem;
+ HBRUSH m_hbr;
+
+ RECT m_cliprect;
+
+ unsigned m_nFramePosition;
+ int m_offset;
+ int m_counter;
+ bool m_running;
+ bool m_sel;
+ bool m_clip;
+
+ void DrawFrame(HDC hdc);
+
+public:
+
+ Animate(SmileyType *sml, RECT &rect, HDC hdcMem, HBRUSH hbr, bool clip);
+ Animate(const Animate& an);
+ ~Animate();
+
+ void Draw(HDC hdc);
+
+ void StartAnimation(void);
+ void SetOffset(int off, int wsize);
+ void SetSel(int x, int y);
+
+ void ProcessTimerTick(HWND hwnd);
+};
+
+class AnimatedPack
+{
+private:
+ OBJLIST<Animate> m_AniList;
+
+ HWND m_hwnd;
+ int m_wsize;
+
+ HBRUSH m_hbr;
+ HBITMAP m_hBmp;
+ HDC m_hdcMem;
+ HBITMAP m_hOld;
+
+ static unsigned CALLBACK AnimateThreadFunc ( void* arg );
+
+public:
+ AnimatedPack(HWND hwnd, int wsize, SIZE& sel, COLORREF bkg);
+ ~AnimatedPack();
+
+ void Add(SmileyType *sml, RECT rect, bool clip);
+ void Draw(HDC hdc);
+ void SetOffset(int off);
+ void SetSel(RECT& rect);
+
+ void ProcessTimerTick(HWND hwnd);
+};
+
+#endif
diff --git a/plugins/SmileyAdd/src/bkstring.cpp b/plugins/SmileyAdd/src/bkstring.cpp
new file mode 100644
index 0000000000..2973783abc
--- /dev/null
+++ b/plugins/SmileyAdd/src/bkstring.cpp
@@ -0,0 +1,215 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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/>.
+*/
+
+#ifndef __GNUC__
+# ifdef _DEBUG
+# define _CRTDBG_MAP_ALLOC
+# include <stdlib.h>
+# include <crtdbg.h>
+# else
+# include <stdlib.h>
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "bkstring.h"
+
+
+bkstring::~bkstring() { if (sizeAlloced) free(buf); }
+
+void bkstring::reserve(size_type len)
+{
+ if (len >= sizeAlloced || sizeAlloced == 0)
+ {
+ if (sizeAlloced == 0) buf = NULL;
+ buf = (value_type*)realloc(buf, (len+1) * sizeof(value_type));
+ if (sizeAlloced == 0) buf[0] = 0;
+ sizeAlloced = len+1;
+ }
+}
+
+void bkstring::appendfmt(const value_type *fmt, ...)
+{
+ areserve(_tcslen(fmt)*2);
+
+ va_list vararg;
+ va_start(vararg, fmt);
+ for (;;)
+ {
+ int len = _vsntprintf(buf + lenBuf, sizeAlloced - lenBuf - 1, fmt, vararg);
+ if (len < 0)
+ reserve(sizeAlloced + 256);
+ else
+ {
+ lenBuf += len;
+ buf[lenBuf] = 0;
+ break;
+ }
+ }
+ va_end(vararg);
+}
+
+bkstring& bkstring::append(const value_type* _Ptr)
+{
+ size_type len = _tcslen(_Ptr);
+ areserve(len);
+ memcpy(buf+lenBuf, _Ptr, (len+1)*sizeof(value_type));
+ lenBuf += len;
+ return *this;
+}
+
+bkstring& bkstring::append(const value_type* _Ptr, size_type _Count)
+{
+ size_type len = min(_tcslen(_Ptr), _Count);
+ areserve(len);
+ memcpy(buf+lenBuf, _Ptr, len*sizeof(value_type));
+ lenBuf += len;
+ buf[lenBuf] = 0;
+ return *this;
+}
+
+bkstring& bkstring::append(const bkstring& _Str, size_type _Off, size_type _Count)
+{
+ size_type len = min(_Count, _Str.size() - _Off);
+ areserve(len);
+ memcpy(buf+lenBuf, _Str.c_str()+_Off, len*sizeof(value_type));
+ lenBuf += len;
+ buf[lenBuf] = 0;
+ return *this;
+}
+
+bkstring& bkstring::append(const bkstring& _Str)
+{
+ size_type len = _Str.size();
+ areserve(len);
+ memcpy(buf+lenBuf, _Str.c_str(), len*sizeof(value_type));
+ lenBuf += len;
+ buf[lenBuf] = 0;
+ return *this;
+}
+
+bkstring& bkstring::append(size_type _Count, value_type _Ch)
+{
+ areserve(_Count);
+ for(size_type i=0; i<_Count; ++i) buf[lenBuf+i] = _Ch;
+ lenBuf += _Count;
+ buf[lenBuf] = 0;
+ return *this;
+}
+
+
+bkstring& bkstring::assign(const value_type* _Ptr, size_type _Count)
+{
+ if (_Count == 0 && sizeAlloced == 0)
+ {
+ buf = (TCHAR*)_T("");
+ }
+ else
+ {
+ reserve(_Count);
+ memcpy(buf, _Ptr, _Count*sizeof(value_type));
+ buf[_Count] = 0;
+ lenBuf = _Count;
+ }
+ return *this;
+}
+
+bkstring& bkstring::assign(const bkstring& _Str, size_type _Off, size_type _Count)
+{
+ size_type len = min(_Count, _Str.size() - _Off);
+ if (len == 0 && sizeAlloced == 0)
+ {
+ buf = (TCHAR*)_T("");
+ }
+ else
+ {
+ reserve(len);
+ memcpy(buf, _Str.c_str() + _Off, len*sizeof(value_type));
+ lenBuf = len;
+ buf[len] = 0;
+ }
+ return *this;
+}
+
+bkstring& bkstring::assign(size_type _Count, value_type _Ch)
+{
+ reserve(_Count);
+ for(size_type i=0; i<_Count; ++i) buf[i] = _Ch;
+ buf[_Count] = 0;
+ lenBuf = _Count;
+ return *this;
+}
+
+bkstring::size_type bkstring::find(value_type _Ch, size_type _Off) const
+{
+ for (size_type i=_Off; i<=lenBuf; ++i)
+ if (buf[i] == _Ch) return i;
+ return (size_type)npos;
+}
+
+bkstring::size_type bkstring::find(const value_type* _Ptr, size_type _Off) const
+{
+ if (_Off > lenBuf) return (size_type)npos;
+
+ value_type* pstr = _tcsstr(buf+_Off, _Ptr);
+ return pstr ? pstr - buf : npos;
+}
+
+bkstring::size_type bkstring::find_last_of(value_type _Ch, size_type _Off) const
+{
+ for (size_type i=(_Off == npos ? lenBuf : _Off); i--;)
+ if (buf[i] == _Ch) return i;
+ return (size_type)npos;
+}
+
+bkstring& bkstring::insert(size_type _P0, const value_type* _Ptr, size_type _Count)
+{
+ size_type len = _tcslen(_Ptr);
+ if (_Count < len) len = _Count;
+ areserve(len);
+ value_type *p = buf + _P0;
+ memmove(p+len, p, (lenBuf-_P0+1)*sizeof(value_type));
+ memcpy(p, _Ptr, _Count*sizeof(value_type));
+ lenBuf += len;
+ return *this;
+}
+
+bkstring& bkstring::insert(size_type _P0, size_type _Count, value_type _Ch)
+{
+ areserve(_Count);
+ value_type *p = buf + _P0;
+ memmove(p+_Count, p, (lenBuf-_P0+1)*sizeof(value_type));
+ for(size_type i=0; i<_Count; ++i) p[i] = _Ch;
+ lenBuf += _Count;
+ return *this;
+}
+
+bkstring& bkstring::erase(size_type _Pos, size_type _Count)
+{
+ if (_Pos < lenBuf)
+ {
+ const size_type len = min(lenBuf - _Pos, _Count);
+ value_type *p = buf + _Pos;
+ lenBuf -= len;
+ memmove(p, p+len, (lenBuf - _Pos)*sizeof(value_type));
+ buf[lenBuf] = 0;
+ }
+ return *this;
+}
diff --git a/plugins/SmileyAdd/src/bkstring.h b/plugins/SmileyAdd/src/bkstring.h
new file mode 100644
index 0000000000..eb164f74e2
--- /dev/null
+++ b/plugins/SmileyAdd/src/bkstring.h
@@ -0,0 +1,270 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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/>.
+*/
+
+#pragma once
+
+#include <tchar.h>
+#include <string.h>
+
+#ifndef min
+#define min(A, B) ((A) < (B) ? (A) : (B))
+#endif
+
+class bkstring
+{
+public:
+ typedef size_t size_type;
+ typedef TCHAR value_type;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1200)
+ enum { npos = -1 };
+#else
+ static const size_type npos = size_type(-1);
+#endif
+
+private:
+ value_type* buf;
+ size_type sizeAlloced;
+ size_type lenBuf;
+
+ void areserve(size_type len) { reserve(lenBuf + len); }
+
+public:
+
+ explicit bkstring() : buf((TCHAR*)_T("")), sizeAlloced(0), lenBuf(0)
+ {}
+
+ bkstring(const value_type* _Ptr, size_type _Count) : sizeAlloced(0), lenBuf(0)
+ { assign(_Ptr, _Count); }
+
+ bkstring(const value_type* _Ptr) : sizeAlloced(0), lenBuf(0)
+ { assign(_Ptr); }
+
+ bkstring(size_type _Count, value_type _Ch) : sizeAlloced(0), lenBuf(0)
+ { assign(_Count, _Ch); }
+
+ bkstring(const bkstring& _Str) : sizeAlloced(0), lenBuf(0)
+ { assign(_Str); }
+
+ bkstring(const bkstring& _Str, size_type _Off, size_type _Count) : sizeAlloced(0), lenBuf(0)
+ { assign(_Str, _Off, _Count); }
+
+ ~bkstring();
+
+ size_type size(void) const { return lenBuf; }
+ const value_type* c_str(void) const { return buf; }
+
+ void clear(void) { if (lenBuf) { lenBuf = 0; buf[0] = 0; } }
+ void insert(const value_type *txt);
+ void reserve(size_type len);
+
+ bkstring& assign(const value_type* _Ptr)
+ { return assign(_Ptr, _tcslen(_Ptr)); }
+
+ bkstring& assign(const bkstring& _Str)
+ { return assign(_Str, 0, (size_type)npos); }
+
+ bkstring& assign(const value_type* _Ptr, size_type _Count);
+ bkstring& assign(const bkstring& _Str, size_type off, size_type _Count);
+ bkstring& assign(size_type _Count, value_type _Ch);
+
+ bkstring& append(const value_type* _Ptr);
+ bkstring& append(const value_type* _Ptr, size_type _Count);
+ bkstring& append(const bkstring& _Str, size_type _Off, size_type _Count);
+ bkstring& append(const bkstring& _Str);
+ bkstring& append(size_type _Count, value_type _Ch);
+
+ int compare(const bkstring& _Str) const
+ { return _tcscmp(buf, _Str.c_str()); }
+
+ int compare(size_type _Pos1, size_type _Num1, const bkstring& _Str) const
+ { return _tcsncmp(&buf[_Pos1], _Str.c_str(), _Num1); }
+
+ int compare(size_type _Pos1, size_type _Num1, const bkstring& _Str, size_type _Off, size_type _Count) const
+ { return _tcsncmp(&buf[_Pos1], _Str.c_str()+_Off, min(_Num1, _Count)); }
+
+ int compare(const value_type* _Ptr) const
+ { return _tcscmp(buf, _Ptr); }
+
+ int compare(size_type _Pos1, size_type _Num1, const value_type* _Ptr) const
+ { return _tcsncmp(&buf[_Pos1], _Ptr, _Num1); }
+
+ int compare(size_type _Pos1, size_type _Num1, const value_type* _Ptr, size_type _Num2) const
+ { return _tcsncmp(&buf[_Pos1], _Ptr, min(_Num1, _Num2)); }
+
+ int comparei(const bkstring& _Str) const
+ { return _tcsicmp(buf, _Str.c_str()); }
+
+ int comparei(size_type _Pos1, size_type _Num1, const bkstring& _Str) const
+ { return _tcsnicmp(&buf[_Pos1], _Str.c_str(), _Num1); }
+
+ int comparei(size_type _Pos1, size_type _Num1, const bkstring& _Str, size_type _Off, size_type _Count) const
+ { return _tcsnicmp(&buf[_Pos1], _Str.c_str()+_Off, min(_Num1, _Count)); }
+
+ int comparei(const value_type* _Ptr) const
+ { return _tcsicmp(buf, _Ptr); }
+
+ int comparei(size_type _Pos1, size_type _Num1, const value_type* _Ptr) const
+ { return _tcsnicmp(&buf[_Pos1], _Ptr, _Num1); }
+
+ int comparei(size_type _Pos1, size_type _Num1, const value_type* _Ptr, size_type _Num2) const
+ { return _tcsnicmp(&buf[_Pos1], _Ptr, min(_Num1, _Num2)); }
+
+ bool empty(void) const { return lenBuf == 0; };
+ bkstring& erase(size_type _Pos = 0, size_type _Count = npos);
+
+ size_type find(value_type _Ch, size_type _Off = 0) const;
+ size_type find(const value_type* _Ptr, size_type _Off = 0) const;
+ size_type find(bkstring& _Str, size_type _Off = 0) const
+ { return find(_Str.c_str(), _Off); }
+
+ size_type find_last_of(value_type _Ch, size_type _Off = npos) const;
+
+ bkstring& insert(size_type _P0, const value_type* _Ptr)
+ { return insert(_P0, _Ptr, _tcslen(_Ptr)); }
+
+ bkstring& insert(size_type _P0, const bkstring& _Str)
+ { return insert(_P0, _Str.c_str(), _Str.size()); };
+
+ bkstring& insert(size_type _P0, const value_type* _Ptr, size_type _Count);
+ bkstring& insert(size_type _P0, size_type _Count, value_type _Ch);
+
+ bkstring substr(size_type _Off = 0, size_type _Count = npos) const
+ { return bkstring(*this, _Off, _Count); }
+
+ bkstring& operator = (const bkstring& _Str)
+ { return assign(_Str); }
+
+ bkstring& operator =(const value_type* _Ptr)
+ { return assign(_Ptr); }
+
+ bkstring& operator = (const value_type _Ch)
+ { return assign(1, _Ch); }
+
+ bkstring& operator +=(const bkstring& _Str)
+ { return append(_Str); }
+
+ bkstring& operator += (const value_type* _Ptr)
+ { return append(_Ptr); }
+
+ bkstring& operator += (const value_type _Ch)
+ { return append(1, _Ch); }
+
+ value_type& operator[] (int ind) const
+ { return buf[ind]; }
+
+ friend bkstring operator+ (const bkstring& _Str1, const bkstring& _Str2)
+ { bkstring s(_Str1); return s.append(_Str2); }
+
+ friend bkstring operator+ (const bkstring& _Str1, const value_type* _Ptr2)
+ { bkstring s(_Str1); return s.append(_Ptr2); }
+
+ friend bkstring operator+(const value_type* _Ptr1, const bkstring& _Str2)
+ { bkstring s(_Ptr1); return s.append(_Str2); }
+
+ friend bkstring operator+ (const bkstring& _Str1, const value_type _Ch)
+ { bkstring s(_Str1); return s.append(1, _Ch); }
+
+ friend bool operator==(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) == 0; }
+
+ friend bool operator==(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) == 0; }
+
+ friend bool operator==(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) == 0; }
+
+ friend bool operator!=(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) != 0; }
+
+ friend bool operator!=(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) != 0; }
+
+ friend bool operator!=(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) != 0; }
+
+ friend bool operator<(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) < 0; }
+
+ friend bool operator<(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) < 0; }
+
+ friend bool operator<(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) > 0; }
+
+ friend bool operator>(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) > 0; }
+
+ friend bool operator>(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) > 0; }
+
+ friend bool operator>(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) < 0; }
+
+ friend bool operator<=(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) <= 0; }
+
+ friend bool operator<=(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) <= 0; }
+
+ friend bool operator<=(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) >= 0; }
+
+ friend bool operator>=(const bkstring& _Str1, const bkstring& _Str2)
+ { return _Str1.compare(_Str2) >= 0; }
+
+ friend bool operator>=(const bkstring& _Str1, const value_type* _Ptr2)
+ { return _Str1.compare(_Ptr2) >= 0; }
+
+ friend bool operator>=(const value_type* _Ptr1, const bkstring& _Str2)
+ { return _Str2.compare(_Ptr1) <= 0; }
+
+ friend bool operator==(const value_type _Ch1, const bkstring& _Str2)
+ { return (_Str2.size() == 1) && (_Str2[0] == _Ch1); }
+
+ friend bool operator==(const bkstring& _Str1, const value_type _Ch2)
+ { return (_Str1.size() == 1) && (_Str1[0] == _Ch2); }
+
+ friend bool operator!=(const value_type _Ch1, const bkstring& _Str2)
+ { return (_Str2.size() != 1) || (_Str2[0] != _Ch1); }
+
+ friend bool operator!=(const bkstring& _Str1, const value_type _Ch2)
+ { return (_Str1.size() != 1) || (_Str1[0] != _Ch2); }
+
+ iterator begin(void)
+ { return buf; }
+
+ const_iterator begin(void) const
+ { return buf; }
+
+ iterator end(void)
+ { return buf + lenBuf; }
+
+ const_iterator end(void) const
+ { return buf + lenBuf; }
+
+ // Custom extentions
+
+ void appendfmt(const value_type *fmt, ...);
+
+ size_type sizebytes(void) const { return lenBuf * sizeof(value_type); }
+};
+
+//const bkstring::size_type bkstring::npos = -1;
diff --git a/plugins/SmileyAdd/src/customsmiley.cpp b/plugins/SmileyAdd/src/customsmiley.cpp
new file mode 100644
index 0000000000..f125f015db
--- /dev/null
+++ b/plugins/SmileyAdd/src/customsmiley.cpp
@@ -0,0 +1,172 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "customsmiley.h"
+
+SmileyPackCListType g_SmileyPackCStore;
+
+
+bool SmileyPackCListType::AddSmileyPack(HANDLE hContact, TCHAR* dir)
+{
+ bool res = true;
+ if (GetSmileyPack(hContact) == NULL)
+ {
+ SmileyPackCType *smileyPack = new SmileyPackCType;
+
+ res = smileyPack->LoadSmileyDir(dir);
+ if (res)
+ {
+ smileyPack->SetId(hContact);
+ m_SmileyPacks.insert(smileyPack);
+ }
+ else
+ delete smileyPack;
+ }
+ return res;
+}
+
+
+bool SmileyPackCListType::AddSmiley(HANDLE hContact, TCHAR* path)
+{
+ SmileyPackCType* smpack = GetSmileyPack(hContact);
+ if (smpack == NULL)
+ {
+ smpack = new SmileyPackCType;
+
+ smpack->SetId(hContact);
+ m_SmileyPacks.insert(smpack);
+ }
+ return smpack->LoadSmiley(path);
+}
+
+
+SmileyPackCType* SmileyPackCListType::GetSmileyPack(HANDLE id)
+{
+ for (int i = 0; i < m_SmileyPacks.getCount(); i++)
+ {
+ if (m_SmileyPacks[i].GetId() == id) return &m_SmileyPacks[i];
+ }
+ return NULL;
+}
+
+
+SmileyCType::SmileyCType(const bkstring& fullpath, const TCHAR* filepath)
+{
+ LoadFromResource(fullpath, 0);
+ CreateTriggerText(T2A_SM(filepath));
+}
+
+bool SmileyCType::CreateTriggerText(char* text)
+{
+ UrlDecode(text);
+
+ int len = (int)strlen(text);
+ if (len == 0) return false;
+
+ int reslen = Netlib_GetBase64DecodedBufferSize(len)+1;
+ char* res = (char*)alloca(reslen);
+
+ NETLIBBASE64 nlb = { text, len, ( PBYTE )res, reslen };
+ if (!CallService(MS_NETLIB_BASE64DECODE, 0, LPARAM( &nlb ))) return false;
+ res[nlb.cbDecoded] = 0;
+
+ TCHAR *txt = mir_utf8decodeT(res);
+
+ if (txt == NULL) return false;
+
+ m_TriggerText = txt;
+ mir_free(txt);
+
+ return true;
+}
+
+
+//
+// SmileyPackCType
+//
+
+bool SmileyPackCType::LoadSmileyDir(TCHAR* dir)
+{
+ bkstring dirs = dir;
+ dirs += _T("\\*.*");
+
+ _tfinddata_t c_file;
+ INT_PTR hFile = _tfindfirst((TCHAR*)dirs.c_str(), &c_file);
+ if (hFile > -1L)
+ {
+ do {
+ if (c_file.name[0] != '.')
+ {
+ bkstring fullpath = dir;
+ fullpath = fullpath + _T("\\") + c_file.name;
+ TCHAR* div = _tcsrchr(c_file.name, '.');
+ if (div)
+ {
+ *div = 0;
+ SmileyCType *smlc = new SmileyCType(fullpath, c_file.name);
+ if (smlc->GetTriggerText().empty())
+ delete smlc;
+ else
+ m_SmileyList.insert(smlc);
+ }
+ }
+ } while( _tfindnext( hFile, &c_file ) == 0 );
+ _findclose( hFile );
+ AddTriggersToSmileyLookup();
+ return true;
+ }
+ return false;
+}
+
+
+bool SmileyPackCType::LoadSmiley(TCHAR* path)
+{
+ bkstring dirs = path;
+ bkstring::size_type slash = dirs.find_last_of('\\');
+ bkstring::size_type dot = dirs.find_last_of('.');
+
+ bkstring name = dirs.substr(slash+1, dot - slash - 1);
+
+ for (int i=0; i < m_SmileyList.getCount(); i++)
+ {
+ if (m_SmileyList[i].GetTriggerText() == name)
+ {
+ m_SmileyList[i].LoadFromResource(dirs, 0);
+ return true;
+ }
+ }
+
+ m_SmileyList.insert(new SmileyCType(dirs, (TCHAR*)name.c_str()));
+
+ bkstring empty;
+ m_SmileyLookup.insert(new SmileyLookup(
+ m_SmileyList[m_SmileyList.getCount()-1].GetTriggerText(), false, m_SmileyList.getCount()-1, empty));
+
+ return true;
+}
+
+
+void SmileyPackCType::AddTriggersToSmileyLookup(void)
+{
+ bkstring empty;
+ for (int dist=0; dist<m_SmileyList.getCount(); dist++)
+ {
+ SmileyLookup *dats = new SmileyLookup(m_SmileyList[dist].GetTriggerText(), false, dist, empty);
+ m_SmileyLookup.insert(dats);
+ }
+}
diff --git a/plugins/SmileyAdd/src/customsmiley.h b/plugins/SmileyAdd/src/customsmiley.h
new file mode 100644
index 0000000000..ce38803c4a
--- /dev/null
+++ b/plugins/SmileyAdd/src/customsmiley.h
@@ -0,0 +1,85 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "smileys.h"
+
+#ifndef SMILEYADD_CUSTOMSMILEY_H_
+#define SMILEYADD_CUSTOMSMILEY_H_
+
+class SmileyCType : public SmileyType
+{
+public:
+ SmileyCType(const bkstring& fullpath, const TCHAR* filepath);
+
+ bool CreateTriggerText(char* text);
+};
+
+class SmileyPackCType
+{
+public:
+ typedef SMOBJLIST<SmileyCType> SmileyVectorType;
+ typedef SMOBJLIST<SmileyLookup> SmileyLookupType;
+
+private:
+ SmileyVectorType m_SmileyList;
+ SmileyLookupType m_SmileyLookup;
+
+ HANDLE m_id;
+
+ void InsertLookup(SmileyCType& sml, bkstring& lk, bool first);
+ void AddTriggersToSmileyLookup(void);
+
+public:
+ SmileyVectorType& GetSmileyList(void) { return m_SmileyList; }
+ SmileyLookupType& GetSmileyLookup(void) { return m_SmileyLookup; }
+
+ int SmileyCount(void) const { return m_SmileyList.getCount(); }
+
+ SmileyCType* GetSmiley(unsigned index) { return &m_SmileyList[index]; }
+
+ HANDLE GetId(void) { return m_id; }
+ void SetId(HANDLE id) { m_id = id; }
+
+ bool LoadSmileyDir(TCHAR* dir);
+ bool LoadSmiley(TCHAR* path);
+};
+
+
+class SmileyPackCListType
+{
+public:
+ typedef SMOBJLIST<SmileyPackCType> SmileyPackVectorType;
+
+private:
+ SmileyPackVectorType m_SmileyPacks;
+
+public:
+ int NumberOfSmileyPacks(void) { return m_SmileyPacks.getCount(); }
+
+ bool AddSmileyPack(HANDLE hContact, TCHAR* dir);
+ bool AddSmiley(HANDLE hContact, TCHAR* path);
+
+ void ClearAndFreeAll(void) { m_SmileyPacks.destroy(); }
+
+ SmileyPackCType* GetSmileyPack(HANDLE id);
+};
+
+extern SmileyPackCListType g_SmileyPackCStore;
+
+#endif
diff --git a/plugins/SmileyAdd/src/dlgboxsubclass.cpp b/plugins/SmileyAdd/src/dlgboxsubclass.cpp
new file mode 100644
index 0000000000..03a0a6e5fd
--- /dev/null
+++ b/plugins/SmileyAdd/src/dlgboxsubclass.cpp
@@ -0,0 +1,568 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2012 Boris Krasnovskiy All Rights Reserved
+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 "services.h"
+#include "options.h"
+
+//***************************************************//
+// DISCLAIMER!!!
+// we are not supposed to use this object, so be aware
+typedef struct NewMessageWindowLParam
+{
+ HANDLE hContact;
+ int isSend;
+ const char *szInitialText;
+}
+msgData;
+// this is an undocumented object!!!!!!!
+// subject to change in miranda versions...!!!!!!
+// DISCLAIMER!!!
+//***************************************************//
+
+extern HINSTANCE g_hInst;
+
+static HHOOK g_hMessageHookPre = NULL;
+static HANDLE g_hMutex = NULL;
+static HANDLE g_hHookMsgWnd = NULL;
+
+static LRESULT CALLBACK MessageDlgSubclas(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+
+//type definitions
+class MsgWndData
+{
+public:
+ HWND hwnd;
+ char ProtocolName[52];
+ HWND REdit;
+ HWND QuoteB;
+ HWND MEdit;
+ HWND MOK;
+ HWND LButton;
+ mutable HWND hSmlButton;
+ mutable HBITMAP hSmlBmp;
+ mutable HICON hSmlIco;
+ int idxLastChar;
+ WNDPROC wpOrigWndProc;
+ HANDLE hContact;
+ bool doSmileyReplace;
+ bool doSmileyButton;
+ bool OldButtonPlace;
+ bool isSplit;
+ bool isSend;
+
+ MsgWndData()
+ {
+ ProtocolName[0] = 0;
+ REdit = NULL;
+ QuoteB = NULL;
+ MEdit = NULL;
+ MOK = NULL;
+ LButton = NULL;
+ hSmlButton = NULL;
+ hSmlBmp = NULL;
+ hSmlIco = NULL;
+ idxLastChar = 0;
+ hContact = NULL;
+ doSmileyReplace = false;
+ doSmileyButton = false;
+ OldButtonPlace = false;
+ isSplit = false;
+ isSend = false;
+ wpOrigWndProc = NULL;
+ }
+
+ MsgWndData(const MsgWndData &dsb)
+ {
+ *this = dsb;
+ dsb.hSmlBmp = NULL;
+ dsb.hSmlIco = NULL;
+ dsb.hSmlButton = NULL;
+ }
+
+
+ ~MsgWndData()
+ {
+ clear();
+ }
+
+ void clear(void)
+ {
+ if (hSmlBmp != NULL) DeleteObject(hSmlBmp);
+ if (hSmlIco != NULL) DestroyIcon(hSmlIco);
+ if (hSmlButton != NULL) DestroyWindow(hSmlButton);
+ hSmlBmp = NULL;
+ hSmlIco = NULL;
+ hSmlButton = NULL;
+ }
+
+ RECT CalcSmileyButtonPos(void)
+ {
+ RECT rect;
+ POINT pt;
+
+ if (OldButtonPlace)
+ {
+ if (isSplit && DBGetContactSettingByte(NULL, "SRMsg", "ShowQuote", FALSE))
+ {
+ GetWindowRect(QuoteB, &rect);
+ pt.x = rect.right + 12;
+ }
+ else
+ {
+ GetWindowRect(MEdit, &rect);
+ pt.x = rect.left;
+ }
+ GetWindowRect(MOK, &rect);
+ pt.y = rect.top;
+ }
+ else
+ {
+ GetWindowRect(LButton, &rect);
+ pt.y = rect.top;
+
+ if ((GetWindowLongPtr(LButton, GWL_STYLE) & WS_VISIBLE) != 0)
+ pt.x = rect.left - 28;
+ else
+ pt.x = rect.left;
+ }
+
+ ScreenToClient(GetParent(LButton), &pt);
+ rect.bottom += pt.y - rect.top;
+ rect.right += pt.x - rect.left;
+ rect.top = pt.y;
+ rect.left = pt.x;
+
+ return rect;
+ }
+
+ //helper function
+ //identifies the message dialog
+ bool IsMessageSendDialog(HWND hwnd)
+ {
+ TCHAR szClassName[32] = _T("");
+
+ GetClassName(hwnd, szClassName, SIZEOF(szClassName));
+ if (_tcscmp(szClassName, _T("#32770"))) return false;
+
+ if ((REdit = GetDlgItem(hwnd, MI_IDC_LOG)) != NULL)
+ {
+ GetClassName(REdit, szClassName, SIZEOF(szClassName));
+ if (_tcscmp(szClassName, _T("RichEdit20A")) != 0 &&
+ _tcscmp(szClassName, _T("RichEdit20W")) != 0 &&
+ _tcscmp(szClassName, _T("RICHEDIT50W")) != 0) return false;
+ }
+ else return false;
+
+ if ((MEdit = GetDlgItem(hwnd, MI_IDC_MESSAGE)) != NULL)
+ {
+ GetClassName(MEdit, szClassName, SIZEOF(szClassName));
+ if (_tcscmp(szClassName, _T("Edit")) != 0 &&
+ _tcscmp(szClassName, _T("RichEdit20A")) != 0 &&
+ _tcscmp(szClassName, _T("RichEdit20W")) != 0 &&
+ _tcscmp(szClassName, _T("RICHEDIT50W")) != 0) return false;
+ }
+ else return false;
+
+ QuoteB = GetDlgItem(hwnd, MI_IDC_QUOTE);
+
+ if ((LButton = GetDlgItem(hwnd, MI_IDC_ADD)) == NULL)
+ return false;
+
+ if (GetDlgItem(hwnd, MI_IDC_NAME) == NULL)
+ return false;
+ if ((MOK = GetDlgItem(hwnd, IDOK)) == NULL)
+ return false;
+
+ return true;
+ }
+
+ void CreateSmileyButton(void)
+ {
+ doSmileyButton = opt.ButtonStatus != 0;
+ OldButtonPlace = opt.ButtonStatus == 2;
+
+ SmileyPackType* SmileyPack = GetSmileyPack(ProtocolName, hContact);
+ doSmileyButton &= SmileyPack != NULL && SmileyPack->VisibleSmileyCount() != 0;
+
+ bool showButtonLine;
+ if (IsOldSrmm())
+ {
+ isSplit = DBGetContactSettingByte(NULL,"SRMsg","Split", TRUE) != 0;
+
+ doSmileyReplace = (isSplit || !isSend);
+ doSmileyButton &= isSplit || isSend;
+ showButtonLine = DBGetContactSettingByte(NULL, "SRMsg", "ShowButtonLine", TRUE) != 0;
+ }
+ else
+ {
+ doSmileyReplace = true;
+ OldButtonPlace = false;
+ showButtonLine = DBGetContactSettingByte(NULL, "SRMM", "ShowButtonLine", TRUE) != 0;
+ }
+
+ doSmileyButton &= OldButtonPlace || showButtonLine;
+
+ if (ProtocolName[0] != 0)
+ {
+ INT_PTR cap = CallProtoService(ProtocolName, PS_GETCAPS, PFLAGNUM_1, 0);
+ doSmileyButton &= ((cap & (PF1_IMSEND | PF1_CHAT)) != 0);
+ doSmileyReplace &= ((cap & (PF1_IMRECV | PF1_CHAT)) != 0);
+ }
+
+ if (doSmileyButton && opt.PluginSupportEnabled)
+ {
+ //create smiley button
+ RECT rect = CalcSmileyButtonPos();
+
+ hSmlButton = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_NOPARENTNOTIFY | WS_EX_TOPMOST,
+ MIRANDABUTTONCLASS,
+ _T("S"),
+ WS_CHILD|WS_VISIBLE|WS_TABSTOP, // window style
+ rect.left, // horizontal position of window
+ rect.top, // vertical position of window
+ rect.bottom - rect.top + 1, // window width
+ rect.bottom - rect.top + 1, // window height
+ GetParent(LButton), // handle to parent or owner window
+ (HMENU) IDC_SMLBUTTON, // menu handle or child identifier
+ NULL, // handle to application instance
+ NULL); // window-creation data
+
+ // Conversion to bitmap done to prevent Miranda from scaling the image
+ SmileyType* sml = FindButtonSmiley(SmileyPack);
+ if (sml != NULL)
+ {
+ hSmlBmp = sml->GetBitmap(GetSysColor(COLOR_BTNFACE), 0, 0);
+ SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hSmlBmp);
+ }
+ else
+ {
+ hSmlIco = GetDefaultIcon();
+ SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hSmlIco);
+ }
+
+ SendMessage(hSmlButton, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Show Smiley Selection Window"), 0);
+ SendMessage(hSmlButton, BUTTONSETASFLATBTN, TRUE, 0);
+ }
+ }
+};
+
+static int CompareMsgWndData(const MsgWndData* p1, const MsgWndData* p2)
+{
+ return (int)((INT_PTR)p1->hwnd - (INT_PTR)p2->hwnd);
+}
+static LIST<MsgWndData> g_MsgWndList(10, CompareMsgWndData);
+
+
+bool IsOldSrmm(void)
+{
+ return ServiceExists(MS_MSG_GETWINDOWCLASS) == 0;
+}
+
+
+int UpdateSrmmDlg(WPARAM wParam, LPARAM /* lParam */)
+{
+ WaitForSingleObject(g_hMutex, 2000);
+ for (int i=0; i<g_MsgWndList.getCount(); ++i)
+ {
+ if (wParam == 0 || g_MsgWndList[i]->hContact == (HANDLE)wParam)
+ {
+ SendMessage(g_MsgWndList[i]->hwnd, WM_SETREDRAW, FALSE, 0);
+ SendMessage(g_MsgWndList[i]->hwnd, DM_OPTIONSAPPLIED, 0, 0);
+ SendMessage(g_MsgWndList[i]->hwnd, WM_SETREDRAW, TRUE, 0);
+ }
+ }
+ ReleaseMutex(g_hMutex);
+
+ return 0;
+}
+
+
+//find the dialog info in the stored list
+static MsgWndData* IsMsgWnd(HWND hwnd)
+{
+ WaitForSingleObject(g_hMutex, 2000);
+ MsgWndData* res = g_MsgWndList.find((MsgWndData*)&hwnd);
+ ReleaseMutex(g_hMutex);
+
+ return res;
+}
+
+
+static void MsgWndDetect(HWND hwndDlg, HANDLE hContact, msgData* datm)
+{
+ MsgWndData dat;
+
+ if (dat.IsMessageSendDialog(hwndDlg))
+ {
+ dat.hwnd = hwndDlg;
+ if (datm != NULL)
+ {
+ dat.isSend = datm->isSend != 0;
+ dat.hContact = datm->hContact;
+ }
+ else
+ dat.hContact = hContact;
+
+ // Get the protocol for this contact to display correct smileys.
+ char *protonam = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO,
+ (WPARAM)DecodeMetaContact(dat.hContact), 0);
+
+ if (protonam)
+ {
+ strncpy(dat.ProtocolName, protonam, sizeof(dat.ProtocolName));
+ dat.ProtocolName[sizeof(dat.ProtocolName)-1] = 0;
+ }
+
+ WaitForSingleObject(g_hMutex, 2000);
+
+ MsgWndData* msgwnd = g_MsgWndList.find((MsgWndData*)&hwndDlg);
+ if (msgwnd == NULL)
+ {
+ msgwnd = new MsgWndData(dat);
+ g_MsgWndList.insert(msgwnd);
+ }
+ else
+ msgwnd = NULL;
+ ReleaseMutex(g_hMutex);
+
+ if (msgwnd != NULL)
+ {
+ msgwnd->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)MessageDlgSubclas);
+ msgwnd->CreateSmileyButton();
+ if (hContact == NULL) SetRichCallback(msgwnd->REdit, msgwnd->hContact, true, true);
+ }
+ }
+}
+
+
+//global subclass function for all dialogs
+static LRESULT CALLBACK MessageDlgSubclas(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MsgWndData* dat = IsMsgWnd(hwnd);
+ if (dat == NULL) return 0;
+
+ switch(uMsg)
+ {
+ case DM_OPTIONSAPPLIED:
+ dat->clear();
+ dat->CreateSmileyButton();
+ break;
+
+ case DM_APPENDTOLOG:
+ if (opt.PluginSupportEnabled)
+ {
+ //get length of text now before things can get added...
+ GETTEXTLENGTHEX gtl;
+ gtl.codepage = 1200;
+ gtl.flags = GTL_PRECISE | GTL_NUMCHARS;
+ dat->idxLastChar = (int)SendMessage(dat->REdit, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
+ }
+ break;
+ }
+
+ LRESULT result = CallWindowProc(dat->wpOrigWndProc, hwnd, uMsg, wParam, lParam);
+
+ if (!opt.PluginSupportEnabled) return result;
+
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ WaitForSingleObject(g_hMutex, 2000);
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)dat->wpOrigWndProc);
+ {
+ int ind = g_MsgWndList.getIndex((MsgWndData*)&hwnd);
+ if ( ind != -1 )
+ {
+ delete g_MsgWndList[ind];
+ g_MsgWndList.remove(ind);
+ }
+ }
+ ReleaseMutex(g_hMutex);
+ break;
+
+ case WM_SIZE:
+ if (dat->doSmileyButton)
+ {
+ RECT rect = dat->CalcSmileyButtonPos();
+ SetWindowPos(dat->hSmlButton, NULL, rect.left, rect.top,
+ 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+ break;
+
+ case DM_APPENDTOLOG:
+ if (dat->doSmileyReplace)
+ {
+ SmileyPackCType* smcp;
+ SmileyPackType* SmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact, &smcp);
+ if (SmileyPack != NULL)
+ {
+ const CHARRANGE sel = { dat->idxLastChar, LONG_MAX };
+ ReplaceSmileys(dat->REdit, SmileyPack, smcp, sel, false, false, false);
+ }
+ }
+ break;
+
+ case DM_REMAKELOG:
+ if (dat->doSmileyReplace)
+ {
+ SmileyPackCType* smcp;
+ SmileyPackType* SmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact, &smcp);
+ if (SmileyPack != NULL)
+ {
+ static const CHARRANGE sel = { 0, LONG_MAX };
+ ReplaceSmileys(dat->REdit, SmileyPack, smcp, sel, false, false, false);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_SMLBUTTON &&
+ HIWORD(wParam) == BN_CLICKED)
+ {
+ SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
+ stwp->pSmileyPack = GetSmileyPack(dat->ProtocolName, dat->hContact);
+
+ stwp->hWndParent = hwnd;
+ stwp->hWndTarget = dat->MEdit;
+ stwp->targetMessage = EM_REPLACESEL;
+ stwp->targetWParam = TRUE;
+
+ RECT rect;
+ GetWindowRect(dat->hSmlButton, &rect);
+
+ if (dat->OldButtonPlace)
+ {
+ stwp->direction = 3;
+ stwp->xPosition = rect.left;
+ stwp->yPosition = rect.top + 4;
+ }
+ else
+ {
+ stwp->direction = 0;
+ stwp->xPosition = rect.left;
+ stwp->yPosition = rect.top + 24;
+ }
+
+ mir_forkthread(SmileyToolThread, stwp);
+ }
+
+ if (LOWORD(wParam) == MI_IDC_ADD &&
+ HIWORD(wParam) == BN_CLICKED &&
+ dat->doSmileyButton)
+ {
+ RECT rect = dat->CalcSmileyButtonPos();
+ SetWindowPos(dat->hSmlButton, NULL, rect.left, rect.top,
+ 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+ break;
+ }
+
+ return result;
+}
+
+static int MsgDlgHook(WPARAM, LPARAM lParam)
+{
+ const MessageWindowEventData *wndEvtData = (MessageWindowEventData*)lParam;
+ switch(wndEvtData->uType)
+ {
+ case MSG_WINDOW_EVT_OPENING:
+ MsgWndDetect(wndEvtData->hwndWindow, wndEvtData->hContact, NULL);
+ if (wndEvtData->cbSize >= sizeof(MessageWindowEventData))
+ {
+ SetRichOwnerCallback(wndEvtData->hwndWindow, wndEvtData->hwndInput, wndEvtData->hwndLog);
+
+ if (wndEvtData->hwndLog)
+ SetRichCallback(wndEvtData->hwndLog, wndEvtData->hContact, false, false);
+ if (wndEvtData->hwndInput)
+ SetRichCallback(wndEvtData->hwndInput, wndEvtData->hContact, false, false);
+ }
+ break;
+
+ case MSG_WINDOW_EVT_OPEN:
+ if (wndEvtData->cbSize >= sizeof(MessageWindowEventData))
+ {
+ SetRichOwnerCallback(wndEvtData->hwndWindow, wndEvtData->hwndInput, wndEvtData->hwndLog);
+ if (wndEvtData->hwndLog)
+ SetRichCallback(wndEvtData->hwndLog, wndEvtData->hContact, true, true);
+ if (wndEvtData->hwndInput)
+ {
+ SetRichCallback(wndEvtData->hwndInput, wndEvtData->hContact, true, true);
+ SendMessage(wndEvtData->hwndInput, WM_REMAKERICH, 0, 0);
+ }
+ }
+ break;
+
+ case MSG_WINDOW_EVT_CLOSE:
+ if (wndEvtData->cbSize >= sizeof(MessageWindowEventData) && wndEvtData->hwndLog)
+ {
+ CloseRichCallback(wndEvtData->hwndLog, true);
+ CloseRichOwnerCallback(wndEvtData->hwndWindow, true);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+//global subclass function for all dialogs
+static LRESULT CALLBACK MsgDlgHookProcPre(int code, WPARAM wParam, LPARAM lParam)
+{
+ const CWPSTRUCT *msg = (CWPSTRUCT*)lParam;
+
+ if (code == HC_ACTION && msg->message == WM_INITDIALOG)
+ MsgWndDetect(msg->hwnd, NULL, (msgData*)msg->lParam);
+
+ return CallNextHookEx(g_hMessageHookPre, code, wParam, lParam);
+}
+
+
+void InstallDialogBoxHook(void)
+{
+ g_hMutex = CreateMutex(NULL, FALSE, NULL);
+
+ g_hHookMsgWnd = HookEvent(ME_MSG_WINDOWEVENT, MsgDlgHook);
+
+ // Hook message API
+ if (g_hHookMsgWnd == NULL)
+ g_hMessageHookPre = SetWindowsHookEx(WH_CALLWNDPROC, MsgDlgHookProcPre,
+ NULL, GetCurrentThreadId());
+}
+
+
+void RemoveDialogBoxHook(void)
+{
+ if (g_hHookMsgWnd) UnhookEvent(g_hHookMsgWnd);
+ if (g_hMessageHookPre) UnhookWindowsHookEx(g_hMessageHookPre);
+
+ WaitForSingleObject(g_hMutex, 2000);
+ for (int i=0; i<g_MsgWndList.getCount(); ++i)
+ delete g_MsgWndList[i];
+ g_MsgWndList.destroy();
+ ReleaseMutex(g_hMutex);
+
+ if (g_hMutex) CloseHandle(g_hMutex);
+
+ g_hHookMsgWnd = NULL;
+ g_hMessageHookPre = NULL;
+ g_hMutex = NULL;
+}
diff --git a/plugins/SmileyAdd/src/download.cpp b/plugins/SmileyAdd/src/download.cpp
new file mode 100644
index 0000000000..39de234634
--- /dev/null
+++ b/plugins/SmileyAdd/src/download.cpp
@@ -0,0 +1,292 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2007 - 2011 Boris Krasnovskiy
+
+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 "m_smileyadd.h"
+#include "m_folders.h"
+
+extern HANDLE hEvent1;
+HANDLE hNetlibUser;
+static HANDLE hFolder, hFolderHook;
+
+struct QueueElem
+{
+ bkstring url;
+ bkstring fname;
+ bool needext;
+
+ QueueElem(bkstring& purl, bkstring& pfname, bool ne)
+ : url(purl), fname(pfname), needext(ne) {}
+};
+
+static HANDLE g_hDlMutex;
+static OBJLIST<QueueElem> dlQueue(10);
+
+static TCHAR cachepath[MAX_PATH];
+static bool threadRunning;
+
+bool InternetDownloadFile(const char *szUrl, char* szDest, HANDLE &hHttpDwnl)
+{
+ int result = 0xBADBAD;
+ char* szRedirUrl = NULL;
+ NETLIBHTTPREQUEST nlhr = {0};
+
+ // initialize the netlib request
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_NODUMP;
+ nlhr.szUrl = (char*)szUrl;
+ nlhr.nlc = hHttpDwnl;
+
+ if (CallService(MS_SYSTEM_GETVERSION, 0, 0) >= PLUGIN_MAKE_VERSION(0,9,0,5))
+ nlhr.flags |= NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT;
+
+ // change the header so the plugin is pretended to be IE 6 + WinXP
+ nlhr.headersCount = 2;
+ nlhr.headers=(NETLIBHTTPHEADER*)alloca(sizeof(NETLIBHTTPHEADER)*nlhr.headersCount);
+ nlhr.headers[0].szName = "User-Agent";
+ nlhr.headers[0].szValue = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
+ nlhr.headers[1].szName = "Connection";
+ nlhr.headers[1].szValue = "close";
+
+ while (result == 0xBADBAD)
+ {
+ // download the page
+ NETLIBHTTPREQUEST *nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION,
+ (WPARAM)hNetlibUser,(LPARAM)&nlhr);
+
+ if (nlhrReply)
+ {
+ hHttpDwnl = nlhrReply->nlc;
+ // if the recieved code is 200 OK
+ if(nlhrReply->resultCode == 200)
+ {
+ char* delim = strrchr(szDest, '\\');
+ if (delim) *delim = '\0';
+ CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)szDest);
+ if (delim) *delim = '\\';
+ int res = -1;
+ int fh = _open(szDest, _O_BINARY | _O_WRONLY | _O_CREAT, _S_IREAD | _S_IWRITE);
+ if (fh != -1)
+ {
+ res = _write(fh, nlhrReply->pData, nlhrReply->dataLength);
+ _close(fh);
+ }
+ if (res < 0)
+ remove(szDest);
+ else
+ result = 0;
+ }
+ // if the recieved code is 302 Moved, Found, etc
+ // workaround for url forwarding
+ else if(nlhrReply->resultCode == 302 || nlhrReply->resultCode == 301 || nlhrReply->resultCode == 307) // page moved
+ {
+ // get the url for the new location and save it to szInfo
+ // look for the reply header "Location"
+ for (int i=0; i<nlhrReply->headersCount; i++)
+ {
+ if (!strcmp(nlhrReply->headers[i].szName, "Location"))
+ {
+ size_t rlen = 0;
+ if (nlhrReply->headers[i].szValue[0] == '/')
+ {
+ const char* szPath;
+ const char* szPref = strstr(szUrl, "://");
+ szPref = szPref ? szPref + 3 : szUrl;
+ szPath = strchr(szPref, '/');
+ rlen = szPath != NULL ? szPath - szUrl : strlen(szUrl);
+ }
+
+ szRedirUrl = (char*)mir_realloc(szRedirUrl,
+ rlen + strlen(nlhrReply->headers[i].szValue)*3 + 1);
+
+ strncpy(szRedirUrl, szUrl, rlen);
+ strcpy(szRedirUrl+rlen, nlhrReply->headers[i].szValue);
+
+ nlhr.szUrl = szRedirUrl;
+ break;
+ }
+ }
+ }
+ else
+ result = 1;
+ }
+ else
+ {
+ hHttpDwnl = NULL;
+ result = 1;
+ }
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)nlhrReply);
+ }
+
+ mir_free(szRedirUrl);
+
+ return result == 0;
+}
+
+
+void __cdecl SmileyDownloadThread(void*)
+{
+ bool needext = false;
+ HANDLE hHttpDwnl = NULL;
+ WaitForSingleObject(g_hDlMutex, 3000);
+ while (!Miranda_Terminated() && dlQueue.getCount())
+ {
+ ReleaseMutex(g_hDlMutex);
+ if (_taccess(dlQueue[0].fname.c_str(), 0) != 0)
+ {
+ InternetDownloadFile(T2A_SM(dlQueue[0].url.c_str()), T2A_SM(dlQueue[0].fname.c_str()), hHttpDwnl);
+ WaitForSingleObject(g_hDlMutex, 3000);
+
+ bkstring fname(dlQueue[0].fname);
+ if (dlQueue[0].needext) { fname += GetImageExt(fname); needext = true; }
+ _trename(dlQueue[0].fname.c_str(), fname.c_str());
+ }
+ else
+ WaitForSingleObject(g_hDlMutex, 3000);
+
+ dlQueue.remove(0);
+ }
+ dlQueue.destroy();
+ if (hHttpDwnl) Netlib_CloseHandle(hHttpDwnl);
+ threadRunning = false;
+ ReleaseMutex(g_hDlMutex);
+
+ if (!Miranda_Terminated())
+ {
+ if (needext)
+ CallServiceSync(MS_SMILEYADD_RELOAD, 0, 0);
+ else
+ NotifyEventHooks(hEvent1, 0, 0);
+ }
+}
+
+
+bool GetSmileyFile(bkstring& url, const bkstring& packstr)
+{
+ _TPattern * urlsplit = _TPattern::compile(_T(".*/(.*)"));
+ _TMatcher * m0 = urlsplit->createTMatcher(url);
+
+ m0->findFirstMatch();
+
+ bkstring filename;
+ filename.appendfmt(_T("%s\\%s\\"), cachepath, packstr.c_str());
+ size_t pathpos = filename.size();
+ filename += m0->getGroup(1);
+
+ delete m0;
+ delete urlsplit;
+
+ bool needext = filename.find('.') == filename.npos;
+ if (needext) filename += _T(".*");
+
+ _tfinddata_t c_file;
+ INT_PTR hFile = _tfindfirst((TCHAR*)filename.c_str(), &c_file);
+ if (hFile > -1)
+ {
+ _findclose(hFile);
+ filename.erase(pathpos);
+ filename += c_file.name;
+ url = filename;
+ return false;
+ }
+ if (needext) filename.erase(filename.size()-1);
+
+ WaitForSingleObject(g_hDlMutex, 3000);
+ dlQueue.insert(new QueueElem(url, filename, needext));
+ ReleaseMutex(g_hDlMutex);
+
+ if (!threadRunning)
+ {
+ threadRunning = true;
+ mir_forkthread(SmileyDownloadThread, NULL);
+ }
+
+ url = filename;
+ return false;
+}
+
+void GetDefaultSmileyCacheFolder(TCHAR* szPath, size_t cbLen)
+{
+ TCHAR *tmpPath = Utils_ReplaceVarsT(_T("%miranda_userdata%\\SmileyCache"));
+ if ((INT_PTR)tmpPath != CALLSERVICE_NOTFOUND)
+ {
+ mir_sntprintf(szPath, cbLen, _T("%s"), tmpPath);
+ mir_free(tmpPath);
+ }
+ else
+ {
+ char dbPath[MAX_PATH];
+ CallService(MS_DB_GETPROFILEPATH, SIZEOF(dbPath), (LPARAM)dbPath);
+ mir_sntprintf(szPath, cbLen, _T("%s\\SmileyCache"), A2T_SM(dbPath));
+ }
+}
+
+int FolderChanged(WPARAM, LPARAM)
+{
+ FOLDERSGETDATA fgd = {0};
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = SIZEOF(cachepath);
+ fgd.szPathT = cachepath;
+ if (CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolder, (LPARAM) &fgd))
+ {
+ GetDefaultSmileyCacheFolder(cachepath, SIZEOF(cachepath));
+ }
+
+ return 0;
+}
+
+void GetSmileyCacheFolder(void)
+{
+ TCHAR defaultPath[MAX_PATH];
+
+ GetDefaultSmileyCacheFolder(defaultPath, SIZEOF(defaultPath));
+
+ FOLDERSDATA fd = {0};
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strcpy(fd.szSection, "SmileyAdd");
+ strcpy(fd.szName,"Smiley Cache");
+ fd.szFormatT = defaultPath;
+ fd.flags = FF_TCHAR;
+ hFolder = (HANDLE)CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+
+ FolderChanged(0, 0);
+
+ hFolderHook = HookEvent(ME_FOLDERS_PATH_CHANGED, FolderChanged);
+}
+
+void DownloadInit(void)
+{
+ NETLIBUSER nlu = {0};
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = NUF_OUTGOING|NUF_HTTPCONNS|NUF_NOHTTPSOPTION;
+ nlu.szSettingsModule = "SmileyAdd";
+ nlu.szDescriptiveName = Translate("SmileyAdd HTTP connections");
+ hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+
+ GetSmileyCacheFolder();
+ g_hDlMutex = CreateMutex(NULL, FALSE, NULL);
+}
+
+void DownloadClose(void)
+{
+ UnhookEvent(hFolderHook);
+ CloseHandle(g_hDlMutex);
+ Netlib_CloseHandle(hNetlibUser);
+}
diff --git a/plugins/SmileyAdd/src/download.h b/plugins/SmileyAdd/src/download.h
new file mode 100644
index 0000000000..bb83926acc
--- /dev/null
+++ b/plugins/SmileyAdd/src/download.h
@@ -0,0 +1,28 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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/>.
+*/
+
+#ifndef SMILEYADD_DOWNLOAD_H_
+#define SMILEYADD_DOWNLOAD_H_
+
+#include "general.h"
+
+bool GetSmileyFile(bkstring& url, const bkstring& packstr);
+void DownloadInit(void);
+void DownloadClose(void);
+
+#endif
diff --git a/plugins/SmileyAdd/src/general.cpp b/plugins/SmileyAdd/src/general.cpp
new file mode 100644
index 0000000000..9e97737f8c
--- /dev/null
+++ b/plugins/SmileyAdd/src/general.cpp
@@ -0,0 +1,299 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2012 Boris Krasnovskiy All Rights Reserved
+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 "m_metacontacts.h"
+
+#ifdef _MSC_VER
+#include <delayimp.h>
+#endif
+
+/*
+#include "m_popup.h"
+*/
+static ULONG_PTR g_gdiplusToken = 0;
+static bool gdiPlusFail = false;
+
+//
+// General functions
+//
+int CalculateTextHeight(HDC hdc, CHARFORMAT2* chf)
+{
+ HDC hcdc = CreateCompatibleDC(hdc);
+
+ int logPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
+ HFONT hFont = CreateFont(-(chf->yHeight * logPixelsY / 1440), 0, 0, 0,
+ chf->wWeight, chf->dwEffects & CFE_ITALIC ? 1 : 0, 0, 0,
+ chf->bCharSet, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
+ DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
+ chf->szFaceName);
+ SelectObject(hcdc, hFont);
+
+ SIZE fontSize;
+ GetTextExtentPoint32(hcdc, _T(")"), 1, &fontSize);
+
+ DeleteObject(hFont);
+ DeleteDC(hcdc);
+
+ return fontSize.cy;
+}
+
+
+HICON GetDefaultIcon(bool copy)
+{
+ HICON resIco = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"SmileyAdd_ButtonSmiley");
+ if ( resIco == NULL || resIco == (HICON)CALLSERVICE_NOTFOUND )
+ {
+ resIco = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SMILINGICON),
+ IMAGE_ICON, 0, 0, copy ? 0 : LR_SHARED);
+ }
+ else
+ {
+ if (copy)
+ {
+ resIco = (HICON)CopyImage(resIco, IMAGE_ICON, 0, 0, 0);
+ CallService(MS_SKIN2_RELEASEICON, 0, (LPARAM)"SmileyAdd_ButtonSmiley");
+ }
+ }
+
+ return resIco;
+}
+
+
+const TCHAR* GetImageExt(bkstring &fname)
+{
+ const TCHAR* ext = _T("");
+
+ int fileId = _topen(fname.c_str(), O_RDONLY | _O_BINARY);
+ if (fileId != -1)
+ {
+ BYTE buf[6];
+
+ int bytes = _read(fileId, buf, sizeof(buf));
+ if (bytes > 4)
+ {
+ if ( *(unsigned short*)buf == 0xd8ff )
+ ext = _T("jpg");
+ else if ( *(unsigned short*)buf == 0x4d42 )
+ ext = _T("bmp");
+ else if ( *(unsigned*)buf == 0x474e5089 )
+ ext = _T("png");
+ else if ( *(unsigned*)buf == 0x38464947 )
+ ext = _T("gif");
+ }
+ _close(fileId);
+ }
+ return ext;
+}
+
+
+
+HICON ImageList_GetIconFixed (HIMAGELIST himl, INT i, UINT fStyle)
+{
+ ICONINFO ii;
+ HICON hIcon;
+ HBITMAP hOldDstBitmap;
+ HDC hdcDst;
+
+ int cx, cy;
+ ImageList_GetIconSize(himl, &cx, &cy);
+
+ hdcDst = CreateCompatibleDC(NULL);
+
+ ii.fIcon = TRUE;
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+
+ /* draw mask*/
+ ii.hbmMask = CreateBitmap (cx, cy, 1, 1, NULL);
+ hOldDstBitmap = (HBITMAP)SelectObject (hdcDst, ii.hbmMask);
+ PatBlt(hdcDst, 0, 0, cx, cy, WHITENESS);
+ ImageList_Draw(himl, i, hdcDst, 0, 0, fStyle | ILD_MASK);
+
+ /* draw image*/
+ ii.hbmColor = CreateBitmap (cx, cy, 1, 32, NULL);
+ SelectObject (hdcDst, ii.hbmColor);
+ PatBlt (hdcDst, 0, 0, cx, cy, BLACKNESS);
+ ImageList_Draw(himl, i, hdcDst, 0, 0, fStyle | ILD_TRANSPARENT);
+
+ /*
+ * CreateIconIndirect requires us to deselect the bitmaps from
+ * the DCs before calling
+ */
+ SelectObject(hdcDst, hOldDstBitmap);
+
+ hIcon = CreateIconIndirect (&ii);
+
+ DeleteObject (ii.hbmMask);
+ DeleteObject (ii.hbmColor);
+ DeleteDC (hdcDst);
+
+ return hIcon;
+}
+
+void pathToRelative(const bkstring& pSrc, bkstring& pOut)
+{
+ TCHAR szOutPath[MAX_PATH];
+ CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)pSrc.c_str(), (LPARAM)szOutPath);
+ pOut = szOutPath;
+}
+
+void pathToAbsolute(const bkstring& pSrc, bkstring& pOut)
+{
+ TCHAR szOutPath[MAX_PATH];
+
+ TCHAR* szVarPath = Utils_ReplaceVarsT(pSrc.c_str());
+ if (szVarPath == (TCHAR*)CALLSERVICE_NOTFOUND || szVarPath == NULL)
+ {
+ TCHAR szExpPath[MAX_PATH];
+ ExpandEnvironmentStrings(pSrc.c_str(), szExpPath, SIZEOF(szExpPath));
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szExpPath, (LPARAM)szOutPath);
+ }
+ else
+ {
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szVarPath, (LPARAM)szOutPath);
+ mir_free(szVarPath);
+ }
+ pOut = szOutPath;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// UrlDecode - converts URL chars like %20 into printable characters
+
+static int __fastcall SingleHexToDecimal(char c)
+{
+ if (c >= '0' && c <= '9') return c-'0';
+ if (c >= 'a' && c <= 'f') return c-'a'+10;
+ if (c >= 'A' && c <= 'F') return c-'A'+10;
+ return -1;
+}
+
+void UrlDecode(char* str)
+{
+ char* s = str, *d = str;
+
+ while(*s)
+ {
+ if (*s == '%')
+ {
+ int digit1 = SingleHexToDecimal(s[1]);
+ if ( digit1 != -1 )
+ {
+ int digit2 = SingleHexToDecimal(s[2]);
+ if ( digit2 != -1 )
+ {
+ s += 3;
+ *d++ = (char)((digit1 << 4) | digit2);
+ continue;
+ }
+ }
+ }
+ *d++ = *s++;
+ }
+
+ *d = 0;
+}
+
+
+bool InitGdiPlus(void)
+{
+ Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+
+ static const TCHAR errmsg[] = _T("GDI+ not installed.\n")
+ _T("GDI+ can be downloaded here: http://www.microsoft.com/downloads");
+
+#ifdef _MSC_VER
+ __try
+#endif
+ {
+ if (g_gdiplusToken == 0 && !gdiPlusFail)
+ {
+ Gdiplus::GdiplusStartup(&g_gdiplusToken, &gdiplusStartupInput, NULL);
+ }
+ }
+#ifdef _MSC_VER
+ __except ( EXCEPTION_EXECUTE_HANDLER )
+ {
+ gdiPlusFail = true;
+ ReportError(errmsg);
+ }
+#endif
+
+ return !gdiPlusFail;
+}
+
+void DestroyGdiPlus(void)
+{
+ if (g_gdiplusToken != 0)
+ {
+ Gdiplus::GdiplusShutdown(g_gdiplusToken);
+#ifdef _MSC_VER
+#if 1200 < _MSC_VER
+ __FUnloadDelayLoadedDLL2("gdiplus.dll");
+#else
+ __FUnloadDelayLoadedDLL("gdiplus.dll");
+#endif
+#endif
+ g_gdiplusToken = 0;
+ }
+}
+
+HANDLE DecodeMetaContact(HANDLE hContact)
+{
+ if (hContact == NULL) return NULL;
+ HANDLE hReal = (HANDLE) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) hContact, 0);
+ if (hReal == NULL || hReal == (HANDLE)CALLSERVICE_NOTFOUND)
+ hReal = hContact;
+
+ return hReal;
+}
+
+bool IsSmileyProto(char* proto)
+{
+ return proto && (!metaProtoName || strcmp(proto, metaProtoName)) &&
+ (CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & (PF1_IM | PF1_CHAT));
+}
+
+void ReportError(const TCHAR* errmsg)
+{
+ static const TCHAR title[] = _T("Miranda SmileyAdd");
+/*
+
+ POPUPDATAW pd = {0};
+
+
+ _tcscpy(pd.lpwzContactName, title);
+ _tcscpy(pd.lpwzText, errmsg);
+
+ pd.iSeconds = -1;
+
+
+ bool popupFail = PUAddPopUpW(&pd) != CALLSERVICE_NOTFOUND;
+
+ if (popupFail)
+*/
+ MessageBox(NULL, errmsg, title, MB_OK | MB_ICONWARNING | MB_TOPMOST);
+}
+
+#pragma warning( disable : 4786 )
+#undef _MT
+
+#include <wcpattern.cpp>
+#include <wcmatcher.cpp>
diff --git a/plugins/SmileyAdd/src/general.h b/plugins/SmileyAdd/src/general.h
new file mode 100644
index 0000000000..8cc13c5619
--- /dev/null
+++ b/plugins/SmileyAdd/src/general.h
@@ -0,0 +1,232 @@
+/*
+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/>.
+*/
+
+#ifndef _GENERAL_
+#define _GENERAL_
+
+#define _HAS_EXCEPTIONS 0
+#define _SECURE_SCL 0
+#define _SECURE_SCL_THROWS 0
+#define __STDC_WANT_SECURE_LIB__ 0
+#define _STRALIGN_USE_SECURE_CRT 0
+#define _NO_EXCEPTIONS
+
+#include <m_stdhdr.h>
+
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <windows.h>
+#include <gdiplus.h>
+
+#include <commctrl.h>
+#include <richedit.h>
+
+#include "bkstring.h"
+
+#undef _MT
+
+#pragma warning( push )
+#pragma warning( disable : 4530 )
+#pragma warning( disable : 4275 )
+#pragma warning( disable : 4390 )
+
+#include <wcpattern.h>
+#include <wcmatcher.h>
+
+typedef WCPattern _TPattern;
+typedef WCMatcher _TMatcher;
+#define createTMatcher createWCMatcher
+
+#pragma warning( pop )
+
+#define _MT
+
+#include "resource.h"
+
+#pragma warning( push )
+#pragma warning( disable : 4100 )
+#define MIRANDA_VER 0x0A00
+#define NETLIB_NOLOGGING
+#define MIRANDA_CUSTOM_LP
+
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_options.h>
+#include <m_protosvc.h>
+#include <m_database.h>
+#include <m_button.h>
+#include <m_message.h>
+#include <m_clist.h>
+#include <m_clui.h>
+#include <m_netlib.h>
+#include <m_icolib.h>
+#include <m_genmenu.h>
+#include <m_imgsrvc.h>
+#pragma warning( pop )
+
+//globals, defined int main.cpp
+extern HINSTANCE g_hInst;
+extern char* metaProtoName;
+
+//some system and NT stuff...
+#ifndef OPENFILENAME_SIZE_VERSION_400
+#define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAME)
+#endif
+
+
+#define IDC_SMLBUTTON (WM_USER + 33)
+
+///////////////////////////////////////////////////
+//
+//defines from miranda im sources (undocumented!)
+//
+#define MI_IDC_LOG 1001 //rich edit
+#define MI_IDC_MESSAGE 1002 //edit control
+#define MI_IDC_QUOTE 1034 //button control
+#define MI_IDC_NAME 1009 //text control
+#define MI_IDC_ADD 1070 //Add button.
+
+#define DM_REMAKELOG (WM_USER + 11)
+#define DM_OPTIONSAPPLIED (WM_USER + 14)
+#define DM_APPENDTOLOG (WM_USER + 17)
+
+#define WM_REMAKERICH (WM_USER + 0x3457)
+
+/////////////////////////////////////////////////////
+
+#define MAX_SMILEY_LENGTH 96
+
+class A2W_SM
+{
+public:
+ wchar_t* m_psz;
+
+ A2W_SM(const char* psz, unsigned nCodePage = CP_ACP)
+ {
+ const int nLength = MultiByteToWideChar(nCodePage, 0, psz, -1, NULL, 0);
+ m_psz = new wchar_t[nLength];
+ MultiByteToWideChar(nCodePage, 0, psz, -1, m_psz, nLength);
+ }
+ ~A2W_SM() { delete [] m_psz; }
+ operator wchar_t*() const { return m_psz; }
+};
+
+
+class W2A_SM
+{
+public:
+ char* m_psz;
+
+ W2A_SM(const wchar_t* psz, unsigned nCodePage = CP_ACP)
+ {
+ const int nLength = WideCharToMultiByte(nCodePage, 0, psz, -1, NULL, 0, NULL, NULL);
+ m_psz = new char[nLength];
+ WideCharToMultiByte(nCodePage, 0, psz, -1, m_psz, nLength, NULL, NULL);
+ }
+ ~W2A_SM() { delete [] m_psz; }
+ operator char*() const { return m_psz; }
+};
+
+#define T2A_SM (char*)W2A_SM
+#define T2W_SM(p1) (wchar_t*)p1
+#define A2T_SM (wchar_t*)A2W_SM
+#define W2T_SM(p1) (TCHAR*)p1
+
+template<class T> struct SMOBJLIST : public OBJLIST<T>
+{
+ SMOBJLIST() : OBJLIST<T>(5) {};
+
+ SMOBJLIST<T>& operator = (const SMOBJLIST<T>& lst)
+ {
+ OBJLIST<T>::destroy();
+ return operator += (lst);
+ }
+
+ SMOBJLIST<T>& operator += (const SMOBJLIST<T>& lst)
+ {
+ for (int i=0; i<lst.getCount(); ++i)
+ {
+ T *p = new T(lst[i]);
+ insert(p);
+ }
+ return *this;
+ }
+
+ void splice(SMOBJLIST<T>& lst)
+ {
+ for (int i=0; i<lst.getCount(); ++i)
+ insert(&lst[i]);
+ lst.LIST<T>::destroy();
+ }
+};
+
+inline unsigned short GetWinVer(void)
+{
+ unsigned short ver = LOWORD(GetVersion());
+ ver = (ver & 0xFF) << 8 | (ver >> 8);
+ return ver;
+}
+
+
+// init functions
+void InstallDialogBoxHook(void);
+void RemoveDialogBoxHook(void);
+int UpdateSrmmDlg(WPARAM wParam, LPARAM lParam);
+bool IsOldSrmm(void);
+
+//functions for general use (defined in general.cpp)
+int CalculateTextHeight(HDC hdc, CHARFORMAT2* chf);
+const TCHAR* GetImageExt(bkstring &fname);
+
+HANDLE DecodeMetaContact(HANDLE hContact);
+bool IsSmileyProto(char* proto);
+
+HICON ImageList_GetIconFixed (HIMAGELIST himl, INT i, UINT fStyle);
+
+void pathToRelative(const bkstring& pSrc, bkstring& pOut);
+void pathToAbsolute(const bkstring& pSrc, bkstring& pOut);
+
+bool InitGdiPlus(void);
+void DestroyGdiPlus(void);
+
+void ReportError(const TCHAR* errmsg);
+HICON GetDefaultIcon(bool copy = true);
+
+void CloseRichCallback(HWND hwnd, bool force);
+void CloseRichOwnerCallback(HWND hwnd, bool force);
+bool SetRichCallback(HWND hwnd, HANDLE hContact, bool subany, bool subnew);
+void SetRichOwnerCallback(HWND hwnd, HWND hwndInput, HWND hwndLog);
+void ProcessAllInputAreas(bool restoreText);
+void RichEditData_Destroy(void);
+
+void CloseSmileys(void);
+void DestroySmileyBase(void);
+void DestroyAniSmileys(void);
+
+void UrlDecode(char* str);
+
+#endif
diff --git a/plugins/SmileyAdd/src/imagecache.cpp b/plugins/SmileyAdd/src/imagecache.cpp
new file mode 100644
index 0000000000..8fdcea9003
--- /dev/null
+++ b/plugins/SmileyAdd/src/imagecache.cpp
@@ -0,0 +1,775 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy
+
+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 "imagecache.h"
+#include "options.h"
+
+typedef BOOL (WINAPI *tAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
+static tAlphaBlend pAlphaBlend;
+
+static FI_INTERFACE *fei;
+
+static HANDLE g_hMutexIm;
+static OBJLIST<ImageBase> g_imagecache(25, ImageType::CompareImg);
+
+static bkstring lastdllname;
+static HMODULE lastmodule;
+static time_t laststamp;
+static UINT_PTR timerId;
+
+static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+ const time_t ts = time(NULL) - 10;
+ if ( lastmodule && ts > laststamp)
+ {
+ FreeLibrary(lastmodule);
+ lastmodule = NULL;
+ lastdllname.clear();
+ }
+
+ for (int i=g_imagecache.getCount(); i--; )
+ g_imagecache[i].ProcessTimerTick(ts);
+
+ if (g_imagecache.getCount() == 0)
+ {
+ g_imagecache.destroy();
+ if (timerId && (timerId+1) && lastmodule == NULL)
+ {
+ KillTimer(NULL, timerId);
+ timerId = 0;
+ }
+ }
+
+ ReleaseMutex(g_hMutexIm);
+}
+
+
+static void CALLBACK sttMainThreadCallback( PVOID )
+{
+ if (timerId == 0xffffffff)
+ timerId = SetTimer(NULL, 0, 10000, (TIMERPROC)timerProc);
+}
+
+
+static HMODULE LoadDll(const bkstring& file)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+
+ if (lastdllname != file)
+ {
+ FreeLibrary(lastmodule);
+ lastdllname = file;
+
+ lastmodule = LoadLibraryEx(file.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE);
+
+ }
+ laststamp = time(NULL);
+
+ ReleaseMutex(g_hMutexIm);
+ return lastmodule;
+}
+
+
+
+ImageBase::ImageBase(unsigned id)
+{
+ m_id = id;
+ m_lRefCount = 1;
+ m_timestamp = 0;
+}
+
+long ImageBase::AddRef(void)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+ long cnt = ++m_lRefCount;
+ ReleaseMutex(g_hMutexIm);
+ return cnt;
+}
+
+long ImageBase::Release(void)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+
+ long cnt = m_lRefCount;
+ if (cnt) m_lRefCount = --cnt;
+ if (cnt == 0) m_timestamp = time(NULL);
+
+ ReleaseMutex(g_hMutexIm);
+ return cnt;
+}
+
+void ImageBase::ProcessTimerTick(time_t ts)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+ if (m_lRefCount == 0 && m_timestamp < ts )
+ {
+ if (!g_imagecache.remove(this))
+ delete this;
+ }
+ ReleaseMutex(g_hMutexIm);
+}
+
+int ImageBase::CompareImg(const ImageBase* p1, const ImageBase* p2)
+{
+ unsigned id1 = p1->m_id;
+ unsigned id2 = p2->m_id;
+
+ if (id1 == id2) return 0;
+ else return id1 < id2 ? -1 : 1;
+}
+
+void ImageBase::Draw(HDC hdc, RECT& rc, bool clip)
+{
+ HRGN hrgn = NULL;
+ if (clip)
+ {
+ hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
+ SelectClipRgn(hdc, hrgn);
+ }
+
+ SIZE iSize;
+ GetSize(iSize);
+
+ const int sizeX = rc.right - rc.left;
+ const int sizeY = rc.bottom - rc.top;
+
+ const int x = rc.left + (sizeX > iSize.cx || clip ? (sizeX - iSize.cx) / 2 : 0);
+ const int y = rc.top + (sizeY > iSize.cy || clip ? (sizeY - iSize.cy) / 2 : 0);
+
+ const int scaleX = sizeX > iSize.cx || clip ? iSize.cx : sizeX;
+ const int scaleY = sizeY > iSize.cy || clip ? iSize.cy : sizeY;
+
+ DrawInternal(hdc, x, y, scaleX, scaleY);
+
+ if (clip)
+ {
+ SelectClipRgn(hdc, NULL);
+ DeleteObject(hrgn);
+ }
+}
+
+
+HBITMAP ImageBase::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
+{
+ RECT rc = { 0, 0, sizeX, sizeY };
+
+ if (sizeX == 0 || sizeY == 0)
+ {
+ SIZE iSize;
+ GetSize(iSize);
+
+ if (sizeX == 0) rc.right = iSize.cx;
+ if (sizeY == 0) rc.bottom = iSize.cy;
+ }
+
+ HBRUSH hBkgBrush = CreateSolidBrush(bkgClr);
+ HDC hdc = GetDC(NULL);
+ HBITMAP hBmp = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ SelectObject(hdcMem, hBmp);
+
+ FillRect(hdcMem, &rc, hBkgBrush);
+
+ Draw(hdcMem, rc, false);
+
+ DeleteDC(hdcMem);
+ ReleaseDC(NULL, hdc);
+ DeleteObject(hBkgBrush);
+
+ return hBmp;
+}
+
+int ImageBase::SelectNextFrame(const int frame)
+{
+ int res = (frame + 1) % GetFrameCount();
+ SelectFrame(res);
+ return res;
+}
+
+
+
+IconType::IconType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type)
+ : ImageBase(id)
+{
+ m_SmileyIcon = NULL;
+
+ switch (type)
+ {
+ case icoDll:
+ {
+ const HMODULE hModule = LoadDll(file);
+ if (hModule != NULL)
+ m_SmileyIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0);
+ }
+ break;
+
+ case icoFile:
+ m_SmileyIcon = (HICON) LoadImage(NULL, file.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
+ break;
+
+ default:
+ ExtractIconEx(file.c_str(), index, NULL, &m_SmileyIcon, 1);
+ break;
+ }
+}
+
+IconType::~IconType()
+{
+ DestroyIcon(m_SmileyIcon);
+}
+
+void IconType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY)
+{
+ if (m_SmileyIcon != NULL)
+ DrawIconEx(hdc, x, y, m_SmileyIcon, sizeX, sizeY, 0, NULL, DI_NORMAL);
+}
+
+HICON IconType::GetIcon(void)
+{
+ return (HICON)CopyImage(m_SmileyIcon, IMAGE_ICON, 0, 0, 0);
+}
+
+void IconType::GetSize(SIZE& size)
+{
+ if (m_SmileyIcon != NULL)
+ {
+ ICONINFO ii;
+ BITMAP bm;
+ GetIconInfo(m_SmileyIcon, &ii);
+ GetObject(ii.hbmColor, sizeof(bm), &bm);
+ size.cx = bm.bmWidth;
+ size.cy = bm.bmHeight;
+ DeleteObject(ii.hbmMask);
+ DeleteObject(ii.hbmColor);
+ }
+}
+
+
+
+ImageListItemType::ImageListItemType(const unsigned id, HIMAGELIST hImList, int index)
+ : ImageBase(id)
+{
+ m_index = index;
+ m_hImList = hImList;
+}
+
+void ImageListItemType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY)
+{
+ SIZE iSize;
+ GetSize(iSize);
+
+ if (sizeX >= iSize.cx && sizeY >= iSize.cy)
+ ImageList_Draw(m_hImList, m_index, hdc, x, y, ILD_TRANSPARENT);
+ else
+ {
+ HICON hIcon = ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT);
+ DrawIconEx(hdc, x, y, hIcon, sizeX, sizeY, 0, NULL, DI_NORMAL);
+ DestroyIcon(hIcon);
+ }
+}
+
+HICON ImageListItemType::GetIcon(void)
+{
+ return ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT);
+}
+
+void ImageListItemType::GetSize(SIZE& size)
+{
+ ImageList_GetIconSize(m_hImList, (int*)&size.cx, (int*)&size.cy);
+}
+
+ImageType::ImageType(const unsigned id, const bkstring& file, IStream* pStream)
+ : ImageBase(id)
+{
+ m_bmp = NULL;
+ m_pPropertyItem = NULL;
+ m_nCurrentFrame = 0;
+ m_nFrameCount = 0;
+
+ if (!InitGdiPlus()) return;
+
+ if (pStream)
+ m_bmp = new Gdiplus::Bitmap(pStream);
+ else
+ m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str()));
+
+ if (m_bmp->GetLastStatus() != Gdiplus::Ok)
+ {
+ delete m_bmp;
+ m_bmp = NULL;
+ return;
+ }
+
+ GUID pageGuid = Gdiplus::FrameDimensionTime;
+ m_nFrameCount = m_bmp->GetFrameCount(&pageGuid);
+
+ if (IsAnimated())
+ {
+ int nSize = m_bmp->GetPropertyItemSize(PropertyTagFrameDelay);
+ m_pPropertyItem = (Gdiplus::PropertyItem*) new char[nSize];
+ m_bmp->GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
+ }
+}
+
+ImageType::ImageType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type)
+ : ImageBase(id)
+{
+ m_bmp = NULL;
+ m_pPropertyItem = NULL;
+ m_nCurrentFrame = 0;
+ m_nFrameCount = 0;
+
+ if (!InitGdiPlus()) return;
+
+ switch (type)
+ {
+ case icoDll:
+ {
+ const HMODULE hModule = LoadDll(file);
+ if (hModule != NULL)
+ {
+ HICON hIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0);
+ m_bmp = new Gdiplus::Bitmap(hIcon);
+ DestroyIcon(hIcon);
+ }
+ }
+ break;
+
+ case icoFile:
+ m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str()));
+ break;
+
+ default:
+ HICON hIcon = NULL;
+ ExtractIconEx(file.c_str(), index, NULL, &hIcon, 1);
+ m_bmp = new Gdiplus::Bitmap(hIcon);
+ DestroyIcon(hIcon);
+ break;
+ }
+
+ if (m_bmp->GetLastStatus() != Gdiplus::Ok)
+ {
+ delete m_bmp;
+ m_bmp = NULL;
+ return;
+ }
+}
+
+
+ImageType::~ImageType(void)
+{
+ if (m_pPropertyItem) delete[] m_pPropertyItem;
+ if (m_bmp) delete m_bmp;
+}
+
+void ImageType::SelectFrame(int frame)
+{
+ if ((unsigned)frame >= (unsigned)m_nFrameCount) frame = 0;
+ if (IsAnimated() && frame != m_nCurrentFrame)
+ {
+ m_nCurrentFrame = frame;
+ GUID pageGuid = Gdiplus::FrameDimensionTime;
+ m_bmp->SelectActiveFrame(&pageGuid, frame);
+ }
+}
+
+
+void ImageType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY)
+{
+ if (m_bmp == NULL) return;
+
+ WaitForSingleObject(g_hMutexIm, 3000);
+ {
+ Gdiplus::Graphics grp(hdc);
+// if (opt.HQScaling) grp.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
+ grp.DrawImage(m_bmp, x, y, sizeX, sizeY);
+ }
+ ReleaseMutex(g_hMutexIm);
+}
+
+int ImageType::GetFrameDelay(void) const
+{
+ return ((long*) m_pPropertyItem->value)[m_nCurrentFrame];
+}
+
+HICON ImageType::GetIcon(void)
+{
+ if (m_bmp == NULL) return NULL;
+
+ HICON hIcon = NULL;
+ WaitForSingleObject(g_hMutexIm, 3000);
+
+ m_bmp->GetHICON(&hIcon);
+
+ ReleaseMutex(g_hMutexIm);
+ return hIcon;
+}
+
+
+void ImageType::GetSize(SIZE& size)
+{
+ if (m_bmp)
+ {
+ size.cx = m_bmp->GetWidth();
+ size.cy = m_bmp->GetHeight();
+ }
+ else
+ {
+ size.cx = 0;
+ size.cy = 0;
+ }
+}
+
+
+ImageFType::ImageFType(const unsigned id)
+ : ImageBase(id)
+{
+ m_bmp = NULL;
+}
+
+ImageFType::ImageFType(const unsigned id, const bkstring& file)
+ : ImageBase(id)
+{
+ m_bmp = NULL;
+
+ FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0);
+ if (fif == FIF_UNKNOWN)
+ fif = fei->FI_GetFIFFromFilenameT(file.c_str());
+ if (fif == FIF_UNKNOWN) return;
+
+ FIBITMAP *dib = fei->FI_LoadT(fif, file.c_str(), 0);
+ if (dib == NULL) return;
+
+ bool transp = fei->FI_IsTransparent(dib) != 0;
+ FREE_IMAGE_TYPE imt = fei->FI_GetImageType(dib);
+ unsigned bpp = fei->FI_GetBPP(dib);
+
+ if (transp && bpp != 32 || imt == FIT_RGBA16)
+ {
+ FIBITMAP *tdib = fei->FI_ConvertTo32Bits(dib);
+ fei->FI_Unload(dib);
+ dib = tdib;
+ }
+ else if (!transp && bpp > 24)
+ {
+ FIBITMAP *tdib = fei->FI_ConvertTo24Bits(dib);
+ fei->FI_Unload(dib);
+ dib = tdib;
+ }
+
+ m_bmp = fei->FI_CreateHBITMAPFromDIB(dib);
+ fei->FI_Unload(dib);
+
+ if (transp)
+ fei->FI_Premultiply(m_bmp);
+}
+
+ImageFType::~ImageFType()
+{
+ DeleteObject(m_bmp);
+}
+
+void ImageFType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY)
+{
+ if (m_bmp == NULL) return;
+
+ HDC hdcImg = CreateCompatibleDC(hdc);
+ HBITMAP oldBmp = (HBITMAP) SelectObject(hdcImg, m_bmp);
+
+ BITMAP bm;
+ GetObject(m_bmp, sizeof(bm), &bm);
+
+ if (bm.bmBitsPixel == 32 && pAlphaBlend)
+ {
+ BLENDFUNCTION bf = {0};
+ bf.SourceConstantAlpha = 255;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ pAlphaBlend(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, bm.bmWidth, bm.bmHeight, bf);
+ }
+ else
+ {
+ BitBlt(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, SRCCOPY);
+ }
+
+ SelectObject(hdcImg, oldBmp);
+ DeleteDC(hdcImg);
+}
+
+HICON ImageFType::GetIcon(void)
+{
+ if (m_bmp == NULL) return NULL;
+
+ HICON hIcon;
+
+ BITMAP bm;
+ GetObject(m_bmp, sizeof(bm), &bm);
+
+ ICONINFO ii;
+ ii.fIcon = TRUE;
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+
+ if (bm.bmBitsPixel == 32 && GetWinVer() < 0x0501)
+ {
+ int slen = bm.bmWidth * 4;
+ int len = bm.bmHeight * slen;
+
+ BYTE* p = (BYTE*)mir_alloc(len);
+ BYTE* maskBits = (BYTE*)mir_calloc(len);
+ BYTE* colorBits = (BYTE*)mir_calloc(len);
+
+ GetBitmapBits(m_bmp, len, p);
+
+ for (int y = 0; y < bm.bmHeight; ++y)
+ {
+ int shift = y * slen;
+ BYTE *px = p + shift;
+ BYTE *color = colorBits + shift;
+ BYTE *mask = maskBits + shift;
+
+ for (int x = 0; x < bm.bmWidth; ++x)
+ {
+ for(int i = 0; i < 4; i++)
+ {
+ mask[i] = px[3];
+ color[i] = px[i];
+ }
+
+ px += 4;
+ mask += 4;
+ color += 4;
+ }
+ }
+
+ ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, maskBits);
+ ii.hbmColor = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, colorBits);
+
+ hIcon = CreateIconIndirect(&ii);
+
+ DeleteObject(ii.hbmMask);
+ DeleteObject(ii.hbmColor);
+
+ mir_free(p);
+ mir_free(maskBits);
+ mir_free(colorBits);
+ }
+ else
+ {
+ ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
+ ii.hbmColor = m_bmp;
+ hIcon = CreateIconIndirect(&ii);
+ DeleteObject(ii.hbmMask);
+ }
+ return hIcon;
+}
+
+void ImageFType::GetSize(SIZE& size)
+{
+ if (m_bmp)
+ {
+ BITMAP bm;
+ GetObject(m_bmp, sizeof(bm), &bm);
+ size.cx = bm.bmWidth;
+ size.cy = bm.bmHeight;
+ }
+ else
+ {
+ size.cx = 0;
+ size.cy = 0;
+ }
+}
+/*
+ImageFAniType::ImageFAniType(const unsigned id, const bkstring& file)
+: ImageFType(id)
+{
+ m_fmbmp = NULL;
+ m_nCurrentFrame = -1;
+ m_FrameDelay = NULL;
+
+ FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0);
+ if (fif == FIF_UNKNOWN)
+ fif = fei->FI_GetFIFFromFilenameT(file.c_str());
+
+ m_fmbmp = fei->FI_OpenMultiBitmap(fif, T2A_SM(file.c_str()), FALSE, TRUE, TRUE, GIF_PLAYBACK);
+ if (m_fmbmp == NULL) return;
+
+ m_nFrameCount = fei->FI_GetPageCount(m_fmbmp);
+ m_bmpl = (HBITMAP*)mir_calloc(m_nFrameCount*sizeof(HBITMAP));
+ m_FrameDelay = (int*)mir_calloc(m_nFrameCount*sizeof(int));
+ SelectFrame(0);
+}
+
+ImageFAniType::~ImageFAniType()
+{
+ if (m_fmbmp) fei->FI_CloseMultiBitmap(m_fmbmp, 0);
+ for (int i=0; i<m_nFrameCount; ++i)
+ if (m_bmp) DeleteObject(m_bmpl[i]);
+
+ mir_free(m_bmpl);
+ mir_free(m_FrameDelay);
+}
+
+void ImageFAniType::SelectFrame(int frame)
+{
+ if ((unsigned)frame >= (unsigned)m_nFrameCount) frame = 0;
+ if (frame == m_nCurrentFrame) return;
+ m_nCurrentFrame = frame;
+
+ if (m_bmpl[frame])
+ {
+ m_bmp = m_bmpl[frame];
+ return;
+ }
+
+ FITAG *tag = NULL;
+
+ FIBITMAP *dib = fei->FI_LockPage(m_fmbmp, frame);
+ if (dib == NULL)
+ return;
+
+ if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameTime", &tag))
+ m_FrameDelay[frame] = *(LONG *)fei->FI_GetTagValue(tag) / 10;
+
+ m_bmpl[frame] = m_bmp = fei->FI_CreateHBITMAPFromDIB(dib);
+
+ if (fei->FI_IsTransparent(dib))
+ fei->FI_Premultiply(m_bmp);
+
+ fei->FI_UnlockPage(m_fmbmp, dib, FALSE);
+}
+*/
+
+#pragma optimize("gt", on)
+
+// MurmurHash2, by Austin Appleby
+unsigned int __fastcall hash( const void * key, unsigned int len )
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ const unsigned int m = 0x5bd1e995;
+ const int r = 24;
+
+ // Initialize the hash to a 'random' value
+ unsigned int h = len;
+
+ // Mix 4 bytes at a time into the hash
+ const unsigned char * data = (const unsigned char *)key;
+
+ while(len >= 4)
+ {
+ unsigned int k = *(unsigned int *)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ // Handle the last few bytes of the input array
+ switch(len)
+ {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ };
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+#pragma optimize("", on)
+
+
+void InitImageCache(void)
+{
+ g_hMutexIm = CreateMutex(NULL, FALSE, NULL);
+ CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM) &fei);
+
+ pAlphaBlend = (tAlphaBlend) GetProcAddress(GetModuleHandleA("gdi32"), "GdiAlphaBlend");
+ if (pAlphaBlend == NULL)
+ pAlphaBlend = (tAlphaBlend) GetProcAddress(LoadLibraryA("msimg32"), "AlphaBlend");
+}
+
+void DestroyImageCache(void)
+{
+ WaitForSingleObject(g_hMutexIm, 3000);
+
+ if (timerId) KillTimer(NULL, timerId);
+ if (lastmodule) FreeLibrary(lastmodule);
+
+ g_imagecache.destroy();
+
+ ReleaseMutex(g_hMutexIm);
+ CloseHandle(g_hMutexIm);
+}
+
+
+ImageBase* AddCacheImage(const bkstring& file, int index)
+{
+ bkstring tmpfile(file); tmpfile.appendfmt(_T("#%d"), index);
+ unsigned id = hash(tmpfile.c_str(), (unsigned int)tmpfile.size() * sizeof(TCHAR));
+
+ WaitForSingleObject(g_hMutexIm, 3000);
+
+ ImageBase srch(id);
+ ImageBase *img = g_imagecache.find(&srch);
+ if (img == NULL)
+ {
+ bkstring::size_type ind = file.find_last_of('.');
+ if (ind == file.npos) return NULL;
+ bkstring ext = file.substr(ind+1);
+
+ if (ext.comparei(_T("dll")) == 0 || ext.comparei(_T("exe")) == 0)
+ img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoDll) : (ImageBase*)new IconType(id, file, index, icoDll);
+ else if (ext.comparei(_T("ico")) == 0)
+ img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, 0, icoFile) : (ImageBase*)new IconType(id, file, 0, icoFile);
+ else if (ext.comparei(_T("icl")) == 0)
+ img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoIcl) : (ImageBase*)new IconType(id, file, index, icoIcl);
+ else if (ext.comparei(_T("gif")) == 0)
+ img = new ImageType(id, file, NULL);
+ else if (fei == NULL || ext.comparei(_T("tif")) == 0 || ext.comparei(_T("tiff")) == 0)
+ img = new ImageType(id, file, NULL);
+ else
+ img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, NULL) : (ImageBase*)new ImageFType(id, file);
+
+ g_imagecache.insert(img);
+
+ if (timerId == 0)
+ {
+ timerId = 0xffffffff;
+ CallFunctionAsync(sttMainThreadCallback, NULL);
+ }
+ }
+ else
+ img->AddRef();
+
+ ReleaseMutex(g_hMutexIm);
+
+ return img;
+}
diff --git a/plugins/SmileyAdd/src/imagecache.h b/plugins/SmileyAdd/src/imagecache.h
new file mode 100644
index 0000000000..aa13e4981c
--- /dev/null
+++ b/plugins/SmileyAdd/src/imagecache.h
@@ -0,0 +1,166 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy
+
+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/>.
+*/
+#ifndef SMILEYADD_IMAGECACHE_H_
+#define SMILEYADD_IMAGECACHE_H_
+
+#include "general.h"
+
+
+class ImageBase
+{
+protected:
+ unsigned m_id;
+ long m_lRefCount;
+ time_t m_timestamp;
+
+public:
+
+ ImageBase(unsigned id);
+ virtual ~ImageBase() {}
+
+ long AddRef(void);
+ long Release(void);
+
+ void ProcessTimerTick(time_t ts);
+
+ virtual void GetSize(SIZE& /* size */) {};
+ virtual int GetFrameCount(void) const { return 0; }
+ virtual int GetFrameDelay(void) const { return 0; }
+ virtual HICON GetIcon(void) { return NULL; };
+ virtual void DrawInternal(HDC /* dc */, int /* x */, int /* y */, int /* sizeX */, int /* sizeY */) {};
+ virtual void SelectFrame(int /* frame */) {}
+
+ bool IsAnimated(void) const { return GetFrameCount() > 1; }
+ HBITMAP GetBitmap(COLORREF bkgClr, int sizeX, int sizeY);
+ void Draw(HDC dc, RECT &rc, bool clip);
+ int SelectNextFrame(const int frame);
+
+ static int CompareImg(const ImageBase* p1, const ImageBase* p2);
+};
+
+
+typedef enum
+{
+ icoDll,
+ icoFile,
+ icoIcl
+}
+IcoTypeEnum;
+
+
+class IconType : public ImageBase
+{
+private:
+ HICON m_SmileyIcon;
+
+public:
+ IconType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type);
+ ~IconType();
+
+ void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY);
+ HICON GetIcon(void);
+ void GetSize(SIZE& size);
+};
+
+
+class ImageListItemType : public ImageBase
+{
+private:
+ int m_index;
+ HIMAGELIST m_hImList;
+
+public:
+ ImageListItemType(const unsigned id, HIMAGELIST hImList, int index);
+
+ void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY);
+ HICON GetIcon(void);
+ void GetSize(SIZE& size);
+};
+
+
+class ImageType : public ImageBase
+{
+private:
+ int m_nCurrentFrame;
+ int m_nFrameCount;
+
+ Gdiplus::Bitmap* m_bmp;
+ Gdiplus::PropertyItem* m_pPropertyItem;
+
+public:
+
+ ImageType(const unsigned id, const bkstring& file, IStream* pStream);
+ ImageType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type);
+ ~ImageType();
+
+ void SelectFrame(int frame);
+
+ void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY);
+ HICON GetIcon(void);
+ void GetSize(SIZE& size);
+
+ int GetFrameDelay(void) const;
+ int GetFrameCount(void) const { return m_nFrameCount; }
+};
+
+class ImageFType : public ImageBase
+{
+protected:
+ HBITMAP m_bmp;
+
+public:
+
+ ImageFType(const unsigned id);
+ ImageFType(const unsigned id, const bkstring& file);
+ ~ImageFType();
+
+ void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY);
+ HICON GetIcon(void);
+ void GetSize(SIZE& size);
+};
+/*
+class ImageFAniType : public ImageFType
+{
+private:
+ int m_nCurrentFrame;
+ int m_nFrameCount;
+ int *m_FrameDelay;
+
+ FIMULTIBITMAP *m_fmbmp;
+ HBITMAP* m_bmpl;
+
+public:
+
+ ImageFAniType(const unsigned id, const bkstring& file);
+ ~ImageFAniType();
+
+ void SelectFrame(int frame);
+
+ int GetFrameDelay(void) const { return m_FrameDelay[m_nCurrentFrame]; }
+ int GetFrameCount(void) const { return m_nFrameCount; }
+};
+*/
+
+
+ImageBase* AddCacheImage(const bkstring& file, int index);
+
+void InitImageCache(void);
+void DestroyImageCache(void);
+
+#endif
+
diff --git a/plugins/SmileyAdd/src/main.cpp b/plugins/SmileyAdd/src/main.cpp
new file mode 100644
index 0000000000..a2deb4399a
--- /dev/null
+++ b/plugins/SmileyAdd/src/main.cpp
@@ -0,0 +1,225 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
+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 "smileys.h"
+#include "customsmiley.h"
+#include "services.h"
+#include "options.h"
+#include "download.h"
+#include "imagecache.h"
+#include "version.h"
+#include "m_metacontacts.h"
+
+//globals
+HINSTANCE g_hInst;
+HANDLE hEvent1, hContactMenuItem;
+extern LIST<void> menuHandleArray;
+
+char* metaProtoName;
+
+
+
+//static globals
+static HANDLE hHooks[7];
+static HANDLE hService[13];
+int hLangpack;
+
+
+static const PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ "SmileyAdd",
+ __VERSION_DWORD,
+ "Smiley support for Miranda Instant Messanger",
+ "Peacow, nightwish, bid, borkra",
+ "borkra@miranda-im.org",
+ "Copyright© 2004 - 2012 Boris Krasnovskiy, portions by Rein-Peter de Boer",
+ "http://nightly.miranda.im/",
+ // "http://nightly.miranda.im/",
+ UNICODE_AWARE,
+ // {BD542BB4-5AE4-4d0e-A435-BA8DBE39607F}
+ { 0xbd542bb4, 0x5ae4, 0x4d0e, { 0xa4, 0x35, 0xba, 0x8d, 0xbe, 0x39, 0x60, 0x7f } }
+};
+
+static SKINICONDESC skinDesc =
+{
+ sizeof(SKINICONDESC), "SmileyAdd", NULL,
+ "SmileyAdd_ButtonSmiley", NULL, -IDI_SMILINGICON
+};
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD /* mirandaVersion */)
+{
+ return (PLUGININFOEX*)&pluginInfoEx;
+}
+
+// MirandaInterfaces - returns the protocol interface to the core
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_SMILEY, MIID_LAST};
+
+static int ModulesLoaded(WPARAM, LPARAM)
+{
+ char path[MAX_PATH];
+ GetModuleFileNameA(g_hInst, path, MAX_PATH);
+
+ skinDesc.pszDefaultFile = path;
+ skinDesc.pszDescription = LPGEN("Button Smiley");
+ HANDLE hSkinIcon = Skin_AddIcon(&skinDesc);
+
+ INT_PTR temp = CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+ metaProtoName = mir_strdup(temp == CALLSERVICE_NOTFOUND ? NULL : (char*)temp);
+
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB;
+ mi.popupPosition = 2000070050;
+ mi.position = 2000070050;
+ mi.icolibItem = hSkinIcon;
+ mi.pszPopupName = (char*)-1;
+ mi.pszName = "Assign Smiley Category";
+ hContactMenuItem = Menu_AddContactMenuItem(&mi);
+
+ DownloadInit();
+
+ //install hooks if enabled
+ InstallDialogBoxHook();
+
+ g_SmileyCategories.AddAllProtocolsAsCategory();
+ g_SmileyCategories.ClearAndLoadAll();
+
+ return 0;
+}
+
+static int MirandaShutdown(WPARAM, LPARAM)
+{
+ CloseSmileys();
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+
+ mir_getLP(&pluginInfoEx);
+
+ if (ServiceExists(MS_SMILEYADD_REPLACESMILEYS))
+ {
+ static const TCHAR errmsg[] = _T("Only one instance of SmileyAdd could be executed.\n")
+ _T("Remove duplicate instances from 'Plugins' directory");
+ ReportError(TranslateTS(errmsg));
+
+ return 1;
+ }
+
+ char temp[80];
+ CallService(MS_SYSTEM_GETVERSIONTEXT, (WPARAM)SIZEOF(temp), (LPARAM)temp);
+
+
+ if (strstr(temp, "Unicode") == NULL)
+ {
+ ReportError(TranslateT("Please update SmileyAdd to ANSI Version"));
+ return 1;
+ }
+
+
+ InitImageCache();
+
+ g_SmileyCategories.SetSmileyPackStore(&g_SmileyPacks);
+
+ opt.Load();
+
+ // create smiley events
+ hEvent1 = CreateHookableEvent(ME_SMILEYADD_OPTIONSCHANGED);
+
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, MirandaShutdown);
+ hHooks[2] = HookEvent(ME_OPT_INITIALISE, SmileysOptionsInitialize);
+ hHooks[3] = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, RebuildContactMenu);
+ hHooks[4] = HookEvent(ME_SMILEYADD_OPTIONSCHANGED, UpdateSrmmDlg);
+ hHooks[5] = HookEvent(ME_PROTO_ACCLISTCHANGED, AccountListChanged);
+ hHooks[6] = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DbSettingChanged);
+
+ //create the smiley services
+ hService[0] = CreateServiceFunction(MS_SMILEYADD_REPLACESMILEYS, ReplaceSmileysCommand);
+ hService[1] = CreateServiceFunction(MS_SMILEYADD_GETSMILEYICON, GetSmileyIconCommand);
+ hService[2] = CreateServiceFunction(MS_SMILEYADD_SHOWSELECTION, ShowSmileySelectionCommand);
+ hService[3] = CreateServiceFunction(MS_SMILEYADD_GETINFO, GetInfoCommand);
+ hService[4] = CreateServiceFunction(MS_SMILEYADD_GETINFO2, GetInfoCommand2);
+ hService[5] = CreateServiceFunction(MS_SMILEYADD_PARSE, ParseText);
+ hService[6] = CreateServiceFunction(MS_SMILEYADD_REGISTERCATEGORY, RegisterPack);
+ hService[7] = CreateServiceFunction(MS_SMILEYADD_BATCHPARSE, ParseTextBatch);
+ hService[8] = CreateServiceFunction(MS_SMILEYADD_BATCHFREE, FreeTextBatch);
+ hService[9] = CreateServiceFunction(MS_SMILEYADD_CUSTOMCATMENU, CustomCatMenu);
+ hService[10] = CreateServiceFunction(MS_SMILEYADD_RELOAD, ReloadPack);
+ hService[11] = CreateServiceFunction(MS_SMILEYADD_LOADCONTACTSMILEYS, LoadContactSmileys);
+
+
+ hService[12] = CreateServiceFunction(MS_SMILEYADD_PARSEW, ParseTextW);
+
+
+ return 0;
+}
+
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ int i;
+
+ RemoveDialogBoxHook();
+
+ for (i=0; i<SIZEOF(hHooks); i++)
+ UnhookEvent(hHooks[i]);
+
+ for (i=0; i<SIZEOF(hService); i++)
+ DestroyServiceFunction(hService[i]);
+
+ DestroyHookableEvent(hEvent1);
+
+ RichEditData_Destroy();
+ DestroyAniSmileys();
+ DestroySmileyBase();
+
+ g_SmileyCategories.ClearAll();
+ g_SmileyPackCStore.ClearAndFreeAll();
+
+ DestroyImageCache();
+ DestroyGdiPlus();
+
+ DownloadClose();
+ menuHandleArray.destroy();
+
+ mir_free(metaProtoName);
+
+ return 0;
+}
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/)
+{
+ switch(fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ g_hInst = hinstDLL;
+ DisableThreadLibraryCalls(hinstDLL);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/plugins/SmileyAdd/src/options.cpp b/plugins/SmileyAdd/src/options.cpp
new file mode 100644
index 0000000000..b485c5ca99
--- /dev/null
+++ b/plugins/SmileyAdd/src/options.cpp
@@ -0,0 +1,728 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
+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 "options.h"
+#include "smileys.h"
+#include "customsmiley.h"
+#include "services.h"
+
+//globals, defined int main.cpp
+extern HINSTANCE g_hInst;
+extern HANDLE hEvent1;
+extern SmileyCategoryListType g_SmileyCategories;
+extern SmileyPackListType g_SmileyPacks;
+
+OptionsType opt;
+
+#define UM_CHECKSTATECHANGE (WM_USER + 100)
+
+class OptionsDialogType
+{
+private:
+ HWND m_hwndDialog;
+ SmileyCategoryListType tmpsmcat;
+ SmileyPackType smPack;
+
+ void InitDialog(void);
+ void DestroyDialog(void);
+ void AddCategory(void);
+ void ApplyChanges(void);
+ void UpdateControls(bool force = false);
+ void SetChanged(void);
+ bool BrowseForSmileyPacks(int item);
+ void FilenameChanged(void);
+ void ShowSmileyPreview(void);
+ void PopulateSmPackList(void);
+ void UserAction(HTREEITEM hItem);
+ long GetSelProto(HTREEITEM hItem = NULL);
+
+public:
+ OptionsDialogType(HWND hWnd) { m_hwndDialog = hWnd; }
+ BOOL DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam);
+};
+
+static INT_PTR CALLBACK DlgProcSmileysOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+//Init and de-init functions, called from main
+int SmileysOptionsInitialize(WPARAM addInfo, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.position = 910000000;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SMILEYS);
+ odp.pszTitle = LPGEN("Smileys");
+ odp.pszGroup = LPGEN("Customize");
+ odp.pfnDlgProc = DlgProcSmileysOptions;
+ odp.flags = ODPF_BOLDGROUPS;
+ Options_AddPage(addInfo, &odp);
+
+ return 0;
+}
+
+
+//
+// dialog procedure for the options dialog. creates or
+// retrieves the options class and calls it
+//
+static INT_PTR CALLBACK DlgProcSmileysOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ OptionsDialogType* pOD;
+ INT_PTR Result;
+
+ pOD = (OptionsDialogType*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (pOD == NULL)
+ {
+ pOD = new OptionsDialogType(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pOD);
+ }
+
+ Result = pOD->DialogProcedure(msg, wParam, lParam);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
+
+ if (msg == WM_NCDESTROY)
+ {
+ delete pOD;
+ }
+
+ return Result;
+}
+
+
+//OptionsDialog class functions
+BOOL OptionsDialogType::DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL Result = FALSE;
+
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ InitDialog();
+ Result = TRUE;
+ break;
+
+ case WM_DESTROY:
+ DestroyDialog();
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_FILENAME:
+ switch(HIWORD(wParam))
+ {
+ case EN_KILLFOCUS:
+ FilenameChanged();
+ break;
+
+ case EN_CHANGE:
+ if (GetFocus() == (HWND)lParam) SetChanged();
+ break;
+ }
+ break;
+
+ case IDC_BROWSE:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ if (BrowseForSmileyPacks(GetSelProto()))
+ {
+ UpdateControls(true);
+ SetChanged();
+ }
+ }
+ break;
+
+ case IDC_SMLOPTBUTTON:
+ if (HIWORD(wParam) == BN_CLICKED) ShowSmileyPreview();
+ break;
+
+ case IDC_USESTDPACK:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ PopulateSmPackList();
+ SetChanged();
+ }
+ break;
+
+ case IDC_PLUGENABLED:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL en = IsDlgButtonChecked(m_hwndDialog, IDC_PLUGENABLED) == BST_UNCHECKED;
+ EnableWindow(GetDlgItem(m_hwndDialog, IDC_SMLBUT), en);
+ SetChanged();
+ }
+ break;
+
+ case IDC_ADDCATEGORY:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ AddCategory();
+ }
+ break;
+
+ case IDC_DELETECATEGORY:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ if (tmpsmcat.DeleteCustomCategory(GetSelProto()))
+ {
+ PopulateSmPackList();
+ SetChanged();
+ }
+ }
+ break;
+
+ case IDC_SPACES:
+ case IDC_SCALETOTEXTHEIGHT:
+ case IDC_APPENDSPACES:
+ case IDC_SMLBUT:
+ case IDC_SCALEALLSMILEYS:
+ case IDC_IEVIEWSTYLE:
+ case IDC_ANIMATESEL:
+ case IDC_ANIMATEDLG:
+ case IDC_INPUTSMILEYS:
+ case IDC_DCURSORSMILEY:
+ case IDC_DISABLECUSTOM:
+ case IDC_HQSCALING:
+ if (HIWORD(wParam) == BN_CLICKED) SetChanged();
+ break;
+
+ case IDC_SELCLR:
+ if (HIWORD(wParam) == CPN_COLOURCHANGED) SetChanged();
+ break;
+
+ case IDC_MAXCUSTSMSZ:
+ case IDC_MINSMSZ:
+ if (HIWORD(wParam) == EN_CHANGE && GetFocus() == (HWND)lParam) SetChanged();
+ break;
+ }
+ break;
+
+ case UM_CHECKSTATECHANGE:
+ UserAction((HTREEITEM)lParam);
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_APPLY:
+ ApplyChanges();
+ break;
+ }
+ break;
+
+ case IDC_CATEGORYLIST:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case NM_CLICK:
+ {
+ TVHITTESTINFO ht = {0};
+
+ DWORD dwpos = GetMessagePos();
+ POINTSTOPOINT(ht.pt, MAKEPOINTS(dwpos));
+ MapWindowPoints(HWND_DESKTOP, ((LPNMHDR)lParam)->hwndFrom, &ht.pt, 1);
+
+ TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &ht);
+ if (TVHT_ONITEM & ht.flags)
+ FilenameChanged();
+ if (TVHT_ONITEMSTATEICON & ht.flags)
+ PostMessage(m_hwndDialog, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.hItem);
+ }
+
+ case TVN_KEYDOWN:
+ if (((LPNMTVKEYDOWN) lParam)->wVKey == VK_SPACE)
+ PostMessage(m_hwndDialog, UM_CHECKSTATECHANGE, 0,
+ (LPARAM)TreeView_GetSelection(((LPNMHDR)lParam)->hwndFrom));
+ break;
+
+ case TVN_SELCHANGEDA:
+ case TVN_SELCHANGEDW:
+ {
+ LPNMTREEVIEW pnmtv = (LPNMTREEVIEW) lParam;
+ if (pnmtv->itemNew.state & TVIS_SELECTED)
+ UpdateControls();
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ return Result;
+}
+
+
+void OptionsDialogType::AddCategory(void)
+{
+ TCHAR cat[30];
+
+ GetDlgItemText(m_hwndDialog, IDC_NEWCATEGORY, cat, SIZEOF(cat));
+ bkstring catd = cat;
+
+ if (!catd.empty())
+ {
+ tmpsmcat.AddCategory(cat, catd, smcCustom);
+
+ PopulateSmPackList();
+ SetChanged();
+ }
+}
+
+
+void OptionsDialogType::UserAction(HTREEITEM hItem)
+{
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+
+ if (TreeView_GetCheckState(hLstView, hItem))
+ {
+ if (!BrowseForSmileyPacks(GetSelProto(hItem)))
+ {
+ TreeView_SetCheckState(hLstView, hItem, TRUE)
+ }
+ }
+ else
+ {
+ tmpsmcat.GetSmileyCategory(GetSelProto(hItem))->ClearFilename();
+ }
+
+ if (hItem == TreeView_GetSelection(hLstView))
+ UpdateControls();
+ else
+ TreeView_SelectItem(hLstView, hItem);
+
+ SetChanged();
+}
+
+
+void OptionsDialogType::SetChanged(void)
+{
+ SendMessage(GetParent(m_hwndDialog), PSM_CHANGED, 0, 0);
+}
+
+
+void OptionsDialogType::UpdateControls(bool force)
+{
+ const SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(GetSelProto());
+ if (smc == NULL) return;
+
+ const bkstring& smf = smc->GetFilename();
+
+ SetDlgItemText(m_hwndDialog, IDC_FILENAME, smf.c_str());
+
+ if (smPack.GetFilename() != smf || force)
+ {
+ smPack.LoadSmileyFile(smf, false, true);
+ }
+
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+ TreeView_SetCheckState(hLstView, TreeView_GetSelection(hLstView), smPack.SmileyCount() != 0);
+
+ SetDlgItemText(m_hwndDialog, IDC_LIBAUTHOR, smPack.GetAuthor().c_str());
+ SetDlgItemText(m_hwndDialog, IDC_LIBVERSION, smPack.GetVersion().c_str());
+ SetDlgItemText(m_hwndDialog, IDC_LIBNAME, TranslateTS(smPack.GetName().c_str()));
+}
+
+
+long OptionsDialogType::GetSelProto(HTREEITEM hItem)
+{
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+ TVITEM tvi = {0};
+
+ tvi.mask = TVIF_PARAM;
+ tvi.hItem = hItem == NULL ? TreeView_GetSelection(hLstView) : hItem;
+
+ TreeView_GetItem(hLstView, &tvi);
+
+ return (long)tvi.lParam;
+}
+
+
+void OptionsDialogType::PopulateSmPackList(void)
+{
+ bool useOne = IsDlgButtonChecked(m_hwndDialog, IDC_USESTDPACK) == BST_UNCHECKED;
+
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+
+ TreeView_SelectItem(hLstView, NULL);
+ TreeView_DeleteAllItems(hLstView);
+
+ TVINSERTSTRUCT tvi = {0};
+ tvi.hParent = TVI_ROOT;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_SELECTED;
+
+ SmileyCategoryListType::SmileyCategoryVectorType& smc = *tmpsmcat.GetSmileyCategoryList();
+ for (int i = 0; i < smc.getCount(); i++)
+ {
+ if (!useOne || !smc[i].IsProto())
+ {
+ tvi.item.pszText = (TCHAR*)smc[i].GetDisplayName().c_str();
+ if (!smc[i].IsProto())
+ {
+ tvi.item.iImage = 0;
+ tvi.item.iSelectedImage = 0;
+ }
+ else
+ {
+ tvi.item.iImage = i;
+ tvi.item.iSelectedImage = i;
+ }
+ tvi.item.lParam = i;
+ tvi.item.state =
+ INDEXTOSTATEIMAGEMASK(smPack.LoadSmileyFile(smc[i].GetFilename(), true, true) ? 2 : 1);
+
+ TreeView_InsertItem(hLstView, &tvi);
+
+ smPack.Clear();
+ }
+ }
+ TreeView_SelectItem(hLstView, TreeView_GetRoot(hLstView));
+}
+
+void OptionsDialogType::InitDialog(void)
+{
+ TranslateDialogDefault(m_hwndDialog);
+
+ CheckDlgButton(m_hwndDialog, IDC_PLUGENABLED, opt.PluginSupportEnabled ? BST_UNCHECKED : BST_CHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_SPACES, opt.EnforceSpaces ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_SCALETOTEXTHEIGHT, opt.ScaleToTextheight ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_USESTDPACK, opt.UseOneForAll ? BST_UNCHECKED : BST_CHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_APPENDSPACES, opt.SurroundSmileyWithSpaces ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_SCALEALLSMILEYS, opt.ScaleAllSmileys ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_IEVIEWSTYLE, opt.IEViewStyle ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_ANIMATESEL, opt.AnimateSel ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_ANIMATEDLG, opt.AnimateDlg ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_INPUTSMILEYS, opt.InputSmileys ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_DCURSORSMILEY, opt.DCursorSmiley ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_DISABLECUSTOM, opt.DisableCustom ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwndDialog, IDC_HQSCALING, opt.HQScaling ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Off"));
+ SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Top"));
+
+ if (IsOldSrmm())
+ SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Bottom"));
+
+ SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_SETCURSEL, opt.ButtonStatus, 0);
+ EnableWindow(GetDlgItem(m_hwndDialog, IDC_SMLBUT), opt.PluginSupportEnabled);
+
+ SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSPIN, UDM_SETRANGE32, 0, 99);
+ SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSPIN, UDM_SETPOS, 0, opt.MaxCustomSmileySize);
+ SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSMSZ, EM_LIMITTEXT, 2, 0);
+
+ SendDlgItemMessage(m_hwndDialog, IDC_MINSPIN, UDM_SETRANGE32, 0, 99);
+ SendDlgItemMessage(m_hwndDialog, IDC_MINSPIN, UDM_SETPOS, 0, opt.MinSmileySize);
+ SendDlgItemMessage(m_hwndDialog, IDC_MINSMSZ, EM_LIMITTEXT, 2, 0);
+
+ SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_SETCOLOUR, 0, opt.SelWndBkgClr);
+ SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_SETDEFAULTCOLOUR, 0, GetSysColor(COLOR_WINDOW));
+
+ // Create and populate image list
+ HIMAGELIST hImList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ ILC_MASK | ILC_COLOR32, g_SmileyCategories.NumberOfSmileyCategories(), 0);
+
+ tmpsmcat = g_SmileyCategories;
+
+ SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList();
+ for (int i = 0; i < smc.getCount(); i++)
+ {
+ HICON hIcon = NULL;
+ if (smc[i].IsProto())
+ {
+ hIcon=(HICON)CallProtoService(T2A_SM(smc[i].GetName().c_str()), PS_LOADICON,
+ PLI_PROTOCOL | PLIF_SMALL, 0);
+ if (hIcon == NULL || (INT_PTR)hIcon == CALLSERVICE_NOTFOUND)
+ {
+ hIcon=(HICON)CallProtoService(T2A_SM(smc[i].GetName().c_str()), PS_LOADICON, PLI_PROTOCOL, 0);
+ }
+ }
+ if (hIcon == NULL || hIcon == (HICON)CALLSERVICE_NOTFOUND)
+ hIcon = GetDefaultIcon();
+
+ ImageList_AddIcon(hImList, hIcon);
+ DestroyIcon(hIcon);
+ }
+
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+ TreeView_SetImageList(hLstView, hImList, TVSIL_NORMAL);
+
+ PopulateSmPackList();
+}
+
+void OptionsDialogType::DestroyDialog(void)
+{
+ HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST);
+ HIMAGELIST hImList = TreeView_SetImageList(hLstView, NULL, TVSIL_NORMAL);
+ ImageList_Destroy(hImList);
+}
+
+
+void OptionsDialogType::ApplyChanges(void)
+{
+ ProcessAllInputAreas(true);
+ CloseSmileys();
+
+ opt.PluginSupportEnabled = IsDlgButtonChecked(m_hwndDialog, IDC_PLUGENABLED) == BST_UNCHECKED;
+ opt.EnforceSpaces = IsDlgButtonChecked(m_hwndDialog, IDC_SPACES) == BST_CHECKED;
+ opt.ScaleToTextheight = IsDlgButtonChecked(m_hwndDialog, IDC_SCALETOTEXTHEIGHT) == BST_CHECKED;
+ opt.UseOneForAll = IsDlgButtonChecked(m_hwndDialog, IDC_USESTDPACK) == BST_UNCHECKED;
+ opt.SurroundSmileyWithSpaces = IsDlgButtonChecked(m_hwndDialog, IDC_APPENDSPACES) == BST_CHECKED;
+ opt.ScaleAllSmileys = IsDlgButtonChecked(m_hwndDialog, IDC_SCALEALLSMILEYS) == BST_CHECKED;
+ opt.IEViewStyle = IsDlgButtonChecked(m_hwndDialog, IDC_IEVIEWSTYLE) == BST_CHECKED;
+ opt.AnimateSel = IsDlgButtonChecked(m_hwndDialog, IDC_ANIMATESEL) == BST_CHECKED;
+ opt.AnimateDlg = IsDlgButtonChecked(m_hwndDialog, IDC_ANIMATEDLG) == BST_CHECKED;
+ opt.InputSmileys = IsDlgButtonChecked(m_hwndDialog, IDC_INPUTSMILEYS) == BST_CHECKED;
+ opt.DCursorSmiley = IsDlgButtonChecked(m_hwndDialog, IDC_DCURSORSMILEY) == BST_CHECKED;
+ opt.DisableCustom = IsDlgButtonChecked(m_hwndDialog, IDC_DISABLECUSTOM) == BST_CHECKED;
+ opt.HQScaling = IsDlgButtonChecked(m_hwndDialog, IDC_HQSCALING) == BST_CHECKED;
+
+ opt.ButtonStatus = (unsigned)SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_GETCURSEL, 0, 0);
+ opt.SelWndBkgClr = (unsigned)SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_GETCOLOUR, 0, 0);
+ opt.MaxCustomSmileySize = GetDlgItemInt(m_hwndDialog, IDC_MAXCUSTSMSZ, NULL, FALSE);
+ opt.MinSmileySize = GetDlgItemInt(m_hwndDialog, IDC_MINSMSZ, NULL, FALSE);
+
+ opt.Save();
+
+ // Cleanup database
+ SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList();
+ for (int i = 0; i < smc.getCount(); i++)
+ {
+ if (tmpsmcat.GetSmileyCategory(smc[i].GetName()) == NULL)
+ {
+ bkstring empty;
+ opt.WritePackFileName(empty, smc[i].GetName());
+ }
+ }
+
+ g_SmileyCategories = tmpsmcat;
+ g_SmileyCategories.SaveSettings();
+ g_SmileyCategories.ClearAndLoadAll();
+
+ smPack.LoadSmileyFile(tmpsmcat.GetSmileyCategory(GetSelProto())->GetFilename(), false);
+
+ NotifyEventHooks(hEvent1, 0, 0);
+ ProcessAllInputAreas(false);
+}
+
+bool OptionsDialogType::BrowseForSmileyPacks(int item)
+{
+ OPENFILENAME ofn = {0};
+
+ TCHAR filename[MAX_PATH] = _T("");
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = SIZEOF(filename);
+
+ bkstring inidir;
+ SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(item);
+ if (smc->GetFilename().empty())
+ {
+ pathToAbsolute(_T("Smileys"), inidir);
+ }
+ else
+ {
+ pathToAbsolute(smc->GetFilename(), inidir);
+ inidir.erase(inidir.find_last_of('\\'));
+ }
+
+ ofn.lpstrInitialDir = inidir.c_str();
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwndDialog;
+
+ TCHAR filter[512], *pfilter;
+ _tcscpy(filter, TranslateT("Smiley Packs"));
+ lstrcat(filter, _T(" (*.msl;*.asl;*.xep)"));
+ pfilter = filter + _tcslen(filter) + 1;
+ _tcscpy(pfilter, _T("*.msl;*.asl;*.xep"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ _tcscpy(pfilter, TranslateT("All Files"));
+ lstrcat(pfilter, _T(" (*.*)"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ _tcscpy(pfilter, _T("*.*"));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_READONLY |
+ OFN_EXPLORER | OFN_LONGNAMES | OFN_NOCHANGEDIR;
+ ofn.lpstrDefExt = _T("msl");
+
+ if (GetOpenFileName(&ofn))
+ {
+ bkstring relpath;
+ pathToRelative(filename, relpath);
+ smc->SetFilename(relpath);
+
+ return true;
+ }
+
+ return false;
+}
+
+void OptionsDialogType::FilenameChanged(void)
+{
+ TCHAR str[MAX_PATH];
+ GetDlgItemText(m_hwndDialog, IDC_FILENAME, str, SIZEOF(str));
+
+ SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(GetSelProto());
+ if (smc->GetFilename() != str)
+ {
+ bkstring temp(str);
+ smc->SetFilename(temp);
+ UpdateControls();
+ }
+}
+
+void OptionsDialogType::ShowSmileyPreview(void)
+{
+ RECT rect;
+ GetWindowRect(GetDlgItem(m_hwndDialog, IDC_SMLOPTBUTTON), &rect);
+
+ SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
+ stwp->pSmileyPack = &smPack;
+ stwp->hWndParent = m_hwndDialog;
+ stwp->hWndTarget = NULL;
+ stwp->targetMessage = 0;
+ stwp->targetWParam = 0;
+ stwp->xPosition = rect.left;
+ stwp->yPosition = rect.bottom + 4;
+ stwp->direction = 1;
+ stwp->hContact = NULL;
+
+ mir_forkthread(SmileyToolThread, stwp);
+}
+
+
+void OptionsType::Save(void)
+{
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "PluginSupportEnabled", PluginSupportEnabled);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "EnforceSpaces", EnforceSpaces);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "ScaleToTextheight", ScaleToTextheight);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "UseOneForAll", UseOneForAll);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "SurroundSmileyWithSpaces", SurroundSmileyWithSpaces);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "ScaleAllSmileys", ScaleAllSmileys);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "IEViewStyle", IEViewStyle);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "AnimateSel", AnimateSel);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "AnimateDlg", AnimateDlg);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "InputSmileys", InputSmileys);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "DCursorSmiley", DCursorSmiley);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "DisableCustom", DisableCustom);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "HQScaling", HQScaling);
+ DBWriteContactSettingByte(NULL, "SmileyAdd", "ButtonStatus", (BYTE)ButtonStatus);
+ DBWriteContactSettingDword(NULL, "SmileyAdd", "SelWndBkgClr", SelWndBkgClr);
+ DBWriteContactSettingDword(NULL, "SmileyAdd", "MaxCustomSmileySize", MaxCustomSmileySize);
+ DBWriteContactSettingDword(NULL, "SmileyAdd", "MinSmileySize", MinSmileySize);
+}
+
+
+void OptionsType::Load(void)
+{
+ PluginSupportEnabled = DBGetContactSettingByte(NULL, "SmileyAdd", "PluginSupportEnabled", TRUE) != 0;
+ EnforceSpaces = DBGetContactSettingByte(NULL, "SmileyAdd", "EnforceSpaces", FALSE) != 0;
+ ScaleToTextheight = DBGetContactSettingByte(NULL, "SmileyAdd", "ScaleToTextheight", FALSE) != 0;
+ UseOneForAll = DBGetContactSettingByte(NULL, "SmileyAdd", "UseOneForAll", TRUE) != 0;
+ SurroundSmileyWithSpaces =
+ DBGetContactSettingByte(NULL, "SmileyAdd", "SurroundSmileyWithSpaces", FALSE) != 0;
+ ScaleAllSmileys = DBGetContactSettingByte(NULL, "SmileyAdd", "ScaleAllSmileys", FALSE) != 0;
+ IEViewStyle = DBGetContactSettingByte(NULL, "SmileyAdd", "IEViewStyle", FALSE) != 0;
+ AnimateSel = DBGetContactSettingByte(NULL, "SmileyAdd", "AnimateSel", TRUE) != 0;
+ AnimateDlg = DBGetContactSettingByte(NULL, "SmileyAdd", "AnimateDlg", TRUE) != 0;
+ InputSmileys = DBGetContactSettingByte(NULL, "SmileyAdd", "InputSmileys", TRUE) != 0;
+ DCursorSmiley = DBGetContactSettingByte(NULL, "SmileyAdd", "DCursorSmiley", FALSE) != 0;
+ DisableCustom = DBGetContactSettingByte(NULL, "SmileyAdd", "DisableCustom", FALSE) != 0;
+ HQScaling = DBGetContactSettingByte(NULL, "SmileyAdd", "HQScaling", FALSE) != 0;
+
+ ButtonStatus = DBGetContactSettingByte(NULL, "SmileyAdd", "ButtonStatus", 1);
+ SelWndBkgClr = DBGetContactSettingDword(NULL, "SmileyAdd", "SelWndBkgClr", GetSysColor(COLOR_WINDOW));
+ MaxCustomSmileySize = DBGetContactSettingDword(NULL, "SmileyAdd", "MaxCustomSmileySize", 0);
+ MinSmileySize = DBGetContactSettingDword(NULL, "SmileyAdd", "MinSmileySize", 0);
+}
+
+
+void OptionsType::ReadPackFileName(bkstring& filename, const bkstring& name,
+ const bkstring& defaultFilename)
+{
+ DBVARIANT dbv;
+ bkstring settingKey = name + _T("-filename");
+
+ INT_PTR res = DBGetContactSettingTString(NULL, "SmileyAdd", T2A_SM(settingKey.c_str()), &dbv);
+ if (res == 0)
+ {
+ filename = dbv.ptszVal;
+ DBFreeVariant(&dbv);
+ }
+ else
+ filename = defaultFilename;
+}
+
+
+void OptionsType::WritePackFileName(const bkstring& filename, const bkstring& name)
+{
+ bkstring settingKey = name + _T("-filename");
+ DBWriteContactSettingTString(NULL, "SmileyAdd", T2A_SM(settingKey.c_str()),
+ filename.c_str());
+}
+
+
+void OptionsType::ReadCustomCategories(bkstring& cats)
+{
+ DBVARIANT dbv;
+
+ INT_PTR res = DBGetContactSettingTString(NULL, "SmileyAdd", "CustomCategories", &dbv);
+ if (res == 0)
+ {
+ cats = dbv.ptszVal;
+ DBFreeVariant(&dbv);
+ }
+}
+
+
+void OptionsType::WriteCustomCategories(const bkstring& cats)
+{
+ if (cats.empty())
+ DBDeleteContactSetting(NULL, "SmileyAdd", "CustomCategories");
+ else
+ DBWriteContactSettingTString(NULL, "SmileyAdd", "CustomCategories", cats.c_str());
+}
+
+
+void OptionsType::ReadContactCategory(HANDLE hContact, bkstring& cats)
+{
+ DBVARIANT dbv;
+
+ INT_PTR res = DBGetContactSettingTString(hContact, "SmileyAdd", "CustomCategory", &dbv);
+ if (res == 0)
+ {
+ cats = dbv.ptszVal;
+ DBFreeVariant(&dbv);
+ }
+}
+
+
+void OptionsType::WriteContactCategory(HANDLE hContact, const bkstring& cats)
+{
+ if (cats.empty())
+ DBDeleteContactSetting(hContact, "SmileyAdd", "CustomCategory");
+ else
+ DBWriteContactSettingTString(hContact, "SmileyAdd", "CustomCategory", cats.c_str());
+}
diff --git a/plugins/SmileyAdd/src/options.h b/plugins/SmileyAdd/src/options.h
new file mode 100644
index 0000000000..3b83b0dc8f
--- /dev/null
+++ b/plugins/SmileyAdd/src/options.h
@@ -0,0 +1,60 @@
+/*
+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/>.
+*/
+
+#ifndef SMILEYADD_OPTIONS_H_
+#define SMILEYADD_OPTIONS_H_
+
+int SmileysOptionsInitialize(WPARAM wParam, LPARAM lParam);
+
+class OptionsType
+{
+public:
+ bool PluginSupportEnabled;
+ bool EnforceSpaces;
+ bool ScaleToTextheight;
+ bool UseOneForAll;
+ bool SurroundSmileyWithSpaces;
+ bool ScaleAllSmileys;
+ bool IEViewStyle;
+ bool AnimateSel;
+ bool AnimateDlg;
+ bool InputSmileys;
+ bool DCursorSmiley;
+ bool DisableCustom;
+ bool HQScaling;
+ unsigned ButtonStatus;
+ unsigned SelWndBkgClr;
+ unsigned MaxCustomSmileySize;
+ unsigned MinSmileySize;
+
+ void Load(void);
+ void Save(void);
+ static void ReadPackFileName(bkstring& filename, const bkstring& name,
+ const bkstring& defaultFilename);
+ static void ReadCustomCategories(bkstring& cats);
+ static void ReadContactCategory(HANDLE hContact, bkstring& cats);
+ static void WritePackFileName(const bkstring& filename, const bkstring& name);
+ static void WriteCustomCategories(const bkstring& cats);
+ static void WriteContactCategory(HANDLE hContact, const bkstring& cats);
+};
+
+extern OptionsType opt;
+
+#endif // SMILEYADD_OPTIONS_H_
+
diff --git a/plugins/SmileyAdd/src/regexp/Matcher.cpp b/plugins/SmileyAdd/src/regexp/Matcher.cpp
new file mode 100644
index 0000000000..ebd1f57850
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/Matcher.cpp
@@ -0,0 +1,178 @@
+#include <Matcher.h>
+#include <Pattern.h>
+
+const int Matcher::MATCH_ENTIRE_STRING = 0x01;
+
+/*
+ Detailed documentation is provided in this class' header file
+
+ @author Jeffery Stuart
+ @since November 2004
+ @version 1.07.00
+*/
+
+Matcher::Matcher(Pattern * pattern, const bkstring & text)
+{
+ pat = pattern;
+ str = &text;
+ gc = pattern->groupCount;
+ ncgc = -pattern->nonCapGroupCount;
+ flags = 0;
+ matchedSomething = false;
+ starts = new int[gc + ncgc];
+ ends = new int[gc + ncgc];
+ groups = new int[gc + ncgc];
+ groupPos = new int[gc + ncgc];
+ groupIndeces = new int[gc + ncgc];
+ starts = starts + ncgc;
+ ends = ends + ncgc;
+ groups = groups + ncgc;
+ groupPos = groupPos + ncgc;
+ groupIndeces = groupIndeces + ncgc;
+ for (int i = 0; i < gc; ++i) starts[i] = ends[i] = 0;
+}
+Matcher::~Matcher()
+{
+ delete [] (starts - ncgc);
+ delete [] (ends - ncgc);
+ delete [] (groups - ncgc);
+ delete [] (groupIndeces - ncgc);
+ delete [] (groupPos - ncgc);
+}
+void Matcher::clearGroups()
+{
+ int i;
+ lm = 0;
+ for (i = 0; i < gc; ++i) groups[i] = starts[i] = ends[i] = -1;
+ for (i = 1; i <= ncgc; ++i) groups[0 - i] = -1;
+}
+bkstring Matcher::replaceWithGroups(const bkstring & str)
+{
+ bkstring ret = "";
+
+ bkstring t = str;
+ while (t.size() > 0)
+ {
+ if (t[0] == '\\')
+ {
+ t.erase(0, 1);
+ if (t.size() == 0)
+ {
+ ret += "\\";
+ }
+ else if (t[0] < '0' || t[0] > '9')
+ {
+ ret += t[0];
+ t.erase(0, 1);
+ }
+ else
+ {
+ int gn = 0;
+ while (t.size() > 0 && t[0] >= '0' && t[0] <= '9')
+ {
+ gn = gn * 10 + (t[0] - '0');
+ t.erase(0, 1);
+ }
+ ret += getGroup(gn);
+ }
+ }
+ else
+ {
+ ret += t[0];
+ t.erase(0, 1);
+ }
+ }
+
+ return ret;
+}
+unsigned long Matcher::getFlags() const
+{
+ return flags;
+}
+const bkstring& Matcher::getText() const
+{
+ return *str;
+}
+
+bool Matcher::matches()
+{
+ flags = MATCH_ENTIRE_STRING;
+ matchedSomething = false;
+ clearGroups();
+ lm = 0;
+ return pat->head->match(*str, this, 0) == (int)str->size();
+}
+bool Matcher::findFirstMatch()
+{
+ starts[0] = 0;
+ flags = 0;
+ clearGroups();
+ start = 0;
+ lm = 0;
+ ends[0] = pat->head->match(*str, this, 0);
+ if (ends[0] >= 0)
+ {
+ matchedSomething = true;
+ return 1;
+ }
+ return 0;
+}
+bool Matcher::findNextMatch()
+{
+ int s = starts[0], e = ends[0];
+
+ if (!matchedSomething) return findFirstMatch();
+ if (s == e) ++e;
+ flags = 0;
+ clearGroups();
+
+ starts[0] = e;
+ if (e >= (int)str->size()) return 0;
+ start = e;
+ lm = e;
+ ends[0] = pat->head->match(*str, this, e);
+ return ends[0] >= 0;
+}
+std::vector<bkstring> Matcher::findAll()
+{
+ std::vector<bkstring> ret;
+ reset();
+ while (findNextMatch())
+ {
+ ret.push_back(getGroup());
+ }
+ return ret;
+}
+
+void Matcher::reset()
+{
+ lm = 0;
+ clearGroups();
+ matchedSomething = false;
+}
+
+int Matcher::getStartingIndex(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return -1;
+ return starts[groupNum];
+}
+int Matcher::getEndingIndex(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return -1;
+ return ends[groupNum];
+}
+bkstring Matcher::getGroup(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return "";
+ if (starts[groupNum] < 0 || ends[groupNum] < 0) return "";
+ return str->substr(starts[groupNum], ends[groupNum] - starts[groupNum]);
+}
+std::vector<bkstring> Matcher::getGroups(const bool includeGroupZero) const
+{
+ int i, start = (includeGroupZero ? 0 : 1);
+ std::vector<bkstring> ret;
+
+ for (i = start; i < gc; ++i) ret.push_back(getGroup(i));
+ return ret;
+}
+
diff --git a/plugins/SmileyAdd/src/regexp/Matcher.h b/plugins/SmileyAdd/src/regexp/Matcher.h
new file mode 100644
index 0000000000..621184d8c2
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/Matcher.h
@@ -0,0 +1,248 @@
+#ifndef __MATCHER_H__
+#define __MATCHER_H__
+
+#include "bkstring.h"
+#include <vector>
+
+class Vector;
+class NFANode;
+class NFAStartNode;
+class NFAEndNode;
+class NFAGroupHeadNode;
+class NFAGroupLoopNode;
+class NFAGroupLoopPrologueNode;
+class NFAGroupTailNode;
+class NFALookBehindNode;
+class NFAStartOfLineNode;
+class NFAEndOfLineNode;
+class NFAEndOfMatchNode;
+class NFAReferenceNode;
+class Pattern;
+
+/**
+ A matcher is a non thread-safe object used to scan strings using a given
+ {@link Pattern Pattern} object. Using a <code>Matcher</code> is the preferred
+ method for scanning strings. Matchers are not thread-safe. Matchers require
+ very little dynamic memory, hence one is encouraged to create several
+ instances of a matcher when necessary as opposed to sharing a single instance
+ of a matcher.
+ <p>
+ The most common methods needed by the matcher are <code>matches</code>,
+ <code>findNextMatch</code>, and <code>getGroup</code>. <code>matches</code>
+ and <code>findNextMatch</code> both return success or failure, and further
+ details can be gathered from their documentation.
+ <p>
+ Unlike Java's <code>Matcher</code>, this class allows you to change the string
+ you are matching against. This provides a small optimization, since you no
+ longer need multiple matchers for a single pattern in a single thread.
+ <p>
+ This class also provides an extremely handy method for replacing text with
+ captured data via the <code>replaceWithGroups</code> method. A typical
+ invocation looks like:
+ <pre>
+ char buf[10000];
+ bkstring str = "\\5 (user name \\1) uses \\7 for his/her shell and \\6 is their home directory";
+ FILE * fp = fopen("/etc/passwd", "r");
+ Pattern::registerPattern("entry", "[^:]+");
+ Pattern * p = Pattern::compile("^({entry}):({entry}):({entry}):({entry}):({entry}):({entry}):({entry})$",
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Pattern::MULTILINE_MATCHING | Pattern::UNIX_LINE_MODE);
+ Matcher * m = p->createMatcher("");
+ while (fgets(buf, 9999, fp))
+ {
+ &nbsp;&nbsp;m->setString(buf);
+ &nbsp;&nbsp;if (m->matches())
+ &nbsp;&nbsp;{
+ &nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n", m->replaceWithGroups(str).c_str());
+ &nbsp;&nbsp;}
+ }
+ fclose(fp);
+
+ </pre>
+ Calling any of the following functions before first calling
+ <code>matches</code>, <code>findFirstMatch</code>, or
+ <code>findNextMatch</code> results in undefined behavior and may cause your
+ program to crash.
+ <code>
+ <ul>
+ <li>replaceWithGroups</code>
+ <li>getStartingIndex</li>
+ <li>getEndingIndex</li>
+ <li>getGroup</li>
+ <li>getGroups</li>
+ </ul>
+ </code>
+ <p>
+ The function <code>findFirstMatch</code> will attempt to find the first match
+ in the input string. The same results can be obtained by first calling
+ <code>reset</code> followed by <code>findNextMatch</code>.
+ <p>
+ To eliminate the necessity of looping through a string to find all the
+ matching substrings, <code>findAll</code> was created. The function will find
+ all matching substrings and return them in a <code>vector</code>. If you need
+ to examine specific capture groups within the substrings, then this method
+ should not be used.
+
+ @author Jeffery Stuart
+ @since March 2003, Stable Since November 2004
+ @version 1.07.00
+ @memo Mutable object used on instances of a Pattern class
+ */
+class Matcher
+{
+ friend class NFANode;
+ friend class NFAStartNode;
+ friend class NFAEndNode;
+ friend class NFAGroupHeadNode;
+ friend class NFAGroupLoopNode;
+ friend class NFAGroupLoopPrologueNode;
+ friend class NFAGroupTailNode;
+ friend class NFALookBehindNode;
+ friend class NFAStartOfLineNode;
+ friend class NFAEndOfLineNode;
+ friend class NFAEndOfMatchNode;
+ friend class NFAReferenceNode;
+ friend class Pattern;
+ private:
+ /**
+ Creates a new matcher object against <code>text</code> using
+ <code>pattern</code>.
+
+ @param pattern The pattern with which to search
+ @param text The text in which to search
+ */
+ Matcher(Pattern * pattern, const bkstring & text);
+ protected:
+ /// The pattern we use to match
+ Pattern * pat;
+ /// The string in which we are matching
+ const bkstring* str;
+ /// The starting point of our match
+ int start;
+ /// An array of the starting positions for each group
+ int * starts;
+ /// An array of the ending positions for each group
+ int * ends;
+ /// An array of private data used by NFANodes during matching
+ int * groups;
+ /// An array of private data used by NFANodes during matching
+ int * groupIndeces;
+ /// An array of private data used by NFANodes during matching
+ int * groupPos;
+ /// The ending index of the last match
+ int lm;
+ /// The number of capturing groups we have
+ int gc;
+ /// The number of non-capturing groups we havew
+ int ncgc;
+ /// Whether or not we have matched something (used only by findFirstMatch and findNextMatch)
+ int matchedSomething;
+ /// The flags with which we were made
+ unsigned long flags;
+ /// Called by reset to clear the group arrays
+ void clearGroups();
+ public:
+ /// Used internally by match to signify we want the entire string matched
+ const static int MATCH_ENTIRE_STRING;
+ public:
+ /// Cleans up the dynamic memory used by this matcher
+ ~Matcher();
+ /**
+ Replaces the contents of <code>str</code> with the appropriate captured
+ text. <code>str</code> should have at least one back reference, otherwise
+ this function does nothing.
+ @param str The string in which to replace text
+ @return A string with all backreferences appropriately replaced
+ */
+ bkstring replaceWithGroups(const bkstring & str);
+ /**
+ The flags currently being used by the matcher.
+ @return Zero
+ */
+ unsigned long getFlags() const;
+ /**
+ The text being searched by the matcher.
+ @return the text being searched by the matcher.
+ */
+ const bkstring& getText() const;
+
+ /**
+ Scans the string from start to finish for a match. The entire string must
+ match for this function to return success. Group variables are
+ appropriately set and can be queried after this function returns.
+
+ @return Success if and only if the entire string matches the pattern
+ */
+ bool matches();
+ /**
+ Scans the string for the first substring matching the pattern. The entire
+ string does not necessarily have to match for this function to return
+ success. Group variables are appropriately set and can be queried after
+ this function returns.
+
+ @return Success if any substring matches the specified pattern
+ */
+ bool findFirstMatch();
+ /**
+ Scans the string for the next substring matching the pattern. If no calls
+ have been made to findFirstMatch of findNextMatch since the last call to
+ reset, matches, or setString, then this function's behavior results to
+ that of findFirstMatch.
+
+ @return Success if another substring can be found that matches the pattern
+ */
+ bool findNextMatch();
+ /**
+ Returns a vector of every substring in order which matches the given
+ pattern.
+
+ @return Every substring in order which matches the given pattern
+ */
+ std::vector<bkstring> findAll();
+ /**
+ Resets the internal state of the matcher
+ */
+ void reset();
+ /**
+ Same as getText. Left n for backwards compatibilty with old source code
+ @return Returns the string that is currently being used for matching
+ */
+ inline const bkstring& getString() const { return *str; }
+ /**
+ Sets the string to scan
+ @param newStr The string to scan for subsequent matches
+ */
+ inline void setString(const bkstring & newStr) { str = &newStr; reset(); }
+
+ /**
+ Returns the starting index of the specified group.
+ @param groupNum The group to query
+ @return The starting index of the group if it was matched, -1 for an
+ invalid group or if the group was not matched
+ */
+ int getStartingIndex(const int groupNum = 0) const;
+ /**
+ Returns the ending index of the specified group.
+ @param groupNum The group to query
+ @return The ending index of the group if it was matched, -1 for an
+ invalid group or if the group was not matched
+ */
+ int getEndingIndex(const int groupNum = 0) const;
+ /**
+ Returns the specified group. An empty string ("") does not necessarily
+ mean the group was not matched. A group such as (a*b?) could be matched by
+ a zero length. If an empty string is returned, getStartingIndex can be
+ called to determine if the group was actually matched.
+ @param groupNum The group to query
+ @return The text of the group
+ */
+ bkstring getGroup(const int groupNum = 0) const;
+ /**
+ Returns every capture group in a vector
+
+ @param includeGroupZero Whether or not include capture group zero
+ @return Every capture group
+ */
+ std::vector<bkstring> getGroups(const bool includeGroupZero = 0) const;
+};
+
+#endif
diff --git a/plugins/SmileyAdd/src/regexp/Pattern.cpp b/plugins/SmileyAdd/src/regexp/Pattern.cpp
new file mode 100644
index 0000000000..4487dddd4b
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/Pattern.cpp
@@ -0,0 +1,1709 @@
+/**
+ From the author (Jeff Stuart)
+ "
+ Let me start by saying this file is pretty big. If you feel up to it, you can
+ try making changes yourself, but you would be better off to just email me at
+ stuart@cs.ucdavis.edu if you think there is a bug, or have something useful you
+ would like added. This project is very "near and dear" to me, so I am fairly
+ quick to make bug fixes. The header files for Pattern and Matcher are fairly
+ well documented and the function names are pretty self-explanatory, but if you
+ are having any trouble, feel free to email me at stuart@cs.ucdavis.edu.
+
+ If you email me, make sure you put something like C++RE in the subject because
+ I tend to delete email if I don't recognize the name and the subject is
+ something like "I Need Your Help" or "Got A Second" or "I Found It".
+ "
+ */
+
+/*
+ Detailed documentation is provided in this class' header file
+
+ @author Jeffery Stuart
+ @since November 2004
+ @version 1.07.00
+*/
+
+#define to_lower(a) (char)(unsigned)CharLowerA((LPSTR)static_cast<unsigned char>(a))
+#define is_alpha IsCharAlphaA
+
+#ifdef _WIN32
+ #pragma warning(push)
+ #pragma warning(disable:4996)
+ #define str_icmp lstrcmpiA
+#else
+ #define str_icmp strcasecmp
+#endif
+
+#include <Pattern.h>
+#include <Matcher.h>
+#include <cstring>
+#include <algorithm>
+
+std::map<bkstring, Pattern *> Pattern::compiledPatterns;
+std::map<bkstring, std::pair<bkstring, unsigned long> > Pattern::registeredPatterns;
+
+const int Pattern::MIN_QMATCH = 0x00000000;
+const int Pattern::MAX_QMATCH = 0x7FFFFFFF;
+
+const unsigned long Pattern::CASE_INSENSITIVE = 0x01;
+const unsigned long Pattern::LITERAL = 0x02;
+const unsigned long Pattern::DOT_MATCHES_ALL = 0x04;
+const unsigned long Pattern::MULTILINE_MATCHING = 0x08;
+const unsigned long Pattern::UNIX_LINE_MODE = 0x10;
+
+Pattern::Pattern(const bkstring & rhs)
+{
+ matcher = NULL;
+ pattern = rhs;
+ curInd = 0;
+ groupCount = 0;
+ nonCapGroupCount = 0;
+ error = 0;
+ head = NULL;
+}
+// convenience function in case we want to add any extra debugging output
+void Pattern::raiseError()
+{
+/* switch (pattern[curInd - 1])
+ {
+ case '*':
+ case ')':
+ case '+':
+ case '?':
+ case ']':
+ case '}':
+ fprintf(stderr, "%s\n%*c^\n", pattern.c_str(), curInd - 1, ' ');
+ fprintf(stderr, "Syntax Error near here. Possible unescaped meta character.\n");
+ break;
+ default:
+ fprintf(stderr, "%s\n%*c^\n", pattern.c_str(), curInd - 1, ' ');
+ fprintf(stderr, "Syntax Error near here. \n");
+ break;
+ }*/
+ error = 1;
+}
+NFANode * Pattern::registerNode(NFANode * node)
+{
+ nodes[node] = 1;
+ return node;
+}
+
+bkstring Pattern::classUnion (bkstring s1, bkstring s2) const
+{
+ char out[300];
+ std::sort(s1.begin(), s1.end());
+ std::sort(s2.begin(), s2.end());
+ *std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out) = 0;
+ return out;
+}
+bkstring Pattern::classIntersect (bkstring s1, bkstring s2) const
+{
+ char out[300];
+ std::sort(s1.begin(), s1.end());
+ std::sort(s2.begin(), s2.end());
+ *std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), out) = 0;
+ return out;
+}
+bkstring Pattern::classNegate (bkstring s1) const
+{
+ char out[300];
+ int i, ind = 0;
+ std::map<char, bool> m;
+
+ for (i = 0; i < (int)s1.size(); ++i) m[s1[i]] = 1;
+ for (i = 0xFF; i >= 0; --i) if (m.find((char)i) == m.end()) out[ind++] = (char)i;
+ out[ind] = 0;
+ return bkstring(out, ind);
+}
+bkstring Pattern::classCreateRange(char low, char hi) const
+{
+ char out[300];
+ int ind = 0;
+ while (low != hi) out[ind++] = low++;
+ out[ind++] = low;
+ return bkstring(out, ind);
+}
+
+int Pattern::getInt(int start, int end)
+{
+ int ret = 0;
+ for (; start <= end; ++start) ret = ret * 10 + (pattern[start] - '0');
+ return ret;
+}
+bool Pattern::quantifyCurly(int & sNum, int & eNum)
+{
+ bool good = 1;
+ int i, ci = curInd + 1;
+ int commaInd = ci, endInd = ci, len = (int)pattern.size();
+ sNum = eNum = 0;
+
+ while (endInd < len && pattern[endInd ] != '}') ++endInd;
+ while (commaInd < endInd && pattern[commaInd] != ',') ++commaInd;
+ if (endInd >= len) { raiseError(); return 0; }
+ for (i = ci; good && i < endInd; ++i) if (i != commaInd && !isdigit(pattern[i])) good = 0;
+ if (!good && commaInd < endInd) { raiseError(); return 0; }
+ if (!good) return 0;
+ /* so now everything in here is either a comma (and there is at most one comma) or a digit */
+ if (commaInd == ci) // {,*}
+ {
+ if (endInd == commaInd + 1) { sNum = MIN_QMATCH; eNum = MAX_QMATCH; } // {,} = *
+ else { sNum = MIN_QMATCH; eNum = getInt(commaInd + 1, endInd - 1); } // {,+}
+ }
+ else if (commaInd == endInd - 1) { sNum = getInt(ci, commaInd - 1); eNum = MAX_QMATCH; } // {+,}
+ else if (commaInd == endInd) { sNum = getInt(ci, endInd - 1); eNum = sNum; } // {+}
+ else { sNum = getInt(ci, commaInd - 1); eNum = getInt(commaInd + 1, endInd - 1); } // {+,+}
+ curInd = endInd + 1;
+ return 1;
+}
+NFANode * Pattern::quantifyGroup(NFANode * start, NFANode * stop, const int gn)
+{
+ NFANode * newNode = NULL;
+ int type = 0;
+
+ if (curInd < (int)pattern.size())
+ {
+ char ch = (curInd + 1 >= (int)pattern.size()) ? -1 : pattern[curInd + 1];
+ switch (pattern[curInd])
+ {
+ case '*':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; type = 1; break;
+ case '+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopNode(start, MIN_QMATCH, MAX_QMATCH, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case '?':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; type = 1; break;
+ case '+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopNode(start, MIN_QMATCH, 1, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case '+':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; type = 1; break;
+ case '+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopNode(start, 1, MAX_QMATCH, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case '{':
+ {
+ int s, e;
+ if (quantifyCurly(s, e))
+ {
+ ch = (curInd < (int)pattern.size()) ? pattern[curInd] : -1;
+ switch (ch)
+ {
+ case '?': ++curInd; type = 1; break;
+ case '+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopNode(start, s, e, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ }
+ }
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+NFANode * Pattern::quantify(NFANode * newNode)
+{
+ if (curInd < (int)pattern.size())
+ {
+ char ch = (curInd + 1 >= (int)pattern.size()) ? -1 : pattern[curInd + 1];
+ switch (pattern[curInd])
+ {
+ case '*':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; newNode = registerNode(new NFALazyQuantifierNode (this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ case '+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierNode(this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierNode (this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ }
+ break;
+ case '?':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; newNode = registerNode(new NFALazyQuantifierNode (this, newNode, MIN_QMATCH, 1)); break;
+ case '+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierNode(this, newNode, MIN_QMATCH, 1)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierNode (this, newNode, MIN_QMATCH, 1)); break;
+ }
+ break;
+ case '+':
+ ++curInd;
+ switch (ch)
+ {
+ case '?': ++curInd; newNode = registerNode(new NFALazyQuantifierNode (this, newNode, 1, MAX_QMATCH)); break;
+ case '+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierNode(this, newNode, 1, MAX_QMATCH)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierNode (this, newNode, 1, MAX_QMATCH)); break;
+ }
+ break;
+ case '{':
+ {
+ int s, e;
+ if (quantifyCurly(s, e))
+ {
+ ch = (curInd < (int)pattern.size()) ? pattern[curInd] : -1;
+ switch (ch)
+ {
+ case '?': ++curInd; newNode = registerNode(new NFALazyQuantifierNode (this, newNode, s, e)); break;
+ case '+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierNode(this, newNode, s, e)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierNode (this, newNode, s, e)); break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return newNode;
+}
+bkstring Pattern::parseClass()
+{
+ bkstring t, ret = "";
+ char ch, c1, c2;
+ bool inv = 0, neg = 0, quo = 0;
+
+ if (curInd < (int)pattern.size() && pattern[curInd] == '^')
+ {
+ ++curInd;
+ neg = 1;
+ }
+ while (curInd < (int)pattern.size() && pattern[curInd] != ']')
+ {
+ ch = pattern[curInd++];
+ if (ch == '[')
+ {
+ t = parseClass();
+ ret = classUnion(ret, t);
+ }
+ /*else if (ch == '-')
+ {
+ raiseError();
+ curInd = pattern.size();
+ }*/
+ else if (ch == '&' && curInd < (int)pattern.size() && pattern[curInd] == '&')
+ {
+ if (pattern[++curInd] != '[')
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else
+ {
+ ++curInd;
+ t = parseClass();
+ ret = classIntersect(ret, t);
+ }
+ }
+ else if (ch == '\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else if (inv || t.size() > 1) // cant be part of a range (a-z)
+ {
+ if (inv) t = classNegate(t);
+ ret = classUnion(ret, t);
+ }
+ else if (curInd < (int)pattern.size() && pattern[curInd] == '-') // part of a range (a-z)
+ {
+ c1 = t[0];
+ ++curInd;
+ if (curInd >= (int)pattern.size()) raiseError();
+ else
+ {
+ c2 = pattern[curInd++];
+ if (c2 == '\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else if (inv || t.size() > 1) raiseError();
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ else if (c2 == '[' || c2 == ']' || c2 == '-' || c2 == '&')
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ }
+ else
+ {
+ ret = classUnion(ret, t);
+ }
+ }
+ else if (curInd < (int)pattern.size() && pattern[curInd] == '-')
+ {
+ c1 = ch;
+ ++curInd;
+ if (curInd >= (int)pattern.size()) raiseError();
+ else
+ {
+ c2 = pattern[curInd++];
+ if (c2 == '\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else if (inv || t.size() > 1) raiseError();
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ else if (c2 == '[' || c2 == ']' || c2 == '-' || c2 == '&')
+ {
+ raiseError();
+ curInd = (int)pattern.size();
+ }
+ else
+ {
+ ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ }
+ }
+ else
+ {
+ ret += " ";
+ ret[ret.size() - 1] = ch;
+ }
+ }
+ if (curInd >= (int)pattern.size() || pattern[curInd] != ']')
+ {
+ raiseError();
+ ret = "";
+ }
+ else
+ {
+ ++curInd;
+ if (neg) ret = classNegate(ret);
+ }
+ return ret;
+}
+bkstring Pattern::parsePosix()
+{
+ bkstring s7 = pattern.substr(curInd, 7);
+ if (s7 == "{Lower}") { curInd += 7; return "abcdefghijklmnopqrstuvwxyz"; }
+ if (s7 == "{Upper}") { curInd += 7; return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
+ if (s7 == "{Alpha}") { curInd += 7; return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
+ if (s7 == "{Digit}") { curInd += 7; return "0123456789"; }
+ if (s7 == "{Alnum}") { curInd += 7; return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; }
+ if (s7 == "{Punct}") { curInd += 7; return "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == "{Graph}") { curInd += 7; return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == "{Print}") { curInd += 7; return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == "{Blank}") { curInd += 7; return " \t"; }
+ if (s7 == "{Space}") { curInd += 7; return " \t\n\x0B\f\r"; }
+ if (s7 == "{Cntrl}")
+ {
+ bkstring::value_type i;
+ bkstring s = " ";
+
+ for (i = 0; i < 5; ++i) s += s;
+ s += " ";
+ for (i = 0; i <= 0x1F; ++i) s[i] = i;
+ s[0x20] = 0x7F;
+ curInd += 7;
+ return s;
+ }
+ if (s7 == "{ASCII}")
+ {
+ bkstring s(0x80, ' ');
+ for (int i = 0; i <= 0x7f; ++i) s[i] = (bkstring::value_type)i;
+ curInd += 7;
+ return s;
+ }
+ if (pattern.substr(curInd, 8) == "{XDigit}") { curInd += 8; return "abcdefABCDEF0123456789"; }
+ raiseError();
+ return "";
+}
+NFANode * Pattern::parseBackref()
+{
+ #define is_dig(x) ((x) >= '0' && (x) <= '9')
+ #define to_int(x) ((x) - '0')
+ int ci = curInd;
+ int oldRef = 0, ref = 0;
+
+ while (ci < (int)pattern.size() && is_dig(pattern[ci]) && (ref < 10 || ref < groupCount))
+ {
+ oldRef = ref;
+ ref = ref * 10 + to_int(pattern[ci++]);
+ }
+ if (ci == (int)pattern.size())
+ {
+ oldRef = ref;
+ ++ci;
+ }
+ if (oldRef < 0 || ci <= curInd)
+ {
+ raiseError();
+ return registerNode(new NFAReferenceNode(-1));
+ }
+ curInd = ci;
+ return registerNode(new NFAReferenceNode(ref));
+
+ #undef is_dig
+ #undef to_int
+}
+bkstring Pattern::parseOctal()
+{
+ #define islowoc(x) ((x) >= '0' && (x) <= '3')
+ #define isoc(x) ((x) >= '0' && (x) <= '7')
+ #define fromoc(x) ((x) - '0')
+ int ci = curInd;
+ char ch1 = (ci + 0 < (int)pattern.size()) ? pattern[ci + 0] : -1;
+ char ch2 = (ci + 1 < (int)pattern.size()) ? pattern[ci + 1] : -1;
+ char ch3 = (ci + 2 < (int)pattern.size()) ? pattern[ci + 2] : -1;
+ bkstring s = " ";
+
+ if (islowoc(ch1) && isoc(ch2))
+ {
+ curInd += 2;
+ s[0] = fromoc(ch1) * 8 + fromoc(ch2);
+ if (isoc(ch3))
+ {
+ ++curInd;
+ s[0] = s[0] * 8 + fromoc(ch3);
+ }
+ }
+ else if (isoc(ch1) && isoc(ch2))
+ {
+ curInd += 2;
+ s[0] = fromoc(ch1) * 8 + fromoc(ch2);
+ }
+ else raiseError();
+
+ return s;
+ #undef islowoc
+ #undef isoc
+ #undef fromoc
+}
+bkstring Pattern::parseHex()
+{
+ #define to_low(x) (((x) >= 'A' && (x) <= 'Z') ? ((x) - 'A' + 'a') : (x))
+ #define is_dig(x) ((x) >= '0' && (x) <= '9')
+ #define is_hex(x) (is_dig(x) || (to_low(x) >= 'a' && to_low(x) <= 'f'))
+ #define to_int(x) ((is_dig(x)) ? ((x) - '0') : (to_low(x) - 'a' + 10))
+
+ int ci = curInd;
+ char ch1 = (ci + 0 < (int)pattern.size()) ? pattern[ci + 0] : -1;
+ char ch2 = (ci + 1 < (int)pattern.size()) ? pattern[ci + 1] : -1;
+ bkstring s = " ";
+
+ if (is_hex(ch1) && is_hex(ch2))
+ {
+ curInd += 2;
+ s[0] = (to_int(ch1) << 4 & 0xF0) | (to_int(ch2) & 0x0F);
+ }
+
+ return s;
+ #undef to_low
+ #undef is_dig
+ #undef is_hex
+ #undef to_int
+}
+bkstring Pattern::parseEscape(bool & inv, bool & quo)
+{
+ char ch = pattern[curInd++];
+ bkstring classes = "";
+
+ if (curInd > (int)pattern.size())
+ {
+ raiseError();
+ return NULL;
+ }
+
+ quo = 0;
+ inv = 0;
+ switch (ch)
+ {
+ case 'p': classes = parsePosix(); break;
+ case 'P': classes = "!!"; classes += parsePosix(); break;
+ case 'd': classes = "0123456789"; break;
+ case 'D': classes = "!!0123456789"; break;
+ case 's': classes = " \t\r\n\f"; break;
+ case 'S': classes = "!! \t\r\n\f"; break;
+ case 'w': classes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; break;
+ case 'W': classes = "!!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; break;
+ case '0': classes = parseOctal(); break;
+ case 'x': classes = parseHex(); break;
+
+ case 'Q': quo = 1; break;
+ case 't': classes = "\t"; break;
+ case 'r': classes = "\r"; break;
+ case 'n': classes = "\n"; break;
+ case 'f': classes = "\f"; break;
+ case 'a': classes = "\a"; break;
+ case 'e': classes = "\r"; break;
+ default: classes = " "; classes[0] = ch; break;
+ }
+ if (classes.substr(0, 2) == "!!")
+ {
+ classes = classes.substr(2);
+ inv = 1;
+ }
+ return classes;
+}
+NFANode * Pattern::parseRegisteredPattern(NFANode ** end)
+{
+ int i, j;
+ bkstring s;
+ NFANode * ret = NULL;
+ for (i = curInd; i < (int)pattern.size() && pattern[i] != '}'; ++i) { }
+ if (pattern[i] != '}') { raiseError(); return NULL; }
+ if (i == curInd + 1) { raiseError(); return NULL; } // {}
+ if (
+ !(
+ (pattern[curInd] >= 'a' && pattern[curInd] <= 'z') ||
+ (pattern[curInd] >= 'A' && pattern[curInd] <= 'Z') ||
+ (pattern[curInd] == '_')
+ )
+ )
+ {
+ raiseError();
+ return NULL;
+ }
+ for (j = curInd; !error && j < i; ++j)
+ {
+ if (
+ !(
+ (pattern[j] >= 'a' && pattern[j] <= 'z') ||
+ (pattern[j] >= 'A' && pattern[j] <= 'Z') ||
+ (pattern[j] >= '0' && pattern[j] <= '9') ||
+ (pattern[j] == '_')
+ )
+ )
+ {
+ raiseError();
+ return NULL;
+ }
+ }
+ s = pattern.substr(curInd, i - curInd);
+ if (registeredPatterns.find(s) == registeredPatterns.end()) raiseError();
+ else
+ {
+ unsigned long oflags = flags;
+ bkstring op = pattern;
+ int ci = i + 1;
+
+ pattern = registeredPatterns[s].first;
+ curInd = 0;
+ flags = registeredPatterns[s].second;
+
+ --groupCount;
+ ret = parse(0, 0, end);
+
+ pattern = op;
+ curInd = ci;
+ flags = oflags;
+ }
+ if (error) { *end = ret = NULL; }
+ return ret;
+}
+
+// look behind should interpret everything as a literal (except \\) since the
+// pattern must have a concrete length
+NFANode * Pattern::parseBehind(const bool pos, NFANode ** end)
+{
+ bkstring t = "";
+ while (curInd < (int)pattern.size() && pattern[curInd] != ')')
+ {
+ char ch = pattern[curInd++];
+ t += " ";
+ if (ch == '\\')
+ {
+ if (curInd + 1 >= (int)pattern.size())
+ {
+ raiseError();
+ return *end = registerNode(new NFACharNode(' '));
+ }
+ ch = pattern[curInd++];
+ }
+ t[t.size() - 1] = ch;
+ }
+ if (curInd >= (int)pattern.size() || pattern[curInd] != ')') raiseError();
+ else ++curInd;
+ return *end = registerNode(new NFALookBehindNode(t, pos));
+}
+NFANode * Pattern::parseQuote()
+{
+ bool done = 0;
+ bkstring s = "";
+
+ while (!done)
+ {
+ if (curInd >= (int)pattern.size())
+ {
+ raiseError();
+ done = 1;
+ }
+ else if (pattern.substr(curInd, 2) == "\\E")
+ {
+ curInd += 2;
+ done = 1;
+ }
+ else if (pattern[curInd] == '\\')
+ {
+ s += " ";
+ s[s.size() - 1] = pattern[++curInd];
+ ++curInd;
+ }
+ else
+ {
+ s += " ";
+ s[s.size() - 1] = pattern[curInd++];
+ }
+ }
+ if ((flags & Pattern::CASE_INSENSITIVE) != 0) return registerNode(new NFACIQuoteNode(s));
+ return registerNode(new NFAQuoteNode(s));
+}
+NFANode * Pattern::parse(const bool inParen, const bool inOr, NFANode ** end)
+{
+ NFANode * start, * cur, * next = NULL;
+ bkstring t;
+ int grc = groupCount++;
+ bool inv, quo;
+ bool ahead = 0, pos = 0, noncap = 0, indep = 0;
+ unsigned long oldFlags = flags;
+
+ if (inParen)
+ {
+ if (pattern[curInd] == '?')
+ {
+ ++curInd;
+ --groupCount;
+ if (pattern[curInd] == ':') { noncap = 1; ++curInd; grc = --nonCapGroupCount; }
+ else if (pattern[curInd] == '=') { ++curInd; ahead = 1; pos = 1; }
+ else if (pattern[curInd] == '!') { ++curInd; ahead = 1; pos = 0; }
+ else if (pattern.substr(curInd, 2) == "<=") { curInd += 2; return parseBehind(1, end); }
+ else if (pattern.substr(curInd, 2) == "<!") { curInd += 2; return parseBehind(0, end); }
+ else if (pattern[curInd] == '>') { ++curInd; indep = 1; }
+ else
+ {
+ bool negate = false, done = false;
+ while (!done)
+ {
+ if (curInd >= (int)pattern.size())
+ {
+ raiseError();
+ return NULL;
+ }
+ else if (negate)
+ {
+ switch (pattern[curInd])
+ {
+ case 'i': flags &= ~Pattern::CASE_INSENSITIVE; break;
+ case 'd': flags &= ~Pattern::UNIX_LINE_MODE; break;
+ case 'm': flags &= ~Pattern::MULTILINE_MATCHING; break;
+ case 's': flags &= ~Pattern::DOT_MATCHES_ALL; break;
+ case ':': done = true; break;
+ case ')':
+ ++curInd;
+ *end = registerNode(new NFALookBehindNode("", true));
+ return *end;
+ case '-':
+ default: raiseError(); return NULL;
+ }
+ }
+ else
+ {
+ switch (pattern[curInd])
+ {
+ case 'i': flags |= Pattern::CASE_INSENSITIVE; break;
+ case 'd': flags |= Pattern::UNIX_LINE_MODE; break;
+ case 'm': flags |= Pattern::MULTILINE_MATCHING; break;
+ case 's': flags |= Pattern::DOT_MATCHES_ALL; break;
+ case ':': done = true; break;
+ case '-': negate = true; break;
+ case ')':
+ ++curInd;
+ *end = registerNode(new NFALookBehindNode("", true));
+ return *end;
+ default: raiseError(); return NULL;
+ }
+ }
+ ++curInd;
+ }
+ noncap = 1;
+ grc = --nonCapGroupCount;
+ }
+ if (noncap) cur = start = registerNode(new NFAGroupHeadNode(grc));
+ else cur = start = registerNode(new NFASubStartNode);
+ }
+ else cur = start = registerNode(new NFAGroupHeadNode(grc));
+ }
+ else cur = start = registerNode(new NFASubStartNode);
+ while (curInd < (int)pattern.size())
+ {
+ char ch = pattern[curInd++];
+
+ next = NULL;
+ if (error) return NULL;
+ switch (ch)
+ {
+ case '^':
+ if ((flags & Pattern::MULTILINE_MATCHING) != 0) next = registerNode(new NFAStartOfLineNode);
+ else next = registerNode(new NFAStartOfInputNode);
+ break;
+ case '$':
+ if ((flags & Pattern::MULTILINE_MATCHING) != 0) next = registerNode(new NFAEndOfLineNode);
+ else next = registerNode(new NFAEndOfInputNode(0));
+ break;
+ case '|':
+ --groupCount;
+ cur->next = registerNode(new NFAAcceptNode);
+ cur = start = registerNode(new NFAOrNode(start, parse(inParen, 1)));
+ break;
+ case '\\':
+ if (curInd < (int)pattern.size())
+ {
+ bool eoi = 0;
+ switch (pattern[curInd])
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': next = parseBackref(); break;
+ case 'A': ++curInd; next = registerNode(new NFAStartOfInputNode); break;
+ case 'B': ++curInd; next = registerNode(new NFAWordBoundaryNode(0)); break;
+ case 'b': ++curInd; next = registerNode(new NFAWordBoundaryNode(1)); break;
+ case 'G': ++curInd; next = registerNode(new NFAEndOfMatchNode); break;
+ case 'Z': eoi = 1;
+ case 'z': ++curInd; next = registerNode(new NFAEndOfInputNode(eoi)); break;
+ default:
+ t = parseEscape(inv, quo);
+ if (!quo)
+ {
+ if (t.size() > 1 || inv)
+ {
+ if ((flags & Pattern::CASE_INSENSITIVE) != 0) next = registerNode(new NFACIClassNode(t, inv));
+ else next = registerNode(new NFAClassNode(t, inv));
+ }
+ else
+ {
+ next = registerNode(new NFACharNode(t[0]));
+ }
+ }
+ else
+ {
+ next = parseQuote();
+ }
+ }
+ }
+ else raiseError();
+ break;
+ case '[':
+ if ((flags & Pattern::CASE_INSENSITIVE) == 0)
+ {
+ NFAClassNode * clazz = new NFAClassNode();
+ bkstring s = parseClass();
+ for (int i = 0; i < (int)s.size(); ++i) clazz->vals[s[i]] = 1;
+ next = registerNode(clazz);
+ }
+ else
+ {
+ NFACIClassNode * clazz = new NFACIClassNode();
+ bkstring s = parseClass();
+ for (int i = 0; i < (int)s.size(); ++i) clazz->vals[to_lower(s[i])] = 1;
+ next = registerNode(clazz);
+ }
+ break;
+ case '.':
+ {
+ bool useN = 1, useR = 1;
+ NFAClassNode * clazz = new NFAClassNode(1);
+ if ((flags & Pattern::UNIX_LINE_MODE) != 0) useR = 0;
+ if ((flags & Pattern::DOT_MATCHES_ALL) != 0) useN = useR = 0;
+ if (useN) clazz->vals['\n'] = 1;
+ if (useR) clazz->vals['\r'] = 1;
+ next = registerNode(clazz);
+ }
+ break;
+ case '(':
+ {
+ NFANode * end, * t1, * t2;
+ t1 = parse(1, 0, &end);
+ if (!t1) raiseError();
+ else if (t1->isGroupHeadNode() && (t2 = quantifyGroup(t1, end, grc)) != NULL)
+ {
+ cur->next = t2;
+ cur = t2->next;
+ }
+ else
+ {
+ cur->next = t1;
+ cur = end;
+ }
+ }
+ break;
+ case ')':
+ if (!inParen) raiseError();
+ else if (inOr)
+ {
+ --curInd;
+ cur = cur->next = registerNode(new NFAAcceptNode);
+ flags = oldFlags;
+ return start;
+ }
+ else
+ {
+ if (ahead)
+ {
+ cur = cur->next = registerNode(new NFAAcceptNode);
+ flags = oldFlags;
+ return *end = registerNode(new NFALookAheadNode(start, pos));
+ }
+ else if (indep)
+ {
+ cur = cur->next = registerNode(new NFAAcceptNode);
+ flags = oldFlags;
+ return *end = registerNode(new NFAPossessiveQuantifierNode(this, start, 1, 1));
+ }
+ else // capping or noncapping, it doesnt matter
+ {
+ *end = cur = cur->next = registerNode(new NFAGroupTailNode(grc));
+ next = quantifyGroup(start, *end, grc);
+ if (next)
+ {
+ start = next;
+ *end = next->next;
+ }
+ flags = oldFlags;
+ return start;
+ }
+ }
+ break;
+ case '{': // registered pattern
+ cur->next = parseRegisteredPattern(&next);
+ if (cur->next) cur = next;
+ break;
+ case '*':
+ case '+':
+ case '?':
+// case '}':
+// case ']':
+ raiseError();
+ break;
+ default:
+ if ((flags & Pattern::CASE_INSENSITIVE) != 0) next = registerNode(new NFACICharNode(ch));
+ else next = registerNode(new NFACharNode(ch));
+ break;
+ }
+ if (next)
+ {
+ cur = cur->next = quantify(next);
+ }
+ }
+ if (inParen) raiseError();
+ else
+ {
+ if (inOr) cur = cur->next = registerNode(new NFAAcceptNode);
+ if (end) *end = cur;
+ }
+
+ flags = oldFlags;
+ if (error) return NULL;
+
+ return start;
+}
+
+Pattern * Pattern::compile(const bkstring & pattern, const unsigned long mode)
+{
+ Pattern * p = new Pattern(pattern);
+ NFANode * end;
+
+ p->flags = mode;
+ if ((mode & Pattern::LITERAL) != 0)
+ {
+ p->head = p->registerNode(new NFAStartNode);
+ if ((mode & Pattern::CASE_INSENSITIVE) != 0) p->head->next = p->registerNode(new NFACIQuoteNode(pattern));
+ else p->head->next = p->registerNode(new NFAQuoteNode(pattern));
+ p->head->next->next = p->registerNode(new NFAEndNode);
+ }
+ else
+ {
+ p->head = p->parse(0, 0, &end);
+ if (!p->head)
+ {
+ delete p;
+ p = NULL;
+ }
+ else
+ {
+ if (!(p->head && p->head->isStartOfInputNode()))
+ {
+ NFANode * n = p->registerNode(new NFAStartNode);
+ n->next = p->head;
+ p->head = n;
+ }
+ end->next = p->registerNode(new NFAEndNode);
+ }
+ }
+ if (p != NULL)
+ {
+ p->matcher = new Matcher(p, "");
+ }
+
+ return p;
+}
+
+Pattern * Pattern::compileAndKeep(const bkstring & pattern, const unsigned long mode)
+{
+ Pattern * ret = NULL;
+ std::map<bkstring, Pattern*>::iterator it = compiledPatterns.find(pattern);
+
+ if (it != compiledPatterns.end())
+ {
+ ret = it->second;
+ }
+ else
+ {
+ ret = compile(pattern, mode);
+ compiledPatterns[pattern] = ret;
+ }
+
+ return ret;
+}
+bkstring Pattern::replace(const bkstring & pattern, const bkstring & str,
+ const bkstring & replacementText, const unsigned long mode)
+{
+ bkstring ret;
+ Pattern * p = Pattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->replace(str, replacementText);
+ delete p;
+ }
+ return ret;
+}
+
+std::vector<bkstring> Pattern::split(const bkstring & pattern, const bkstring & str, const bool keepEmptys,
+ const unsigned long limit, const unsigned long mode)
+{
+ std::vector<bkstring> ret;
+ Pattern * p = Pattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->split(str, keepEmptys, limit);
+ delete p;
+ }
+ return ret;
+}
+
+std::vector<bkstring> Pattern::findAll(const bkstring & pattern, const bkstring & str, const unsigned long mode)
+{
+ std::vector<bkstring> ret;
+ Pattern * p = Pattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->findAll(str);
+ delete p;
+ }
+ return ret;
+}
+
+bool Pattern::matches(const bkstring & pattern, const bkstring & str, const unsigned long mode)
+{
+ bool ret = 0;
+ Pattern * p = compile(pattern, mode);
+
+ if (p)
+ {
+ ret = p->matches(str);
+ delete p;
+ }
+
+ return ret;
+}
+
+bool Pattern::registerPattern(const bkstring & name, const bkstring & pattern, const unsigned long mode)
+{
+ Pattern * p = Pattern::compile(pattern, mode);
+ if (!p) return 0;
+ Pattern::registeredPatterns[name] = std::make_pair(pattern, mode);
+ delete p;
+ return 1;
+}
+
+void Pattern::unregisterPatterns()
+{
+ registeredPatterns.clear();
+}
+void Pattern::clearPatternCache()
+{
+ std::map<bkstring, Pattern*>::iterator it;
+ for (it = compiledPatterns.begin(); it != compiledPatterns.end(); ++it)
+ {
+ delete it->second;
+ }
+ compiledPatterns.clear();
+}
+
+std::pair<bkstring, int> Pattern::findNthMatch(const bkstring & pattern, const bkstring & str,
+ const int matchNum, const unsigned long mode)
+{
+ std::pair<bkstring, int> ret;
+ Pattern * p = Pattern::compile(pattern, mode);
+
+ ret.second = -1;
+ if (p)
+ {
+ int i = -1;
+ p->matcher->setString(str);
+ while (i < matchNum && p->matcher->findNextMatch()) { ++i; }
+ if (i == matchNum && p->matcher->getStartingIndex() >= 0)
+ {
+ ret.first = p->matcher->getGroup(0);
+ ret.second = p->matcher->getStartingIndex();
+ }
+ delete p;
+ }
+
+ return ret;
+}
+
+Pattern::~Pattern()
+{
+ if (matcher) delete matcher;
+ for (std::map<NFANode*, bool>::iterator it = nodes.begin(); it != nodes.end(); ++it)
+ {
+ delete it->first;
+ }
+}
+bkstring Pattern::replace(const bkstring & str, const bkstring & replacementText)
+{
+ int li = 0;
+ bkstring ret = "";
+
+ matcher->setString(str);
+ while (matcher->findNextMatch())
+ {
+ ret += str.substr(li, matcher->getStartingIndex() - li);
+ ret += matcher->replaceWithGroups(replacementText);
+ li = matcher->getEndingIndex();
+ }
+ ret += str.substr(li);
+
+ return ret;
+}
+std::vector<bkstring> Pattern::split(const bkstring & str, const bool keepEmptys, const unsigned long limit)
+{
+ unsigned long lim = (limit == 0 ? MAX_QMATCH : limit);
+ int li = 0;
+ std::vector<bkstring> ret;
+
+ matcher->setString(str);
+
+ while (matcher->findNextMatch() && ret.size() < lim)
+ {
+ if (matcher->getStartingIndex() == 0 && keepEmptys) ret.push_back("");
+ if ((matcher->getStartingIndex() != matcher->getEndingIndex()) || keepEmptys)
+ {
+ if (li != matcher->getStartingIndex() || keepEmptys)
+ {
+ ret.push_back(str.substr(li, matcher->getStartingIndex() - li));
+ }
+ li = matcher->getEndingIndex();
+ }
+ }
+ if (li < (int)str.size()) ret.push_back(str.substr(li));
+
+ return ret;
+}
+std::vector<bkstring> Pattern::findAll(const bkstring & str)
+{
+ matcher->setString(str);
+ return matcher->findAll();
+}
+bool Pattern::matches(const bkstring & str)
+{
+ matcher->setString(str);
+ return matcher->matches();
+}
+unsigned long Pattern::getFlags() const
+{
+ return flags;
+}
+bkstring Pattern::getPattern() const
+{
+ return pattern;
+}
+Matcher * Pattern::createMatcher(const bkstring & str)
+{
+ return new Matcher(this, str);
+}
+
+// NFANode
+
+NFANode::NFANode() { next = NULL; }
+NFANode::~NFANode() { }
+void NFANode::findAllNodes(std::map<NFANode*, bool> & soFar)
+{
+ if (soFar.find(this) == soFar.end()) return;
+ soFar[this] = 1;
+ if (next) next->findAllNodes(soFar);
+}
+
+// NFACharNode
+
+NFACharNode::NFACharNode(const char c) { ch = c; }
+int NFACharNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && str[curInd] == ch) return next->match(str, matcher, curInd + 1);
+ return -1;
+}
+
+// NFACICharNode
+
+NFACICharNode::NFACICharNode(const char c) { ch = to_lower(c); }
+int NFACICharNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && to_lower(str[curInd]) == ch) return next->match(str, matcher, curInd + 1);
+ return -1;
+}
+
+// NFAStartNode
+
+NFAStartNode::NFAStartNode() { }
+int NFAStartNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ret = -1, ci = curInd;
+
+ matcher->starts[0] = curInd;
+ if ((matcher->getFlags() & Matcher::MATCH_ENTIRE_STRING) == (unsigned int)Matcher::MATCH_ENTIRE_STRING)
+ {
+ if (curInd != 0)
+ {
+ matcher->starts[0] = -1;
+ return -1;
+ }
+ return next->match(str, matcher, 0);
+ }
+ while ((ret = next->match(str, matcher, ci)) == -1 && ci < (int)str.size())
+ {
+ matcher->clearGroups();
+ matcher->starts[0] = ++ci;
+ }
+ if (ret < 0) matcher->starts[0] = -1;
+ return ret;
+}
+
+// NFAEndNode
+
+NFAEndNode::NFAEndNode() { }
+int NFAEndNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ matcher->ends[0] = curInd;
+ if ((matcher->getFlags() & Matcher::MATCH_ENTIRE_STRING) != 0)
+ {
+ if (curInd == (int)str.size()) return curInd;
+ matcher->ends[0] = -1;
+ return -1;
+ }
+ return curInd;
+}
+
+// NFAQuantifierNode
+
+void NFAQuantifierNode::findAllNodes(std::map<NFANode*, bool> & soFar)
+{
+ inner->findAllNodes(soFar);
+ NFANode::findAllNodes(soFar);
+}
+NFAQuantifierNode::NFAQuantifierNode(Pattern * pat, NFANode * internal, const int minMatch, const int maxMatch)
+{
+ inner = internal;
+ inner->next = pat->registerNode(new NFAAcceptNode);
+ min = (minMatch < Pattern::MIN_QMATCH) ? Pattern::MIN_QMATCH : minMatch;
+ max = (maxMatch > Pattern::MAX_QMATCH) ? Pattern::MAX_QMATCH : maxMatch;
+}
+
+int NFAQuantifierNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int i0, i1, i2 = 0;
+
+ i0 = i1 = curInd;
+ while (i2 < min)
+ {
+
+ ++i2;
+ i1 = inner->match(str, matcher, i0);
+ if (i1 <= i0) return i1; // i1 < i0 means i1 is -1
+ i0 = i1;
+ }
+
+ return i1;
+}
+// NFAGreedyQuantifierNode
+
+NFAGreedyQuantifierNode::NFAGreedyQuantifierNode(Pattern * pat, NFANode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierNode(pat, internal, minMatch, maxMatch) { }
+int NFAGreedyQuantifierNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int t = NFAQuantifierNode::match(str, matcher, curInd);
+ if (t != -1) return matchInternal(str, matcher, t, min);
+ return t;
+}
+int NFAGreedyQuantifierNode::matchInternal(const bkstring & str, Matcher * matcher, const int curInd, const int soFar) const
+{
+ if (soFar >= max) return next->match(str, matcher, curInd);
+
+ int i, j;
+
+ i = inner->match(str, matcher, curInd);
+ if (i != -1)
+ {
+ j = matchInternal(str, matcher, i, soFar + 1);
+ if (j != -1) return j;
+ }
+ return next->match(str, matcher, curInd);
+}
+
+// NFALazyQuantifierNode
+
+NFALazyQuantifierNode::NFALazyQuantifierNode(Pattern * pat, NFANode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierNode(pat, internal, minMatch, maxMatch) { }
+int NFALazyQuantifierNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int i, j, m = NFAQuantifierNode::match(str, matcher, curInd);
+
+ if (m == -1) return -1;
+
+ for (i = min; i < max; ++i)
+ {
+ j = next->match(str, matcher, m);
+ if (j == -1)
+ {
+ j = inner->match(str, matcher, m);
+ // if j < m, then j is -1, so we bail.
+ // if j == m, then we would just go and call next->match on the same index,
+ // but it already failed trying to match right there, so we know we can
+ // just bail
+ if (j <= m) return -1;
+ m = j;
+ }
+ else return j;
+ }
+ return next->match(str, matcher, m);
+}
+
+// NFAPossessiveQuantifierNode
+
+NFAPossessiveQuantifierNode::NFAPossessiveQuantifierNode(Pattern * pat, NFANode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierNode(pat, internal, minMatch, maxMatch) { }
+int NFAPossessiveQuantifierNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int i, j, m = NFAQuantifierNode::match(str, matcher, curInd);
+
+ if (m == -1) return -1;
+ for (i = min; i < max; ++i)
+ {
+ j = inner->match(str, matcher, m);
+ if (j <= m) return next->match(str, matcher, m);
+ m = j;
+ }
+ return next->match(str, matcher, m);
+}
+
+// NFAAcceptNode
+
+NFAAcceptNode::NFAAcceptNode() { }
+int NFAAcceptNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (!next) return curInd;
+ else return next->match(str, matcher, curInd);
+}
+
+// NFAClassNode
+
+NFAClassNode::NFAClassNode(const bool invert)
+{
+ inv = invert;
+}
+NFAClassNode::NFAClassNode(const bkstring & clazz, const bool invert)
+{
+ inv = invert;
+ for (int i = 0; i < (int)clazz.size(); ++i) vals[clazz[i]] = 1;
+}
+int NFAClassNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && ((vals.find(str[curInd]) != vals.end()) ^ inv))
+ {
+ return next->match(str, matcher, curInd + 1);
+ }
+ return -1;
+}
+
+// NFACIClassNode
+
+NFACIClassNode::NFACIClassNode(const bool invert)
+{
+ inv = invert;
+}
+NFACIClassNode::NFACIClassNode(const bkstring & clazz, const bool invert)
+{
+ inv = invert;
+ for (int i = 0; i < (int)clazz.size(); ++i) vals[to_lower(clazz[i])] = 1;
+}
+int NFACIClassNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && ((vals.find(to_lower(str[curInd])) != vals.end()) ^ inv))
+ {
+ return next->match(str, matcher, curInd + 1);
+ }
+ return -1;
+}
+
+// NFASubStartNode
+
+NFASubStartNode::NFASubStartNode() { }
+int NFASubStartNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ return next->match(str, matcher, curInd);
+}
+
+// NFAOrNode
+
+NFAOrNode::NFAOrNode(NFANode * first, NFANode * second) : one(first), two(second) { }
+void NFAOrNode::findAllNodes(std::map<NFANode*, bool> & soFar)
+{
+ if (one) one->findAllNodes(soFar);
+ if (two) two->findAllNodes(soFar);
+ NFANode::findAllNodes(soFar);
+}
+int NFAOrNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ci = one->match(str, matcher, curInd);
+
+ if (ci != -1) ci = next->match(str, matcher, ci);
+ if (ci != -1) return ci;
+ if (ci == -1) ci = two->match(str, matcher, curInd);
+ if (ci != -1) ci = next->match(str, matcher, ci);
+ return ci;
+}
+
+// NFAQuoteNode
+
+NFAQuoteNode::NFAQuoteNode(const bkstring & quoted) : qStr(quoted) { }
+int NFAQuoteNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd + qStr.size() > str.size()) return -1;
+ if (str.substr(curInd, qStr.size()) != qStr) return -1;
+ return next->match(str, matcher, curInd + qStr.size());
+}
+
+// NFACIQuoteNode
+
+NFACIQuoteNode::NFACIQuoteNode(const bkstring & quoted) : qStr(quoted) { }
+int NFACIQuoteNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd + qStr.size() > str.size()) return -1;
+ if (str_icmp(str.substr(curInd, qStr.size()).c_str(), qStr.c_str())) return -1;
+ return next->match(str, matcher, qStr.size());
+}
+
+// NFALookAheadNode
+
+NFALookAheadNode::NFALookAheadNode(NFANode * internal, const bool positive) : NFANode(), pos(positive), inner(internal) { }
+void NFALookAheadNode::findAllNodes(std::map<NFANode*, bool> & soFar)
+{
+ if (inner) inner->findAllNodes(soFar);
+ NFANode::findAllNodes(soFar);
+}
+int NFALookAheadNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ return ((inner->match(str, matcher, curInd) == -1) ^ pos) ? next->match(str, matcher, curInd) : -1;
+}
+
+// NFALookBehindNode
+
+NFALookBehindNode::NFALookBehindNode(const bkstring & str, const bool positive) : pos(positive), mStr(str) { }
+int NFALookBehindNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (pos)
+ {
+ if (curInd < (int)mStr.size()) return -1;
+ if (str.substr(curInd - mStr.size(), mStr.size()) == mStr) return next->match(str, matcher, curInd);
+ }
+ else
+ {
+ if (curInd < (int)mStr.size()) return next->match(str, matcher, curInd);
+ if (str.substr(curInd - mStr.size(), mStr.size()) == mStr) return -1;
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAStartOfLineNode
+
+NFAStartOfLineNode::NFAStartOfLineNode() { }
+int NFAStartOfLineNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd == 0 || str[curInd - 1] == '\n' || str[curInd - 1] == '\r')
+ {
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAEndOfLineNode
+
+NFAEndOfLineNode::NFAEndOfLineNode() { }
+int NFAEndOfLineNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd >= (int)str.size() || str[curInd] == '\n' || str[curInd] == '\r')
+ {
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAReferenceNode
+
+NFAReferenceNode::NFAReferenceNode(const int groupIndex) : gi(groupIndex) { }
+int NFAReferenceNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int len = matcher->ends[gi] - matcher->starts[gi];
+ int ni = -1;
+ if (gi < 1 || matcher->ends[gi] < matcher->starts[gi] || len == 0) ni = curInd;
+ else if (curInd + len > (int)str.size()) return -1;
+ else if (str.substr(curInd, len) != str.substr(matcher->starts[gi], len)) return -1;
+ else ni = curInd + len;
+
+ return next->match(str, matcher, ni);
+}
+
+// NFAStartOfInputNode
+
+NFAStartOfInputNode::NFAStartOfInputNode() { }
+int NFAStartOfInputNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd == 0) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAEndOfInputNode
+
+NFAEndOfInputNode::NFAEndOfInputNode(const bool lookForTerm) : term(lookForTerm) { }
+int NFAEndOfInputNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int len = (int)str.size();
+ if (curInd == len) return next->match(str, matcher, curInd);
+ else if (term)
+ {
+ if (curInd == len - 1 && (str[curInd] == '\r' || str[curInd] == '\n'))
+ {
+ return next->match(str, matcher, curInd);
+ }
+ else if (curInd == len - 2 && str.substr(curInd, 2) == "\r\n")
+ {
+ return next->match(str, matcher, curInd);
+ }
+ }
+ return -1;
+}
+
+// NFAWordBoundaryNode
+
+NFAWordBoundaryNode::NFAWordBoundaryNode(const bool positive) : pos(positive) { }
+int NFAWordBoundaryNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int len = (int)str.size();
+
+ char c1 = (curInd - 1 < len && curInd > 0) ? str[curInd - 1] : '\n';
+ char c2 = (curInd < len) ? str[curInd ] : '\n';
+
+ if (curInd == len) return next->match(str, matcher, curInd);
+ bool ok = is_alpha(c1) != is_alpha(c2);
+ if (ok && pos) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAEndOfMatchNode
+
+NFAEndOfMatchNode::NFAEndOfMatchNode() { }
+int NFAEndOfMatchNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ if (curInd == matcher->lm) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAGroupHeadNode
+
+NFAGroupHeadNode::NFAGroupHeadNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupHeadNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ret, o = matcher->starts[gi];
+
+ matcher->starts[gi] = curInd;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0) matcher->starts[gi] = o;
+
+ return ret;
+}
+
+// NFAGroupTailNode
+
+NFAGroupTailNode::NFAGroupTailNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupTailNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ret, o = matcher->ends[gi];
+
+ matcher->ends[gi] = curInd;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0) matcher->ends[gi] = o;
+
+ return ret;
+}
+
+// NFAGroupLoopPrologueNode
+
+NFAGroupLoopPrologueNode::NFAGroupLoopPrologueNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupLoopPrologueNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ret, o1 = matcher->groups[gi], o2 = matcher->groupPos[gi], o3 = matcher->groupIndeces[gi];
+
+ matcher->groups[gi] = 0;
+ matcher->groupPos[gi] = 0;
+ matcher->groupIndeces[gi] = -1;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0)
+ {
+ matcher->groups[gi] = o1;
+ matcher->groupPos[gi] = o2;
+ matcher->groupIndeces[gi] = o3;
+ }
+
+ return ret;
+}
+
+// NFAGroupLoopNode
+
+NFAGroupLoopNode::NFAGroupLoopNode(NFANode * internal, const int minMatch, const int maxMatch,
+ const int groupIndex, const int matchType)
+{
+ inner = internal;
+ min = minMatch;
+ max = maxMatch;
+ gi = groupIndex;
+ type = matchType;
+}
+void NFAGroupLoopNode::findAllNodes(std::map<NFANode*, bool> & soFar)
+{
+ if (inner) inner->findAllNodes(soFar);
+ NFANode::findAllNodes(soFar);
+}
+int NFAGroupLoopNode::match(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ bool b = (curInd > matcher->groupIndeces[gi]);
+
+ if (b && matcher->groups[gi] < min)
+ {
+ ++matcher->groups[gi];
+ int o = matcher->groupIndeces[gi];
+ matcher->groupIndeces[gi] = curInd;
+ int ret = inner->match(str, matcher, curInd);
+ if (ret < 0)
+ {
+ matcher->groupIndeces[gi] = o;
+ --matcher->groups[gi];
+ }
+ return ret;
+ }
+ else if (!b || matcher->groups[gi] >= max)
+ {
+ return next->match(str, matcher, curInd);
+ }
+ else
+ {
+ switch (type)
+ {
+ case 0: return matchGreedy(str, matcher, curInd);
+ case 1: return matchLazy(str, matcher, curInd);
+ case 2: return matchPossessive(str, matcher, curInd);
+ }
+ }
+ return -1;
+}
+int NFAGroupLoopNode::matchGreedy(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int o = matcher->groupIndeces[gi]; // save our info for backtracking
+ matcher->groupIndeces[gi] = curInd; // move along
+ ++matcher->groups[gi];
+ int ret = inner->match(str, matcher, curInd); // match internally
+ if (ret < 0)
+ { // if we failed, then restore info and match next
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ ret = next->match(str, matcher, curInd);
+ }
+ return ret;
+}
+int NFAGroupLoopNode::matchLazy(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int ret = next->match(str, matcher, curInd); // be lazy, just go on
+ if (ret < 0)
+ {
+ int o = matcher->groupIndeces[gi]; // save info for backtracking
+ matcher->groupIndeces[gi] = curInd; // advance our position
+ ++matcher->groups[gi];
+ ret = inner->match(str, matcher, curInd); // match our internal stuff
+ if (ret < 0) // if we failed, then restore the info
+ {
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ }
+ }
+ return ret;
+}
+int NFAGroupLoopNode::matchPossessive(const bkstring & str, Matcher * matcher, const int curInd) const
+{
+ int o = matcher->groupIndeces[gi]; // save info for backtracking
+ matcher->groupPos[gi] = matcher->groups[gi]; // set a flag stating we have matcher at least this much
+ matcher->groupIndeces[gi] = curInd; // move along
+ ++matcher->groups[gi];
+ int ret = inner->match(str, matcher, curInd); // try and match again
+ if (ret < 0)
+ { // if we fail, back off, but to an extent
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ if (matcher->groups[gi] == matcher->groupPos[gi]) ret = next->match(str, matcher, curInd);
+ }
+ return ret;
+}
+
+#ifdef _WIN32
+ #pragma warning(pop)
+#endif
diff --git a/plugins/SmileyAdd/src/regexp/Pattern.h b/plugins/SmileyAdd/src/regexp/Pattern.h
new file mode 100644
index 0000000000..bb16ad90fa
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/Pattern.h
@@ -0,0 +1,1663 @@
+#ifndef __PATTERN_H__
+#define __PATTERN_H__
+
+#ifdef _WIN32
+ #pragma warning(disable:4786)
+#endif
+
+#include <vector>
+#include <map>
+
+#include "bkstring.h"
+
+class Matcher;
+class NFANode;
+class NFAQuantifierNode;
+
+/**
+ This pattern class is very similar in functionality to Java's
+ java.util.regex.Pattern class. The pattern class represents an immutable
+ regular expression object. Instead of having a single object contain both the
+ regular expression object and the matching object, instead the two objects are
+ split apart. The {@link Matcher Matcher} class represents the maching
+ object.
+
+ The Pattern class works primarily off of "compiled" patterns. A typical
+ instantiation of a regular expression looks like:
+
+ <pre>
+ Pattern * p = Pattern::compile("a*b");
+ Matcher * m = p->createMatcher("aaaaaab");
+ if (m->matches()) ...
+ </pre>
+
+ However, if you do not need to use a pattern more than once, it is often times
+ okay to use the Pattern's static methods insteads. An example looks like this:
+
+ <pre>
+ if (Pattern::matches("a*b", "aaaab")) { ... }
+ </pre>
+
+ This class does not currently support unicode. The unicode update for this
+ class is coming soon.
+
+ This class is partially immutable. It is completely safe to call createMatcher
+ concurrently in different threads, but the other functions (e.g. split) should
+ not be called concurrently on the same <code>Pattern</code>.
+
+ <table border="0" cellpadding="1" cellspacing="0">
+ <tr align="left" bgcolor="#CCCCFF">
+ <td>
+ <b>Construct</b>
+ </td>
+ <td>
+ <b>Matches</b>
+ </th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Characters</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x</i></code>
+ </td>
+ <td>
+ The character <code><i>x</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\\</code>
+ </td>
+ <td>
+ The character <code>\</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\0<i>nn</i></code>
+ </td>
+ <td>
+ The character with octal ASCII value <code><i>nn</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\0<i>nnn</i></code>
+ </td>
+ <td>
+ The character with octal ASCII value <code><i>nnn</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\x<i>hh</i></code>
+ </td>
+ <td>
+ The character with hexadecimal ASCII value <code><i>hh</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\t</code>
+ </td>
+ <td>
+ A tab character
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\r</code>
+ </td>
+ <td>
+ A carriage return character
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\n</code>
+ </td>
+ <td>
+ A new-line character
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>Character Classes</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[abc]</code>
+ </td>
+ <td>
+ Either <code>a</code>, <code>b</code>, or <code>c</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[^abc]</code>
+ </td>
+ <td>
+ Any character but <code>a</code>, <code>b</code>, or <code>c</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-zA-Z]</code>
+ </td>
+ <td>
+ Any character ranging from <code>a</code> thru <code>z</code>, or
+ <code>A</code> thru <code>Z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[^a-zA-Z]</code>
+ </td>
+ <td>
+ Any character except those ranging from <code>a</code> thru
+ <code>z</code>, or <code>A</code> thru <code>Z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a\-z]</code>
+ </td>
+ <td>
+ Either <code>a</code>, <code>-</code>, or <code>z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z[A-Z]]</code>
+ </td>
+ <td>
+ Same as <code>[a-zA-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z&&[g-i]]</code>
+ </td>
+ <td>
+ Any character in the intersection of <code>a-z</code> and
+ <code>g-i</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z&&[^g-i]]</code>
+ </td>
+ <td>
+ Any character in <code>a-z</code> and not in <code>g-i</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Prefefined character classes</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><b>.</b></code>
+ </td>
+ <td>
+ Any character. Multiline matching must be compiled into the pattern for
+ <code><b>.</b></code> to match a <code>\r</code> or a <code>\n</code>.
+ Even if multiline matching is enabled, <code><b>.</b></code> will not
+ match a <code>\r\n</code>, only a <code>\r</code> or a <code>\n</code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\d</code>
+ </td>
+ <td>
+ <code>[0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\D</code>
+ </td>
+ <td>
+ <code>[^\d]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\s</code>
+ </td>
+ <td>
+ <code>[&nbsp;\t\r\n\x0B]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\S</code>
+ </td>
+ <td>
+ <code>[^\s]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\w</code>
+ </td>
+ <td>
+ <code>[a-zA-Z0-9_]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\W</code>
+ </td>
+ <td>
+ <code>[^\w]</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>POSIX character classes
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Lower}</code>
+ </td>
+ <td>
+ <code>[a-z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Upper}</code>
+ </td>
+ <td>
+ <code>[A-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{ASCII}</code>
+ </td>
+ <td>
+ <code>[\x00-\x7F]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Alpha}</code>
+ </td>
+ <td>
+ <code>[a-zA-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Digit}</code>
+ </td>
+ <td>
+ <code>[0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Alnum}</code>
+ </td>
+ <td>
+ <code>[\w&&[^_]]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Punct}</code>
+ </td>
+ <td>
+ <code>[!"#$%&'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{XDigit}</code>
+ </td>
+ <td>
+ <code>[a-fA-F0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Boundary Matches</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>^</code>
+ </td>
+ <td>
+ The beginning of a line. Also matches the beginning of input.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>$</code>
+ </td>
+ <td>
+ The end of a line. Also matches the end of input.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\b</code>
+ </td>
+ <td>
+ A word boundary
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\B</code>
+ </td>
+ <td>
+ A non word boundary
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\A</code>
+ </td>
+ <td>
+ The beginning of input
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\G</code>
+ </td>
+ <td>
+ The end of the previous match. Ensures that a "next" match will only
+ happen if it begins with the character immediately following the end of
+ the "current" match.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\Z</code>
+ </td>
+ <td>
+ The end of input. Will also match if there is a single trailing
+ <code>\r\n</code>, a single trailing <code>\r</code>, or a single
+ trailing <code>\n</code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\z</code>
+ </td>
+ <td>
+ The end of input
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Greedy Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x?</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x+</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Possessive Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x?+</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*+</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x++</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Reluctant Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x??</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*?</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x+?</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Operators</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>xy</i></code>
+ </td>
+ <td>
+ <code><i>x</i></code> then <code><i>y</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x</i></code>|<code><i>y</i></code>
+ </td>
+ <td>
+ <code><i>x</i></code> or <code><i>y</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code> as a capturing group
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Quoting</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\Q</code>
+ </td>
+ <td>
+ Nothing, but treat every character (including \s) literally until a
+ matching <code>\E</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\E</code>
+ </td>
+ <td>
+ Nothing, but ends its matching <code>\Q</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Special Constructs</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?:<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, but not as a capturing group
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?=<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via positive lookahead. This means that the
+ expression will match only if it is trailed by <code><i>x</i></code>.
+ It will not "eat" any of the characters matched by
+ <code><i>x</i></code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?!<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via negative lookahead. This means that the
+ expression will match only if it is not trailed by
+ <code><i>x</i></code>. It will not "eat" any of the characters
+ matched by <code><i>x</i></code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?<=<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via positive lookbehind. <code><i>x</i></code>
+ cannot contain any quantifiers.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?<!<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via negative lookbehind. <code><i>x</i></code>
+ cannot contain any quantifiers.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?><i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i>{1}+</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Registered Expression Matching</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>{<i>x</i>}</code>
+ </td>
+ <td>
+ The registered pattern <code><i>x</i></code>
+ </td>
+ </tr>
+ </table>
+
+ <hr>
+
+ <i>Begin Text Extracted And Modified From java.util.regex.Pattern documentation</i>
+
+ <h4> Backslashes, escapes, and quoting </h4>
+
+ <p> The backslash character (<tt>'\'</tt>) serves to introduce escaped
+ constructs, as defined in the table above, as well as to quote characters
+ that otherwise would be interpreted as unescaped constructs. Thus the
+ expression <tt>\\</tt> matches a single backslash and <tt>\{</tt> matches a
+ left brace.
+
+ <p> It is an error to use a backslash prior to any alphabetic character that
+ does not denote an escaped construct; these are reserved for future
+ extensions to the regular-expression language. A backslash may be used
+ prior to a non-alphabetic character regardless of whether that character is
+ part of an unescaped construct.
+
+ <p>It is necessary to double backslashes in string literals that represent
+ regular expressions to protect them from interpretation by a compiler. The
+ string literal <tt>"&#92;b"</tt>, for example, matches a single backspace
+ character when interpreted as a regular expression, while
+ <tt>"&#92;&#92;b"</tt> matches a word boundary. The string litera
+ <tt>"&#92;(hello&#92;)"</tt> is illegal and leads to a compile-time error;
+ in order to match the string <tt>(hello)</tt> the string literal
+ <tt>"&#92;&#92;(hello&#92;&#92;)"</tt> must be used.
+
+ <h4> Character Classes </h4>
+
+ <p> Character classes may appear within other character classes, and
+ may be composed by the union operator (implicit) and the intersection
+ operator (<tt>&amp;&amp;</tt>).
+ The union operator denotes a class that contains every character that is
+ in at least one of its operand classes. The intersection operator
+ denotes a class that contains every character that is in both of its
+ operand classes.
+
+ <p> The precedence of character-class operators is as follows, from
+ highest to lowest:
+
+ <blockquote><table border="0" cellpadding="1" cellspacing="0"
+ summary="Precedence of character class operators.">
+
+ <tr><th>1&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Literal escape&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td><tt>\x</tt></td></tr>
+ <tr><th>2&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Range</td>
+ <td><tt>a-z</tt></td></tr>
+ <tr><th>3&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Grouping</td>
+ <td><tt>[...]</tt></td></tr>
+ <tr><th>4&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Intersection</td>
+ <td><tt>[a-z&&[aeiou]]</tt></td></tr>
+ <tr><th>5&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Union</td>
+ <td><tt>[a-e][i-u]<tt></td></tr>
+ </table></blockquote>
+
+ <p> Note that a different set of metacharacters are in effect inside
+ a character class than outside a character class. For instance, the
+ regular expression <tt>.</tt> loses its special meaning inside a
+ character class, while the expression <tt>-</tt> becomes a range
+ forming metacharacter.
+
+ <a name="lt">
+
+ <a name="cg">
+ <h4> Groups and capturing </h4>
+
+ <p> Capturing groups are numbered by counting their opening parentheses from
+ left to right. In the expression <tt>((A)(B(C)))</tt>, for example, there
+ are four such groups: </p>
+
+ <blockquote><table cellpadding=1 cellspacing=0 summary="Capturing group numberings">
+
+ <tr><th>1&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>((A)(B(C)))</tt></td></tr>
+ <tr><th>2&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(A)</tt></td></tr>
+ <tr><th>3&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(B(C))</tt></td></tr>
+
+ <tr><th>4&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(C)</tt></td></tr>
+ </table></blockquote>
+
+ <p> Group zero always stands for the entire expression.
+
+ <p> Capturing groups are so named because, during a match, each subsequence
+ of the input sequence that matches such a group is saved. The captured
+ subsequence may be used later in the expression, via a back reference, and
+ may also be retrieved from the matcher once the match operation is complete.
+
+ <p> The captured input associated with a group is always the subsequence
+ that the group most recently matched. If a group is evaluated a second time
+ because of quantification then its previously-captured value, if any, will
+ be retained if the second evaluation fails. Matching the string
+ <tt>"aba"</tt> against the expression <tt>(a(b)?)+</tt>, for example, leaves
+ group two set to <tt>"b"</tt>. All captured input is discarded at the
+ beginning of each match.
+
+ <p> Groups beginning with <tt>(?</tt> are pure, <i>non-capturing</i> groups
+ that do not capture text and do not count towards the group total.
+
+
+ <h4> Unicode support </h4>
+
+ <p> Coming Soon.
+
+ <h4> Comparison to Perl 5 </h4>
+
+ <p>The <code>Pattern</code> engine performs traditional NFA-based matching
+ with ordered alternation as occurs in Perl 5.
+
+ <p> Perl constructs not supported by this class: </p>
+
+ <ul>
+
+ <li><p> The conditional constructs <tt>(?{</tt><i>X</i><tt>})</tt> and
+ <tt>(?(</tt><i>condition</i><tt>)</tt><i>X</i><tt>|</tt><i>Y</i><tt>)</tt>,
+ </p></li>
+
+ <li><p> The embedded code constructs <tt>(?{</tt><i>code</i><tt>})</tt>
+ and <tt>(??{</tt><i>code</i><tt>})</tt>,</p></li>
+
+ <li><p> The embedded comment syntax <tt>(?#comment)</tt>, and </p></li>
+
+ <li><p> The preprocessing operations <tt>\l</tt> <tt>&#92;u</tt>,
+ <tt>\L</tt>, and <tt>\U</tt>. </p></li>
+
+ <li><p> Embedded flags</p></li>
+
+ </ul>
+
+ <p> Constructs supported by this class but not by Perl: </p>
+
+ <ul>
+
+ <li><p> Possessive quantifiers, which greedily match as much as they can
+ and do not back off, even when doing so would allow the overall match to
+ succeed. </p></li>
+
+ <li><p> Character-class union and intersection as described
+ above.</p></li>
+
+ </ul>
+
+ <p> Notable differences from Perl: </p>
+
+ <ul>
+
+ <li><p> In Perl, <tt>\1</tt> through <tt>\9</tt> are always interpreted
+ as back references; a backslash-escaped number greater than <tt>9</tt> is
+ treated as a back reference if at least that many subexpressions exist,
+ otherwise it is interpreted, if possible, as an octal escape. In this
+ class octal escapes must always begin with a zero. In this class,
+ <tt>\1</tt> through <tt>\9</tt> are always interpreted as back
+ references, and a larger number is accepted as a back reference if at
+ least that many subexpressions exist at that point in the regular
+ expression, otherwise the parser will drop digits until the number is
+ smaller or equal to the existing number of groups or it is one digit.
+ </p></li>
+
+ <li><p> Perl uses the <tt>g</tt> flag to request a match that resumes
+ where the last match left off. This functionality is provided implicitly
+ by the <CODE>Matcher</CODE> class: Repeated invocations of the
+ <code>find</code> method will resume where the last match left off,
+ unless the matcher is reset. </p></li>
+
+ <li><p> Perl is forgiving about malformed matching constructs, as in the
+ expression <tt>*a</tt>, as well as dangling brackets, as in the
+ expression <tt>abc]</tt>, and treats them as literals. This
+ class also strict and will not compile a pattern when dangling characters
+ are encountered.</p></li>
+
+ </ul>
+
+
+ <p> For a more precise description of the behavior of regular expression
+ constructs, please see <a href="http://www.oreilly.com/catalog/regex2/">
+ <i>Mastering Regular Expressions, 2nd Edition</i>, Jeffrey E. F. Friedl,
+ O'Reilly and Associates, 2002.</a>
+ </p>
+ <P>
+
+ <i>End Text Extracted And Modified From java.util.regex.Pattern documentation</i>
+
+ <hr>
+
+ @author Jeffery Stuart
+ @since March 2003, Stable Since November 2004
+ @version 1.07.00
+ @memo A class used to represent "PERL 5"-ish regular expressions
+ */
+class Pattern
+{
+ friend class Matcher;
+ friend class NFANode;
+ friend class NFAQuantifierNode;
+ private:
+ /**
+ This constructor should not be called directly. Those wishing to use the
+ Pattern class should instead use the {@link compile compile} method.
+
+ @param rhs The pattern to compile
+ @memo Creates a new pattern from the regular expression in <code>rhs</code>.
+ */
+ Pattern(const bkstring & rhs);
+ protected:
+ /**
+ This currently is not used, so don't try to do anything with it.
+ @memo Holds all the compiled patterns for quick access.
+ */
+ static std::map<bkstring, Pattern *> compiledPatterns;
+ /**
+ Holds all of the registered patterns as strings. Due to certain problems
+ with compilation of patterns, especially with capturing groups, this seemed
+ to be the best way to do it.
+ */
+ static std::map<bkstring, std::pair<bkstring, unsigned long> > registeredPatterns;
+ protected:
+ /**
+ Holds all the NFA nodes used. This makes deletion of a pattern, as well as
+ clean-up from an unsuccessful compile much easier and faster.
+ */
+ std::map<NFANode*, bool> nodes;
+ /**
+ Used when methods like split are called. The matcher class uses a lot of
+ dynamic memeory, so having an instance increases speedup of certain
+ operations.
+ */
+ Matcher * matcher;
+ /**
+ The front node of the NFA.
+ */
+ NFANode * head;
+ /**
+ The actual regular expression we rerpesent
+ */
+ bkstring pattern;
+ /**
+ Flag used during compilation. Once the pattern is successfully compiled,
+ <code>error</code> is no longer used.
+ */
+ bool error;
+ /**
+ Used during compilation to keep track of the current index into
+ <code>{@link pattern pattern}<code>. Once the pattern is successfully
+ compiled, <code>error</code> is no longer used.
+ */
+ int curInd;
+ /**
+ The number of capture groups this contains.
+ */
+ int groupCount;
+ /**
+ The number of non-capture groups this contains.
+ */
+ int nonCapGroupCount;
+ /**
+ The flags specified when this was compiled.
+ */
+ unsigned long flags;
+ protected:
+ /**
+ Raises an error during compilation. Compilation will cease at that point
+ and compile will return <code>NULL</code>.
+ */
+ void raiseError();
+ /**
+ Convenience function for registering a node in <code>nodes</code>.
+ @param node The node to register
+ @return The registered node
+ */
+ NFANode * registerNode(NFANode * node);
+
+ /**
+ Calculates the union of two strings. This function will first sort the
+ strings and then use a simple selection algorithm to find the union.
+ @param s1 The first "class" to union
+ @param s2 The second "class" to union
+ @return A new string containing all unique characters. Each character
+ must have appeared in one or both of <code>s1</code> and
+ <code>s2</code>.
+ */
+ bkstring classUnion (bkstring s1, bkstring s2) const;
+ /**
+ Calculates the intersection of two strings. This function will first sort
+ the strings and then use a simple selection algorithm to find the
+ intersection.
+ @param s1 The first "class" to intersect
+ @param s2 The second "class" to intersect
+ @return A new string containing all unique characters. Each character
+ must have appeared both <code>s1</code> and <code>s2</code>.
+ */
+ bkstring classIntersect (bkstring s1, bkstring s2) const;
+ /**
+ Calculates the negation of a string. The negation is the set of all
+ characters between <code>\x00</code> and <code>\xFF</code> not
+ contained in <code>s1</code>.
+ @param s1 The "class" to be negated.
+ @param s2 The second "class" to intersect
+ @return A new string containing all unique characters. Each character
+ must have appeared both <code>s1</code> and <code>s2</code>.
+ */
+ bkstring classNegate (bkstring s1) const;
+ /**
+ Creates a new "class" representing the range from <code>low</code> thru
+ <code>hi</code>. This function will wrap if <code>low</code> &gt;
+ <code>hi</code>. This is a feature, not a buf. Sometimes it is useful
+ to be able to say [\x70-\x10] instead of [\x70-\x7F\x00-\x10].
+ @param low The beginning character
+ @param hi The ending character
+ @return A new string containing all the characters from low thru hi.
+ */
+ bkstring classCreateRange(char low, char hi) const;
+
+ /**
+ Extracts a decimal number from the substring of member-variable
+ <code>{@link pattern pattern}<code> starting at <code>start</code> and
+ ending at <code>end</code>.
+ @param start The starting index in <code>{@link pattern pattern}<code>
+ @param end The last index in <code>{@link pattern pattern}<code>
+ @return The decimal number in <code>{@link pattern pattern}<code>
+ */
+ int getInt(int start, int end);
+ /**
+ Parses a <code>{n,m}</code> string out of the member-variable
+ <code>{@link pattern pattern}<code> stores the result in <code>sNum</code>
+ and <code>eNum</code>.
+ @param sNum Output parameter. The minimum number of matches required
+ by the curly quantifier are stored here.
+ @param eNum Output parameter. The maximum number of matches allowed
+ by the curly quantifier are stored here.
+ @return Success/Failure. Fails when the curly does not have the proper
+ syntax
+ */
+ bool quantifyCurly(int & sNum, int & eNum);
+ /**
+ Tries to quantify the currently parsed group. If the group being parsed
+ is indeed quantified in the member-variable
+ <code>{@link pattern pattern}<code>, then the NFA is modified accordingly.
+ @param start The starting node of the current group being parsed
+ @param stop The ending node of the current group being parsed
+ @param gn The group number of the current group being parsed
+ @return The node representing the starting node of the group. If the
+ group becomes quantified, then this node is not necessarily
+ a GroupHead node.
+ */
+ NFANode * quantifyGroup(NFANode * start, NFANode * stop, const int gn);
+
+ /**
+ Tries to quantify the last parsed expression. If the character was indeed
+ quantified, then the NFA is modified accordingly.
+ @param newNode The recently created expression node
+ @return The node representing the last parsed expression. If the
+ expression was quantified, <code>return value != newNode</code>
+ */
+ NFANode * quantify(NFANode * newNode);
+ /**
+ Parses the current class being examined in
+ <code>{@link pattern pattern}</code>.
+ @return A string of unique characters contained in the current class being
+ parsed
+ */
+ bkstring parseClass();
+ /**
+ Parses the current POSIX class being examined in
+ <code>{@link pattern pattern}</code>.
+ @return A string of unique characters representing the POSIX class being
+ parsed
+ */
+ bkstring parsePosix();
+ /**
+ Returns a string containing the octal character being parsed
+ @return The string contained the octal value being parsed
+ */
+ bkstring parseOctal();
+ /**
+ Returns a string containing the hex character being parsed
+ @return The string contained the hex value being parsed
+ */
+ bkstring parseHex();
+ /**
+ Returns a new node representing the back reference being parsed
+ @return The new node representing the back reference being parsed
+ */
+ NFANode * parseBackref();
+ /**
+ Parses the escape sequence currently being examined. Determines if the
+ escape sequence is a class, a single character, or the beginning of a
+ quotation sequence.
+ @param inv Output parameter. Whether or not to invert the returned class
+ @param quo Output parameter. Whether or not this sequence starts a
+ quotation.
+ @return The characters represented by the class
+ */
+ bkstring parseEscape(bool & inv, bool & quo);
+ /**
+ Parses a supposed registered pattern currently under compilation. If the
+ sequence of characters does point to a registered pattern, then the
+ registered pattern is appended to <code>*end<code>. The registered pattern
+ is parsed with the current compilation flags.
+ @param end The ending node of the thus-far compiled pattern
+ @return The new end node of the current pattern
+ */
+ NFANode * parseRegisteredPattern(NFANode ** end);
+ /**
+ Parses a lookbehind expression. Appends the necessary nodes
+ <code>*end</code>.
+ @param pos Positive or negative look behind
+ @param end The ending node of the current pattern
+ @return The new end node of the current pattern
+ */
+ NFANode * parseBehind(const bool pos, NFANode ** end);
+ /**
+ Parses the current expression and tacks on nodes until a \E is found.
+ @return The end of the current pattern
+ */
+ NFANode * parseQuote();
+ /**
+ Parses <code>{@link pattern pattern}</code>. This function is called
+ recursively when an or (<code>|</code>) or a group is encountered.
+ @param inParen Are we currently parsing inside a group
+ @param inOr Are we currently parsing one side of an or (<code>|</code>)
+ @param end The end of the current expression
+ @return The starting node of the NFA constructed from this parse
+ */
+ NFANode * parse(const bool inParen = 0, const bool inOr = 0, NFANode ** end = NULL);
+ public:
+ /// We should match regardless of case
+ const static unsigned long CASE_INSENSITIVE;
+ /// We are implicitly quoted
+ const static unsigned long LITERAL;
+ /// @memo We should treat a <code><b>.</b></code> as [\x00-\x7F]
+ const static unsigned long DOT_MATCHES_ALL;
+ /** <code>^</code> and <code>$</code> should anchor to the beginning and
+ ending of lines, not all input
+ */
+ const static unsigned long MULTILINE_MATCHING;
+ /** When enabled, only instances of <code>\n</codes> are recognized as
+ line terminators
+ */
+ const static unsigned long UNIX_LINE_MODE;
+ /// The absolute minimum number of matches a quantifier can match (0)
+ const static int MIN_QMATCH;
+ /// The absolute maximum number of matches a quantifier can match (0x7FFFFFFF)
+ const static int MAX_QMATCH;
+ public:
+ /**
+ Call this function to compile a regular expression into a
+ <code>Pattern</code> object. Special values can be assigned to
+ <code>mode</code> when certain non-standard behaviors are expected from
+ the <code>Pattern</code> object.
+ @param pattern The regular expression to compile
+ @param mode A bitwise or of flags signalling what special behaviors are
+ wanted from this <code>Pattern</code> object
+ @return If successful, <code>compile</code> returns a <code>Pattern</code>
+ pointer. Upon failure, <code>compile</code> returns
+ <code>NULL</code>
+ */
+ static Pattern * compile (const bkstring & pattern,
+ const unsigned long mode = 0);
+ /**
+ Dont use this function. This function will compile a pattern, and cache
+ the result. This will eventually be used as an optimization when people
+ just want to call static methods using the same pattern over and over
+ instead of first compiling the pattern and then using the compiled
+ instance for matching.
+ @param pattern The regular expression to compile
+ @param mode A bitwise or of flags signalling what special behaviors are
+ wanted from this <code>Pattern</code> object
+ @return If successful, <code>compileAndKeep</code> returns a
+ <code>Pattern</code> pointer. Upon failure, <code>compile</code>
+ returns <code>NULL</code>.
+ */
+ static Pattern * compileAndKeep (const bkstring & pattern,
+ const unsigned long mode = 0);
+
+ /**
+ Searches through <code>replace</code> and replaces all substrings matched
+ by <code>pattern</code> with <code>str</code>. <code>str</code> may
+ contain backreferences (e.g. <code>\1</code>) to capture groups. A typical
+ invocation looks like:
+ <p>
+ <code>
+ Pattern::replace("(a+)b(c+)", "abcccbbabcbabc", "\\2b\\1");
+ </code>
+ <p>
+ which would replace <code>abcccbbabcbabc</code> with
+ <code>cccbabbcbabcba</code>.
+ @param pattern The regular expression
+ @param str The replacement text
+ @param replacementText The string in which to perform replacements
+ @param mode The special mode requested of the <code>Pattern</code>
+ during the replacement process
+ @return The text with the replacement string substituted where necessary
+ */
+ static bkstring replace (const bkstring & pattern,
+ const bkstring & str,
+ const bkstring & replacementText,
+ const unsigned long mode = 0);
+
+ /**
+ Splits the specified string over occurrences of the specified pattern.
+ Empty strings can be optionally ignored. The number of strings returned is
+ configurable. A typical invocation looks like:
+ <p>
+ <code>
+ bkstring str(strSize, '\0');<br>
+ FILE * fp = fopen(fileName, "r");<br>
+ fread((char*)str.data(), strSize, 1, fp);<br>
+ fclose(fp);<br>
+ <br>
+ std::vector&lt;bkstring&gt; lines = Pattern::split("[\r\n]+", str, true);<br>
+ <br>
+ </code>
+
+ @param pattern The regular expression
+ @param replace The string to split
+ @param keepEmptys Whether or not to keep empty strings
+ @param limit The maximum number of splits to make
+ @param mode The special mode requested of the <code>Pattern</code>
+ during the split process
+ @return All substrings of <code>str</code> split across <code>pattern</code>.
+ */
+ static std::vector<bkstring> split (const bkstring & pattern,
+ const bkstring & str,
+ const bool keepEmptys = 0,
+ const unsigned long limit = 0,
+ const unsigned long mode = 0);
+
+ /**
+ Finds all the instances of the specified pattern within the string. You
+ should be careful to only pass patterns with a minimum length of one. For
+ example, the pattern <code>a*</code> can be matched by an empty string, so
+ instead you should pass <code>a+</code> since at least one character must
+ be matched. A typical invocation of <code>findAll</code> looks like:
+ <p>
+ <code>
+ std::vector&lt;td::string&gt; numbers = Pattern::findAll("\\d+", string);
+ </code>
+ <p>
+
+ @param pattern The pattern for which to search
+ @param str The string to search
+ @param mode The special mode requested of the <code>Pattern</code>
+ during the find process
+ @return All instances of <code>pattern</code> in <code>str</code>
+ */
+ static std::vector<bkstring> findAll (const bkstring & pattern,
+ const bkstring & str,
+ const unsigned long mode = 0);
+
+ /**
+ Determines if an entire string matches the specified pattern
+
+ @param pattern The pattern for to match
+ @param str The string to match
+ @param mode The special mode requested of the <code>Pattern</code>
+ during the replacement process
+ @return True if <code>str</code> is recognized by <code>pattern</code>
+ */
+ static bool matches (const bkstring & pattern,
+ const bkstring & str,
+ const unsigned long mode = 0);
+
+ /**
+ Registers a pattern under a specific name for use in later compilations.
+ A typical invocation and later use looks like:
+ <p>
+ <code>
+ Pattern::registerPattern("ip", "(?:\\d{1,3}\\.){3}\\d{1,3}");<br>
+ Pattern * p1 = Pattern::compile("{ip}:\\d+");<br>
+ Pattern * p2 = Pattern::compile("Connection from ({ip}) on port \\d+");<br>
+ </code>
+ <p>
+ Multiple calls to <code>registerPattern</code> with the same
+ <code>name</code> will result in the pattern getting overwritten.
+
+ @param name The name to give to the pattern
+ @param pattern The pattern to register
+ @param mode Any special flags to use when compiling pattern
+ @return Success/Failure. Fails only if <code>pattern</code> has invalid
+ syntax
+ */
+ static bool registerPattern(const bkstring & name,
+ const bkstring & pattern,
+ const unsigned long mode = 0);
+
+ /**
+ Clears the pattern registry
+ */
+ static void unregisterPatterns();
+ /**
+ Don't use
+ */
+ static void clearPatternCache();
+
+ /**
+ Searches through a string for the <code>n<sup>th</sup></code> match of the
+ given pattern in the string. Match indeces start at zero, not one.
+ A typical invocation looks like this:
+ <p>
+ <code>
+ std::pair&lt;bkstring, int&gt; match = Pattern::findNthMatch("\\d{1,3}", "192.168.1.101:22", 1);<br>
+ printf("%s %i\n", match.first.c_str(), match.second);<br>
+ <br>
+ Output: 168 4<br>
+ <br>
+
+ @param pattern The pattern for which to search
+ @param str The string to search
+ @param matchNum Which match to find
+ @param mode Any special flags to use during the matching process
+ @return A string and an integer. The string is the string matched. The
+ integer is the starting location of the matched string in
+ <code>str</code>. You can check for success/failure by making sure
+ that the integer returned is greater than or equal to zero.
+ */
+ static std::pair<bkstring, int> findNthMatch (const bkstring & pattern,
+ const bkstring & str,
+ const int matchNum,
+ const unsigned long mode = 0);
+ public:
+ /**
+ Deletes all NFA nodes allocated during compilation
+ */
+ ~Pattern();
+
+ bkstring replace (const bkstring & str,
+ const bkstring & replacementText);
+ std::vector<bkstring> split (const bkstring & str, const bool keepEmptys = 0,
+ const unsigned long limit = 0);
+ std::vector<bkstring> findAll (const bkstring & str);
+ bool matches (const bkstring & str);
+ /**
+ Returns the flags used during compilation of this pattern
+ @return The flags used during compilation of this pattern
+ */
+ unsigned long getFlags () const;
+ /**
+ Returns the regular expression this pattern represents
+ @return The regular expression this pattern represents
+ */
+ bkstring getPattern () const;
+ /**
+ Creates a matcher object using the specified string and this pattern.
+ @param str The string to match against
+ @return A new matcher using object using this pattern and the specified
+ string
+ */
+ Matcher * createMatcher (const bkstring & str);
+};
+
+class NFANode
+{
+ friend class Matcher;
+ public:
+ NFANode * next;
+ NFANode();
+ virtual ~NFANode();
+ virtual void findAllNodes(std::map<NFANode*, bool> & soFar);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const = 0;
+ inline virtual bool isGroupHeadNode() const { return false; }
+ inline virtual bool isStartOfInputNode() const { return false; }
+};
+class NFACharNode : public NFANode
+{
+ protected:
+ char ch;
+ public:
+ NFACharNode(const char c);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFACICharNode : public NFANode
+{
+ protected:
+ char ch;
+ public:
+ NFACICharNode(const char c);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAStartNode : public NFANode
+{
+ public:
+ NFAStartNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAEndNode : public NFANode
+{
+ public:
+ NFAEndNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAQuantifierNode : public NFANode
+{
+ public:
+ int min, max;
+ NFANode * inner;
+ virtual void findAllNodes(std::map<NFANode*, bool> & soFar);
+ NFAQuantifierNode(Pattern * pat, NFANode * internal,
+ const int minMatch = Pattern::MIN_QMATCH,
+ const int maxMatch = Pattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAGreedyQuantifierNode : public NFAQuantifierNode
+{
+ public:
+ NFAGreedyQuantifierNode(Pattern * pat, NFANode * internal,
+ const int minMatch = Pattern::MIN_QMATCH,
+ const int maxMatch = Pattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ virtual int matchInternal(const bkstring & str, Matcher * matcher, const int curInd, const int soFar) const;
+};
+class NFALazyQuantifierNode : public NFAQuantifierNode
+{
+ public:
+ NFALazyQuantifierNode(Pattern * pat, NFANode * internal,
+ const int minMatch = Pattern::MIN_QMATCH,
+ const int maxMatch = Pattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAPossessiveQuantifierNode : public NFAQuantifierNode
+{
+ public:
+ NFAPossessiveQuantifierNode(Pattern * pat, NFANode * internal,
+ const int minMatch = Pattern::MIN_QMATCH,
+ const int maxMatch = Pattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAAcceptNode : public NFANode
+{
+ public:
+ NFAAcceptNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAClassNode : public NFANode
+{
+ public:
+ bool inv;
+ std::map<char, bool> vals;
+ NFAClassNode(const bool invert = 0);
+ NFAClassNode(const bkstring & clazz, const bool invert);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFACIClassNode : public NFANode
+{
+ public:
+ bool inv;
+ std::map<char, bool> vals;
+ NFACIClassNode(const bool invert = 0);
+ NFACIClassNode(const bkstring & clazz, const bool invert);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFASubStartNode : public NFANode
+{
+ public:
+ NFASubStartNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAOrNode : public NFANode
+{
+ public:
+ NFANode * one;
+ NFANode * two;
+ NFAOrNode(NFANode * first, NFANode * second);
+ virtual void findAllNodes(std::map<NFANode*, bool> & soFar);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAQuoteNode : public NFANode
+{
+ public:
+ bkstring qStr;
+ NFAQuoteNode(const bkstring & quoted);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFACIQuoteNode : public NFANode
+{
+ public:
+ bkstring qStr;
+ NFACIQuoteNode(const bkstring & quoted);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFALookAheadNode : public NFANode
+{
+ public:
+ bool pos;
+ NFANode * inner;
+ NFALookAheadNode(NFANode * internal, const bool positive);
+ virtual void findAllNodes(std::map<NFANode*, bool> & soFar);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFALookBehindNode : public NFANode
+{
+ public:
+ bool pos;
+ bkstring mStr;
+ NFALookBehindNode(const bkstring & str, const bool positive);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAStartOfLineNode : public NFANode
+{
+ public:
+ NFAStartOfLineNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAEndOfLineNode : public NFANode
+{
+ public:
+ NFAEndOfLineNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAReferenceNode : public NFANode
+{
+ public:
+ int gi;
+ NFAReferenceNode(const int groupIndex);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAStartOfInputNode : public NFANode
+{
+ public:
+ NFAStartOfInputNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ inline virtual bool isStartOfInputNode() const { return true; }
+};
+class NFAEndOfInputNode : public NFANode
+{
+ public:
+ bool term;
+ NFAEndOfInputNode(const bool lookForTerm);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAWordBoundaryNode : public NFANode
+{
+ public:
+ bool pos;
+ NFAWordBoundaryNode(const bool positive);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAEndOfMatchNode : public NFANode
+{
+ public:
+ NFAEndOfMatchNode();
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupHeadNode : public NFANode
+{
+ public:
+ int gi;
+ NFAGroupHeadNode(const int groupIndex);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ inline virtual bool isGroupHeadNode() const { return true; }
+};
+class NFAGroupTailNode : public NFANode
+{
+ public:
+ int gi;
+ NFAGroupTailNode(const int groupIndex);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupLoopPrologueNode : public NFANode
+{
+ public:
+ int gi;
+ NFAGroupLoopPrologueNode(const int groupIndex);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupLoopNode : public NFANode
+{
+ public:
+ int gi, min, max, type;
+ NFANode * inner;
+ NFAGroupLoopNode(NFANode * internal, const int minMatch,
+ const int maxMatch, const int groupIndex, const int matchType);
+ virtual void findAllNodes(std::map<NFANode*, bool> & soFar);
+ virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ int matchGreedy(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ int matchLazy(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+ int matchPossessive(const bkstring & str, Matcher * matcher, const int curInd = 0) const;
+};
+
+#endif
+
diff --git a/plugins/SmileyAdd/src/regexp/WCMatcher.cpp b/plugins/SmileyAdd/src/regexp/WCMatcher.cpp
new file mode 100644
index 0000000000..bed5a1944b
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/WCMatcher.cpp
@@ -0,0 +1,181 @@
+#include "WCMatcher.h"
+#include "WCPattern.h"
+
+const int WCMatcher::MATCH_ENTIRE_STRING = 0x01;
+
+/*
+ Detailed documentation is provided in this class' header file
+
+ @author Jeffery Stuart
+ @since November 2004
+ @version 1.07.00
+*/
+
+WCMatcher::WCMatcher(WCPattern * pattern, const bkstring & text)
+{
+ pat = pattern;
+ str = &text;
+ gc = pattern->groupCount;
+ ncgc = -pattern->nonCapGroupCount;
+ flags = 0;
+ matchedSomething = false;
+ starts = new int[gc + ncgc];
+ ends = new int[gc + ncgc];
+ groups = new int[gc + ncgc];
+ groupPos = new int[gc + ncgc];
+ groupIndeces = new int[gc + ncgc];
+ starts = starts + ncgc;
+ ends = ends + ncgc;
+ groups = groups + ncgc;
+ groupPos = groupPos + ncgc;
+ groupIndeces = groupIndeces + ncgc;
+ for (int i = 0; i < gc; ++i) starts[i] = ends[i] = 0;
+}
+WCMatcher::~WCMatcher()
+{
+ delete [] (starts - ncgc);
+ delete [] (ends - ncgc);
+ delete [] (groups - ncgc);
+ delete [] (groupIndeces - ncgc);
+ delete [] (groupPos - ncgc);
+}
+void WCMatcher::clearGroups()
+{
+ int i;
+ lm = 0;
+ for (i = 0; i < gc; ++i) groups[i] = starts[i] = ends[i] = -1;
+ for (i = 1; i <= ncgc; ++i) groups[0 - i] = starts[0 - i] = ends[0 - i] = -1;
+}
+bkstring WCMatcher::replaceWithGroups(const bkstring & str)
+{
+ bkstring ret = L"";
+
+ bkstring t = str;
+ while (t.size() > 0)
+ {
+ if (t[0] == (wchar_t)'\\')
+ {
+ t.erase(0, 1);
+ if (t.size() == 0)
+ {
+ ret += L"\\";
+ }
+ else if (t[0] < (wchar_t)'0' || t[0] > (wchar_t)'9')
+ {
+ ret += t[0];
+ t.erase(0, 1);
+ }
+ else
+ {
+ int gn = 0;
+ while (t.size() > 0 && t[0] >= (wchar_t)'0' && t[0] <= (wchar_t)'9')
+ {
+ gn = gn * 10 + (t[0] - (wchar_t)'0');
+ t.erase(0, 1);
+ }
+ ret += getGroup(gn);
+ }
+ }
+ else
+ {
+ ret += t[0];
+ t.erase(0, 1);
+ }
+ }
+
+ return ret;
+}
+unsigned long WCMatcher::getFlags() const
+{
+ return flags;
+}
+const bkstring& WCMatcher::getText() const
+{
+ return *str;
+}
+
+bool WCMatcher::matches()
+{
+ flags = MATCH_ENTIRE_STRING;
+ matchedSomething = false;
+ clearGroups();
+ lm = 0;
+ return pat->head->match(*str, this, 0) == (int)str->size();
+}
+bool WCMatcher::findFirstMatch()
+{
+ starts[0] = 0;
+ flags = 0;
+ clearGroups();
+ start = 0;
+ lm = 0;
+ ends[0] = pat->head->match(*str, this, 0);
+ if (ends[0] >= 0)
+ {
+ matchedSomething = true;
+ return 1;
+ }
+ return 0;
+}
+bool WCMatcher::findNextMatch()
+{
+ int s = starts[0], e = ends[0];
+
+ if (!matchedSomething) return findFirstMatch();
+ if (s == e) ++e;
+ flags = 0;
+ clearGroups();
+
+ starts[0] = e;
+ if (e >= (int)str->size()) return 0;
+ start = e;
+ lm = e;
+ ends[0] = pat->head->match(*str, this, e);
+ return ends[0] >= 0;
+}
+std::vector<bkstring> WCMatcher::findAll()
+{
+ std::vector<bkstring> ret;
+ reset();
+ while (findNextMatch())
+ {
+ ret.push_back(getGroup());
+ }
+ return ret;
+}
+
+void WCMatcher::reset()
+{
+ lm = 0;
+ clearGroups();
+ matchedSomething = false;
+}
+
+int WCMatcher::getStartingIndex(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return -1;
+ return starts[groupNum];
+}
+int WCMatcher::getEndingIndex(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return -1;
+ return ends[groupNum];
+}
+bkstring WCMatcher::getGroup(const int groupNum) const
+{
+ if (groupNum < 0 || groupNum >= gc) return L"";
+ if (starts[groupNum] < 0 || ends[groupNum] < 0) return L"";
+ return str->substr(starts[groupNum], ends[groupNum] - starts[groupNum]);
+}
+std::vector<bkstring> WCMatcher::getGroups(const bool includeGroupZero) const
+{
+ int i, start = (includeGroupZero ? 0 : 1);
+ std::vector<bkstring> ret;
+
+ for (i = start; i < gc; ++i)
+ {
+ ret.push_back(getGroup(i));
+ }
+ return ret;
+}
+
diff --git a/plugins/SmileyAdd/src/regexp/WCMatcher.h b/plugins/SmileyAdd/src/regexp/WCMatcher.h
new file mode 100644
index 0000000000..23cc49e41f
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/WCMatcher.h
@@ -0,0 +1,234 @@
+#ifndef __WCMATCHER_H__
+#define __WCMATCHER_H__
+
+#include "bkstring.h"
+#include <vector>
+#include <WCPattern.h>
+
+/**
+ A matcher is a non thread-safe object used to scan strings using a given
+ {@link WCPattern WCPattern} object. Using a <code>WCMatcher</code> is the preferred
+ method for scanning strings. WCMatchers are not thread-safe. WCMatchers require
+ very little dynamic memory, hence one is encouraged to create several
+ instances of a matcher when necessary as opposed to sharing a single instance
+ of a matcher.
+ <p>
+ The most common methods needed by the matcher are <code>matches</code>,
+ <code>findNextMatch</code>, and <code>getGroup</code>. <code>matches</code>
+ and <code>findNextMatch</code> both return success or failure, and further
+ details can be gathered from their documentation.
+ <p>
+ Unlike Java's <code>WCMatcher</code>, this class allows you to change the string
+ you are matching against. This provides a small optimization, since you no
+ longer need multiple matchers for a single pattern in a single thread.
+ <p>
+ This class also provides an extremely handy method for replacing text with
+ captured data via the <code>replaceWithGroups</code> method. A typical
+ invocation looks like:
+ <pre>
+ wchar_t buf[10000];
+ bkstring str = "\\5 (user name \\1) uses \\7 for his/her shell and \\6 is their home directory";
+ FILE * fp = fopen("/etc/passwd", "r");
+ WCPattern::registerWCPattern("entry", "[^:]+");
+ WCPattern * p = WCPattern::compile("^({entry}):({entry}):({entry}):({entry}):({entry}):({entry}):({entry})$",
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WCPattern::MULTILINE_MATCHING | WCPattern::UNIX_LINE_MODE);
+ WCMatcher * m = p->createWCMatcher("");
+ while (fgets(buf, 9999, fp))
+ {
+ &nbsp;&nbsp;m->setString(buf);
+ &nbsp;&nbsp;if (m->matches())
+ &nbsp;&nbsp;{
+ &nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n", m->replaceWithGroups(str).c_str());
+ &nbsp;&nbsp;}
+ }
+ fclose(fp);
+
+ </pre>
+ Calling any of the following functions before first calling
+ <code>matches</code>, <code>findFirstMatch</code>, or
+ <code>findNextMatch</code> results in undefined behavior and may cause your
+ program to crash.
+ <code>
+ <ul>
+ <li>replaceWithGroups</code>
+ <li>getStartingIndex</li>
+ <li>getEndingIndex</li>
+ <li>getGroup</li>
+ <li>getGroups</li>
+ </ul>
+ </code>
+ <p>
+ The function <code>findFirstMatch</code> will attempt to find the first match
+ in the input string. The same results can be obtained by first calling
+ <code>reset</code> followed by <code>findNextMatch</code>.
+ <p>
+ To eliminate the necessity of looping through a string to find all the
+ matching substrings, <code>findAll</code> was created. The function will find
+ all matching substrings and return them in a <code>vector</code>. If you need
+ to examine specific capture groups within the substrings, then this method
+ should not be used.
+
+ @author Jeffery Stuart
+ @since March 2003, Stable Since November 2004
+ @version 1.05.00
+ @memo Mutable object used on instances of a WCPattern class
+ */
+class WCMatcher
+{
+ friend class NFAUNode;
+ friend class NFAStartUNode;
+ friend class NFAEndUNode;
+ friend class NFAGroupHeadUNode;
+ friend class NFAGroupLoopUNode;
+ friend class NFAGroupLoopPrologueUNode;
+ friend class NFAGroupTailUNode;
+ friend class NFALookBehindUNode;
+ friend class NFAStartOfLineUNode;
+ friend class NFAEndOfLineUNode;
+ friend class NFAEndOfMatchUNode;
+ friend class NFAReferenceUNode;
+ friend class WCPattern;
+ private:
+ /**
+ Creates a new matcher object against <code>text</code> using
+ <code>pattern</code>.
+
+ @param pattern The pattern with which to search
+ @param text The text in which to search
+ */
+ WCMatcher(WCPattern * pattern, const bkstring & text);
+ protected:
+ /// The pattern we use to match
+ WCPattern * pat;
+ /// The string in which we are matching
+ const bkstring * str;
+ /// The starting point of our match
+ int start;
+ /// An array of the starting positions for each group
+ int * starts;
+ /// An array of the ending positions for each group
+ int * ends;
+ /// An array of private data used by NFAUNodes during matching
+ int * groups;
+ /// An array of private data used by NFAUNodes during matching
+ int * groupIndeces;
+ /// An array of private data used by NFAUNodes during matching
+ int * groupPos;
+ /// The ending index of the last match
+ int lm;
+ /// The number of capturing groups we have
+ int gc;
+ /// The number of non-capturing groups we havew
+ int ncgc;
+ /// Whether or not we have matched something (used only by findFirstMatch and findNextMatch)
+ int matchedSomething;
+ /// The flags with which we were made
+ unsigned long flags;
+ /// Called by reset to clear the group arrays
+ void clearGroups();
+ public:
+ /// Used internally by match to signify we want the entire string matched
+ const static int MATCH_ENTIRE_STRING;
+ public:
+ /// Cleans up the dynamic memory used by this matcher
+ ~WCMatcher();
+ /**
+ Replaces the contents of <code>str</code> with the appropriate captured
+ text. <code>str</code> should have at least one back reference, otherwise
+ this function does nothing.
+ @param str The string in which to replace text
+ @return A string with all backreferences appropriately replaced
+ */
+ bkstring replaceWithGroups(const bkstring & str);
+ /**
+ The flags currently being used by the matcher.
+ @return Zero
+ */
+ unsigned long getFlags() const;
+ /**
+ The text being searched by the matcher.
+ @return the text being searched by the matcher.
+ */
+ const bkstring& getText() const;
+
+ /**
+ Scans the string from start to finish for a match. The entire string must
+ match for this function to return success. Group variables are
+ appropriately set and can be queried after this function returns.
+
+ @return Success if and only if the entire string matches the pattern
+ */
+ bool matches();
+ /**
+ Scans the string for the first substring matching the pattern. The entire
+ string does not necessarily have to match for this function to return
+ success. Group variables are appropriately set and can be queried after
+ this function returns.
+
+ @return Success if any substring matches the specified pattern
+ */
+ bool findFirstMatch();
+ /**
+ Scans the string for the next substring matching the pattern. If no calls
+ have been made to findFirstMatch of findNextMatch since the last call to
+ reset, matches, or setString, then this function's behavior results to
+ that of findFirstMatch.
+
+ @return Success if another substring can be found that matches the pattern
+ */
+ bool findNextMatch();
+ /**
+ Returns a vector of every substring in order which matches the given
+ pattern.
+
+ @return Every substring in order which matches the given pattern
+ */
+ std::vector<bkstring> findAll();
+ /**
+ Resets the internal state of the matcher
+ */
+ void reset();
+ /**
+ Same as getText. Left n for backwards compatibilty with old source code
+ @return Returns the string that is currently being used for matching
+ */
+ inline const bkstring& getString() const { return *str; }
+ /**
+ Sets the string to scan
+ @param newStr The string to scan for subsequent matches
+ */
+ inline void setString(const bkstring & newStr) { str = &newStr; reset(); }
+
+ /**
+ Returns the starting index of the specified group.
+ @param groupNum The group to query
+ @return The starting index of the group if it was matched, -1 for an
+ invalid group or if the group was not matched
+ */
+ int getStartingIndex(const int groupNum = 0) const;
+ /**
+ Returns the ending index of the specified group.
+ @param groupNum The group to query
+ @return The ending index of the group if it was matched, -1 for an
+ invalid group or if the group was not matched
+ */
+ int getEndingIndex(const int groupNum = 0) const;
+ /**
+ Returns the specified group. An empty string ("") does not necessarily
+ mean the group was not matched. A group such as (a*b?) could be matched by
+ a zero length. If an empty string is returned, getStartingIndex can be
+ called to determine if the group was actually matched.
+ @param groupNum The group to query
+ @return The text of the group
+ */
+ bkstring getGroup(const int groupNum = 0) const;
+ /**
+ Returns every capture group in a vector
+
+ @param includeGroupZero Whether or not include capture group zero
+ @return Every capture group
+ */
+ std::vector<bkstring> getGroups(const bool includeGroupZero = 0) const;
+};
+
+#endif
diff --git a/plugins/SmileyAdd/src/regexp/WCPattern.cpp b/plugins/SmileyAdd/src/regexp/WCPattern.cpp
new file mode 100644
index 0000000000..25f379f5e4
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/WCPattern.cpp
@@ -0,0 +1,1747 @@
+/**
+ From the author (Jeff Stuart)
+ "
+ Let me start by saying this file is pretty big. If you feel up to it, you can
+ try making changes yourself, but you would be better off to just email me at
+ stuart@cs.ucdavis.edu if you think there is a bug, or have something useful you
+ would like added. This project is very "near and dear" to me, so I am fairly quick
+ to make bug fixes. The header files for WCPattern and WCMatcher are fairly well
+ documented and the function names are pretty self-explanatory, but if you are having
+ any trouble, feel free to email me at stuart@cs.ucdavis.edu.
+
+ If you email me, make sure you put something like C++RE in the subject because
+ I tend to delete email if I don't recognize the name and the subject is
+ something like "I Need Your Help" or "Got A Second" or "I Found It".
+ "
+ */
+
+/*
+ Detailed documentation is provided in this class' header file
+
+ @author Jeffery Stuart
+ @since November 2004
+ @version 1.07.00
+*/
+
+#ifdef _WIN32
+ #pragma warning(push)
+ #pragma warning(disable:4996)
+#endif
+
+#include <WCPattern.h>
+#include <WCMatcher.h>
+#include <wchar.h>
+#include <algorithm>
+#ifndef _WIN32
+ #include <wctype.h>
+#endif
+
+std::map<bkstring, WCPattern *> WCPattern::compiledWCPatterns;
+std::map<bkstring, std::pair<bkstring, unsigned long> > WCPattern::registeredWCPatterns;
+
+const int WCPattern::MIN_QMATCH = 0x00000000;
+const int WCPattern::MAX_QMATCH = 0x7FFFFFFF;
+
+const unsigned long WCPattern::CASE_INSENSITIVE = 0x01;
+const unsigned long WCPattern::LITERAL = 0x02;
+const unsigned long WCPattern::DOT_MATCHES_ALL = 0x04;
+const unsigned long WCPattern::MULTILINE_MATCHING = 0x08;
+const unsigned long WCPattern::UNIX_LINE_MODE = 0x10;
+
+#define to_lower(a) (wchar_t)(UINT_PTR)CharLowerW((LPWSTR)(unsigned)a)
+#define is_alpha IsCharAlphaW
+
+#if defined(_WIN32)
+ #define str_icmp lstrcmpiW
+#elif defined(__CYGWIN__) || defined(__APPLE__)
+ #include <wctype.h>
+ static inline int str_icmp(const wchar_t * a, const wchar_t * b)
+ {
+ while (*a && *b)
+ {
+ const int t = (int)towlower(*a) - (int)tolower(*b);
+ if (t) return t;
+ ++a; ++b;
+ }
+ if (*a)
+ {
+ if (*b) return (int)towlower(*a) - (int)tolower(*b);
+ return 1;
+ }
+ else if (*b) return 1;
+ return 0;
+ }
+#else
+ #define str_icmp wcscasecmp
+#endif
+
+WCPattern::WCPattern(const bkstring & rhs)
+{
+ matcher = NULL;
+ pattern = rhs;
+ curInd = 0;
+ groupCount = 0;
+ nonCapGroupCount = 0;
+ error = 0;
+ head = NULL;
+}
+// convenience function in case we want to add any extra debugging output
+void WCPattern::raiseError()
+{
+/* switch (pattern[curInd - 1])
+ {
+ case '*':
+ case ')':
+ case '+':
+ case '?':
+ case ']':
+ case '}':
+ fwprintf(stderr, L"%s\n%*c^\n", pattern.c_str(), curInd - 1, ' ');
+ fwprintf(stderr, L"Syntax Error near here. Possible unescaped meta character.\n");
+ break;
+ default:
+ fwprintf(stderr, L"%s\n%*c^\n", pattern.c_str(), curInd - 1, ' ');
+ fwprintf(stderr, L"Syntax Error near here. \n");
+ break;
+ }*/
+ error = 1;
+}
+NFAUNode * WCPattern::registerNode(NFAUNode * node)
+{
+ nodes[node] = 1;
+ return node;
+}
+
+bkstring WCPattern::classUnion (bkstring s1, bkstring s2) const
+{
+ wchar_t * out = new wchar_t[66000];
+ std::sort(s1.begin(), s1.end());
+ std::sort(s2.begin(), s2.end());
+ wchar_t* p = std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out); *p = 0;
+ bkstring ret = out;
+ delete [] out;
+ return ret;
+}
+bkstring WCPattern::classIntersect (bkstring s1, bkstring s2) const
+{
+ wchar_t * out = new wchar_t[66000];
+ std::sort(s1.begin(), s1.end());
+ std::sort(s2.begin(), s2.end());
+ *std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), out) = 0;
+ bkstring ret = out;
+ delete [] out;
+ return ret;
+}
+bkstring WCPattern::classNegate (bkstring s1) const
+{
+ wchar_t * out = new wchar_t[66000];
+ int i, ind = 0;
+ std::map<wchar_t, bool> m;
+
+ for (i = 0; i < (int)s1.size(); ++i) m[s1[i]] = 1;
+ for (i = 0xFF; i >= 0; --i) if (m.find((wchar_t)i) == m.end()) out[ind++] = (wchar_t)i;
+ out[ind] = 0;
+ bkstring ret(out, ind);
+ delete [] out;
+ return ret;
+}
+bkstring WCPattern::classCreateRange(wchar_t low, wchar_t hi) const
+{
+ wchar_t out[300];
+ int ind = 0;
+ while (low != hi) out[ind++] = low++;
+ out[ind++] = low;
+ return bkstring(out, ind);
+}
+
+int WCPattern::getInt(int start, int end)
+{
+ int ret = 0;
+ for (; start <= end; ++start) ret = ret * 10 + (pattern[start] - (wchar_t)'0');
+ return ret;
+}
+bool WCPattern::quantifyCurly(int & sNum, int & eNum)
+{
+ bool good = 1;
+ int i, ci = curInd + 1;
+ int commaInd = ci, endInd = ci, len = pattern.size();
+ sNum = eNum = 0;
+
+ while (endInd < len && pattern[endInd ] != (wchar_t)'}') ++endInd;
+ while (commaInd < endInd && pattern[commaInd] != (wchar_t)',') ++commaInd;
+ if (endInd >= len) { raiseError(); return 0; }
+ for (i = ci; good && i < endInd; ++i) if (i != commaInd && !isdigit(pattern[i])) good = 0;
+ if (!good && commaInd < endInd) { raiseError(); return 0; }
+ if (!good) return 0;
+ /* so now everything in here is either a comma (and there is at most one comma) or a digit */
+ if (commaInd == ci) // {,*}
+ {
+ if (endInd == commaInd + 1) { sNum = MIN_QMATCH; eNum = MAX_QMATCH; } // {,} = *
+ else { sNum = MIN_QMATCH; eNum = getInt(commaInd + 1, endInd - 1); } // {,+}
+ }
+ else if (commaInd == endInd - 1) { sNum = getInt(ci, commaInd - 1); eNum = MAX_QMATCH; } // {+,}
+ else if (commaInd == endInd) { sNum = getInt(ci, endInd - 1); eNum = sNum; } // {+}
+ else { sNum = getInt(ci, commaInd - 1); eNum = getInt(commaInd + 1, endInd - 1); } // {+,+}
+ curInd = endInd + 1;
+ return 1;
+}
+NFAUNode * WCPattern::quantifyGroup(NFAUNode * start, NFAUNode * stop, const int gn)
+{
+ NFAUNode * newNode = NULL;
+ int type = 0;
+
+ if (curInd < (int)pattern.size())
+ {
+ wchar_t ch = (curInd + 1 >= (int)pattern.size()) ? USHRT_MAX : pattern[curInd + 1];
+ switch (pattern[curInd])
+ {
+ case (wchar_t)'*':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; type = 1; break;
+ case (wchar_t)'+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueUNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopUNode(start, MIN_QMATCH, MAX_QMATCH, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case (wchar_t)'?':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; type = 1; break;
+ case (wchar_t)'+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueUNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopUNode(start, MIN_QMATCH, 1, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case (wchar_t)'+':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; type = 1; break;
+ case (wchar_t)'+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueUNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopUNode(start, 1, MAX_QMATCH, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ case (wchar_t)'{':
+ {
+ int s, e;
+ if (quantifyCurly(s, e))
+ {
+ ch = (curInd < (int)pattern.size()) ? pattern[curInd] : USHRT_MAX;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; type = 1; break;
+ case (wchar_t)'+': ++curInd; type = 2; break;
+ }
+ newNode = registerNode(new NFAGroupLoopPrologueUNode(gn));
+ newNode->next = registerNode(new NFAGroupLoopUNode(start, s, e, gn, type));
+ stop->next = newNode->next;
+ return newNode;
+ }
+ }
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+NFAUNode * WCPattern::quantify(NFAUNode * newNode)
+{
+ if (curInd < (int)pattern.size())
+ {
+ wchar_t ch = (curInd + 1 >= (int)pattern.size()) ? USHRT_MAX : pattern[curInd + 1];
+ switch (pattern[curInd])
+ {
+ case (wchar_t)'*':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; newNode = registerNode(new NFALazyQuantifierUNode (this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ case (wchar_t)'+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierUNode(this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierUNode (this, newNode, MIN_QMATCH, MAX_QMATCH)); break;
+ }
+ break;
+ case (wchar_t)'?':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; newNode = registerNode(new NFALazyQuantifierUNode (this, newNode, MIN_QMATCH, 1)); break;
+ case (wchar_t)'+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierUNode(this, newNode, MIN_QMATCH, 1)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierUNode (this, newNode, MIN_QMATCH, 1)); break;
+ }
+ break;
+ case (wchar_t)'+':
+ ++curInd;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; newNode = registerNode(new NFALazyQuantifierUNode (this, newNode, 1, MAX_QMATCH)); break;
+ case (wchar_t)'+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierUNode(this, newNode, 1, MAX_QMATCH)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierUNode (this, newNode, 1, MAX_QMATCH)); break;
+ }
+ break;
+ case (wchar_t)'{':
+ {
+ int s, e;
+ if (quantifyCurly(s, e))
+ {
+ ch = (curInd < (int)pattern.size()) ? pattern[curInd] : USHRT_MAX;
+ switch (ch)
+ {
+ case (wchar_t)'?': ++curInd; newNode = registerNode(new NFALazyQuantifierUNode (this, newNode, s, e)); break;
+ case (wchar_t)'+': ++curInd; newNode = registerNode(new NFAPossessiveQuantifierUNode(this, newNode, s, e)); break;
+ default: newNode = registerNode(new NFAGreedyQuantifierUNode (this, newNode, s, e)); break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return newNode;
+}
+bkstring WCPattern::parseClass()
+{
+ bkstring t, ret = L"";
+ wchar_t ch, c1, c2;
+ bool inv = 0, neg = 0, quo = 0;
+
+ if (curInd < (int)pattern.size() && pattern[curInd] == (wchar_t)'^')
+ {
+ ++curInd;
+ neg = 1;
+ }
+ while (curInd < (int)pattern.size() && pattern[curInd] != (wchar_t)']')
+ {
+ ch = pattern[curInd++];
+ if (ch == (wchar_t)'[')
+ {
+ t = parseClass();
+ ret = classUnion(ret, t);
+ }
+ /*else if (ch == (wchar_t)'-')
+ {
+ raiseError();
+ curInd = pattern.size();
+ }*/
+ else if (ch == (wchar_t)'&' && curInd < (int)pattern.size() && pattern[curInd] == (wchar_t)'&')
+ {
+ if (pattern[++curInd] != (wchar_t)'[')
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else
+ {
+ ++curInd;
+ t = parseClass();
+ ret = classIntersect(ret, t);
+ }
+ }
+ else if (ch == (wchar_t)'\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else if (inv || t.size() > 1) // cant be part of a range (a-z)
+ {
+ if (inv) t = classNegate(t);
+ ret = classUnion(ret, t);
+ }
+ else if (curInd < (int)pattern.size() && pattern[curInd] == (wchar_t)'-') // part of a range (a-z)
+ {
+ c1 = t[0];
+ ++curInd;
+ if (curInd >= (int)pattern.size()) raiseError();
+ else
+ {
+ c2 = pattern[curInd++];
+ if (c2 == (wchar_t)'\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else if (inv || t.size() > 1) raiseError();
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ else if (c2 == (wchar_t)'[' || c2 == (wchar_t)']' || c2 == (wchar_t)'-' || c2 == (wchar_t)'&')
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ }
+ else
+ {
+ ret = classUnion(ret, t);
+ }
+ }
+ else if (curInd < (int)pattern.size() && pattern[curInd] == (wchar_t)'-')
+ {
+ c1 = ch;
+ ++curInd;
+ if (curInd >= (int)pattern.size()) raiseError();
+ else
+ {
+ c2 = pattern[curInd++];
+ if (c2 == (wchar_t)'\\')
+ {
+ t = parseEscape(inv, quo);
+ if (quo)
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else if (inv || t.size() > 1) raiseError();
+ else ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ else if (c2 == (wchar_t)'[' || c2 == (wchar_t)']' || c2 == (wchar_t)'-' || c2 == (wchar_t)'&')
+ {
+ raiseError();
+ curInd = pattern.size();
+ }
+ else
+ {
+ ret = classUnion(ret, classCreateRange(c1, c2));
+ }
+ }
+ }
+ else
+ {
+ ret += L" ";
+ ret[ret.size() - 1] = ch;
+ }
+ }
+ if (curInd >= (int)pattern.size() || pattern[curInd] != (wchar_t)']')
+ {
+ raiseError();
+ ret = L"";
+ }
+ else
+ {
+ ++curInd;
+ if (neg) ret = classNegate(ret);
+ }
+ return ret;
+}
+bkstring WCPattern::parsePosix()
+{
+ bkstring s7 = pattern.substr(curInd, 7);
+ if (s7 == L"{Lower}") { curInd += 7; return L"abcdefghijklmnopqrstuvwxyz"; }
+ if (s7 == L"{Upper}") { curInd += 7; return L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
+ if (s7 == L"{Alpha}") { curInd += 7; return L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
+ if (s7 == L"{Digit}") { curInd += 7; return L"0123456789"; }
+ if (s7 == L"{Alnum}") { curInd += 7; return L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; }
+ if (s7 == L"{Punct}") { curInd += 7; return L"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == L"{Graph}") { curInd += 7; return L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == L"{Print}") { curInd += 7; return L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
+ if (s7 == L"{Blank}") { curInd += 7; return L" \t"; }
+ if (s7 == L"{Space}") { curInd += 7; return L" \t\n\x0B\f\r"; }
+ if (s7 == L"{Cntrl}")
+ {
+ bkstring::value_type i;
+ bkstring s = L" ";
+
+ for (i = 0; i < 5; ++i) s += s;
+ s += L" ";
+ for (i = 0; i <= 0x1F; ++i) s[i] = i;
+ s[0x20] = 0x7F;
+ curInd += 7;
+ return s;
+ }
+ if (s7 == L"{ASCII}")
+ {
+ bkstring s(0x80, (wchar_t)' ');
+ for (bkstring::value_type i = 0; i <= 0x7f; ++i) s[i] = i;
+ curInd += 7;
+ return s;
+ }
+ if (pattern.substr(curInd, 8) == L"{XDigit}") { curInd += 8; return L"abcdefABCDEF0123456789"; }
+ raiseError();
+ return L"";
+}
+NFAUNode * WCPattern::parseBackref()
+{
+ #define is_dig(x) ((x) >= (wchar_t)'0' && (x) <= (wchar_t)'9')
+ #define to_int(x) ((x) - (wchar_t)'0')
+ int ci = curInd;
+ int oldRef = 0, ref = 0;
+
+ while (ci < (int)pattern.size() && is_dig(pattern[ci]) && (ref < 10 || ref < groupCount))
+ {
+ oldRef = ref;
+ ref = ref * 10 + to_int(pattern[ci++]);
+ }
+ if (ci == (int)pattern.size())
+ {
+ oldRef = ref;
+ ++ci;
+ }
+ if (oldRef < 0 || ci <= curInd)
+ {
+ raiseError();
+ return registerNode(new NFAReferenceUNode(-1));
+ }
+ curInd = ci;
+ return registerNode(new NFAReferenceUNode(ref));
+
+ #undef is_dig
+ #undef to_int
+}
+bkstring WCPattern::parseOctal()
+{
+ #define islowoc(x) ((x) >= (wchar_t)'0' && (x) <= (wchar_t)'3')
+ #define isoc(x) ((x) >= (wchar_t)'0' && (x) <= (wchar_t)'7')
+ #define fromoc(x) ((x) - (wchar_t)'0')
+ int ci = curInd;
+ wchar_t ch1 = (ci + 0 < (int)pattern.size()) ? pattern[ci + 0] : USHRT_MAX;
+ wchar_t ch2 = (ci + 1 < (int)pattern.size()) ? pattern[ci + 1] : USHRT_MAX;
+ wchar_t ch3 = (ci + 2 < (int)pattern.size()) ? pattern[ci + 2] : USHRT_MAX;
+ bkstring s = L" ";
+
+ if (islowoc(ch1) && isoc(ch2))
+ {
+ curInd += 2;
+ s[0] = fromoc(ch1) * 8 + fromoc(ch2);
+ if (isoc(ch3))
+ {
+ ++curInd;
+ s[0] = s[0] * 8 + fromoc(ch3);
+ }
+ }
+ else if (isoc(ch1) && isoc(ch2))
+ {
+ curInd += 2;
+ s[0] = fromoc(ch1) * 8 + fromoc(ch2);
+ }
+ else raiseError();
+
+ return s;
+ #undef islowoc
+ #undef isoc
+ #undef fromoc
+}
+bkstring WCPattern::parseHex()
+{
+ #define to_low(x) (((x) >= (wchar_t)'A' && (x) <= (wchar_t)'Z') ? ((x) - (wchar_t)'A' + (wchar_t)'a') : (x))
+ #define is_dig(x) ((x) >= (wchar_t)'0' && (x) <= (wchar_t)'9')
+ #define is_hex(x) (is_dig(x) || (to_low(x) >= (wchar_t)'a' && to_low(x) <= (wchar_t)'f'))
+ #define to_int(x) ((is_dig(x)) ? ((x) - (wchar_t)'0') : (to_low(x) - (wchar_t)'a' + 10))
+
+ int ci = curInd;
+ wchar_t ch1 = (ci + 0 < (int)pattern.size()) ? pattern[ci + 0] : USHRT_MAX;
+ wchar_t ch2 = (ci + 1 < (int)pattern.size()) ? pattern[ci + 1] : USHRT_MAX;
+ wchar_t ch3 = (ci + 2 < (int)pattern.size()) ? pattern[ci + 2] : USHRT_MAX;
+ wchar_t ch4 = (ci + 3 < (int)pattern.size()) ? pattern[ci + 3] : USHRT_MAX;
+ bkstring s = L" ";
+
+ if (is_hex(ch1) && is_hex(ch2) && is_hex(ch3) && is_hex(ch4))
+ {
+ curInd += 2;
+ s[0] = (to_int(ch1) << 12 & 0xF000) | (to_int(ch2) << 8 & 0x0F00) |
+ (to_int(ch3) << 4 & 0x0F00) | (to_int(ch4) & 0x000F);
+ }
+ else if (is_hex(ch1) && is_hex(ch2))
+ {
+ curInd += 2;
+ s[0] = (to_int(ch1) << 4 & 0xF0) | (to_int(ch2) & 0x0F);
+ }
+
+ return s;
+ #undef to_low
+ #undef is_dig
+ #undef is_hex
+ #undef to_int
+}
+bkstring WCPattern::parseEscape(bool & inv, bool & quo)
+{
+ wchar_t ch = pattern[curInd++];
+ bkstring classes = L"";
+
+ if (curInd > (int)pattern.size())
+ {
+ raiseError();
+ return NULL;
+ }
+
+ quo = 0;
+ inv = 0;
+ switch (ch)
+ {
+ case (wchar_t)'p': classes = parsePosix(); break;
+ case (wchar_t)'P': classes = L"!!"; classes += parsePosix(); break;
+ case (wchar_t)'d': classes = L"0123456789"; break;
+ case (wchar_t)'D': classes = L"!!0123456789"; break;
+ case (wchar_t)'s': classes = L" \t\r\n\f"; break;
+ case (wchar_t)'S': classes = L"!! \t\r\n\f"; break;
+ case (wchar_t)'w': classes = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; break;
+ case (wchar_t)'W': classes = L"!!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; break;
+ case (wchar_t)'0': classes = parseOctal(); break;
+ case (wchar_t)'x': classes = parseHex(); break;
+
+ case (wchar_t)'Q': quo = 1; break;
+ case (wchar_t)'t': classes = L"\t"; break;
+ case (wchar_t)'r': classes = L"\r"; break;
+ case (wchar_t)'n': classes = L"\n"; break;
+ case (wchar_t)'f': classes = L"\f"; break;
+ case (wchar_t)'a': classes = L"\a"; break;
+ case (wchar_t)'e': classes = L"\r"; break;
+ default: classes = L" "; classes[0] = ch; break;
+ }
+ if (classes.substr(0, 2) == L"!!")
+ {
+ classes = classes.substr(2);
+ inv = 1;
+ }
+ return classes;
+}
+NFAUNode * WCPattern::parseRegisteredWCPattern(NFAUNode ** end)
+{
+ int i, j;
+ bkstring s;
+ NFAUNode * ret = NULL;
+ for (i = curInd; i < (int)pattern.size() && pattern[i] != (wchar_t)'}'; ++i) { }
+ if (pattern[i] != (wchar_t)'}') { raiseError(); return NULL; }
+ if (i == curInd + 1) { raiseError(); return NULL; } // {}
+ if (
+ !(
+ (pattern[curInd] >= (wchar_t)'a' && pattern[curInd] <= (wchar_t)'z') ||
+ (pattern[curInd] >= (wchar_t)'A' && pattern[curInd] <= (wchar_t)'Z') ||
+ (pattern[curInd] == (wchar_t)'_')
+ )
+ )
+ {
+ raiseError();
+ return NULL;
+ }
+ for (j = curInd; !error && j < i; ++j)
+ {
+ if (
+ !(
+ (pattern[j] >= (wchar_t)'a' && pattern[j] <= (wchar_t)'z') ||
+ (pattern[j] >= (wchar_t)'A' && pattern[j] <= (wchar_t)'Z') ||
+ (pattern[j] >= (wchar_t)'0' && pattern[j] <= (wchar_t)'9') ||
+ (pattern[j] == (wchar_t)'_')
+ )
+ )
+ {
+ raiseError();
+ return NULL;
+ }
+ }
+ s = pattern.substr(curInd, i - curInd);
+ if (registeredWCPatterns.find(s) == registeredWCPatterns.end()) raiseError();
+ else
+ {
+ unsigned long oflags = flags;
+ bkstring op = pattern;
+ int ci = i + 1;
+
+ pattern = registeredWCPatterns[s].first;
+ curInd = 0;
+ flags = registeredWCPatterns[s].second;
+
+ --groupCount;
+ ret = parse(0, 0, end);
+
+ pattern = op;
+ curInd = ci;
+ flags = oflags;
+ }
+ if (error) { *end = ret = NULL; }
+ return ret;
+}
+
+// look behind should interpret everything as a literal (except \\) since the
+// pattern must have a concrete length
+NFAUNode * WCPattern::parseBehind(const bool pos, NFAUNode ** end)
+{
+ bkstring t = L"";
+ while (curInd < (int)pattern.size() && pattern[curInd] != (wchar_t)')')
+ {
+ wchar_t ch = pattern[curInd++];
+ t += L" ";
+ if (ch == (wchar_t)'\\')
+ {
+ if (curInd + 1 >= (int)pattern.size())
+ {
+ raiseError();
+ return *end = registerNode(new NFACharUNode((wchar_t)' '));
+ }
+ ch = pattern[curInd++];
+ }
+ t[t.size() - 1] = ch;
+ }
+ if (curInd >= (int)pattern.size() || pattern[curInd] != (wchar_t)')') raiseError();
+ else ++curInd;
+ return *end = registerNode(new NFALookBehindUNode(t, pos));
+}
+NFAUNode * WCPattern::parseQuote()
+{
+ bool done = 0;
+ bkstring s = L"";
+
+ while (!done)
+ {
+ if (curInd >= (int)pattern.size())
+ {
+ raiseError();
+ done = 1;
+ }
+ else if (pattern.substr(curInd, 2) == L"\\E")
+ {
+ curInd += 2;
+ done = 1;
+ }
+ else if (pattern[curInd] == (wchar_t)'\\')
+ {
+ s += L" ";
+ s[s.size() - 1] = pattern[++curInd];
+ ++curInd;
+ }
+ else
+ {
+ s += L" ";
+ s[s.size() - 1] = pattern[curInd++];
+ }
+ }
+ if ((flags & WCPattern::CASE_INSENSITIVE) != 0) return registerNode(new NFACIQuoteUNode(s));
+ return registerNode(new NFAQuoteUNode(s));
+}
+NFAUNode * WCPattern::parse(const bool inParen, const bool inOr, NFAUNode ** end)
+{
+ NFAUNode * start, * cur, * next = NULL;
+ bkstring t;
+ int grc = groupCount++;
+ bool inv, quo;
+ bool ahead = 0, pos = 0, noncap = 0, indep = 0;
+ unsigned long oldFlags = flags;
+
+ if (inParen)
+ {
+ if (pattern[curInd] == (wchar_t)'?')
+ {
+ ++curInd;
+ --groupCount;
+ if (pattern[curInd] == (wchar_t)':') { noncap = 1; ++curInd; grc = --nonCapGroupCount; }
+ else if (pattern[curInd] == (wchar_t)'=') { ++curInd; ahead = 1; pos = 1; }
+ else if (pattern[curInd] == (wchar_t)'!') { ++curInd; ahead = 1; pos = 0; }
+ else if (pattern.substr(curInd, 2) == L"<=") { curInd += 2; return parseBehind(1, end); }
+ else if (pattern.substr(curInd, 2) == L"<!") { curInd += 2; return parseBehind(0, end); }
+ else if (pattern[curInd] == (wchar_t)'>') { ++curInd; indep = 1; }
+ else
+ {
+ bool negate = false, done = false;
+ while (!done)
+ {
+ if (curInd >= (int)pattern.size())
+ {
+ raiseError();
+ return NULL;
+ }
+ else if (negate)
+ {
+ switch (pattern[curInd])
+ {
+ case (wchar_t)'i': flags &= ~WCPattern::CASE_INSENSITIVE; break;
+ case (wchar_t)'d': flags &= ~WCPattern::UNIX_LINE_MODE; break;
+ case (wchar_t)'m': flags &= ~WCPattern::MULTILINE_MATCHING; break;
+ case (wchar_t)'s': flags &= ~WCPattern::DOT_MATCHES_ALL; break;
+ case (wchar_t)':': done = true; break;
+ case (wchar_t)')':
+ ++curInd;
+ *end = registerNode(new NFALookBehindUNode(L"", true));
+ return *end;
+ case (wchar_t)'-':
+ default: raiseError(); return NULL;
+ }
+ }
+ else
+ {
+ switch (pattern[curInd])
+ {
+ case (wchar_t)'i': flags |= WCPattern::CASE_INSENSITIVE; break;
+ case (wchar_t)'d': flags |= WCPattern::UNIX_LINE_MODE; break;
+ case (wchar_t)'m': flags |= WCPattern::MULTILINE_MATCHING; break;
+ case (wchar_t)'s': flags |= WCPattern::DOT_MATCHES_ALL; break;
+ case (wchar_t)':': done = true; break;
+ case (wchar_t)'-': negate = true; break;
+ case (wchar_t)')':
+ ++curInd;
+ *end = registerNode(new NFALookBehindUNode(L"", true));
+ return *end;
+ default: raiseError(); return NULL;
+ }
+ }
+ ++curInd;
+ }
+ noncap = 1;
+ grc = --nonCapGroupCount;
+ }
+
+ if (noncap) cur = start = registerNode(new NFAGroupHeadUNode(grc));
+ else cur = start = registerNode(new NFASubStartUNode);
+ }
+ else cur = start = registerNode(new NFAGroupHeadUNode(grc));
+ }
+ else cur = start = registerNode(new NFASubStartUNode);
+ while (curInd < (int)pattern.size())
+ {
+ wchar_t ch = pattern[curInd++];
+
+ next = NULL;
+ if (error) return NULL;
+ switch (ch)
+ {
+ case (wchar_t)'^':
+ if ((flags & WCPattern::MULTILINE_MATCHING) != 0) next = registerNode(new NFAStartOfLineUNode);
+ else next = registerNode(new NFAStartOfInputUNode);
+ break;
+ case (wchar_t)'$':
+ if ((flags & WCPattern::MULTILINE_MATCHING) != 0) next = registerNode(new NFAEndOfLineUNode);
+ else next = registerNode(new NFAEndOfInputUNode(0));
+ break;
+ case (wchar_t)'|':
+ --groupCount;
+ cur->next = registerNode(new NFAAcceptUNode);
+ cur = start = registerNode(new NFAOrUNode(start, parse(inParen, 1)));
+ break;
+ case (wchar_t)'\\':
+ if (curInd < (int)pattern.size())
+ {
+ bool eoi = 0;
+ switch (pattern[curInd])
+ {
+ case (wchar_t)'1':
+ case (wchar_t)'2':
+ case (wchar_t)'3':
+ case (wchar_t)'4':
+ case (wchar_t)'5':
+ case (wchar_t)'6':
+ case (wchar_t)'7':
+ case (wchar_t)'8':
+ case (wchar_t)'9': next = parseBackref(); break;
+ case (wchar_t)'A': ++curInd; next = registerNode(new NFAStartOfInputUNode); break;
+ case (wchar_t)'B': ++curInd; next = registerNode(new NFAWordBoundaryUNode(0)); break;
+ case (wchar_t)'b': ++curInd; next = registerNode(new NFAWordBoundaryUNode(1)); break;
+ case (wchar_t)'G': ++curInd; next = registerNode(new NFAEndOfMatchUNode); break;
+ case (wchar_t)'Z': eoi = 1;
+ case (wchar_t)'z': ++curInd; next = registerNode(new NFAEndOfInputUNode(eoi)); break;
+ default:
+ t = parseEscape(inv, quo);
+ //printf("inv quo classes { %c %c %s }\n", inv ? (wchar_t)'t' : (wchar_t)'f', quo ? (wchar_t)'t' : (wchar_t)'f', t.c_str());
+ if (!quo)
+ {
+ if (t.size() > 1 || inv)
+ {
+ if ((flags & WCPattern::CASE_INSENSITIVE) != 0) next = registerNode(new NFACIClassUNode(t, inv));
+ else next = registerNode(new NFAClassUNode(t, inv));
+ }
+ else
+ {
+ next = registerNode(new NFACharUNode(t[0]));
+ }
+ }
+ else
+ {
+ next = parseQuote();
+ }
+ }
+ }
+ else raiseError();
+ break;
+ case (wchar_t)'[':
+ if ((flags & WCPattern::CASE_INSENSITIVE) == 0)
+ {
+ NFAClassUNode * clazz = new NFAClassUNode();
+ bkstring s = parseClass();
+ for (int i = 0; i < (int)s.size(); ++i) clazz->vals[s[i]] = 1;
+ next = registerNode(clazz);
+ }
+ else
+ {
+ NFACIClassUNode * clazz = new NFACIClassUNode();
+ bkstring s = parseClass();
+ for (int i = 0; i < (int)s.size(); ++i) clazz->vals[to_lower(s[i])] = 1;
+ next = registerNode(clazz);
+ }
+ break;
+ case (wchar_t)'.':
+ {
+ bool useN = 1, useR = 1;
+ NFAClassUNode * clazz = new NFAClassUNode(1);
+ if ((flags & WCPattern::UNIX_LINE_MODE) != 0) useR = 0;
+ if ((flags & WCPattern::DOT_MATCHES_ALL) != 0) useN = useR = 0;
+ if (useN) clazz->vals[(wchar_t)'\n'] = 1;
+ if (useR) clazz->vals[(wchar_t)'\r'] = 1;
+ next = registerNode(clazz);
+ }
+ break;
+ case (wchar_t)'(':
+ {
+ NFAUNode * end, * t1, * t2;
+ t1 = parse(1, 0, &end);
+ if (!t1) raiseError();
+ else if (t1->isGroupHeadNode() && (t2 = quantifyGroup(t1, end, grc)) != NULL)
+ {
+ cur->next = t2;
+ cur = t2->next;
+ }
+ else
+ {
+ cur->next = t1;
+ cur = end;
+ }
+ }
+ break;
+ case (wchar_t)')':
+ if (!inParen) raiseError();
+ else if (inOr)
+ {
+ --curInd;
+ cur = cur->next = registerNode(new NFAAcceptUNode);
+ flags = oldFlags;
+ return start;
+ }
+ else
+ {
+ if (ahead)
+ {
+ cur = cur->next = registerNode(new NFAAcceptUNode);
+ flags = oldFlags;
+ return *end = registerNode(new NFALookAheadUNode(start, pos));
+ }
+ else if (indep)
+ {
+ cur = cur->next = registerNode(new NFAAcceptUNode);
+ flags = oldFlags;
+ return *end = registerNode(new NFAPossessiveQuantifierUNode(this, start, 1, 1));
+ }
+ else // capping or noncapping, it doesnt matter
+ {
+ *end = cur = cur->next = registerNode(new NFAGroupTailUNode(grc));
+ next = quantifyGroup(start, *end, grc);
+ if (next)
+ {
+ start = next;
+ *end = next->next;
+ }
+ flags = oldFlags;
+ return start;
+ }
+ }
+ break;
+ case (wchar_t)'{': // registered pattern
+ cur->next = parseRegisteredWCPattern(&next);
+ if (cur->next) cur = next;
+ break;
+ case (wchar_t)'*':
+ case (wchar_t)'+':
+ case (wchar_t)'?':
+// case (wchar_t)'}':
+// case (wchar_t)']':
+ raiseError();
+ break;
+ default:
+ if ((flags & WCPattern::CASE_INSENSITIVE) != 0) next = registerNode(new NFACICharUNode(ch));
+ else next = registerNode(new NFACharUNode(ch));
+ break;
+ }
+ if (next) cur = cur->next = quantify(next);
+ }
+ if (inParen) raiseError();
+ else
+ {
+ if (inOr) cur = cur->next = registerNode(new NFAAcceptUNode);
+ if (end) *end = cur;
+ }
+
+ flags = oldFlags;
+ if (error) return NULL;
+
+ return start;
+}
+
+WCPattern * WCPattern::compile(const bkstring & pattern, const unsigned long mode)
+{
+ WCPattern * p = new WCPattern(pattern);
+ NFAUNode * end;
+
+ p->flags = mode;
+ if ((mode & WCPattern::LITERAL) != 0)
+ {
+ p->head = p->registerNode(new NFAStartUNode);
+ if ((mode & WCPattern::CASE_INSENSITIVE) != 0) p->head->next = p->registerNode(new NFACIQuoteUNode(pattern));
+ else p->head->next = p->registerNode(new NFAQuoteUNode(pattern));
+ p->head->next->next = p->registerNode(new NFAEndUNode);
+ }
+ else
+ {
+ p->head = p->parse(0, 0, &end);
+ if (!p->head)
+ {
+ delete p;
+ p = NULL;
+ }
+ else
+ {
+ if (!(p->head && p->head->isStartOfInputNode()))
+ {
+ NFAUNode * n = p->registerNode(new NFAStartUNode);
+ n->next = p->head;
+ p->head = n;
+ }
+ end->next = p->registerNode(new NFAEndUNode);
+ }
+ }
+ if (p != NULL)
+ {
+ p->matcher = new WCMatcher(p, L"");
+ }
+
+ return p;
+}
+
+WCPattern * WCPattern::compileAndKeep(const bkstring & pattern, const unsigned long mode)
+{
+ WCPattern * ret = NULL;
+ std::map<bkstring, WCPattern*>::iterator it = compiledWCPatterns.find(pattern);
+
+ if (it != compiledWCPatterns.end())
+ {
+ ret = it->second;
+ }
+ else
+ {
+ ret = compile(pattern, mode);
+ compiledWCPatterns[pattern] = ret;
+ }
+
+ return ret;
+}
+bkstring WCPattern::replace(const bkstring & pattern, const bkstring & str,
+ const bkstring & replacementText, const unsigned long mode)
+{
+ bkstring ret;
+ WCPattern * p = WCPattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->replace(str, replacementText);
+ delete p;
+ }
+ return ret;
+}
+
+std::vector<bkstring> WCPattern::split(const bkstring & pattern, const bkstring & str, const bool keepEmptys,
+ const unsigned long limit, const unsigned long mode)
+{
+ std::vector<bkstring> ret;
+ WCPattern * p = WCPattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->split(str, keepEmptys, limit);
+ delete p;
+ }
+ return ret;
+}
+
+std::vector<bkstring> WCPattern::findAll(const bkstring & pattern, const bkstring & str, const unsigned long mode)
+{
+ std::vector<bkstring> ret;
+ WCPattern * p = WCPattern::compile(pattern, mode);
+ if (p)
+ {
+ ret = p->findAll(str);
+ delete p;
+ }
+ return ret;
+}
+
+bool WCPattern::matches(const bkstring & pattern, const bkstring & str, const unsigned long mode)
+{
+ bool ret = 0;
+ WCPattern * p = compile(pattern, mode);
+
+ if (p)
+ {
+ ret = p->matches(str);
+ delete p;
+ }
+
+ return ret;
+}
+
+bool WCPattern::registerWCPattern(const bkstring & name, const bkstring & pattern, const unsigned long mode)
+{
+ WCPattern * p = WCPattern::compile(pattern, mode);
+ if (!p) return 0;
+ WCPattern::registeredWCPatterns[name] = std::make_pair(pattern, mode);
+ delete p;
+ return 1;
+}
+
+void WCPattern::unregisterWCPatterns()
+{
+ registeredWCPatterns.clear();
+}
+void WCPattern::clearWCPatternCache()
+{
+ std::map<bkstring, WCPattern*>::iterator it;
+ for (it = compiledWCPatterns.begin(); it != compiledWCPatterns.end(); ++it)
+ {
+ delete it->second;
+ }
+ compiledWCPatterns.clear();
+}
+
+std::pair<bkstring, int> WCPattern::findNthMatch(const bkstring & pattern, const bkstring & str,
+ const int matchNum, const unsigned long mode)
+{
+ std::pair<bkstring, int> ret;
+ WCPattern * p = WCPattern::compile(pattern, mode);
+
+ ret.second = -1;
+ if (p)
+ {
+ int i = -1;
+ p->matcher->setString(str);
+ while (i < matchNum && p->matcher->findNextMatch()) { ++i; }
+ if (i == matchNum && p->matcher->getStartingIndex() >= 0)
+ {
+ ret.first = p->matcher->getGroup(0);
+ ret.second = p->matcher->getStartingIndex();
+ }
+ delete p;
+ }
+
+ return ret;
+}
+
+WCPattern::~WCPattern()
+{
+ /*
+ nodes.clear();
+ if (head) head->findAllNodes(nodes);
+ */
+ if (matcher) delete matcher;
+ for (std::map<NFAUNode*, bool>::iterator it = nodes.begin(); it != nodes.end(); ++it) delete it->first;
+}
+bkstring WCPattern::replace(const bkstring & str, const bkstring & replacementText)
+{
+ int li = 0;
+ bkstring ret = L"";
+
+ matcher->setString(str);
+ while (matcher->findNextMatch())
+ {
+ ret += str.substr(li, matcher->getStartingIndex() - li);
+ ret += matcher->replaceWithGroups(replacementText);
+ li = matcher->getEndingIndex();
+ }
+ ret += str.substr(li);
+
+ return ret;
+}
+std::vector<bkstring> WCPattern::split(const bkstring & str, const bool keepEmptys, const unsigned long limit)
+{
+ unsigned long lim = (limit == 0 ? MAX_QMATCH : limit);
+ int li = 0;
+ std::vector<bkstring> ret;
+
+ matcher->setString(str);
+
+ while (matcher->findNextMatch() && ret.size() < lim)
+ {
+ if (matcher->getStartingIndex() == 0 && keepEmptys) ret.push_back(L"");
+ if ((matcher->getStartingIndex() != matcher->getEndingIndex()) || keepEmptys)
+ {
+ if (li != matcher->getStartingIndex() || keepEmptys)
+ {
+ ret.push_back(str.substr(li, matcher->getStartingIndex() - li));
+ }
+ li = matcher->getEndingIndex();
+ }
+ }
+ if (li < (int)str.size()) ret.push_back(str.substr(li));
+
+ return ret;
+}
+std::vector<bkstring> WCPattern::findAll(const bkstring & str)
+{
+ matcher->setString(str);
+ return matcher->findAll();
+}
+bool WCPattern::matches(const bkstring & str)
+{
+ matcher->setString(str);
+ return matcher->matches();
+}
+unsigned long WCPattern::getFlags() const
+{
+ return flags;
+}
+bkstring WCPattern::getWCPattern() const
+{
+ return pattern;
+}
+WCMatcher * WCPattern::createWCMatcher(const bkstring & str)
+{
+ return new WCMatcher(this, str);
+}
+
+// NFAUNode
+
+NFAUNode::NFAUNode() { next = NULL; }
+NFAUNode::~NFAUNode() { }
+void NFAUNode::findAllNodes(std::map<NFAUNode*, bool> & soFar)
+{
+ if (soFar.find(this) == soFar.end()) return;
+ soFar[this] = 1;
+ if (next) next->findAllNodes(soFar);
+}
+
+// NFACharUNode
+
+NFACharUNode::NFACharUNode(const wchar_t c) { ch = c; }
+int NFACharUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && str[curInd] == ch) return next->match(str, matcher, curInd + 1);
+ return -1;
+}
+
+// NFACICharUNode
+
+NFACICharUNode::NFACICharUNode(const wchar_t c) { ch = to_lower(c); }
+int NFACICharUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && to_lower(str[curInd]) == ch) return next->match(str, matcher, curInd + 1);
+ return -1;
+}
+
+// NFAStartUNode
+
+NFAStartUNode::NFAStartUNode() { }
+int NFAStartUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ret = -1, ci = curInd;
+
+ matcher->starts[0] = curInd;
+ if ((matcher->getFlags() & WCMatcher::MATCH_ENTIRE_STRING) == (unsigned int)WCMatcher::MATCH_ENTIRE_STRING)
+ {
+ if (curInd != 0)
+ {
+ matcher->starts[0] = -1;
+ return -1;
+ }
+ return next->match(str, matcher, 0);
+ }
+ while ((ret = next->match(str, matcher, ci)) == -1 && ci < (int)str.size())
+ {
+ matcher->clearGroups();
+ matcher->starts[0] = ++ci;
+ }
+ if (ret < 0) matcher->starts[0] = -1;
+ return ret;
+}
+
+// NFAEndUNode
+
+NFAEndUNode::NFAEndUNode() { }
+int NFAEndUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ matcher->ends[0] = curInd;
+ if ((matcher->getFlags() & WCMatcher::MATCH_ENTIRE_STRING) != 0)
+ {
+ if (curInd == (int)str.size()) return curInd;
+ matcher->ends[0] = -1;
+ return -1;
+ }
+ return curInd;
+}
+
+// NFAQuantifierUNode
+
+void NFAQuantifierUNode::findAllNodes(std::map<NFAUNode*, bool> & soFar)
+{
+ inner->findAllNodes(soFar);
+ NFAUNode::findAllNodes(soFar);
+}
+NFAQuantifierUNode::NFAQuantifierUNode(WCPattern * pat, NFAUNode * internal, const int minMatch, const int maxMatch)
+{
+ inner = internal;
+ inner->next = pat->registerNode(new NFAAcceptUNode);
+ min = (minMatch < WCPattern::MIN_QMATCH) ? WCPattern::MIN_QMATCH : minMatch;
+ max = (maxMatch > WCPattern::MAX_QMATCH) ? WCPattern::MAX_QMATCH : maxMatch;
+}
+
+int NFAQuantifierUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int i0, i1, i2 = 0;
+
+ i0 = i1 = curInd;
+ while (i2 < min)
+ {
+
+ ++i2;
+ i1 = inner->match(str, matcher, i0);
+ if (i1 <= i0) return i1; // i1 < i0 means i1 is -1
+ i0 = i1;
+ }
+
+ return i1;
+}
+// NFAGreedyQuantifierUNode
+
+NFAGreedyQuantifierUNode::NFAGreedyQuantifierUNode(WCPattern * pat, NFAUNode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierUNode(pat, internal, minMatch, maxMatch) { }
+int NFAGreedyQuantifierUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int t = NFAQuantifierUNode::match(str, matcher, curInd);
+ if (t != -1) return matchInternal(str, matcher, t, min);
+ return t;
+}
+int NFAGreedyQuantifierUNode::matchInternal(const bkstring & str, WCMatcher * matcher, const int curInd, const int soFar) const
+{
+ if (soFar >= max) return next->match(str, matcher, curInd);
+
+ int i, j;
+
+ i = inner->match(str, matcher, curInd);
+ if (i != -1)
+ {
+ j = matchInternal(str, matcher, i, soFar + 1);
+ if (j != -1) return j;
+ }
+ return next->match(str, matcher, curInd);
+}
+
+// NFALazyQuantifierUNode
+
+NFALazyQuantifierUNode::NFALazyQuantifierUNode(WCPattern * pat, NFAUNode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierUNode(pat, internal, minMatch, maxMatch) { }
+int NFALazyQuantifierUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int i, j, m = NFAQuantifierUNode::match(str, matcher, curInd);
+
+ if (m == -1) return -1;
+
+ for (i = min; i < max; ++i)
+ {
+ j = next->match(str, matcher, m);
+ if (j == -1)
+ {
+ j = inner->match(str, matcher, m);
+ // if j < m, then j is -1, so we bail.
+ // if j == m, then we would just go and call next->match on the same index,
+ // but it already failed trying to match right there, so we know we can
+ // just bail
+ if (j <= m) return -1;
+ m = j;
+ }
+ else return j;
+ }
+ return next->match(str, matcher, m);
+}
+
+// NFAPossessiveQuantifierUNode
+
+NFAPossessiveQuantifierUNode::NFAPossessiveQuantifierUNode(WCPattern * pat, NFAUNode * internal, const int minMatch, const int maxMatch)
+ : NFAQuantifierUNode(pat, internal, minMatch, maxMatch) { }
+int NFAPossessiveQuantifierUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int i, j, m = NFAQuantifierUNode::match(str, matcher, curInd);
+
+ if (m == -1) return -1;
+ for (i = min; i < max; ++i)
+ {
+ j = inner->match(str, matcher, m);
+ if (j <= m) return next->match(str, matcher, m);
+ m = j;
+ }
+ return next->match(str, matcher, m);
+}
+
+// NFAAcceptUNode
+
+NFAAcceptUNode::NFAAcceptUNode() { }
+int NFAAcceptUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (!next) return curInd;
+ else return next->match(str, matcher, curInd);
+}
+
+// NFAClassUNode
+
+NFAClassUNode::NFAClassUNode(const bool invert)
+{
+ inv = invert;
+}
+NFAClassUNode::NFAClassUNode(const bkstring & clazz, const bool invert)
+{
+ inv = invert;
+ for (int i = 0; i < (int)clazz.size(); ++i) vals[clazz[i]] = 1;
+}
+int NFAClassUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && ((vals.find(str[curInd]) != vals.end()) ^ inv))
+ {
+ return next->match(str, matcher, curInd + 1);
+ }
+ return -1;
+}
+
+// NFACIClassUNode
+
+NFACIClassUNode::NFACIClassUNode(const bool invert)
+{
+ inv = invert;
+}
+NFACIClassUNode::NFACIClassUNode(const bkstring & clazz, const bool invert)
+{
+ inv = invert;
+ for (int i = 0; i < (int)clazz.size(); ++i) vals[to_lower(clazz[i])] = 1;
+}
+int NFACIClassUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd < (int)str.size() && ((vals.find(to_lower(str[curInd])) != vals.end()) ^ inv))
+ {
+ return next->match(str, matcher, curInd + 1);
+ }
+ return -1;
+}
+
+// NFASubStartUNode
+
+NFASubStartUNode::NFASubStartUNode() { }
+int NFASubStartUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ return next->match(str, matcher, curInd);
+}
+
+// NFAOrUNode
+
+NFAOrUNode::NFAOrUNode(NFAUNode * first, NFAUNode * second) : one(first), two(second) { }
+void NFAOrUNode::findAllNodes(std::map<NFAUNode*, bool> & soFar)
+{
+ if (one) one->findAllNodes(soFar);
+ if (two) two->findAllNodes(soFar);
+ NFAUNode::findAllNodes(soFar);
+}
+int NFAOrUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ci = one->match(str, matcher, curInd);
+
+ if (ci != -1) ci = next->match(str, matcher, ci);
+ if (ci != -1) return ci;
+ if (ci == -1) ci = two->match(str, matcher, curInd);
+ if (ci != -1) ci = next->match(str, matcher, ci);
+ return ci;
+}
+
+// NFAQuoteUNode
+
+NFAQuoteUNode::NFAQuoteUNode(const bkstring & quoted) : qStr(quoted) { }
+int NFAQuoteUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd + qStr.size() > str.size()) return -1;
+ if (str.substr(curInd, qStr.size()) != qStr) return -1;
+ return next->match(str, matcher, curInd + (int)qStr.size());
+}
+
+// NFACIQuoteUNode
+
+NFACIQuoteUNode::NFACIQuoteUNode(const bkstring & quoted) : qStr(quoted) { }
+int NFACIQuoteUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd + qStr.size() > str.size()) return -1;
+ if (str_icmp(str.substr(curInd, qStr.size()).c_str(), qStr.c_str())) return -1;
+ return next->match(str, matcher, (int)qStr.size());
+}
+
+// NFALookAheadUNode
+
+NFALookAheadUNode::NFALookAheadUNode(NFAUNode * internal, const bool positive) : NFAUNode(), pos(positive), inner(internal) { }
+void NFALookAheadUNode::findAllNodes(std::map<NFAUNode*, bool> & soFar)
+{
+ if (inner) inner->findAllNodes(soFar);
+ NFAUNode::findAllNodes(soFar);
+}
+int NFALookAheadUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ return ((inner->match(str, matcher, curInd) == -1) ^ pos) ? next->match(str, matcher, curInd) : -1;
+}
+
+// NFALookBehindUNode
+
+NFALookBehindUNode::NFALookBehindUNode(const bkstring & str, const bool positive) : pos(positive), mStr(str) { }
+int NFALookBehindUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (pos)
+ {
+ if (curInd < (int)mStr.size()) return -1;
+ if (str.substr(curInd - mStr.size(), mStr.size()) == mStr) return next->match(str, matcher, curInd);
+ }
+ else
+ {
+ if (curInd < (int)mStr.size()) return next->match(str, matcher, curInd);
+ if (str.substr(curInd - mStr.size(), mStr.size()) == mStr) return -1;
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAStartOfLineUNode
+
+NFAStartOfLineUNode::NFAStartOfLineUNode() { }
+int NFAStartOfLineUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd == 0 || str[curInd - 1] == (wchar_t)'\n' || str[curInd - 1] == (wchar_t)'\r')
+ {
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAEndOfLineUNode
+
+NFAEndOfLineUNode::NFAEndOfLineUNode() { }
+int NFAEndOfLineUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd >= (int)str.size() || str[curInd] == (wchar_t)'\n' || str[curInd] == (wchar_t)'\r')
+ {
+ return next->match(str, matcher, curInd);
+ }
+ return -1;
+}
+
+// NFAReferenceUNode
+
+NFAReferenceUNode::NFAReferenceUNode(const int groupIndex) : gi(groupIndex) { }
+int NFAReferenceUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int len = matcher->ends[gi] - matcher->starts[gi];
+ int ni = -1;
+ if (gi < 1 || matcher->ends[gi] < matcher->starts[gi] || len == 0) ni = curInd;
+ else if (curInd + len > (int)str.size()) return -1;
+ else if (str.substr(curInd, len) != str.substr(matcher->starts[gi], len)) return -1;
+ else ni = curInd + len;
+
+ return next->match(str, matcher, ni);
+}
+
+// NFAStartOfInputUNode
+
+NFAStartOfInputUNode::NFAStartOfInputUNode() { }
+int NFAStartOfInputUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd == 0) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAEndOfInputUNode
+
+NFAEndOfInputUNode::NFAEndOfInputUNode(const bool lookForTerm) : term(lookForTerm) { }
+int NFAEndOfInputUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int len = (int)str.size();
+ if (curInd == len) return next->match(str, matcher, curInd);
+ else if (term)
+ {
+ if (curInd == len - 1 && (str[curInd] == (wchar_t)'\r' || str[curInd] == (wchar_t)'\n'))
+ {
+ return next->match(str, matcher, curInd);
+ }
+ else if (curInd == len - 2 && str.substr(curInd, 2) == L"\r\n")
+ {
+ return next->match(str, matcher, curInd);
+ }
+ }
+ return -1;
+}
+
+// NFAWordBoundaryUNode
+
+NFAWordBoundaryUNode::NFAWordBoundaryUNode(const bool positive) : pos(positive) { }
+int NFAWordBoundaryUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int len = (int)str.size();
+
+ wchar_t c1 = (curInd - 1 < len && curInd > 0) ? str[curInd - 1] : '\n';
+ wchar_t c2 = (curInd < len) ? str[curInd ] : '\n';
+
+ if (curInd == len) return next->match(str, matcher, curInd);
+ bool ok = is_alpha(c1) != is_alpha(c2);
+ if (ok && pos) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAEndOfMatchUNode
+
+NFAEndOfMatchUNode::NFAEndOfMatchUNode() { }
+int NFAEndOfMatchUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ if (curInd == matcher->lm) return next->match(str, matcher, curInd);
+ return -1;
+}
+
+// NFAGroupHeadUNode
+
+NFAGroupHeadUNode::NFAGroupHeadUNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupHeadUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ret, o = matcher->starts[gi];
+
+ matcher->starts[gi] = curInd;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0) matcher->starts[gi] = o;
+
+ return ret;
+}
+
+// NFAGroupTailUNode
+
+NFAGroupTailUNode::NFAGroupTailUNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupTailUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ret, o = matcher->ends[gi];
+
+ matcher->ends[gi] = curInd;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0) matcher->ends[gi] = o;
+
+ return ret;
+}
+
+// NFAGroupLoopPrologueUNode
+
+NFAGroupLoopPrologueUNode::NFAGroupLoopPrologueUNode(const int groupIndex) : gi(groupIndex) { }
+int NFAGroupLoopPrologueUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ret, o1 = matcher->groups[gi], o2 = matcher->groupPos[gi], o3 = matcher->groupIndeces[gi];
+
+ matcher->groups[gi] = 0;
+ matcher->groupPos[gi] = 0;
+ matcher->groupIndeces[gi] = -1;
+ ret = next->match(str, matcher, curInd);
+ if (ret < 0)
+ {
+ matcher->groups[gi] = o1;
+ matcher->groupPos[gi] = o2;
+ matcher->groupIndeces[gi] = o3;
+ }
+
+ return ret;
+}
+
+// NFAGroupLoopUNode
+
+NFAGroupLoopUNode::NFAGroupLoopUNode(NFAUNode * internal, const int minMatch, const int maxMatch,
+ const int groupIndex, const int matchType)
+{
+ inner = internal;
+ min = minMatch;
+ max = maxMatch;
+ gi = groupIndex;
+ type = matchType;
+}
+void NFAGroupLoopUNode::findAllNodes(std::map<NFAUNode*, bool> & soFar)
+{
+ if (inner) inner->findAllNodes(soFar);
+ NFAUNode::findAllNodes(soFar);
+}
+int NFAGroupLoopUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ bool b = (curInd > matcher->groupIndeces[gi]);
+
+ if (b && matcher->groups[gi] < min)
+ {
+ ++matcher->groups[gi];
+ int o = matcher->groupIndeces[gi];
+ matcher->groupIndeces[gi] = curInd;
+ int ret = inner->match(str, matcher, curInd);
+ if (ret < 0)
+ {
+ matcher->groupIndeces[gi] = o;
+ --matcher->groups[gi];
+ }
+ return ret;
+ }
+ else if (!b || matcher->groups[gi] >= max)
+ {
+ return next->match(str, matcher, curInd);
+ }
+ else
+ {
+ switch (type)
+ {
+ case 0: return matchGreedy(str, matcher, curInd);
+ case 1: return matchLazy(str, matcher, curInd);
+ case 2: return matchPossessive(str, matcher, curInd);
+ }
+ }
+ return -1;
+}
+int NFAGroupLoopUNode::matchGreedy(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int o = matcher->groupIndeces[gi]; // save our info for backtracking
+ matcher->groupIndeces[gi] = curInd; // move along
+ ++matcher->groups[gi];
+ int ret = inner->match(str, matcher, curInd); // match internally
+ if (ret < 0)
+ { // if we failed, then restore info and match next
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ ret = next->match(str, matcher, curInd);
+ }
+ return ret;
+}
+int NFAGroupLoopUNode::matchLazy(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int ret = next->match(str, matcher, curInd); // be lazy, just go on
+ if (ret < 0)
+ {
+ int o = matcher->groupIndeces[gi]; // save info for backtracking
+ matcher->groupIndeces[gi] = curInd; // advance our position
+ ++matcher->groups[gi];
+ ret = inner->match(str, matcher, curInd); // match our internal stuff
+ if (ret < 0) // if we failed, then restore the info
+ {
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ }
+ }
+ return ret;
+}
+int NFAGroupLoopUNode::matchPossessive(const bkstring & str, WCMatcher * matcher, const int curInd) const
+{
+ int o = matcher->groupIndeces[gi]; // save info for backtracking
+ matcher->groupPos[gi] = matcher->groups[gi]; // set a flag stating we have matcher at least this much
+ matcher->groupIndeces[gi] = curInd; // move along
+ ++matcher->groups[gi];
+ int ret = inner->match(str, matcher, curInd); // try and match again
+ if (ret < 0)
+ { // if we fail, back off, but to an extent
+ --matcher->groups[gi];
+ matcher->groupIndeces[gi] = o;
+ if (matcher->groups[gi] == matcher->groupPos[gi]) ret = next->match(str, matcher, curInd);
+ }
+ return ret;
+}
+
+#ifdef _WIN32
+ #pragma warning(pop)
+#endif
diff --git a/plugins/SmileyAdd/src/regexp/WCPattern.h b/plugins/SmileyAdd/src/regexp/WCPattern.h
new file mode 100644
index 0000000000..3d52a7fd2e
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/WCPattern.h
@@ -0,0 +1,1663 @@
+#ifndef __WCPATTERN_H__
+#define __WCPATTERN_H__
+
+#ifdef _WIN32
+ #pragma warning(disable:4786)
+#endif
+
+#include "bkstring.h"
+
+#include <vector>
+#include <map>
+
+class WCMatcher;
+class NFAUNode;
+class NFAQuantifierUNode;
+
+/**
+ This pattern class is very similar in functionality to Java's
+ java.util.regex.WCPattern class. The pattern class represents an immutable
+ regular expression object. Instead of having a single object contain both the
+ regular expression object and the matching object, instead the two objects are
+ split apart. The {@link WCMatcher WCMatcher} class represents the maching
+ object.
+
+ The WCPattern class works primarily off of "compiled" patterns. A typical
+ instantiation of a regular expression looks like:
+
+ <pre>
+ WCPattern * p = WCPattern::compile(L"a*b");
+ WCMatcher * m = p->createWCMatcher(L"aaaaaab");
+ if (m->matches()) ...
+ </pre>
+
+ However, if you do not need to use a pattern more than once, it is often times
+ okay to use the WCPattern's static methods insteads. An example looks like this:
+
+ <pre>
+ if (WCPattern::matches(L"a*b", L"aaaab")) { ... }
+ </pre>
+
+ This class does not currently support unicode. The unicode update for this
+ class is coming soon.
+
+ This class is partially immutable. It is completely safe to call createWCMatcher
+ concurrently in different threads, but the other functions (e.g. split) should
+ not be called concurrently on the same <code>WCPattern</code>.
+
+ <table border="0" cellpadding="1" cellspacing="0">
+ <tr align="left" bgcolor="#CCCCFF">
+ <td>
+ <b>Construct</b>
+ </td>
+ <td>
+ <b>Matches</b>
+ </th>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Characters</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x</i></code>
+ </td>
+ <td>
+ The character <code><i>x</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\\</code>
+ </td>
+ <td>
+ The character <code>\</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\0<i>nn</i></code>
+ </td>
+ <td>
+ The character with octal ASCII value <code><i>nn</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\0<i>nnn</i></code>
+ </td>
+ <td>
+ The character with octal ASCII value <code><i>nnn</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\x<i>hh</i></code>
+ </td>
+ <td>
+ The character with hexadecimal ASCII value <code><i>hh</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\t</code>
+ </td>
+ <td>
+ A tab character
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\r</code>
+ </td>
+ <td>
+ A carriage return character
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\n</code>
+ </td>
+ <td>
+ A new-line character
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>Character Classes</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[abc]</code>
+ </td>
+ <td>
+ Either <code>a</code>, <code>b</code>, or <code>c</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[^abc]</code>
+ </td>
+ <td>
+ Any character but <code>a</code>, <code>b</code>, or <code>c</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-zA-Z]</code>
+ </td>
+ <td>
+ Any character ranging from <code>a</code> thru <code>z</code>, or
+ <code>A</code> thru <code>Z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[^a-zA-Z]</code>
+ </td>
+ <td>
+ Any character except those ranging from <code>a</code> thru
+ <code>z</code>, or <code>A</code> thru <code>Z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a\-z]</code>
+ </td>
+ <td>
+ Either <code>a</code>, <code>-</code>, or <code>z</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z[A-Z]]</code>
+ </td>
+ <td>
+ Same as <code>[a-zA-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z&&[g-i]]</code>
+ </td>
+ <td>
+ Any character in the intersection of <code>a-z</code> and
+ <code>g-i</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>[a-z&&[^g-i]]</code>
+ </td>
+ <td>
+ Any character in <code>a-z</code> and not in <code>g-i</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Prefefined character classes</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><b>.</b></code>
+ </td>
+ <td>
+ Any character. Multiline matching must be compiled into the pattern for
+ <code><b>.</b></code> to match a <code>\r</code> or a <code>\n</code>.
+ Even if multiline matching is enabled, <code><b>.</b></code> will not
+ match a <code>\r\n</code>, only a <code>\r</code> or a <code>\n</code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\d</code>
+ </td>
+ <td>
+ <code>[0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\D</code>
+ </td>
+ <td>
+ <code>[^\d]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\s</code>
+ </td>
+ <td>
+ <code>[&nbsp;\t\r\n\x0B]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\S</code>
+ </td>
+ <td>
+ <code>[^\s]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\w</code>
+ </td>
+ <td>
+ <code>[a-zA-Z0-9_]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\W</code>
+ </td>
+ <td>
+ <code>[^\w]</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>POSIX character classes
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Lower}</code>
+ </td>
+ <td>
+ <code>[a-z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Upper}</code>
+ </td>
+ <td>
+ <code>[A-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{ASCII}</code>
+ </td>
+ <td>
+ <code>[\x00-\x7F]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Alpha}</code>
+ </td>
+ <td>
+ <code>[a-zA-Z]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Digit}</code>
+ </td>
+ <td>
+ <code>[0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Alnum}</code>
+ </td>
+ <td>
+ <code>[\w&&[^_]]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{Punct}</code>
+ </td>
+ <td>
+ <code>[!"#$%&'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~]</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\p{XDigit}</code>
+ </td>
+ <td>
+ <code>[a-fA-F0-9]</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Boundary Matches</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>^</code>
+ </td>
+ <td>
+ The beginning of a line. Also matches the beginning of input.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>$</code>
+ </td>
+ <td>
+ The end of a line. Also matches the end of input.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\b</code>
+ </td>
+ <td>
+ A word boundary
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\B</code>
+ </td>
+ <td>
+ A non word boundary
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\A</code>
+ </td>
+ <td>
+ The beginning of input
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\G</code>
+ </td>
+ <td>
+ The end of the previous match. Ensures that a "next" match will only
+ happen if it begins with the character immediately following the end of
+ the "current" match.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\Z</code>
+ </td>
+ <td>
+ The end of input. Will also match if there is a single trailing
+ <code>\r\n</code>, a single trailing <code>\r</code>, or a single
+ trailing <code>\n</code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\z</code>
+ </td>
+ <td>
+ The end of input
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Greedy Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x?</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x+</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Possessive Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x?+</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*+</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x++</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}+</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Reluctant Quantifiers</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x??</i></code>
+ </td>
+ <td>
+ <i>x</i>, either zero times or one time
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x*?</i></code>
+ </td>
+ <td>
+ <i>x</i>, zero or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x+?</i></code>
+ </td>
+ <td>
+ <i>x</i>, one or more times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, exactly n times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{,m}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at most <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x{n,m}?</i></code>
+ </td>
+ <td>
+ <i>x</i>, at least <code><i>n</i></code> times and at most
+ <code><i>m</i></code> times
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Operators</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>xy</i></code>
+ </td>
+ <td>
+ <code><i>x</i></code> then <code><i>y</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code><i>x</i></code>|<code><i>y</i></code>
+ </td>
+ <td>
+ <code><i>x</i></code> or <code><i>y</i></code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code> as a capturing group
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Quoting</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\Q</code>
+ </td>
+ <td>
+ Nothing, but treat every character (including \s) literally until a
+ matching <code>\E</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>\E</code>
+ </td>
+ <td>
+ Nothing, but ends its matching <code>\Q</code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Special Constructs</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?:<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, but not as a capturing group
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?=<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via positive lookahead. This means that the
+ expression will match only if it is trailed by <code><i>x</i></code>.
+ It will not "eat" any of the characters matched by
+ <code><i>x</i></code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?!<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via negative lookahead. This means that the
+ expression will match only if it is not trailed by
+ <code><i>x</i></code>. It will not "eat" any of the characters
+ matched by <code><i>x</i></code>.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?<=<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via positive lookbehind. <code><i>x</i></code>
+ cannot contain any quantifiers.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?<!<i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i></code>, via negative lookbehind. <code><i>x</i></code>
+ cannot contain any quantifiers.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>(?><i>x</i>)</code>
+ </td>
+ <td>
+ <code><i>x</i>{1}+</code>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ &nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>Registered Expression Matching</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>{<i>x</i>}</code>
+ </td>
+ <td>
+ The registered pattern <code><i>x</i></code>
+ </td>
+ </tr>
+ </table>
+
+ <hr>
+
+ <i>Begin Text Extracted And Modified From java.util.regex.WCPattern documentation</i>
+
+ <h4> Backslashes, escapes, and quoting </h4>
+
+ <p> The backslash character (<tt>(wchar_t)'\'</tt>) serves to introduce escaped
+ constructs, as defined in the table above, as well as to quote characters
+ that otherwise would be interpreted as unescaped constructs. Thus the
+ expression <tt>\\</tt> matches a single backslash and <tt>\{</tt> matches a
+ left brace.
+
+ <p> It is an error to use a backslash prior to any alphabetic character that
+ does not denote an escaped construct; these are reserved for future
+ extensions to the regular-expression language. A backslash may be used
+ prior to a non-alphabetic character regardless of whether that character is
+ part of an unescaped construct.
+
+ <p>It is necessary to double backslashes in string literals that represent
+ regular expressions to protect them from interpretation by a compiler. The
+ string literal <tt>"&#92;b"</tt>, for example, matches a single backspace
+ character when interpreted as a regular expression, while
+ <tt>"&#92;&#92;b"</tt> matches a word boundary. The string litera
+ <tt>"&#92;(hello&#92;)"</tt> is illegal and leads to a compile-time error;
+ in order to match the string <tt>(hello)</tt> the string literal
+ <tt>"&#92;&#92;(hello&#92;&#92;)"</tt> must be used.
+
+ <h4> Character Classes </h4>
+
+ <p> Character classes may appear within other character classes, and
+ may be composed by the union operator (implicit) and the intersection
+ operator (<tt>&amp;&amp;</tt>).
+ The union operator denotes a class that contains every character that is
+ in at least one of its operand classes. The intersection operator
+ denotes a class that contains every character that is in both of its
+ operand classes.
+
+ <p> The precedence of character-class operators is as follows, from
+ highest to lowest:
+
+ <blockquote><table border="0" cellpadding="1" cellspacing="0"
+ summary="Precedence of character class operators.">
+
+ <tr><th>1&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Literal escape&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td><tt>\x</tt></td></tr>
+ <tr><th>2&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Range</td>
+ <td><tt>a-z</tt></td></tr>
+ <tr><th>3&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Grouping</td>
+ <td><tt>[...]</tt></td></tr>
+ <tr><th>4&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Intersection</td>
+ <td><tt>[a-z&&[aeiou]]</tt></td></tr>
+ <tr><th>5&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td>Union</td>
+ <td><tt>[a-e][i-u]<tt></td></tr>
+ </table></blockquote>
+
+ <p> Note that a different set of metacharacters are in effect inside
+ a character class than outside a character class. For instance, the
+ regular expression <tt>.</tt> loses its special meaning inside a
+ character class, while the expression <tt>-</tt> becomes a range
+ forming metacharacter.
+
+ <a name="lt">
+
+ <a name="cg">
+ <h4> Groups and capturing </h4>
+
+ <p> Capturing groups are numbered by counting their opening parentheses from
+ left to right. In the expression <tt>((A)(B(C)))</tt>, for example, there
+ are four such groups: </p>
+
+ <blockquote><table cellpadding=1 cellspacing=0 summary="Capturing group numberings">
+
+ <tr><th>1&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>((A)(B(C)))</tt></td></tr>
+ <tr><th>2&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(A)</tt></td></tr>
+ <tr><th>3&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(B(C))</tt></td></tr>
+
+ <tr><th>4&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ <td><tt>(C)</tt></td></tr>
+ </table></blockquote>
+
+ <p> Group zero always stands for the entire expression.
+
+ <p> Capturing groups are so named because, during a match, each subsequence
+ of the input sequence that matches such a group is saved. The captured
+ subsequence may be used later in the expression, via a back reference, and
+ may also be retrieved from the matcher once the match operation is complete.
+
+ <p> The captured input associated with a group is always the subsequence
+ that the group most recently matched. If a group is evaluated a second time
+ because of quantification then its previously-captured value, if any, will
+ be retained if the second evaluation fails. Matching the string
+ <tt>L"aba"</tt> against the expression <tt>(a(b)?)+</tt>, for example, leaves
+ group two set to <tt>L"b"</tt>. All captured input is discarded at the
+ beginning of each match.
+
+ <p> Groups beginning with <tt>(?</tt> are pure, <i>non-capturing</i> groups
+ that do not capture text and do not count towards the group total.
+
+
+ <h4> WC support </h4>
+
+ <p> Coming Soon.
+
+ <h4> Comparison to Perl 5 </h4>
+
+ <p>The <code>WCPattern</code> engine performs traditional NFA-based matching
+ with ordered alternation as occurs in Perl 5.
+
+ <p> Perl constructs not supported by this class: </p>
+
+ <ul>
+
+ <li><p> The conditional constructs <tt>(?{</tt><i>X</i><tt>})</tt> and
+ <tt>(?(</tt><i>condition</i><tt>)</tt><i>X</i><tt>|</tt><i>Y</i><tt>)</tt>,
+ </p></li>
+
+ <li><p> The embedded code constructs <tt>(?{</tt><i>code</i><tt>})</tt>
+ and <tt>(??{</tt><i>code</i><tt>})</tt>,</p></li>
+
+ <li><p> The embedded comment syntax <tt>(?#comment)</tt>, and </p></li>
+
+ <li><p> The preprocessing operations <tt>\l</tt> <tt>&#92;u</tt>,
+ <tt>\L</tt>, and <tt>\U</tt>. </p></li>
+
+ <li><p> Embedded flags</p></li>
+
+ </ul>
+
+ <p> Constructs supported by this class but not by Perl: </p>
+
+ <ul>
+
+ <li><p> Possessive quantifiers, which greedily match as much as they can
+ and do not back off, even when doing so would allow the overall match to
+ succeed. </p></li>
+
+ <li><p> Character-class union and intersection as described
+ above.</p></li>
+
+ </ul>
+
+ <p> Notable differences from Perl: </p>
+
+ <ul>
+
+ <li><p> In Perl, <tt>\1</tt> through <tt>\9</tt> are always interpreted
+ as back references; a backslash-escaped number greater than <tt>9</tt> is
+ treated as a back reference if at least that many subexpressions exist,
+ otherwise it is interpreted, if possible, as an octal escape. In this
+ class octal escapes must always begin with a zero. In this class,
+ <tt>\1</tt> through <tt>\9</tt> are always interpreted as back
+ references, and a larger number is accepted as a back reference if at
+ least that many subexpressions exist at that point in the regular
+ expression, otherwise the parser will drop digits until the number is
+ smaller or equal to the existing number of groups or it is one digit.
+ </p></li>
+
+ <li><p> Perl uses the <tt>g</tt> flag to request a match that resumes
+ where the last match left off. This functionality is provided implicitly
+ by the <CODE>WCMatcher</CODE> class: Repeated invocations of the
+ <code>find</code> method will resume where the last match left off,
+ unless the matcher is reset. </p></li>
+
+ <li><p> Perl is forgiving about malformed matching constructs, as in the
+ expression <tt>*a</tt>, as well as dangling brackets, as in the
+ expression <tt>abc]</tt>, and treats them as literals. This
+ class also strict and will not compile a pattern when dangling characters
+ are encountered.</p></li>
+
+ </ul>
+
+
+ <p> For a more precise description of the behavior of regular expression
+ constructs, please see <a href="http://www.oreilly.com/catalog/regex2/">
+ <i>Mastering Regular Expressions, 2nd Edition</i>, Jeffrey E. F. Friedl,
+ O'Reilly and Associates, 2002.</a>
+ </p>
+ <P>
+
+ <i>End Text Extracted And Modified From java.util.regex.WCPattern documentation</i>
+
+ <hr>
+
+ @author Jeffery Stuart
+ @since March 2003, Stable Since November 2004
+ @version 1.07.00
+ @memo A class used to represent "PERL 5"-ish regular expressions
+ */
+class WCPattern
+{
+ friend class WCMatcher;
+ friend class NFAUNode;
+ friend class NFAQuantifierUNode;
+ private:
+ /**
+ This constructor should not be called directly. Those wishing to use the
+ WCPattern class should instead use the {@link compile compile} method.
+
+ @param rhs The pattern to compile
+ @memo Creates a new pattern from the regular expression in <code>rhs</code>.
+ */
+ WCPattern(const bkstring & rhs);
+ protected:
+ /**
+ This currently is not used, so don't try to do anything with it.
+ @memo Holds all the compiled patterns for quick access.
+ */
+ static std::map<bkstring, WCPattern *> compiledWCPatterns;
+ /**
+ Holds all of the registered patterns as strings. Due to certain problems
+ with compilation of patterns, especially with capturing groups, this seemed
+ to be the best way to do it.
+ */
+ static std::map<bkstring, std::pair<bkstring, unsigned long> > registeredWCPatterns;
+ protected:
+ /**
+ Holds all the NFA nodes used. This makes deletion of a pattern, as well as
+ clean-up from an unsuccessful compile much easier and faster.
+ */
+ std::map<NFAUNode*, bool> nodes;
+ /**
+ Used when methods like split are called. The matcher class uses a lot of
+ dynamic memeory, so having an instance increases speedup of certain
+ operations.
+ */
+ WCMatcher * matcher;
+ /**
+ The front node of the NFA.
+ */
+ NFAUNode * head;
+ /**
+ The actual regular expression we rerpesent
+ */
+ bkstring pattern;
+ /**
+ Flag used during compilation. Once the pattern is successfully compiled,
+ <code>error</code> is no longer used.
+ */
+ bool error;
+ /**
+ Used during compilation to keep track of the current index into
+ <code>{@link pattern pattern}<code>. Once the pattern is successfully
+ compiled, <code>error</code> is no longer used.
+ */
+ int curInd;
+ /**
+ The number of capture groups this contains.
+ */
+ int groupCount;
+ /**
+ The number of non-capture groups this contains.
+ */
+ int nonCapGroupCount;
+ /**
+ The flags specified when this was compiled.
+ */
+ unsigned long flags;
+ protected:
+ /**
+ Raises an error during compilation. Compilation will cease at that point
+ and compile will return <code>NULL</code>.
+ */
+ void raiseError();
+ /**
+ Convenience function for registering a node in <code>nodes</code>.
+ @param node The node to register
+ @return The registered node
+ */
+ NFAUNode * registerNode(NFAUNode * node);
+
+ /**
+ Calculates the union of two strings. This function will first sort the
+ strings and then use a simple selection algorithm to find the union.
+ @param s1 The first "class" to union
+ @param s2 The second "class" to union
+ @return A new string containing all unique characters. Each character
+ must have appeared in one or both of <code>s1</code> and
+ <code>s2</code>.
+ */
+ bkstring classUnion (bkstring s1, bkstring s2) const;
+ /**
+ Calculates the intersection of two strings. This function will first sort
+ the strings and then use a simple selection algorithm to find the
+ intersection.
+ @param s1 The first "class" to intersect
+ @param s2 The second "class" to intersect
+ @return A new string containing all unique characters. Each character
+ must have appeared both <code>s1</code> and <code>s2</code>.
+ */
+ bkstring classIntersect (bkstring s1, bkstring s2) const;
+ /**
+ Calculates the negation of a string. The negation is the set of all
+ characters between <code>\x00</code> and <code>\xFF</code> not
+ contained in <code>s1</code>.
+ @param s1 The "class" to be negated.
+ @param s2 The second "class" to intersect
+ @return A new string containing all unique characters. Each character
+ must have appeared both <code>s1</code> and <code>s2</code>.
+ */
+ bkstring classNegate (bkstring s1) const;
+ /**
+ Creates a new "class" representing the range from <code>low</code> thru
+ <code>hi</code>. This function will wrap if <code>low</code> &gt;
+ <code>hi</code>. This is a feature, not a buf. Sometimes it is useful
+ to be able to say [\x70-\x10] instead of [\x70-\x7F\x00-\x10].
+ @param low The beginning character
+ @param hi The ending character
+ @return A new string containing all the characters from low thru hi.
+ */
+ bkstring classCreateRange(wchar_t low, wchar_t hi) const;
+
+ /**
+ Extracts a decimal number from the substring of member-variable
+ <code>{@link pattern pattern}<code> starting at <code>start</code> and
+ ending at <code>end</code>.
+ @param start The starting index in <code>{@link pattern pattern}<code>
+ @param end The last index in <code>{@link pattern pattern}<code>
+ @return The decimal number in <code>{@link pattern pattern}<code>
+ */
+ int getInt(int start, int end);
+ /**
+ Parses a <code>{n,m}</code> string out of the member-variable
+ <code>{@link pattern pattern}<code> stores the result in <code>sNum</code>
+ and <code>eNum</code>.
+ @param sNum Output parameter. The minimum number of matches required
+ by the curly quantifier are stored here.
+ @param eNum Output parameter. The maximum number of matches allowed
+ by the curly quantifier are stored here.
+ @return Success/Failure. Fails when the curly does not have the proper
+ syntax
+ */
+ bool quantifyCurly(int & sNum, int & eNum);
+ /**
+ Tries to quantify the currently parsed group. If the group being parsed
+ is indeed quantified in the member-variable
+ <code>{@link pattern pattern}<code>, then the NFA is modified accordingly.
+ @param start The starting node of the current group being parsed
+ @param stop The ending node of the current group being parsed
+ @param gn The group number of the current group being parsed
+ @return The node representing the starting node of the group. If the
+ group becomes quantified, then this node is not necessarily
+ a GroupHead node.
+ */
+ NFAUNode * quantifyGroup(NFAUNode * start, NFAUNode * stop, const int gn);
+
+ /**
+ Tries to quantify the last parsed expression. If the character was indeed
+ quantified, then the NFA is modified accordingly.
+ @param newNode The recently created expression node
+ @return The node representing the last parsed expression. If the
+ expression was quantified, <code>return value != newNode</code>
+ */
+ NFAUNode * quantify(NFAUNode * newNode);
+ /**
+ Parses the current class being examined in
+ <code>{@link pattern pattern}</code>.
+ @return A string of unique characters contained in the current class being
+ parsed
+ */
+ bkstring parseClass();
+ /**
+ Parses the current POSIX class being examined in
+ <code>{@link pattern pattern}</code>.
+ @return A string of unique characters representing the POSIX class being
+ parsed
+ */
+ bkstring parsePosix();
+ /**
+ Returns a string containing the octal character being parsed
+ @return The string contained the octal value being parsed
+ */
+ bkstring parseOctal();
+ /**
+ Returns a string containing the hex character being parsed
+ @return The string contained the hex value being parsed
+ */
+ bkstring parseHex();
+ /**
+ Returns a new node representing the back reference being parsed
+ @return The new node representing the back reference being parsed
+ */
+ NFAUNode * parseBackref();
+ /**
+ Parses the escape sequence currently being examined. Determines if the
+ escape sequence is a class, a single character, or the beginning of a
+ quotation sequence.
+ @param inv Output parameter. Whether or not to invert the returned class
+ @param quo Output parameter. Whether or not this sequence starts a
+ quotation.
+ @return The characters represented by the class
+ */
+ bkstring parseEscape(bool & inv, bool & quo);
+ /**
+ Parses a supposed registered pattern currently under compilation. If the
+ sequence of characters does point to a registered pattern, then the
+ registered pattern is appended to <code>*end<code>. The registered pattern
+ is parsed with the current compilation flags.
+ @param end The ending node of the thus-far compiled pattern
+ @return The new end node of the current pattern
+ */
+ NFAUNode * parseRegisteredWCPattern(NFAUNode ** end);
+ /**
+ Parses a lookbehind expression. Appends the necessary nodes
+ <code>*end</code>.
+ @param pos Positive or negative look behind
+ @param end The ending node of the current pattern
+ @return The new end node of the current pattern
+ */
+ NFAUNode * parseBehind(const bool pos, NFAUNode ** end);
+ /**
+ Parses the current expression and tacks on nodes until a \E is found.
+ @return The end of the current pattern
+ */
+ NFAUNode * parseQuote();
+ /**
+ Parses <code>{@link pattern pattern}</code>. This function is called
+ recursively when an or (<code>|</code>) or a group is encountered.
+ @param inParen Are we currently parsing inside a group
+ @param inOr Are we currently parsing one side of an or (<code>|</code>)
+ @param end The end of the current expression
+ @return The starting node of the NFA constructed from this parse
+ */
+ NFAUNode * parse(const bool inParen = 0, const bool inOr = 0, NFAUNode ** end = NULL);
+ public:
+ /// We should match regardless of case
+ const static unsigned long CASE_INSENSITIVE;
+ /// We are implicitly quoted
+ const static unsigned long LITERAL;
+ /// @memo We should treat a <code><b>.</b></code> as [\x00-\x7F]
+ const static unsigned long DOT_MATCHES_ALL;
+ /** <code>^</code> and <code>$</code> should anchor to the beginning and
+ ending of lines, not all input
+ */
+ const static unsigned long MULTILINE_MATCHING;
+ /** When enabled, only instances of <code>\n</codes> are recognized as
+ line terminators
+ */
+ const static unsigned long UNIX_LINE_MODE;
+ /// The absolute minimum number of matches a quantifier can match (0)
+ const static int MIN_QMATCH;
+ /// The absolute maximum number of matches a quantifier can match (0x7FFFFFFF)
+ const static int MAX_QMATCH;
+ public:
+ /**
+ Call this function to compile a regular expression into a
+ <code>WCPattern</code> object. Special values can be assigned to
+ <code>mode</code> when certain non-standard behaviors are expected from
+ the <code>WCPattern</code> object.
+ @param pattern The regular expression to compile
+ @param mode A bitwise or of flags signalling what special behaviors are
+ wanted from this <code>WCPattern</code> object
+ @return If successful, <code>compile</code> returns a <code>WCPattern</code>
+ pointer. Upon failure, <code>compile</code> returns
+ <code>NULL</code>
+ */
+ static WCPattern * compile (const bkstring & pattern,
+ const unsigned long mode = 0);
+ /**
+ Dont use this function. This function will compile a pattern, and cache
+ the result. This will eventually be used as an optimization when people
+ just want to call static methods using the same pattern over and over
+ instead of first compiling the pattern and then using the compiled
+ instance for matching.
+ @param pattern The regular expression to compile
+ @param mode A bitwise or of flags signalling what special behaviors are
+ wanted from this <code>WCPattern</code> object
+ @return If successful, <code>compileAndKeep</code> returns a
+ <code>WCPattern</code> pointer. Upon failure, <code>compile</code>
+ returns <code>NULL</code>.
+ */
+ static WCPattern * compileAndKeep (const bkstring & pattern,
+ const unsigned long mode = 0);
+
+ /**
+ Searches through <code>replace</code> and replaces all substrings matched
+ by <code>pattern</code> with <code>str</code>. <code>str</code> may
+ contain backreferences (e.g. <code>\1</code>) to capture groups. A typical
+ invocation looks like:
+ <p>
+ <code>
+ WCPattern::replace(L"(a+)b(c+)", L"abcccbbabcbabc", L"\\2b\\1");
+ </code>
+ <p>
+ which would replace <code>abcccbbabcbabc</code> with
+ <code>cccbabbcbabcba</code>.
+ @param pattern The regular expression
+ @param str The replacement text
+ @param replacementText The string in which to perform replacements
+ @param mode The special mode requested of the <code>WCPattern</code>
+ during the replacement process
+ @return The text with the replacement string substituted where necessary
+ */
+ static bkstring replace (const bkstring & pattern,
+ const bkstring & str,
+ const bkstring & replacementText,
+ const unsigned long mode = 0);
+
+ /**
+ Splits the specified string over occurrences of the specified pattern.
+ Empty strings can be optionally ignored. The number of strings returned is
+ configurable. A typical invocation looks like:
+ <p>
+ <code>
+ bkstring str(strSize, 0);<br>
+ FILE * fp = fopen(fileName, "r");<br>
+ fread((char*)str.data(), strSize * 2, 1, fp);<br>
+ fclose(fp);<br>
+ <br>
+ std::vector&lt;bkstring&gt; lines = WCPattern::split(L"[\r\n]+", str, true);<br>
+ <br>
+ </code>
+
+ @param pattern The regular expression
+ @param replace The string to split
+ @param keepEmptys Whether or not to keep empty strings
+ @param limit The maximum number of splits to make
+ @param mode The special mode requested of the <code>WCPattern</code>
+ during the split process
+ @return All substrings of <code>str</code> split across <code>pattern</code>.
+ */
+ static std::vector<bkstring> split (const bkstring & pattern,
+ const bkstring & str,
+ const bool keepEmptys = 0,
+ const unsigned long limit = 0,
+ const unsigned long mode = 0);
+
+ /**
+ Finds all the instances of the specified pattern within the string. You
+ should be careful to only pass patterns with a minimum length of one. For
+ example, the pattern <code>a*</code> can be matched by an empty string, so
+ instead you should pass <code>a+</code> since at least one character must
+ be matched. A typical invocation of <code>findAll</code> looks like:
+ <p>
+ <code>
+ std::vector&lt;td::string&gt; numbers = WCPattern::findAll(L"\\d+", string);
+ </code>
+ <p>
+
+ @param pattern The pattern for which to search
+ @param str The string to search
+ @param mode The special mode requested of the <code>WCPattern</code>
+ during the find process
+ @return All instances of <code>pattern</code> in <code>str</code>
+ */
+ static std::vector<bkstring> findAll (const bkstring & pattern,
+ const bkstring & str,
+ const unsigned long mode = 0);
+
+ /**
+ Determines if an entire string matches the specified pattern
+
+ @param pattern The pattern for to match
+ @param str The string to match
+ @param mode The special mode requested of the <code>WCPattern</code>
+ during the replacement process
+ @return True if <code>str</code> is recognized by <code>pattern</code>
+ */
+ static bool matches (const bkstring & pattern,
+ const bkstring & str,
+ const unsigned long mode = 0);
+
+ /**
+ Registers a pattern under a specific name for use in later compilations.
+ A typical invocation and later use looks like:
+ <p>
+ <code>
+ WCPattern::registerWCPattern(L"ip", L"(?:\\d{1,3}\\.){3}\\d{1,3}");<br>
+ WCPattern * p1 = WCPattern::compile(L"{ip}:\\d+");<br>
+ WCPattern * p2 = WCPattern::compile(L"Connection from ({ip}) on port \\d+");<br>
+ </code>
+ <p>
+ Multiple calls to <code>registerWCPattern</code> with the same
+ <code>name</code> will result in the pattern getting overwritten.
+
+ @param name The name to give to the pattern
+ @param pattern The pattern to register
+ @param mode Any special flags to use when compiling pattern
+ @return Success/Failure. Fails only if <code>pattern</code> has invalid
+ syntax
+ */
+ static bool registerWCPattern(const bkstring & name,
+ const bkstring & pattern,
+ const unsigned long mode = 0);
+
+ /**
+ Clears the pattern registry
+ */
+ static void unregisterWCPatterns();
+ /**
+ Don't use
+ */
+ static void clearWCPatternCache();
+
+ /**
+ Searches through a string for the <code>n<sup>th</sup></code> match of the
+ given pattern in the string. Match indeces start at zero, not one.
+ A typical invocation looks like this:
+ <p>
+ <code>
+ std::pair&lt;bkstring, int&gt; match = WCPattern::findNthMatch(L"\\d{1,3}", L"192.168.1.101:22", 1);<br>
+ wprintf(L"%s %i\n", match.first.c_str(), match.second);<br>
+ <br>
+ Output: 168 4<br>
+ <br>
+
+ @param pattern The pattern for which to search
+ @param str The string to search
+ @param matchNum Which match to find
+ @param mode Any special flags to use during the matching process
+ @return A string and an integer. The string is the string matched. The
+ integer is the starting location of the matched string in
+ <code>str</code>. You can check for success/failure by making sure
+ that the integer returned is greater than or equal to zero.
+ */
+ static std::pair<bkstring, int> findNthMatch (const bkstring & pattern,
+ const bkstring & str,
+ const int matchNum,
+ const unsigned long mode = 0);
+ public:
+ /**
+ Deletes all NFA nodes allocated during compilation
+ */
+ ~WCPattern();
+
+ bkstring replace (const bkstring & str,
+ const bkstring & replacementText);
+ std::vector<bkstring> split (const bkstring & str, const bool keepEmptys = 0,
+ const unsigned long limit = 0);
+ std::vector<bkstring> findAll (const bkstring & str);
+ bool matches (const bkstring & str);
+ /**
+ Returns the flags used during compilation of this pattern
+ @return The flags used during compilation of this pattern
+ */
+ unsigned long getFlags () const;
+ /**
+ Returns the regular expression this pattern represents
+ @return The regular expression this pattern represents
+ */
+ bkstring getWCPattern () const;
+ /**
+ Creates a matcher object using the specified string and this pattern.
+ @param str The string to match against
+ @return A new matcher using object using this pattern and the specified
+ string
+ */
+ WCMatcher * createWCMatcher (const bkstring & str);
+};
+
+class NFAUNode
+{
+ friend class WCMatcher;
+ public:
+ NFAUNode * next;
+ NFAUNode();
+ virtual ~NFAUNode();
+ virtual void findAllNodes(std::map<NFAUNode*, bool> & soFar);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const = 0;
+ inline virtual bool isGroupHeadNode() const { return false; }
+ inline virtual bool isStartOfInputNode() const { return false; }
+};
+class NFACharUNode : public NFAUNode
+{
+ protected:
+ wchar_t ch;
+ public:
+ NFACharUNode(const wchar_t c);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFACICharUNode : public NFAUNode
+{
+ protected:
+ wchar_t ch;
+ public:
+ NFACICharUNode(const wchar_t c);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAStartUNode : public NFAUNode
+{
+ public:
+ NFAStartUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAEndUNode : public NFAUNode
+{
+ public:
+ NFAEndUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAQuantifierUNode : public NFAUNode
+{
+ public:
+ int min, max;
+ NFAUNode * inner;
+ virtual void findAllNodes(std::map<NFAUNode*, bool> & soFar);
+ NFAQuantifierUNode(WCPattern * pat, NFAUNode * internal,
+ const int minMatch = WCPattern::MIN_QMATCH,
+ const int maxMatch = WCPattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAGreedyQuantifierUNode : public NFAQuantifierUNode
+{
+ public:
+ NFAGreedyQuantifierUNode(WCPattern * pat, NFAUNode * internal,
+ const int minMatch = WCPattern::MIN_QMATCH,
+ const int maxMatch = WCPattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ virtual int matchInternal(const bkstring & str, WCMatcher * matcher, const int curInd, const int soFar) const;
+};
+class NFALazyQuantifierUNode : public NFAQuantifierUNode
+{
+ public:
+ NFALazyQuantifierUNode(WCPattern * pat, NFAUNode * internal,
+ const int minMatch = WCPattern::MIN_QMATCH,
+ const int maxMatch = WCPattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAPossessiveQuantifierUNode : public NFAQuantifierUNode
+{
+ public:
+ NFAPossessiveQuantifierUNode(WCPattern * pat, NFAUNode * internal,
+ const int minMatch = WCPattern::MIN_QMATCH,
+ const int maxMatch = WCPattern::MAX_QMATCH);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAAcceptUNode : public NFAUNode
+{
+ public:
+ NFAAcceptUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAClassUNode : public NFAUNode
+{
+ public:
+ bool inv;
+ std::map<wchar_t, bool> vals;
+ NFAClassUNode(const bool invert = 0);
+ NFAClassUNode(const bkstring & clazz, const bool invert);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFACIClassUNode : public NFAUNode
+{
+ public:
+ bool inv;
+ std::map<wchar_t, bool> vals;
+ NFACIClassUNode(const bool invert = 0);
+ NFACIClassUNode(const bkstring & clazz, const bool invert);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFASubStartUNode : public NFAUNode
+{
+ public:
+ NFASubStartUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAOrUNode : public NFAUNode
+{
+ public:
+ NFAUNode * one;
+ NFAUNode * two;
+ NFAOrUNode(NFAUNode * first, NFAUNode * second);
+ virtual void findAllNodes(std::map<NFAUNode*, bool> & soFar);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAQuoteUNode : public NFAUNode
+{
+ public:
+ bkstring qStr;
+ NFAQuoteUNode(const bkstring & quoted);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFACIQuoteUNode : public NFAUNode
+{
+ public:
+ bkstring qStr;
+ NFACIQuoteUNode(const bkstring & quoted);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFALookAheadUNode : public NFAUNode
+{
+ public:
+ bool pos;
+ NFAUNode * inner;
+ NFALookAheadUNode(NFAUNode * internal, const bool positive);
+ virtual void findAllNodes(std::map<NFAUNode*, bool> & soFar);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFALookBehindUNode : public NFAUNode
+{
+ public:
+ bool pos;
+ bkstring mStr;
+ NFALookBehindUNode(const bkstring & str, const bool positive);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAStartOfLineUNode : public NFAUNode
+{
+ public:
+ NFAStartOfLineUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAEndOfLineUNode : public NFAUNode
+{
+ public:
+ NFAEndOfLineUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAReferenceUNode : public NFAUNode
+{
+ public:
+ int gi;
+ NFAReferenceUNode(const int groupIndex);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAStartOfInputUNode : public NFAUNode
+{
+ public:
+ NFAStartOfInputUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ inline virtual bool isStartOfInputNode() const { return false; }
+};
+class NFAEndOfInputUNode : public NFAUNode
+{
+ public:
+ bool term;
+ NFAEndOfInputUNode(const bool lookForTerm);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAWordBoundaryUNode : public NFAUNode
+{
+ public:
+ bool pos;
+ NFAWordBoundaryUNode(const bool positive);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAEndOfMatchUNode : public NFAUNode
+{
+ public:
+ NFAEndOfMatchUNode();
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupHeadUNode : public NFAUNode
+{
+ public:
+ int gi;
+ NFAGroupHeadUNode(const int groupIndex);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ inline virtual bool isGroupHeadNode() const { return false; }
+};
+class NFAGroupTailUNode : public NFAUNode
+{
+ public:
+ int gi;
+ NFAGroupTailUNode(const int groupIndex);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupLoopPrologueUNode : public NFAUNode
+{
+ public:
+ int gi;
+ NFAGroupLoopPrologueUNode(const int groupIndex);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+class NFAGroupLoopUNode : public NFAUNode
+{
+ public:
+ int gi, min, max, type;
+ NFAUNode * inner;
+ NFAGroupLoopUNode(NFAUNode * internal, const int minMatch,
+ const int maxMatch, const int groupIndex, const int matchType);
+ virtual void findAllNodes(std::map<NFAUNode*, bool> & soFar);
+ virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ int matchGreedy(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ int matchLazy(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+ int matchPossessive(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const;
+};
+
+#endif
+
diff --git a/plugins/SmileyAdd/src/regexp/test.cpp b/plugins/SmileyAdd/src/regexp/test.cpp
new file mode 100644
index 0000000000..bb41566f96
--- /dev/null
+++ b/plugins/SmileyAdd/src/regexp/test.cpp
@@ -0,0 +1,38 @@
+#include <regexp/Matcher.h>
+#include <regexp/Pattern.h>
+#include <regexp/UnicodeMatcher.h>
+#include <regexp/UnicodePattern.h>
+
+int main()
+{
+ {
+ Pattern * p = Pattern::compile("^([^:]*)://([^/:]*)((?::[0-9]+)?)/?(([^?]*)((?:\\?.*)?))$");
+ Matcher * m0 = p->createMatcher("http://www.example.com:80/test.php?a=1&a=1&a=1");
+
+ if (m0->matches())
+ {
+ std::vector<std::string> groups = m0->getGroups(true);
+ for (int i = 0; i < (int)groups.size(); ++i)
+ {
+ printf("m->group(%d): %s\n", i, groups[i].c_str());
+ }
+ }
+ }
+ {
+ std::wstring pat = L"^([^:]*)://([^/:]*)((?::[0-9]+)?)/?(([^?]*)((?:\\?.*)?))$";
+ std::wstring mat = L"http://www.example.com:80/test.php?a=1&a=1&a=1";
+ UnicodePattern * p = UnicodePattern::compile(pat);
+ UnicodeMatcher * m0 = p->createUnicodeMatcher(mat);
+
+ if (m0->matches())
+ {
+ std::vector<std::wstring> groups = m0->getGroups(true);
+ for (int i = 0; i < (int)groups.size(); ++i)
+ {
+ wprintf(L"m->group(%d): %s\n", i, groups[i].c_str());
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/SmileyAdd/src/resource.h b/plugins/SmileyAdd/src/resource.h
new file mode 100644
index 0000000000..bdf799fe2c
--- /dev/null
+++ b/plugins/SmileyAdd/src/resource.h
@@ -0,0 +1,49 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDI_SMILINGICON 124
+#define IDD_OPT_SMILEYS 245
+#define IDC_CATEGORYLIST 1017
+#define IDC_PLUGENABLED 1019
+#define IDC_INPUTSMILEYS 1020
+#define IDC_DCURSORSMILEY 1021
+#define IDC_LIBNAME 1023
+#define IDC_SPACES 1024
+#define IDC_LIBAUTHOR 1026
+#define IDC_LIBVERSION 1027
+#define IDC_SCALETOTEXTHEIGHT 1037
+#define IDC_APPENDSPACES 1040
+#define IDC_SMLOPTBUTTON 1045
+#define IDC_SCALEALLSMILEYS 1046
+#define IDC_IEVIEWSTYLE 1047
+#define IDC_ANIMATESEL 1048
+#define IDC_USESTDPACK 1049
+#define IDC_ANIMATEDLG 1051
+#define IDC_DISABLECUSTOM 1052
+#define IDC_DISABLECUSTOM2 1053
+#define IDC_HQSCALING 1053
+#define IDC_SELCLR 1055
+#define IDC_NEWCATEGORY 1058
+#define IDC_ADDCATEGORY 1059
+#define IDC_DELETECATEGORY 1060
+#define IDC_SMLBUT 1061
+#define IDC_MAXCUSTSMSZ 1062
+#define IDC_MAXCUSTSPIN 1063
+#define IDC_MINSMSZ 1064
+#define IDC_MINSPIN 1065
+#define IDC_BROWSE 1184
+#define IDC_FILENAME 1271
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 127
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1064
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/SmileyAdd/src/richcall.cpp b/plugins/SmileyAdd/src/richcall.cpp
new file mode 100644
index 0000000000..d9b9123c48
--- /dev/null
+++ b/plugins/SmileyAdd/src/richcall.cpp
@@ -0,0 +1,554 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved
+
+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 "smileys.h"
+#include "smileyroutines.h"
+#include "services.h"
+#include "options.h"
+#include "SmileyBase.h"
+
+#include <richole.h>
+#include <tom.h>
+
+typedef struct
+{
+ HWND hwnd;
+ HANDLE hContact;
+ WNDPROC wpOrigWndProc;
+ HWND hToolTip;
+ int tipActive;
+ bool inputarea;
+ bool dontReplace;
+} RichEditData;
+
+typedef struct
+{
+ HWND hwnd;
+ WNDPROC wpOrigWndProc;
+ HWND hwndInput;
+ HWND hwndLog;
+} RichEditOwnerData;
+
+static int CompareRichEditData(const RichEditData* p1, const RichEditData* p2)
+{
+ return (int)((INT_PTR)p1->hwnd - (INT_PTR)p2->hwnd);
+}
+static LIST<RichEditData> g_RichEditList(10, CompareRichEditData);
+
+
+static int CompareRichEditData(const RichEditOwnerData* p1, const RichEditOwnerData* p2)
+{
+ return (int)((INT_PTR)p1->hwnd - (INT_PTR)p2->hwnd);
+}
+static LIST<RichEditOwnerData> g_RichEditOwnerList(5, CompareRichEditData);
+
+
+static void SetPosition(HWND hwnd)
+{
+ 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;
+ }
+ TextDocument->Release();
+
+ 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;
+
+ ISmileyBase *igsc = NULL;
+ if (reObj.clsid == CLSID_NULL)
+ reObj.poleobj->QueryInterface(IID_ISmileyAddSmiley, (void**) &igsc);
+
+ reObj.poleobj->Release();
+ if (igsc == NULL) continue;
+
+ TextRange->SetRange(reObj.cp, reObj.cp);
+
+ BOOL res;
+ POINT pt;
+ RECT rect;
+ hr = TextRange->GetPoint(tomStart | TA_BOTTOM | TA_LEFT, &pt.x, &pt.y);
+ if (hr == S_OK)
+ {
+ res = ScreenToClient(hwnd, &pt);
+ rect.bottom = pt.y;
+ rect.left = pt.x;
+ }
+ else
+ rect.bottom = -1;
+
+ hr = TextRange->GetPoint(tomStart | TA_TOP | TA_LEFT, &pt.x, &pt.y);
+ if (hr == S_OK)
+ {
+ res = ScreenToClient(hwnd, &pt);
+ rect.top = pt.y;
+ rect.left = pt.x;
+ }
+ else
+ rect.top = -1;
+
+ igsc->SetPosition(hwnd, &rect);
+ igsc->Release();
+ }
+ TextRange->Release();
+ RichEditOle->Release();
+}
+
+static void SetTooltip(long x, long y, HWND hwnd, RichEditData* rdt)
+{
+ TCHAR* smltxt;
+ int needtip = CheckForTip(x, y, hwnd, &smltxt);
+ if (needtip != rdt->tipActive)
+ {
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.hwnd = hwnd;
+ ti.uId = (UINT_PTR)ti.hwnd;
+
+ if (needtip != -1)
+ {
+ if (rdt->tipActive == -1)
+ {
+ rdt->hToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, _T(""),
+ TTS_NOPREFIX | WS_POPUP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, NULL, g_hInst, NULL);
+
+ SendMessage(rdt->hToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ }
+
+ ti.lpszText = smltxt;
+ SendMessage(rdt->hToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+ SendMessage(rdt->hToolTip, TTM_ACTIVATE, TRUE, 0);
+ }
+ else
+ {
+ if (rdt->tipActive != -1)
+ {
+ SendMessage(rdt->hToolTip, TTM_ACTIVATE, FALSE, 0);
+ DestroyWindow(rdt->hToolTip);
+ rdt->hToolTip = NULL;
+ }
+ }
+ rdt->tipActive = needtip;
+ }
+}
+
+static const CHARRANGE allsel = { 0, LONG_MAX };
+
+
+static void ReplaceContactSmileys(RichEditData *rdt, const CHARRANGE &sel, bool ignoreLast, bool unFreeze)
+{
+ if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return;
+ SmileyPackCType *smcp = NULL;
+ SmileyPackType* SmileyPack = GetSmileyPack(NULL, rdt->hContact, rdt->inputarea ? NULL : &smcp);
+ ReplaceSmileys(rdt->hwnd, SmileyPack, smcp, sel, false, ignoreLast, unFreeze);
+}
+
+static void ReplaceContactSmileysWithText(RichEditData *rdt, CHARRANGE &sel, bool freeze)
+{
+ if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return;
+ ReplaceSmileysWithText(rdt->hwnd, sel, freeze);
+}
+
+static void SmileyToTextCutPrep(RichEditData* rdt)
+{
+ if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return;
+
+ SendMessage(rdt->hwnd, WM_SETREDRAW, FALSE, 0);
+ CHARRANGE sel;
+ SendMessage(rdt->hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+ ReplaceContactSmileysWithText(rdt, sel, true);
+}
+
+static void SmileyToTextCutRest(RichEditData* rdt)
+{
+ if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return;
+
+ CHARRANGE sel;
+ SendMessage(rdt->hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+ ReplaceContactSmileys(rdt, sel, false, true);
+ SendMessage(rdt->hwnd, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(rdt->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
+}
+
+
+static LRESULT CALLBACK RichEditSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ RichEditData* rdt = g_RichEditList.find((RichEditData*)&hwnd);
+ if (rdt == NULL) return 0;
+
+ CHARRANGE sel;
+
+ WNDPROC wpOrigWndProc = rdt->wpOrigWndProc;
+
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ CloseRichCallback(hwnd, false);
+ break;
+
+ case WM_COPY:
+ case WM_CUT:
+ SmileyToTextCutPrep(rdt);
+ break;
+
+ case WM_PAINT:
+ SetPosition(hwnd);
+ break;
+
+ case EM_STREAMOUT:
+ if (wParam & SFF_SELECTION)
+ SmileyToTextCutPrep(rdt);
+ else
+ {
+ sel = allsel;
+ ReplaceContactSmileysWithText(rdt, sel, true);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ if ((wParam == 'C' || wParam == VK_INSERT) && (GetKeyState(VK_CONTROL) & 0x8000))
+ {
+ SmileyToTextCutPrep(rdt);
+ }
+ else if ((wParam == 'X' && (GetKeyState(VK_CONTROL) & 0x8000)) ||
+ (wParam == VK_DELETE && (GetKeyState(VK_SHIFT) & 0x8000)))
+ {
+ SmileyToTextCutPrep(rdt);
+ }
+ else if (wParam == VK_TAB && ((GetKeyState(VK_CONTROL) | GetKeyState(VK_SHIFT)) & 0x8000) == 0)
+ {
+ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+ sel.cpMin = max(sel.cpMin - 20, 0);
+
+ ReplaceContactSmileysWithText(rdt, sel, true);
+ }
+ break;
+ }
+
+ LRESULT result = CallWindowProc(wpOrigWndProc, hwnd, uMsg, wParam, lParam);
+
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ CloseRichCallback(hwnd, true);
+ break;
+
+ case WM_MOUSEMOVE:
+ SetTooltip(LOWORD(lParam), HIWORD(lParam), hwnd, rdt);
+ break;
+
+ case WM_PAINT:
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ SetPosition(hwnd);
+ break;
+
+ case WM_COPY:
+ case WM_CUT:
+ SmileyToTextCutRest(rdt);
+ break;
+
+ case EM_STREAMOUT:
+ if (wParam & SFF_SELECTION)
+ SmileyToTextCutRest(rdt);
+ else
+ ReplaceContactSmileys(rdt, allsel, false, true);
+ break;
+
+ case WM_KEYDOWN:
+ if ((wParam == 'C' || wParam == VK_INSERT) && (GetKeyState(VK_CONTROL) & 0x8000))
+ {
+ SmileyToTextCutRest(rdt);
+ }
+ else if ((wParam == 'X' && (GetKeyState(VK_CONTROL) & 0x8000)) ||
+ (wParam == VK_DELETE && (GetKeyState(VK_SHIFT) & 0x8000)))
+ {
+ SmileyToTextCutRest(rdt);
+ }
+ else if (wParam == VK_TAB && ((GetKeyState(VK_CONTROL) | GetKeyState(VK_SHIFT)) & 0x8000) == 0)
+ {
+ sel.cpMax = LONG_MAX;
+ bool hascont = rdt->hContact != NULL;
+ ReplaceContactSmileys(rdt, sel, false, hascont);
+ }
+ break;
+
+ case WM_CHAR:
+ if (!rdt->inputarea || (rdt->inputarea && !opt.InputSmileys))
+ break;
+
+ if (lParam & (1 << 28)) // ALT key
+ break;
+
+ if ((lParam & 0xFF) > 2) // Repeat rate
+ break;
+
+ if (wParam > ' ' && opt.EnforceSpaces)
+ break;
+
+ if (wParam == 0x16)
+ {
+ ReplaceContactSmileys(rdt, allsel, false, false);
+ break;
+ }
+
+ if (opt.DCursorSmiley)
+ {
+ ReplaceContactSmileys(rdt, allsel, true, true);
+ }
+ else
+ {
+ if (wParam >= ' ' || wParam == '\n' || wParam == '\r')
+ {
+ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+ sel.cpMin = max(sel.cpMin - 20, 0);
+ sel.cpMax += 20;
+
+ ReplaceContactSmileysWithText(rdt, sel, true);
+ ReplaceContactSmileys(rdt, sel, false, true);
+ }
+ }
+ break;
+
+ case EM_PASTESPECIAL:
+ case WM_PASTE:
+ case EM_REPLACESEL:
+ case WM_SETTEXT:
+ case EM_SETTEXTEX:
+ if (rdt->inputarea)
+ ReplaceContactSmileys(rdt, allsel, false, false);
+ break;
+
+ case WM_REMAKERICH:
+ ReplaceContactSmileys(rdt, allsel, false, false);
+ break;
+ }
+
+ return result;
+}
+
+void CloseRichCallback(HWND hwnd, bool force)
+{
+ int ind = g_RichEditList.getIndex((RichEditData*)&hwnd);
+ if ( ind != -1 )
+ {
+ RichEditData* rdt = g_RichEditList[ind];
+ bool richsub = GetWindowLongPtr(hwnd, GWLP_WNDPROC) == (LONG_PTR)RichEditSubclass;
+ if (richsub) SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)rdt->wpOrigWndProc);
+ if (richsub || force)
+ {
+ if (rdt->hToolTip) DestroyWindow(rdt->hToolTip);
+ delete rdt;
+ g_RichEditList.remove(ind);
+ }
+ }
+}
+
+bool SetRichCallback(HWND hwnd, HANDLE hContact, bool subany, bool subnew)
+{
+ RichEditData* rdt = g_RichEditList.find((RichEditData*)&hwnd);
+ if (rdt == NULL)
+ {
+ IRichEditOle* RichEditOle;
+ if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0)
+ return false;
+ RichEditOle->Release();
+
+ rdt = new RichEditData;
+
+ rdt->hwnd = hwnd;
+ rdt->hContact = hContact;
+ rdt->inputarea = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) == 0;
+ rdt->dontReplace = false;
+ rdt->tipActive = -1;
+ rdt->wpOrigWndProc = NULL;
+ rdt->hToolTip = NULL;
+ g_RichEditList.insert(rdt);
+
+ if (subnew)
+ rdt->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditSubclass);
+ }
+ else
+ {
+ if (hContact && !rdt->hContact) rdt->hContact = hContact;
+ if (subany && rdt->wpOrigWndProc == NULL)
+ rdt->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditSubclass);
+ }
+ return true;
+}
+
+static LRESULT CALLBACK RichEditOwnerSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ RichEditOwnerData* rdto = g_RichEditOwnerList.find((RichEditOwnerData*)&hwnd);
+ if (rdto == NULL) return 0;
+
+ WNDPROC wpOrigWndProc = rdto->wpOrigWndProc;
+
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ {
+ RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput);
+ if (rdt && (!rdt->inputarea || opt.InputSmileys))
+ {
+ CHARRANGE sel = allsel;
+ rdt->dontReplace = true;
+ ReplaceSmileysWithText(rdt->hwnd, sel, false);
+ }
+ }
+ CloseRichOwnerCallback(hwnd, false);
+ break;
+
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 1624)
+ {
+ RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput);
+ if (rdt && (!rdt->inputarea || opt.InputSmileys))
+ {
+ rdt->dontReplace = true;
+ CHARRANGE sel = allsel;
+ ReplaceSmileysWithText(rdt->hwnd, sel, false);
+ }
+ }
+ break;
+ }
+
+ LRESULT result = CallWindowProc(wpOrigWndProc, hwnd, uMsg, wParam, lParam);
+
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ CloseRichOwnerCallback(hwnd, true);
+ break;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 1624) // && lParam == 0)
+ {
+ RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput);
+ if (rdt)
+ {
+ CHARRANGE sel = allsel;
+ if (!result) ReplaceContactSmileys(rdt, sel, false, false);
+ rdt->dontReplace = false;
+ }
+ }
+ break;
+ }
+ return result;
+}
+
+void CloseRichOwnerCallback(HWND hwnd, bool force)
+{
+ int ind = g_RichEditOwnerList.getIndex((RichEditOwnerData*)&hwnd);
+ if ( ind != -1 )
+ {
+ RichEditOwnerData* rdto = g_RichEditOwnerList[ind];
+ bool richsub = GetWindowLongPtr(hwnd, GWLP_WNDPROC) == (LONG_PTR)RichEditOwnerSubclass;
+ if (richsub)
+ {
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)rdto->wpOrigWndProc);
+ rdto->wpOrigWndProc = NULL;
+ }
+ if (force)
+ {
+ CloseRichCallback(rdto->hwndInput, true);
+ CloseRichCallback(rdto->hwndLog, true);
+
+ delete rdto;
+ g_RichEditOwnerList.remove(ind);
+ }
+ }
+}
+
+void SetRichOwnerCallback(HWND hwnd, HWND hwndInput, HWND hwndLog)
+{
+ RichEditOwnerData* rdto = g_RichEditOwnerList.find((RichEditOwnerData*)&hwnd);
+ if (rdto == NULL)
+ {
+ rdto = new RichEditOwnerData;
+
+ rdto->hwnd = hwnd;
+ rdto->hwndInput = hwndInput;
+ rdto->hwndLog = hwndLog;
+ rdto->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditOwnerSubclass);
+ g_RichEditOwnerList.insert(rdto);
+ }
+ else
+ {
+ if (rdto->hwndInput == NULL) rdto->hwndInput = hwndInput;
+ if (rdto->hwndLog == NULL) rdto->hwndLog = hwndLog;
+ }
+}
+
+void ProcessAllInputAreas(bool restoreText)
+{
+ for (int i=g_RichEditList.getCount(); i--; )
+ {
+ RichEditData* rdt = g_RichEditList[i];
+ if (rdt->inputarea)
+ {
+ if (restoreText)
+ {
+ CHARRANGE sel = allsel;
+ ReplaceContactSmileysWithText(rdt, sel, false);
+ }
+ else
+ {
+ ReplaceContactSmileys(rdt, allsel, false, false);
+ }
+ }
+ }
+}
+
+
+void RichEditData_Destroy(void)
+{
+ int i;
+ for (i=g_RichEditList.getCount(); i--; )
+ CloseRichCallback(g_RichEditList[i]->hwnd, true);
+ g_RichEditList.destroy();
+
+ for (i=g_RichEditOwnerList.getCount(); i--; )
+ CloseRichOwnerCallback(g_RichEditOwnerList[i]->hwnd, true);
+ g_RichEditOwnerList.destroy();
+}
diff --git a/plugins/SmileyAdd/src/services.cpp b/plugins/SmileyAdd/src/services.cpp
new file mode 100644
index 0000000000..fed1b4c7ad
--- /dev/null
+++ b/plugins/SmileyAdd/src/services.cpp
@@ -0,0 +1,590 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
+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 "smltool.h"
+#include "smileyroutines.h"
+#include "services.h"
+#include "options.h"
+
+
+//globals, defined int main.cpp
+extern HANDLE hEvent1, hContactMenuItem;
+
+LIST<void> menuHandleArray(5);
+
+//implementation of service functions
+
+SmileyPackType* GetSmileyPack(const char* proto, HANDLE hContact, SmileyPackCType** smlc)
+{
+ bkstring categoryName;
+
+ hContact = DecodeMetaContact(hContact);
+ if (smlc) *smlc = opt.DisableCustom ? NULL : g_SmileyPackCStore.GetSmileyPack(hContact);
+
+ if (proto != NULL && IsBadStringPtrA(proto, 10)) return NULL;
+
+ if (hContact != NULL)
+ {
+ opt.ReadContactCategory(hContact, categoryName);
+ if (categoryName == _T("<None>")) return NULL;
+ if (!categoryName.empty() &&
+ g_SmileyCategories.GetSmileyCategory(categoryName) == NULL)
+ {
+ categoryName.clear();
+ opt.WriteContactCategory(hContact, categoryName);
+ }
+
+ if (categoryName.empty() && !opt.UseOneForAll)
+ {
+ char *protonam = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (protonam != NULL)
+ {
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, protonam, "Transport", &dbv) == 0)
+ {
+ categoryName = dbv.ptszVal;
+ DBFreeVariant(&dbv);
+ }
+ else
+ categoryName = A2T_SM(protonam);
+ }
+ }
+ }
+
+ if (categoryName.empty())
+ {
+ if (proto == NULL || proto[0] == 0)
+ {
+ categoryName = _T("Standard");
+ }
+ else
+ {
+ categoryName = A2T_SM(proto);
+ if (opt.UseOneForAll)
+ {
+ SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(categoryName);
+ if (smc == NULL || smc->IsProto()) categoryName = _T("Standard");
+ }
+ }
+ }
+
+ return g_SmileyCategories.GetSmileyPack(categoryName);
+}
+
+
+INT_PTR ReplaceSmileysCommand(WPARAM, LPARAM lParam)
+{
+ SMADD_RICHEDIT3* smre = (SMADD_RICHEDIT3*) lParam;
+ if (smre == NULL || smre->cbSize < SMADD_RICHEDIT_SIZE_V1) return FALSE;
+
+ SMADD_RICHEDIT3 smrec = {0};
+ memcpy(&smrec, smre, min(smre->cbSize, sizeof(smrec)));
+
+ static const CHARRANGE selection = { 0, LONG_MAX };
+ if (smre->rangeToReplace == NULL) smrec.rangeToReplace = (CHARRANGE*)&selection;
+ else if (smrec.rangeToReplace->cpMax < 0) smrec.rangeToReplace->cpMax = LONG_MAX;
+
+ SmileyPackCType* smcp = NULL;
+ SmileyPackType* SmileyPack = GetSmileyPack(smrec.Protocolname, smrec.hContact,
+ (smrec.flags & (SAFLRE_OUTGOING | SAFLRE_NOCUSTOM)) ? NULL : &smcp);
+
+ ReplaceSmileys(smre->hwndRichEditControl, SmileyPack, smcp, *smrec.rangeToReplace,
+ smrec.hContact == NULL, false, false);
+
+ return TRUE;
+}
+
+
+INT_PTR ShowSmileySelectionCommand(WPARAM, LPARAM lParam)
+{
+ SMADD_SHOWSEL3* smaddInfo = (SMADD_SHOWSEL3*) lParam;
+
+ if (smaddInfo == NULL || smaddInfo->cbSize < SMADD_SHOWSEL_SIZE_V1) return FALSE;
+ HWND parent = smaddInfo->cbSize > SMADD_SHOWSEL_SIZE_V1 ? smaddInfo->hwndParent : NULL;
+ HANDLE hContact = smaddInfo->cbSize > SMADD_SHOWSEL_SIZE_V2 ? smaddInfo->hContact : NULL;
+
+ SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
+ stwp->pSmileyPack = GetSmileyPack(smaddInfo->Protocolname, hContact);
+ stwp->hContact = hContact;
+
+ stwp->hWndParent = parent;
+ stwp->hWndTarget = smaddInfo->hwndTarget;
+ stwp->targetMessage = smaddInfo->targetMessage;
+ stwp->targetWParam = smaddInfo->targetWParam;
+ stwp->xPosition = smaddInfo->xPosition;
+ stwp->yPosition = smaddInfo->yPosition;
+ stwp->direction = smaddInfo->Direction;
+
+ mir_forkthread(SmileyToolThread, stwp);
+
+ return TRUE;
+}
+
+INT_PTR GetSmileyIconCommand(WPARAM, LPARAM lParam)
+{
+ SMADD_GETICON* smre = (SMADD_GETICON*) lParam;
+
+ if (smre == NULL || smre->cbSize < sizeof(SMADD_GETICON)) return FALSE;
+
+ SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname);
+
+ if (SmileyPack == NULL || IsBadStringPtrA(smre->SmileySequence, MAX_SMILEY_LENGTH))
+ {
+ smre->SmileyIcon = NULL;
+ smre->Smileylength = 0;
+ return FALSE;
+ }
+
+ unsigned start, size;
+ SmileyType* sml;
+ FindSmileyInText(SmileyPack, A2T_SM(smre->SmileySequence), start, size, &sml);
+
+ if (size == 0 || start != 0)
+ {
+ smre->SmileyIcon = NULL;
+ smre->Smileylength = 0;
+ }
+ else
+ {
+ smre->SmileyIcon = sml->GetIcon();
+ smre->Smileylength = size;
+ }
+
+ return TRUE;
+}
+
+
+static int GetInfoCommandE(SMADD_INFO2* smre, bool retDup)
+{
+ if (smre == NULL || smre->cbSize < SMADD_INFO_SIZE_V1) return FALSE;
+ HANDLE hContact = smre->cbSize > SMADD_INFO_SIZE_V1 ? smre->hContact : NULL;
+
+ SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname, hContact);
+
+ if (SmileyPack == NULL || SmileyPack->SmileyCount() == 0)
+ {
+ smre->ButtonIcon = NULL;
+ smre->NumberOfSmileys = 0;
+ smre->NumberOfVisibleSmileys = 0;
+ return FALSE;
+ }
+
+ SmileyType* sml = FindButtonSmiley(SmileyPack);
+
+ if (sml != NULL)
+ smre->ButtonIcon = retDup ? sml->GetIconDup() : sml->GetIcon();
+ else
+ smre->ButtonIcon = GetDefaultIcon(retDup);
+
+ smre->NumberOfSmileys = SmileyPack->SmileyCount();
+ smre->NumberOfVisibleSmileys = SmileyPack->VisibleSmileyCount();
+
+ return TRUE;
+}
+
+
+INT_PTR GetInfoCommand(WPARAM, LPARAM lParam)
+{
+ return GetInfoCommandE((SMADD_INFO2*) lParam, false);
+}
+
+
+INT_PTR GetInfoCommand2(WPARAM, LPARAM lParam)
+{
+ return GetInfoCommandE((SMADD_INFO2*) lParam, true);
+}
+
+
+INT_PTR ParseText(WPARAM, LPARAM lParam)
+{
+ SMADD_PARSE* smre = (SMADD_PARSE*) lParam;
+
+ if (smre == NULL || smre->cbSize < sizeof(SMADD_PARSE)) return FALSE;
+
+ SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname);
+
+ if (SmileyPack == NULL)
+ {
+ smre->SmileyIcon = NULL;
+ smre->size = 0;
+ return FALSE;
+ }
+
+ unsigned strtChrOff = smre->startChar + smre->size;
+ char* workstr = smre->str + strtChrOff;
+
+ if (strtChrOff > 1024 || IsBadStringPtrA(workstr, 10))
+ {
+ smre->SmileyIcon = NULL;
+ smre->size = 0;
+ return FALSE;
+ }
+
+ SmileyType* sml;
+ FindSmileyInText(SmileyPack, A2T_SM(workstr), smre->startChar, smre->size, &sml);
+
+ if (smre->size == 0)
+ {
+ smre->SmileyIcon = NULL;
+ }
+ else
+ {
+ smre->SmileyIcon = sml->GetIconDup();
+ smre->startChar += strtChrOff;
+ }
+
+ return TRUE;
+}
+
+
+INT_PTR ParseTextW(WPARAM, LPARAM lParam)
+{
+ SMADD_PARSEW* smre = (SMADD_PARSEW*) lParam;
+
+ if (smre == NULL || smre->cbSize < sizeof(SMADD_PARSEW)) return FALSE;
+
+ SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname);
+
+ if (SmileyPack == NULL)
+ {
+ smre->SmileyIcon = NULL;
+ smre->size = 0;
+ return FALSE;
+ }
+
+ unsigned strtChrOff = smre->startChar + smre->size;
+ wchar_t* workstr = smre->str + strtChrOff;
+
+ if (strtChrOff > 1024 || IsBadStringPtrW(workstr, 10))
+ {
+ smre->SmileyIcon = NULL;
+ smre->size = 0;
+ return FALSE;
+ }
+
+ SmileyType* sml;
+ FindSmileyInText(SmileyPack, workstr, smre->startChar, smre->size, &sml);
+
+ if (smre->size == 0)
+ {
+ smre->SmileyIcon = NULL;
+ }
+ else
+ {
+ smre->SmileyIcon = sml->GetIconDup();
+ smre->startChar += strtChrOff;
+ }
+
+ return TRUE;
+}
+
+
+INT_PTR ParseTextBatch(WPARAM, LPARAM lParam)
+{
+ SMADD_BATCHPARSE2* smre = (SMADD_BATCHPARSE2*) lParam;
+
+ if (smre == NULL || smre->cbSize < SMADD_BATCHPARSE_SIZE_V1) return FALSE;
+ HANDLE hContact = smre->cbSize > SMADD_BATCHPARSE_SIZE_V1 ? smre->hContact : NULL;
+
+ SmileyPackCType* smcp = NULL;
+ SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname, hContact,
+ (smre->flag & (SAFL_OUTGOING | SAFL_NOCUSTOM)) ? NULL : &smcp);
+
+ SmileysQueueType smllist;
+
+ if (smre->flag & SAFL_UNICODE)
+ LookupAllSmileys(SmileyPack, smcp, W2T_SM(smre->wstr), smllist, false);
+ else
+ LookupAllSmileys(SmileyPack, smcp, A2T_SM(smre->astr), smllist, false);
+
+ if (smllist.getCount() == 0) return 0;
+
+ SMADD_BATCHPARSERES *res = new SMADD_BATCHPARSERES[smllist.getCount()];
+ SMADD_BATCHPARSERES* cres = res;
+ for (int j = 0; j < smllist.getCount(); j++)
+ {
+ cres->startChar = smllist[j].loc.cpMin;
+ cres->size = smllist[j].loc.cpMax - smllist[j].loc.cpMin;
+ if (smllist[j].sml)
+ {
+ if (smre->flag & SAFL_PATH)
+ cres->filepath = smllist[j].sml->GetFilePath().c_str();
+ else
+ cres->hIcon = smllist[j].sml->GetIconDup();
+ }
+ else
+ {
+ if (smre->flag & SAFL_PATH)
+ cres->filepath = smllist[j].smlc->GetFilePath().c_str();
+ else
+ cres->hIcon = smllist[j].smlc->GetIcon();
+ }
+
+ cres++;
+ }
+
+ smre->numSmileys = smllist.getCount();
+
+ smre->oflag = smre->flag | SAFL_UNICODE;
+
+ return (INT_PTR)res;
+}
+
+INT_PTR FreeTextBatch(WPARAM, LPARAM lParam)
+{
+ delete[] (SMADD_BATCHPARSERES*)lParam;
+ return TRUE;
+}
+
+INT_PTR RegisterPack(WPARAM, LPARAM lParam)
+{
+ SMADD_REGCAT* smre = (SMADD_REGCAT*) lParam;
+
+ if (smre == NULL || smre->cbSize < sizeof(SMADD_REGCAT)) return FALSE;
+ if (IsBadStringPtrA(smre->name, 50) || IsBadStringPtrA(smre->dispname, 50)) return FALSE;
+
+
+ unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
+ if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP;
+
+
+
+ bkstring nmd(A2W_SM(smre->dispname, lpcp));
+
+
+ bkstring nm(A2T_SM(smre->name));
+ g_SmileyCategories.AddAndLoad(nm, nmd);
+
+ return TRUE;
+}
+
+INT_PTR CustomCatMenu(WPARAM wParam, LPARAM lParam)
+{
+ const HANDLE hContact = (HANDLE)wParam;
+ if (lParam != 0)
+ {
+ SmileyCategoryType* smct = g_SmileyCategories.GetSmileyCategory((unsigned)lParam - 3);
+ if (smct != NULL)
+ opt.WriteContactCategory(hContact, smct->GetName());
+ else
+ {
+ bkstring empty;
+ if (lParam == 1) empty = _T("<None>");
+ opt.WriteContactCategory(hContact, empty);
+ }
+ NotifyEventHooks(hEvent1, (WPARAM)hContact, 0);
+ }
+
+ for (int i = 0; i < menuHandleArray.getCount(); i++)
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)menuHandleArray[i], 0);
+ menuHandleArray.destroy();
+
+ return TRUE;
+}
+
+
+int RebuildContactMenu(WPARAM wParam, LPARAM)
+{
+ int i;
+ CLISTMENUITEM mi = {0};
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB;
+
+ SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList();
+
+ char* protnam = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ bool haveMenu = IsSmileyProto(protnam);
+ if (haveMenu && opt.UseOneForAll)
+ {
+ unsigned cnt = 0;
+ for (i = 0; i < smc.getCount(); ++i)
+ cnt += smc[i].IsCustom();
+ haveMenu = cnt != 0;
+ }
+
+ if (!haveMenu) mi.flags |= CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenuItem, (LPARAM)&mi);
+
+ for (i = 0; i < menuHandleArray.getCount(); ++i)
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)menuHandleArray[i], 0);
+ menuHandleArray.destroy();
+
+ if (haveMenu)
+ {
+ bkstring cat;
+ opt.ReadContactCategory((HANDLE)wParam, cat);
+
+ mi.pszPopupName = (char*)hContactMenuItem;
+ mi.flags = CMIF_CHILDPOPUP | CMIM_FLAGS | CMIF_TCHAR;
+ mi.pszService = MS_SMILEYADD_CUSTOMCATMENU;
+
+ bool nonecheck = true;
+ HGENMENU hMenu;
+
+ for (i = 0; i < smc.getCount(); i++)
+ {
+ if (smc[i].IsExt() || (smc[i].IsProto() && opt.UseOneForAll)) continue;
+
+ const int ind = i + 3;
+
+ mi.position = ind;
+ mi.popupPosition = ind;
+ mi.ptszName = (TCHAR*)smc[i].GetDisplayName().c_str();
+
+ if (cat == smc[i].GetName())
+ {
+ mi.flags |= CMIF_CHECKED;
+ nonecheck = false;
+ }
+
+ hMenu = Menu_AddContactMenuItem(&mi);
+ menuHandleArray.insert(hMenu);
+ mi.flags &= ~CMIF_CHECKED;
+ }
+
+ mi.position = 1;
+ mi.popupPosition = 1;
+ mi.ptszName = _T("<None>");
+ if (cat == _T("<None>")) {
+ mi.flags |= CMIF_CHECKED;
+ nonecheck = false;
+ }
+
+ hMenu = Menu_AddContactMenuItem(&mi);
+ menuHandleArray.insert(hMenu);
+
+ mi.position = 2;
+ mi.popupPosition = 2;
+ mi.ptszName = _T("Protocol specific");
+ if (nonecheck) mi.flags |= CMIF_CHECKED; else mi.flags &= ~CMIF_CHECKED;
+
+ hMenu = Menu_AddContactMenuItem(&mi);
+ menuHandleArray.insert(hMenu);
+ }
+
+ return 0;
+}
+
+INT_PTR ReloadPack(WPARAM, LPARAM lParam)
+{
+ if (lParam)
+ {
+ bkstring categoryName = A2T_SM((char*)lParam);
+ SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(categoryName);
+ if (smc != NULL) smc->Load();
+ }
+ else
+ g_SmileyCategories.ClearAndLoadAll();
+
+ NotifyEventHooks(hEvent1, 0, 0);
+ return 0;
+}
+
+INT_PTR LoadContactSmileys(WPARAM, LPARAM lParam)
+{
+ if (opt.DisableCustom) return 0;
+
+ SMADD_CONT* cont = (SMADD_CONT*)lParam;
+
+ switch (cont->type)
+ {
+ case 0:
+ g_SmileyPackCStore.AddSmileyPack(cont->hContact, cont->path);
+ NotifyEventHooks(hEvent1, (WPARAM)cont->hContact, 0);
+ break;
+
+ case 1:
+ g_SmileyPackCStore.AddSmiley(cont->hContact, cont->path);
+ NotifyEventHooks(hEvent1, (WPARAM)cont->hContact, 0);
+ break;
+ }
+ return 0;
+}
+
+int AccountListChanged(WPARAM wParam, LPARAM lParam)
+{
+ PROTOACCOUNT* acc = (PROTOACCOUNT*)lParam;
+
+ switch (wParam)
+ {
+ case PRAC_ADDED:
+ if (acc != NULL)
+ {
+ bkstring catname(_T("Standard"));
+ const bkstring& defaultFile = g_SmileyCategories.GetSmileyCategory(catname)->GetFilename();
+ g_SmileyCategories.AddAccountAsCategory(acc, defaultFile);
+ }
+ break;
+
+ case PRAC_CHANGED:
+ if (acc != NULL && acc->szModuleName != NULL)
+ {
+ bkstring name(A2T_SM(acc->szModuleName));
+ SmileyCategoryType* smc = g_SmileyCategories.GetSmileyCategory(name);
+ if (smc != NULL)
+ {
+ if (acc->tszAccountName) name = acc->tszAccountName;
+ smc->SetDisplayName(name);
+ }
+ }
+ break;
+
+ case PRAC_REMOVED:
+ g_SmileyCategories.DeleteAccountAsCategory(acc);
+ break;
+
+ case PRAC_CHECKED:
+ if (acc != NULL)
+ {
+ if (acc->bIsEnabled)
+ {
+ bkstring catname(_T("Standard"));
+ const bkstring& defaultFile = g_SmileyCategories.GetSmileyCategory(catname)->GetFilename();
+ g_SmileyCategories.AddAccountAsCategory(acc, defaultFile);
+ }
+ else
+ {
+ g_SmileyCategories.DeleteAccountAsCategory(acc);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int DbSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ DBCONTACTWRITESETTING* cws = (DBCONTACTWRITESETTING*)lParam;
+
+ if (hContact == NULL) return 0;
+ if (cws->value.type == DBVT_DELETED) return 0;
+
+ if (strcmp(cws->szSetting, "Transport") == 0)
+ {
+ bkstring catname(_T("Standard"));
+ SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(catname);
+ if (smc != NULL)
+ g_SmileyCategories.AddContactTransportAsCategory(hContact, smc->GetFilename());
+ }
+ return 0;
+}
diff --git a/plugins/SmileyAdd/src/services.h b/plugins/SmileyAdd/src/services.h
new file mode 100644
index 0000000000..9311e3637d
--- /dev/null
+++ b/plugins/SmileyAdd/src/services.h
@@ -0,0 +1,48 @@
+/*
+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/>.
+*/
+
+#ifndef SMILEYADD_SERVICES_H_
+#define SMILEYADD_SERVICES_H_
+
+#include "m_smileyadd.h"
+#include "m_smileyadd_deprecated.h"
+
+// service commands
+INT_PTR ReplaceSmileysCommand(WPARAM wParam, LPARAM lParam);
+INT_PTR GetSmileyIconCommand(WPARAM wParam, LPARAM lParam);
+INT_PTR ShowSmileySelectionCommand(WPARAM wParam, LPARAM lParam);
+INT_PTR GetInfoCommand(WPARAM wParam, LPARAM);
+INT_PTR GetInfoCommand2(WPARAM wParam, LPARAM);
+INT_PTR ParseText(WPARAM wParam, LPARAM lParam);
+INT_PTR ParseTextW(WPARAM wParam, LPARAM lParam);
+INT_PTR RegisterPack(WPARAM wParam, LPARAM lParam);
+INT_PTR ParseTextBatch(WPARAM wParam, LPARAM lParam);
+INT_PTR FreeTextBatch(WPARAM wParam, LPARAM lParam);
+INT_PTR CustomCatMenu(WPARAM, LPARAM lParam);
+int RebuildContactMenu(WPARAM wParam, LPARAM);
+INT_PTR ReloadPack(WPARAM, LPARAM lParam);
+INT_PTR LoadContactSmileys(WPARAM, LPARAM lParam);
+int AccountListChanged(WPARAM wParam, LPARAM lParam);
+int DbSettingChanged(WPARAM wParam, LPARAM lParam);
+
+SmileyPackType* GetSmileyPack(const char* proto, HANDLE hContact = NULL, SmileyPackCType** smlc = NULL);
+
+
+#endif // SMILEYADD_SERVICES_H_
+
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();
+}
diff --git a/plugins/SmileyAdd/src/smileyroutines.h b/plugins/SmileyAdd/src/smileyroutines.h
new file mode 100644
index 0000000000..6b12ee6ead
--- /dev/null
+++ b/plugins/SmileyAdd/src/smileyroutines.h
@@ -0,0 +1,49 @@
+/*
+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/>.
+*/
+
+#ifndef _SMILEYROUTINES_
+#define _SMILEYROUTINES_
+
+#include "smileys.h"
+#include "customsmiley.h"
+
+typedef struct ReplaceSmileyType_tag
+{
+ CHARRANGE loc;
+ SmileyType* sml;
+ SmileyCType* smlc;
+ bool ldspace;
+ bool trspace;
+} ReplaceSmileyType;
+
+// Queue to store smileys found
+typedef SMOBJLIST<ReplaceSmileyType> SmileysQueueType;
+
+
+
+void LookupAllSmileys(SmileyPackType* smileyPack, SmileyPackCType* smileyCPack, const TCHAR* lpstrText,
+ SmileysQueueType& smllist, const bool firstOnly);
+void ReplaceSmileys(HWND hwnd, SmileyPackType* smp, SmileyPackCType* smcp, const CHARRANGE& sel,
+ bool useHidden, bool ignoreLast, bool unFreeze);
+void ReplaceSmileysWithText(HWND hwnd, CHARRANGE& sel, bool keepFrozen);
+void FindSmileyInText(SmileyPackType* smp, const TCHAR* str,
+ unsigned& first, unsigned& size, SmileyType** index);
+SmileyType* FindButtonSmiley(SmileyPackType* smp);
+
+#endif
diff --git a/plugins/SmileyAdd/src/smileys.cpp b/plugins/SmileyAdd/src/smileys.cpp
new file mode 100644
index 0000000000..2f16cee51e
--- /dev/null
+++ b/plugins/SmileyAdd/src/smileys.cpp
@@ -0,0 +1,1146 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
+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 "smileys.h"
+#include "smileyroutines.h"
+#include "options.h"
+#include "download.h"
+
+SmileyPackListType g_SmileyPacks;
+SmileyCategoryListType g_SmileyCategories;
+
+extern HANDLE hNetlibUser;
+
+//
+// SmileyType
+//
+
+
+SmileyType::SmileyType(void)
+{
+ m_SmileyIcon = NULL;
+ m_xepimg = NULL;
+ m_flags = 0;
+ m_index = 0;
+ m_size.cx = 0;
+ m_size.cy = 0;
+}
+
+SmileyType::~SmileyType()
+{
+ if (m_xepimg) m_xepimg->Release();
+ m_xepimg = NULL;
+
+ if (m_SmileyIcon != NULL) DestroyIcon(m_SmileyIcon);
+ m_SmileyIcon = NULL;
+}
+
+
+HICON SmileyType::GetIcon(void)
+{
+ if (m_SmileyIcon == NULL)
+ {
+ ImageBase* img = CreateCachedImage();
+ if (!img) return NULL;
+ img->SelectFrame(m_index);
+ m_SmileyIcon = img->GetIcon();
+ img->Release();
+ }
+ return m_SmileyIcon;
+}
+
+
+HICON SmileyType::GetIconDup(void)
+{
+ ImageBase* img = CreateCachedImage();
+ img->SelectFrame(m_index);
+ HICON hIcon = img->GetIcon();
+ img->Release();
+ return hIcon;
+}
+
+
+bool SmileyType::LoadFromImage(IStream* pStream)
+{
+ if (m_xepimg) m_xepimg->Release();
+
+ bkstring name;
+ m_xepimg = new ImageType(0, name, pStream);
+
+ return true;
+}
+
+
+bool SmileyType::LoadFromResource(const bkstring& file, const int index)
+{
+ m_index = index;
+ m_filepath = file;
+
+ return true;
+}
+
+
+void SmileyType::GetSize(SIZE& size)
+{
+ if (m_size.cy == 0)
+ {
+ ImageBase* img = CreateCachedImage();
+ if (img)
+ {
+ img->GetSize(m_size);
+ img->Release();
+ }
+ }
+ size = m_size;
+}
+
+
+ImageBase* SmileyType::CreateCachedImage(void)
+{
+ if (m_xepimg)
+ {
+ m_xepimg->AddRef();
+ return m_xepimg;
+ }
+ return AddCacheImage(m_filepath, m_index);
+}
+
+
+void SmileyType::SetImList(HIMAGELIST hImLst, long i)
+{
+ if (m_xepimg) m_xepimg->Release();
+ m_xepimg = new ImageListItemType(0, hImLst, i);
+}
+
+
+HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
+{
+ ImageBase* img = CreateCachedImage();
+ if (!img) return NULL;
+ img->SelectFrame(m_index);
+ HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
+ img->Release();
+
+ return hBmp;
+}
+
+
+//
+// SmileyPackType
+//
+
+SmileyPackType::SmileyPackType()
+{
+ m_hSmList = NULL;
+ errorFound = false;
+}
+
+SmileyType* SmileyPackType::GetSmiley(unsigned index)
+{
+ return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : NULL;
+}
+
+
+static DWORD_PTR ConvertServiceParam(HANDLE hContact, const TCHAR *param)
+{
+ DWORD_PTR ret;
+ if (param == NULL)
+ ret = 0;
+ else if (_tcsicmp(_T("hContact"), param) == 0)
+ ret = (DWORD_PTR)hContact;
+ else if (_istdigit(*param))
+ ret = _ttoi(param);
+ else
+ ret = (DWORD_PTR)param;
+
+ return ret;
+}
+
+
+void SmileyType::CallSmileyService(HANDLE hContact)
+{
+ _TPattern * srvsplit = _TPattern::compile(_T("(.*)\\|(.*)\\|(.*)"));
+ _TMatcher * m0 = srvsplit->createTMatcher(GetTriggerText());
+ m0->findFirstMatch();
+
+ bkstring name = m0->getGroup(1);
+ bkstring par1 = m0->getGroup(2);
+ bkstring par2 = m0->getGroup(3);
+
+ delete m0;
+ delete srvsplit;
+
+ char str[MAXMODULELABELLENGTH];
+ const char *proto = "";
+
+ if (name[0] == '/')
+ {
+ proto = (const char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (proto == NULL) return;
+ }
+ mir_snprintf(str, sizeof(str), "%s%s", proto, T2A_SM(name.c_str()));
+ CallService(str,
+ ConvertServiceParam(hContact, par1.c_str()),
+ ConvertServiceParam(hContact, par2.c_str()));
+}
+
+
+
+SmileyPackType::~SmileyPackType()
+{
+ if (m_hSmList != NULL) ImageList_Destroy(m_hSmList);
+}
+
+static const TCHAR urlRegEx[] =
+ _T("(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*");
+static const TCHAR pathRegEx[] = _T("[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*");
+static const TCHAR timeRegEx[] = _T("\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}");
+
+void SmileyPackType::AddTriggersToSmileyLookup(void)
+{
+ _TPattern * p = _TPattern::compile(_T("\\s+"));
+
+ {
+ bkstring emptystr;
+ m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr));
+ }
+
+ for (int dist = 0; dist < m_SmileyList.getCount(); dist++)
+ {
+ if (m_SmileyList[dist].IsRegEx())
+ {
+ SmileyLookup* dats = new SmileyLookup(m_SmileyList[dist].GetTriggerText(), true, dist, GetFilename());
+ if (dats->IsValid())
+ m_SmileyLookup.insert(dats);
+ else
+ errorFound = true;
+ if (m_SmileyList[dist].m_InsertText.empty()) m_SmileyList[dist].m_InsertText = m_SmileyList[dist].m_ToolText;
+ }
+ else if (!m_SmileyList[dist].IsService())
+ {
+ bool first = true;
+ int li = 0;
+ _TMatcher * m0 = p->createTMatcher(m_SmileyList[dist].GetTriggerText());
+ while (m0->findNextMatch())
+ {
+ int stind = m0->getStartingIndex();
+ if (li != stind)
+ {
+ bkstring out;
+ ReplaceAllSpecials(m0->getString().substr(li, stind - li), out);
+ SmileyLookup *dats = new SmileyLookup(out, false, dist, GetFilename());
+ if (dats->IsValid())
+ {
+ m_SmileyLookup.insert(dats);
+ if (first)
+ {
+ m_SmileyList[dist].m_InsertText = out;
+ first = false;
+ }
+ }
+ }
+ li = m0->getEndingIndex();
+ }
+
+ int stind = (int)m0->getString().size();
+ if (li < stind)
+ {
+ bkstring out;
+ ReplaceAllSpecials(m0->getString().substr(li, stind - li), out);
+ SmileyLookup *dats = new SmileyLookup(out, false, dist, GetFilename());
+ if (dats->IsValid())
+ {
+ m_SmileyLookup.insert(dats);
+ if (first)
+ {
+ m_SmileyList[dist].m_InsertText = out;
+ first = false;
+ }
+ }
+ }
+ delete m0;
+ }
+ }
+ delete p;
+}
+
+
+void SmileyPackType::ReplaceAllSpecials(const bkstring& Input, bkstring& Output)
+{
+ Output = _TPattern::replace(_T("%%_{1,2}%%"), Input, _T(" "));
+ Output = _TPattern::replace(_T("%%''%%"), Output, _T("\""));
+}
+
+
+void SmileyPackType::Clear(void)
+{
+ m_SmileyList.destroy();
+ m_SmileyLookup.destroy();
+ if (m_hSmList != NULL) { ImageList_Destroy(m_hSmList); m_hSmList = NULL; }
+ m_Filename.clear();
+ m_Name.clear();
+ m_Date.clear();
+ m_Version.clear();
+ m_Author.clear();
+ m_VisibleCount = 0;
+ m_ButtonSmiley.clear();
+ errorFound = false;
+}
+
+bool SmileyPackType::LoadSmileyFile(const bkstring& filename, bool onlyInfo, bool noerr)
+{
+ Clear();
+
+ if (filename.empty())
+ {
+ m_Name = _T("Nothing loaded");
+ return false;
+ }
+
+ bkstring modpath;
+ pathToAbsolute(filename, modpath);
+
+ // Load xep file
+ int fh = _topen(modpath.c_str(), _O_BINARY | _O_RDONLY);
+ if (fh == -1)
+ {
+ if (!noerr)
+ {
+ static const TCHAR errmsg[] = _T("Smiley Pack %s not found.\n")
+ _T("Select correct Smiley Pack in the Miranda Options | Customize | Smileys.");
+ TCHAR msgtxt[1024];
+ mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), modpath.c_str());
+ ReportError(msgtxt);
+ }
+
+ m_Name = _T("Nothing loaded");
+ return false;
+ }
+
+ m_Filename = filename;
+
+ // Find file size
+ const long flen = _filelength(fh);
+
+ // Allocate file buffer
+ char* buf = new char[flen + sizeof(wchar_t)];
+
+ // Read xep file in
+ int len = _read(fh, buf, flen);
+ *(wchar_t*)(buf+len) = 0;
+
+ // Close file
+ _close(fh);
+
+ bkstring tbuf;
+
+ if (len>2 && *(wchar_t*)buf == 0xfeff)
+ {
+ tbuf = W2T_SM((wchar_t*)buf+1);
+ }
+ else if (len>3 && buf[0]=='\xef' && buf[1]=='\xbb' && buf[2]=='\xbf')
+ {
+ tbuf = W2T_SM(A2W_SM(buf+3, CP_UTF8));
+ }
+ else
+ {
+ tbuf = A2T_SM(buf);
+ }
+
+ delete[] buf;
+
+ bool res;
+ if (filename.find(_T(".xep")) == filename.npos)
+ res = LoadSmileyFileMSL(tbuf, onlyInfo, modpath);
+ else
+ res = LoadSmileyFileXEP(tbuf, onlyInfo, modpath);
+
+ if (errorFound) ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee Network Log for details."));
+
+ return res;
+}
+
+bool SmileyPackType::LoadSmileyFileMSL(bkstring& tbuf, bool onlyInfo, bkstring& modpath)
+{
+ _TPattern * pathsplit = _TPattern::compile(_T("(.*\\\\)(.*)\\.|$"));
+ _TMatcher * m0 = pathsplit->createTMatcher(modpath);
+
+ m0->findFirstMatch();
+ const bkstring pathstr = m0->getGroup(1);
+ const bkstring packstr = m0->getGroup(2);
+
+ delete m0;
+ delete pathsplit;
+
+ _TPattern * otherf = _TPattern::compile(
+ _T("^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\""),
+ _TPattern::MULTILINE_MATCHING);
+
+ m0 = otherf->createTMatcher(tbuf);
+
+ while (m0->findNextMatch())
+ {
+ if (m0->getGroup(1) == _T("Name")) m_Name = m0->getGroup(2);
+ if (m0->getGroup(1) == _T("Author")) m_Author = m0->getGroup(2);
+ if (m0->getGroup(1) == _T("Date")) m_Date = m0->getGroup(2);
+ if (m0->getGroup(1) == _T("Version")) m_Version = m0->getGroup(2);
+ if (m0->getGroup(1) == _T("ButtonSmiley")) m_ButtonSmiley = m0->getGroup(2);
+ }
+ delete m0;
+ delete otherf;
+
+ if (!onlyInfo)
+ {
+ selec.x = 0;
+ selec.y = 0;
+ win.x = 0;
+ win.y = 0;
+ {
+ _TPattern * pat = _TPattern::compile(
+ _T("^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)"),
+ _TPattern::MULTILINE_MATCHING);
+ _TMatcher * m0 = pat->createTMatcher(tbuf);
+ while (m0->findNextMatch())
+ {
+ POINT tpt;
+ tpt.x = _ttol(m0->getGroup(2).c_str());
+ tpt.y = _ttol(m0->getGroup(3).c_str());
+
+ if (m0->getGroup(1) == _T("Selection"))
+ selec = tpt;
+ else if (m0->getGroup(1) == _T("Window"))
+ win = tpt;
+ }
+ delete m0;
+ delete pat;
+ }
+
+ _TPattern * smiley = _TPattern::compile(
+ _T("^\\s*Smiley(\\*)?\\s*=") // Is Hidden
+ _T("(?:\\s*\"(.*)\")") // Smiley file name
+ _T("(?:[\\s,]+(\\-?\\d+))") // Icon resource id
+ _T("(?:[\\s,]+(R|S)?\"(.*?)\")") // Trigger text
+ _T("(?:[\\s,]+\"(.*?)\")?") // Tooltip or insert text
+ _T("(?:[\\s,]+\"(.*?)\")?"), // Tooltip text
+ _TPattern::MULTILINE_MATCHING);
+
+ _TMatcher * m0 = smiley->createTMatcher(tbuf);
+
+ SmileyVectorType hiddenSmileys;
+
+ unsigned smnum = 0;
+ while (m0->findNextMatch())
+ {
+ bkstring resname = m0->getGroup(2);
+ if (resname.find(_T("http://")) != resname.npos)
+ {
+ if (GetSmileyFile(resname, packstr)) continue;
+ }
+ else
+ {
+ if (!resname.empty()) resname.insert(0, pathstr);
+ }
+
+ SmileyType *dat = new SmileyType;
+
+ const int iconIndex = _ttol(m0->getGroup(3).c_str());
+
+ dat->SetHidden(m0->getStartingIndex(1) >= 0);
+ if (m0->getStartingIndex(4) >= 0)
+ {
+ dat->SetRegEx(m0->getGroup(4) == _T("R"));
+ dat->SetService(m0->getGroup(4) == _T("S"));
+ }
+ dat->m_TriggerText = m0->getGroup(5);
+ if (dat->IsRegEx())
+ {
+ if (m0->getStartingIndex(6) >= 0)
+ ReplaceAllSpecials(m0->getGroup(6), dat->m_InsertText);
+
+ if (m0->getStartingIndex(7) >= 0)
+ ReplaceAllSpecials(m0->getGroup(7), dat->m_ToolText);
+ else
+ dat->m_ToolText = dat->m_InsertText;
+ }
+ else
+ {
+ if (m0->getStartingIndex(6) >= 0)
+ ReplaceAllSpecials(m0->getGroup(6), dat->m_ToolText);
+ else
+ ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText);
+ }
+
+ bool noerr;
+ if (resname.empty())
+ {
+ dat->SetHidden(true);
+ dat->SetText(true);
+ noerr = true;
+ }
+ else
+ noerr = dat->LoadFromResource(resname, iconIndex);
+
+ if (dat->IsHidden())
+ hiddenSmileys.insert(dat);
+ else
+ m_SmileyList.insert(dat);
+
+ if (!noerr)
+ {
+ static const TCHAR errmsg[] = _T("Smiley #%u in file %s for Smiley Pack %s not found.");
+ TCHAR msgtxt[1024];
+ mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), smnum, resname.c_str(), modpath.c_str());
+ CallService(MS_NETLIB_LOG,(WPARAM) hNetlibUser, (LPARAM)(char*)T2A_SM(msgtxt));
+ errorFound = true;
+ }
+ smnum++;
+ }
+ delete m0;
+ delete smiley;
+
+ m_VisibleCount = m_SmileyList.getCount();
+
+ m_SmileyList.splice(hiddenSmileys);
+
+ AddTriggersToSmileyLookup();
+ }
+
+ return true;
+}
+
+
+static void DecodeHTML(bkstring& str)
+{
+ if (str.find('&') != str.npos)
+ {
+ str = _TPattern::replace(bkstring(_T("&lt;")), str, bkstring(_T("<")));
+ str = _TPattern::replace(bkstring(_T("&gt;")), str, bkstring(_T(">")));
+ }
+}
+
+
+static IStream* DecodeBase64Data(const char* data)
+{
+ NETLIBBASE64 nlb64;
+ nlb64.pszEncoded = (char*)data;
+ nlb64.cchEncoded = (int)strlen(data);
+ nlb64.cbDecoded = Netlib_GetBase64DecodedBufferSize(nlb64.cchEncoded);
+
+ IStream* pStream = NULL;
+
+ // Read image list
+ HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, nlb64.cbDecoded);
+ if (hBuffer)
+ {
+ nlb64.pbDecoded = (PBYTE)GlobalLock(hBuffer);
+ CallService(MS_NETLIB_BASE64DECODE, 0, (LPARAM)&nlb64);
+ GlobalUnlock(hBuffer);
+
+ CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
+ }
+
+ return pStream;
+}
+
+
+bool SmileyPackType::LoadSmileyFileXEP(bkstring& tbuf, bool onlyInfo, bkstring& )
+{
+ _TMatcher *m0, *m1, *m2;
+
+ _TPattern * dbname_re = _TPattern::compile(_T("<DataBaseName>\\s*\"(.*?)\"\\s*</DataBaseName>"),
+ _TPattern::MULTILINE_MATCHING);
+ _TPattern * author_re = _TPattern::compile(_T("<PackageAuthor>\\s*\"(.*?)\"\\s*</PackageAuthor>"),
+ _TPattern::MULTILINE_MATCHING);
+ _TPattern * settings_re = _TPattern::compile(_T("<settings>(.*?)</settings>"),
+ _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
+
+ m0 = settings_re->createTMatcher(tbuf);
+ if (m0->findFirstMatch())
+ {
+ bkstring settings = m0->getGroup(1);
+
+ m1 = author_re->createTMatcher(settings);
+ if (m1->findFirstMatch())
+ {
+ m_Author = m1->getGroup(1);
+ DecodeHTML(m_Author);
+ }
+ delete m1;
+
+ m1 = dbname_re->createTMatcher(settings);
+ if (m1->findFirstMatch())
+ {
+ m_Name = m1->getGroup(1);
+ DecodeHTML(m_Name);
+ }
+ delete m1;
+ }
+ delete m0;
+
+ delete dbname_re;
+ delete author_re;
+ delete settings_re;
+
+ if (!onlyInfo)
+ {
+ _TPattern * record_re = _TPattern::compile(_T("<record.*?ImageIndex=\"(.*?)\".*?>(?:\\s*\"(.*?)\")?(.*?)</record>"),
+ _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
+ _TPattern * expression_re = _TPattern::compile(_T("<Expression>\\s*\"(.*?)\"\\s*</Expression>"),
+ _TPattern::MULTILINE_MATCHING);
+ _TPattern * pastetext_re = _TPattern::compile(_T("<PasteText>\\s*\"(.*?)\"\\s*</PasteText>"),
+ _TPattern::MULTILINE_MATCHING);
+ _TPattern * images_re = _TPattern::compile(_T("<images>(.*?)</images>"),
+ _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
+ _TPattern * image_re = _TPattern::compile(_T("<Image>(.*?)</Image>"),
+ _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
+ _TPattern * imagedt_re = _TPattern::compile(_T("<!\\[CDATA\\[(.*?)\\]\\]>"),
+ _TPattern::MULTILINE_MATCHING );
+
+ m0 = images_re->createTMatcher(tbuf);
+ if (m0->findFirstMatch())
+ {
+ bkstring images = m0->getGroup(1);
+
+ m1 = imagedt_re->createTMatcher(images);
+ if (m1->findFirstMatch())
+ {
+ IStream* pStream = DecodeBase64Data(T2A_SM(m1->getGroup(1).c_str()));
+ if (pStream != NULL)
+ {
+ if (m_hSmList != NULL) ImageList_Destroy(m_hSmList);
+ m_hSmList = ImageList_Read(pStream);
+ pStream->Release();
+ }
+ }
+ delete m1;
+ }
+ delete m0;
+
+ m0 = record_re->createTMatcher(tbuf);
+ while (m0->findNextMatch())
+ {
+ SmileyType *dat = new SmileyType;
+
+ dat->SetRegEx(true);
+ dat->SetImList(m_hSmList, _ttol(m0->getGroup(1).c_str()));
+ dat->m_ToolText = m0->getGroup(2);
+ DecodeHTML(dat->m_ToolText);
+
+ bkstring rec = m0->getGroup(3);
+
+ m1 = expression_re->createTMatcher(rec);
+ if (m1->findFirstMatch())
+ {
+ dat->m_TriggerText = m1->getGroup(1);
+ DecodeHTML(dat->m_TriggerText);
+ }
+ delete m1;
+
+ m1 = pastetext_re->createTMatcher(rec);
+ if (m1->findFirstMatch())
+ {
+ dat->m_InsertText = m1->getGroup(1);
+ DecodeHTML(dat->m_InsertText);
+ }
+ delete m1;
+ dat->SetHidden(dat->m_InsertText.empty());
+
+ m1 = image_re->createTMatcher(rec);
+ if (m1->findFirstMatch())
+ {
+ bkstring images = m1->getGroup(1);
+
+ m2 = imagedt_re->createTMatcher(images);
+ if (m2->findFirstMatch())
+ {
+ IStream* pStream = DecodeBase64Data(T2A_SM(m2->getGroup(1).c_str()));
+ if (pStream != NULL)
+ {
+ dat->LoadFromImage(pStream);
+ pStream->Release();
+ }
+ }
+ delete m2;
+ }
+ delete m1;
+
+ m_SmileyList.insert(dat);
+ }
+ delete m0;
+
+ delete record_re;
+ delete expression_re;
+ delete pastetext_re;
+ delete images_re;
+ delete image_re;
+ delete imagedt_re;
+ }
+
+ m_VisibleCount = m_SmileyList.getCount();
+
+ AddTriggersToSmileyLookup();
+
+ selec.x = 0;
+ selec.y = 0;
+ win.x = 0;
+ win.y = 0;
+
+ return true;
+}
+
+
+//
+// SmileyPackListType
+//
+
+
+bool SmileyPackListType::AddSmileyPack(bkstring& filename)
+{
+ bool res = true;
+ if (GetSmileyPack(filename) == NULL)
+ { //not exist yet, so add
+ SmileyPackType *smileyPack = new SmileyPackType;
+
+ res = smileyPack->LoadSmileyFile(filename, FALSE);
+ if (res)
+ m_SmileyPacks.insert(smileyPack);
+ else
+ delete smileyPack;
+ }
+ return res;
+}
+
+
+SmileyPackType* SmileyPackListType::GetSmileyPack(bkstring& filename)
+{
+ bkstring modpath;
+ pathToAbsolute(filename, modpath);
+
+ for (int i = 0; i < m_SmileyPacks.getCount(); i++)
+ {
+ bkstring modpath1;
+ pathToAbsolute(m_SmileyPacks[i].GetFilename(), modpath1);
+ if (lstrcmpi(modpath.c_str(), modpath1.c_str()) == 0) return &m_SmileyPacks[i];
+ }
+ return NULL;
+}
+
+void SmileyPackListType::ClearAndFreeAll()
+{
+ m_SmileyPacks.destroy();
+}
+
+
+//
+// SmileyCategoryType
+//
+
+
+SmileyCategoryType::SmileyCategoryType(SmileyPackListType* pSPS, const bkstring& name,
+ const bkstring& displayName,
+ const bkstring& defaultFilename, SmcType typ)
+{
+ m_pSmileyPackStore = pSPS;
+ type = typ;
+ m_Name = name;
+ m_DisplayName = displayName;
+
+ opt.ReadPackFileName(m_Filename, m_Name, defaultFilename);
+}
+
+
+void SmileyCategoryType::Load(void)
+{
+ if (!opt.UseOneForAll || type != smcProto)
+ m_pSmileyPackStore->AddSmileyPack(m_Filename);
+}
+
+
+SmileyPackType* SmileyCategoryType::GetSmileyPack(void)
+{
+ return m_pSmileyPackStore->GetSmileyPack(m_Filename);
+}
+
+
+void SmileyCategoryType::SaveSettings(void)
+{
+ opt.WritePackFileName(m_Filename, m_Name);
+}
+
+//
+// SmileyCategoryListType
+//
+
+void SmileyCategoryListType::ClearAndLoadAll(void)
+{
+ m_pSmileyPackStore->ClearAndFreeAll();
+
+ for (int i = 0; i < m_SmileyCategories.getCount(); i++)
+ m_SmileyCategories[i].Load();
+}
+
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const bkstring& name)
+{
+ for (int i = 0; i < m_SmileyCategories.getCount(); i++)
+ {
+ if (name.comparei(m_SmileyCategories[i].GetName()) == 0) return &m_SmileyCategories[i];
+ }
+ return NULL;
+}
+
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
+{
+ return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : NULL;
+}
+
+
+SmileyPackType* SmileyCategoryListType::GetSmileyPack(bkstring& categoryname)
+{
+ SmileyCategoryType* smc = GetSmileyCategory(categoryname);
+ return smc != NULL ? smc->GetSmileyPack() : NULL;
+}
+
+
+void SmileyCategoryListType::SaveSettings(void)
+{
+ bkstring catstr;
+ for (int i = 0; i < m_SmileyCategories.getCount(); i++)
+ {
+ m_SmileyCategories[i].SaveSettings();
+ if (m_SmileyCategories[i].IsCustom())
+ {
+ if (!catstr.empty()) catstr += '#';
+ catstr += m_SmileyCategories[i].GetName();
+ }
+ }
+ opt.WriteCustomCategories(catstr);
+}
+
+
+void SmileyCategoryListType::AddAndLoad(const bkstring& name, const bkstring& displayName)
+{
+ if (GetSmileyCategory(name) != NULL) return;
+
+ AddCategory(name, displayName, smcExt);
+ // Load only if other smileys have been loaded already
+ if (m_SmileyCategories.getCount() > 1)
+ m_SmileyCategories[m_SmileyCategories.getCount()-1].Load();
+}
+
+
+void SmileyCategoryListType::AddCategory(const bkstring& name, const bkstring& displayName,
+ SmcType typ, const bkstring& defaultFilename)
+{
+ if (GetSmileyCategory(name) == NULL)
+ m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name,
+ displayName, defaultFilename, typ));
+}
+
+
+bool SmileyCategoryListType::DeleteCustomCategory(int index)
+{
+ if (index < m_SmileyCategories.getCount())
+ {
+ if (m_SmileyCategories[index].IsCustom())
+ {
+ m_SmileyCategories.remove(index);
+ return true;
+ }
+
+ }
+ return false;
+}
+
+void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const bkstring& defaultFile)
+{
+ if (IsAccountEnabled(acc) && acc->szProtoName && IsSmileyProto(acc->szModuleName))
+ {
+ bkstring displayName(acc->tszAccountName ? acc->tszAccountName : A2T_SM(acc->szModuleName));
+
+ const char* packnam = acc->szProtoName;
+ if (strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+ else if (strstr(packnam, "SIP") != NULL)
+ packnam = "MSN";
+
+ char path[MAX_PATH];
+ mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packnam);
+
+ bkstring paths = A2T_SM(path), patha;
+ pathToAbsolute(paths, patha);
+
+ if (_taccess(patha.c_str(), 0) != 0)
+ paths = defaultFile;
+
+ bkstring tname(A2T_SM(acc->szModuleName));
+ AddCategory(tname, displayName, smcProto, paths);
+ }
+}
+
+void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
+{
+ bkstring tname(A2T_SM(acc->szModuleName));
+
+ HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL)
+ {
+ char* proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (proto)
+ {
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, proto, "Transport", &dbv) == 0)
+ {
+ bool found = (tname.comparei(dbv.ptszVal) == 0);
+ DBFreeVariant(&dbv);
+ if (found) return;
+ }
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+
+ for (int i = 0; i < m_SmileyCategories.getCount(); i++)
+ {
+ if (tname.comparei(m_SmileyCategories[i].GetName()) == 0)
+ {
+ m_SmileyCategories.remove(i);
+ break;
+ }
+ }
+}
+
+void SmileyCategoryListType::AddContactTransportAsCategory(HANDLE hContact, const bkstring& defaultFile)
+{
+ char* proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (proto == NULL) return;
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, proto, "Transport", &dbv) == 0)
+ {
+ if (dbv.ptszVal[0] == '\0')
+ {
+ DBFreeVariant(&dbv);
+ return;
+ }
+ char* trsp = mir_strdup(T2A_SM(dbv.ptszVal));
+ _strlwr(trsp);
+
+ const char *packname = NULL;
+ if (strstr(trsp, "msn") != NULL)
+ packname = "msn";
+ else if (strstr(trsp, "icq") != NULL)
+ packname = "icq";
+ else if (strstr(trsp, "yahoo") != NULL)
+ packname = "yahoo";
+ else if (strstr(trsp, "aim") != NULL)
+ packname = "aim";
+ else if (strstr(trsp, "lcs") != NULL)
+ packname = "msn";
+
+ mir_free(trsp);
+
+ bkstring displayName = dbv.ptszVal;
+ if (packname != NULL)
+ {
+ char path[MAX_PATH];
+ mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packname);
+
+ bkstring paths = A2T_SM(path), patha;
+ pathToAbsolute(paths, patha);
+
+ if (_taccess(patha.c_str(), 0) != 0)
+ paths = defaultFile;
+
+ AddCategory(displayName, displayName, smcProto, paths);
+ }
+ else
+ AddCategory(displayName, displayName, smcProto, defaultFile);
+
+ DBFreeVariant(&dbv);
+ }
+}
+
+
+void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
+{
+ bkstring displayName = TranslateT("Standard");
+ bkstring tname = _T("Standard");
+ AddCategory(tname, displayName, smcStd);
+
+ const bkstring& defaultFile = GetSmileyCategory(tname)->GetFilename();
+
+
+ unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
+ if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP;
+
+
+ PROTOCOLDESCRIPTOR **protoList;
+ PROTOACCOUNT **accList;
+ int protoCount;
+
+ if (ProtoEnumAccounts(&protoCount, &accList) == CALLSERVICE_NOTFOUND || (protoCount > 0 && accList[0]->cbSize == 0))
+ {
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&protoCount, (LPARAM)&protoList);
+ for (int i = 0; i < protoCount; i++)
+ {
+ if (protoList[i]->type != PROTOTYPE_PROTOCOL) continue;
+
+ if (IsSmileyProto(protoList[i]->szName))
+ {
+ const char* packnam = protoList[i]->szName;
+ if (strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+ else if (strstr(packnam, "SIP") != NULL)
+ packnam = "MSN";
+
+ char path[MAX_PATH];
+ mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packnam);
+
+ bkstring paths = A2T_SM(path), patha;
+ pathToAbsolute(paths, patha);
+
+ if (_taccess(patha.c_str(), 0) != 0)
+ paths = defaultFile;
+
+ char protoName[128];
+ CallProtoService(protoList[i]->szName, PS_GETNAME, sizeof(protoName), (LPARAM)protoName);
+
+
+ displayName = A2W_SM(protoName, lpcp);
+
+ tname = A2T_SM(protoList[i]->szName);
+ AddCategory(tname, displayName, smcProto, paths);
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < protoCount; i++)
+ AddAccountAsCategory(accList[i], defaultFile);
+ }
+
+ HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL)
+ {
+ AddContactTransportAsCategory(hContact, defaultFile);
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+
+ bkstring cats;
+ opt.ReadCustomCategories(cats);
+
+ bkstring::size_type cppv = 0;
+ for (;;)
+ {
+ bkstring::size_type cp = cats.find('#', cppv);
+ if (cp != cats.npos)
+ {
+ displayName = cats.substr(cppv, cp - cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ cppv = cp + 1;
+ }
+ else break;
+ }
+ if (cppv != cats.size())
+ {
+ displayName = cats.substr(cppv, cats.size() - cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ }
+}
+
+
+SmileyLookup::SmileyLookup(const bkstring& str, const bool regexs, const int ind, const bkstring& smpt)
+{
+ TCHAR msgtxt[1024];
+
+ m_ind = ind;
+ if (regexs)
+ {
+ static const bkstring testString(_T("Test String"));
+ m_pattern = _TPattern::compile(str);
+ m_valid = m_pattern != NULL;
+ if (m_valid)
+ {
+ _TMatcher* matcher = m_pattern->createTMatcher(testString);
+ m_valid &= (!matcher->findFirstMatch() ||
+ matcher->getStartingIndex() != matcher->getEndingIndex());
+ if (!m_valid)
+ {
+ static const TCHAR errmsg[] = _T("Regular Expression \"%s\" in smiley pack \"%s\" could produce \"empty matches\".");
+ mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), str.c_str(), smpt.c_str());
+ }
+ delete matcher;
+ }
+ else
+ {
+ static const TCHAR errmsg[] = _T("Regular Expression \"%s\" in smiley pack \"%s\" malformed.") ;
+ mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), str.c_str(), smpt.c_str());
+ }
+
+ if (!m_valid) CallService(MS_NETLIB_LOG, (WPARAM) hNetlibUser, (LPARAM)(char*)T2A_SM(msgtxt));
+ }
+ else
+ {
+ m_text = str;
+ m_pattern = NULL;
+ m_valid = !str.empty();
+ }
+}
+
+
+SmileyLookup::~SmileyLookup()
+{
+ if (m_pattern) delete m_pattern;
+}
+
+
+void SmileyLookup::find(const bkstring& str, SmileyLocVecType& smlcur, bool firstOnly) const
+{
+ if (!m_valid) return;
+
+ if (m_text.empty())
+ {
+ _TMatcher* matcher = m_pattern->createTMatcher(str);
+ while( matcher->findNextMatch())
+ {
+ bkstring::size_type st = matcher->getStartingIndex();
+ bkstring::size_type sz = matcher->getEndingIndex() - st;
+ if (sz != 0)
+ {
+ smlcur.insert(new SmileyLocType(st, sz));
+ if (firstOnly && m_ind != -1) return;
+ }
+ }
+ delete matcher;
+ }
+ else
+ {
+ const TCHAR* pos = str.c_str();
+ while( (pos = _tcsstr(pos, m_text.c_str())) != NULL )
+ {
+ smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.size()));
+ pos += m_text.size();
+ if (firstOnly && m_ind != -1) return;
+ }
+ }
+}
diff --git a/plugins/SmileyAdd/src/smileys.h b/plugins/SmileyAdd/src/smileys.h
new file mode 100644
index 0000000000..2b127eb225
--- /dev/null
+++ b/plugins/SmileyAdd/src/smileys.h
@@ -0,0 +1,287 @@
+/*
+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/>.
+*/
+
+#ifndef SMILEYADD_SMILEYS_H_
+#define SMILEYADD_SMILEYS_H_
+
+#include "general.h"
+#include "smltool.h"
+#include "imagecache.h"
+
+#define MS_SMILEYADD_CUSTOMCATMENU "SmileyAdd/CustomCatMenu"
+
+const unsigned HiddenSmiley = 1;
+const unsigned RegExSmiley = 2;
+const unsigned ServiceSmiley = 4;
+const unsigned TextSmiley = 8;
+const unsigned SoundSmiley = 16;
+
+class SmileyType
+{
+protected:
+ unsigned m_flags;
+ int m_index;
+
+ SIZE m_size;
+
+ HICON m_SmileyIcon;
+ ImageBase* m_xepimg;
+
+ bkstring m_filepath;
+
+ void SetFlagsBit(unsigned flag, bool set)
+ { if (set) m_flags |= flag; else m_flags &= ~flag; }
+
+public:
+
+ bkstring m_TriggerText;
+ bkstring m_ToolText;
+ bkstring m_InsertText;
+
+ SmileyType(void);
+ ~SmileyType();
+
+ const bkstring& GetTriggerText(void) const { return m_TriggerText; }
+ const bkstring& GetToolText(void) const { return m_ToolText; }
+ const bkstring& GetInsertText(void) const { return m_InsertText; }
+ const bkstring& GetFilePath(void) const { return m_filepath; }
+
+ bool IsHidden(void) const { return (m_flags & HiddenSmiley) != 0; }
+ bool IsRegEx(void) const { return (m_flags & RegExSmiley) != 0; }
+ bool IsService(void) const { return (m_flags & ServiceSmiley) != 0; }
+ bool IsSound(void) const { return (m_flags & SoundSmiley) != 0; }
+ bool IsText(void) const { return (m_flags & TextSmiley) != 0; }
+
+ bool IsFileFound(void) { return _taccess(m_filepath.c_str(), 0) == 0; }
+ bool IsValid(void) { return m_size.cx != 0; }
+
+ ImageBase* CreateCachedImage(void);
+
+ void GetSize(SIZE& size);
+ int GetStaticFrame(void) const { return m_index; }
+
+ HICON GetIcon(void);
+ HICON GetIconDup(void);
+ HBITMAP GetBitmap(COLORREF bkgClr, int sizeX, int sizeY);
+
+ void CallSmileyService(HANDLE hContact);
+
+ void SetHidden(bool hid) { SetFlagsBit(HiddenSmiley, hid); }
+ void SetRegEx(bool regex) { SetFlagsBit(RegExSmiley, regex); }
+ void SetService(bool service) { SetFlagsBit(ServiceSmiley, service); }
+ void SetSound(bool sound) { SetFlagsBit(SoundSmiley, sound); }
+ void SetText(bool text) { SetFlagsBit(TextSmiley, text); }
+
+ void SetImList(HIMAGELIST hImLst, long i);
+
+ bool LoadFromResource(const bkstring& file, const int index);
+ bool LoadFromImage(IStream* pStream);
+};
+
+
+class SmileyLookup
+{
+private:
+ _TPattern* m_pattern;
+
+ int m_ind;
+ bkstring m_text;
+ bool m_valid;
+
+public:
+ struct SmileyLocType
+ {
+ size_t pos, len;
+ SmileyLocType(size_t p, size_t l) : pos(p), len(l) {}
+ SmileyLocType() {}
+ };
+ typedef SMOBJLIST<SmileyLocType> SmileyLocVecType;
+
+ SmileyLookup() { m_ind = 0; m_valid = false; m_pattern = NULL; };
+ SmileyLookup(const bkstring& str, const bool regexs, const int ind, const bkstring& smpt);
+ ~SmileyLookup();
+
+ void find(const bkstring& str, SmileyLocVecType& smlcur, bool firstOnly) const;
+ int GetIndex(void) const { return m_ind; }
+ bool IsValid(void) const { return m_valid; }
+};
+
+
+class SmileyPackType
+{
+public:
+ typedef SMOBJLIST<SmileyType> SmileyVectorType;
+ typedef SMOBJLIST<SmileyLookup> SmileyLookupType;
+
+ POINT selec, win;
+
+private:
+ bkstring m_Filename; //used as identification
+ bkstring m_Name;
+ bkstring m_Author;
+ bkstring m_Date;
+ bkstring m_Version;
+ bkstring m_ButtonSmiley;
+
+ HIMAGELIST m_hSmList;
+
+ int m_VisibleCount;
+
+ SmileyVectorType m_SmileyList;
+ SmileyLookupType m_SmileyLookup;
+
+ bool errorFound;
+
+ void InsertLookup(SmileyType& sml, bkstring& lk, bool first);
+ void AddTriggersToSmileyLookup(void);
+ void ReplaceAllSpecials(const bkstring& Input, bkstring& Output);
+ bool LoadSmileyFileMSL(bkstring& tbuf, bool onlyInfo, bkstring& modpath);
+ bool LoadSmileyFileXEP(bkstring& tbuf, bool onlyInfo, bkstring& modpath);
+
+public:
+ SmileyPackType();
+ ~SmileyPackType();
+
+ SmileyVectorType& GetSmileyList(void) { return m_SmileyList; }
+ SmileyLookupType* GetSmileyLookup(void) { return &m_SmileyLookup; }
+
+ const bkstring& GetFilename(void) const { return m_Filename; }
+ const bkstring& GetName(void) const { return m_Name; }
+ const bkstring& GetAuthor(void) const { return m_Author; }
+ const bkstring& GetDate(void) const { return m_Date; }
+ const bkstring& GetVersion(void) const { return m_Version; }
+
+ int SmileyCount(void) const { return m_SmileyList.getCount(); }
+ int VisibleSmileyCount(void) const { return m_VisibleCount; }
+
+ SmileyType* GetSmiley(unsigned index);
+
+ const TCHAR* GetButtonSmiley(void) const { return m_ButtonSmiley.c_str(); }
+
+ bool LoadSmileyFile(const bkstring& filename, bool onlyInfo, bool noerr = false);
+
+ void Clear(void);
+};
+
+
+class SmileyPackListType
+{
+public:
+ typedef SMOBJLIST<SmileyPackType> SmileyPackVectorType;
+
+private:
+ SmileyPackVectorType m_SmileyPacks;
+
+public:
+ int NumberOfSmileyPacks(void) { return m_SmileyPacks.getCount(); }
+
+ bool AddSmileyPack(bkstring& filename);
+ void ClearAndFreeAll(void);
+ SmileyPackType* GetSmileyPack(bkstring& filename);
+};
+
+
+typedef enum
+{
+ smcNone,
+ smcStd,
+ smcProto,
+ smcCustom,
+ smcExt
+} SmcType;
+
+
+class SmileyCategoryType
+{
+private:
+ bkstring m_Name;
+ bkstring m_DisplayName;
+ bkstring m_Filename; //functions as identification
+
+ SmcType type;
+
+ SmileyPackListType* m_pSmileyPackStore;
+
+public:
+ SmileyCategoryType() { type = smcNone; m_pSmileyPackStore = NULL; };
+ SmileyCategoryType(SmileyPackListType* pSPS, const bkstring& name,
+ const bkstring& displayName, const bkstring& defaultFilename, SmcType typ);
+
+ const bkstring& GetDisplayName(void) const { return m_DisplayName; }
+ const bkstring& GetName(void) const { return m_Name; }
+ const bkstring& GetFilename(void) const { return m_Filename; }
+
+ bool IsCustom(void) { return type == smcCustom; }
+ bool IsProto(void) { return type == smcProto; }
+ bool IsExt(void) { return type == smcExt; }
+
+ SmcType GetType(void) { return type; }
+
+ SmileyPackType* GetSmileyPack(void);
+
+ void SetFilename(bkstring& name) { m_Filename = name; }
+ void SetDisplayName(bkstring& name) { m_DisplayName = name; }
+
+ void ClearFilename(void) { m_Filename.clear(); }
+ void SaveSettings(void);
+
+ void Load(void);
+};
+
+
+class SmileyCategoryListType
+{
+public:
+ typedef SMOBJLIST<SmileyCategoryType> SmileyCategoryVectorType;
+
+private:
+ SmileyCategoryVectorType m_SmileyCategories;
+ SmileyPackListType* m_pSmileyPackStore;
+
+public:
+ void SetSmileyPackStore(SmileyPackListType* pSPS) { m_pSmileyPackStore = pSPS; }
+
+ SmileyCategoryType* GetSmileyCategory(const bkstring& name);
+ SmileyCategoryType* GetSmileyCategory(unsigned index);
+ SmileyPackType* GetSmileyPack(bkstring& name);
+ SmileyCategoryVectorType* GetSmileyCategoryList(void) { return &m_SmileyCategories; };
+
+ int NumberOfSmileyCategories(void) { return m_SmileyCategories.getCount(); }
+
+ void AddCategory(const bkstring& name, const bkstring& displayName, SmcType typ,
+ const bkstring& defaultFilename = bkstring(_T("Smileys\\nova\\default.msl")));
+ void AddAndLoad(const bkstring& name, const bkstring& displayName);
+ void AddAllProtocolsAsCategory(void);
+ void AddAccountAsCategory(PROTOACCOUNT *acc, const bkstring& defaultFile);
+ void AddContactTransportAsCategory(HANDLE hContact, const bkstring& defaultFile);
+
+ void ClearAndLoadAll(void);
+ void ClearAll(void)
+ { m_pSmileyPackStore->ClearAndFreeAll(); m_SmileyCategories.destroy(); }
+
+ bool DeleteCustomCategory(int index);
+ void DeleteAccountAsCategory(PROTOACCOUNT *acc);
+
+ void SaveSettings(void);
+};
+
+extern SmileyPackListType g_SmileyPacks;
+extern SmileyCategoryListType g_SmileyCategories;
+
+#endif //SMILEYADD_SMILEYS_H_
diff --git a/plugins/SmileyAdd/src/smltool.cpp b/plugins/SmileyAdd/src/smltool.cpp
new file mode 100644
index 0000000000..31695a516a
--- /dev/null
+++ b/plugins/SmileyAdd/src/smltool.cpp
@@ -0,0 +1,785 @@
+/*
+Miranda SmileyAdd Plugin
+Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
+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 "options.h"
+#include "smileys.h"
+#include "smltool.h"
+#include "anim.h"
+
+#define SB_MYMOVE 20
+
+//
+// SmileyToolwindowType
+//
+class SmileyToolWindowType
+{
+private:
+ unsigned m_NumberOfVerticalButtons;
+ unsigned m_NumberOfHorizontalButtons;
+ SIZE m_BitmapWidth;
+ SIZE m_ButtonSize;
+ unsigned m_ButtonSpace;
+ unsigned m_NumberOfButtons;
+ int m_WindowSizeY;
+
+ HWND m_hwndDialog;
+ HWND m_hToolTip;
+ HWND m_hWndTarget;
+ SmileyPackType* m_pSmileyPack;
+ int m_CurrentHotTrack;
+ int m_XPosition;
+ int m_YPosition;
+ int m_Direction;
+ UINT m_TargetMessage;
+ WPARAM m_TargetWParam;
+ HANDLE m_hContact;
+ int rowSel;
+ bool m_Choosing;
+
+ AnimatedPack* m_AniPack;
+
+ void InitDialog(LPARAM lParam);
+ void PaintWindow(void);
+ void InsertSmiley(void);
+ void MouseMove(int x, int y);
+ void KeyUp(WPARAM wParam, LPARAM lParam);
+ void SmileySel(int but);
+ void ScrollV(int action, int dist = 0);
+
+ int GetRowSize(void) const { return m_ButtonSize.cy + m_ButtonSpace; }
+
+ void CreateSmileyBitmap(HDC hdc);
+ void CreateSmileyWinDim(void);
+ RECT CalculateButtonToCoordinates(int buttonPosition, int scroll);
+ int CalculateCoordinatesToButton(POINT pt, int scroll);
+
+public:
+ SmileyToolWindowType(HWND hWnd);
+ LRESULT DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam);
+};
+
+
+LRESULT CALLBACK DlgProcSmileyToolWindow(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ SmileyToolWindowType* pOD;
+ LRESULT Result;
+
+ pOD = (SmileyToolWindowType*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (pOD == NULL) {
+ pOD = new SmileyToolWindowType(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pOD);
+ }
+
+ Result = pOD->DialogProcedure(msg, wParam, lParam);
+ // SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
+
+ if (msg == WM_NCDESTROY)
+ {
+ delete pOD;
+ Result = FALSE;
+ }
+
+ return Result;
+}
+
+
+SmileyToolWindowType::SmileyToolWindowType(HWND hWnd)
+{
+ m_hwndDialog = hWnd;
+ rowSel = -1;
+ m_AniPack = NULL;
+
+ m_NumberOfVerticalButtons = 0;
+ m_NumberOfHorizontalButtons = 0;
+ m_BitmapWidth.cx = 0;
+ m_BitmapWidth.cy = 0;
+ m_ButtonSize.cx = 0;
+ m_ButtonSize.cy = 0;
+ m_ButtonSpace = 1;
+ m_NumberOfButtons = 0;
+}
+
+
+LRESULT SmileyToolWindowType::DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT Result = FALSE;
+
+ switch (msg)
+ {
+ case WM_ACTIVATE:
+ if (wParam == WA_INACTIVE)
+ DestroyWindow(m_hwndDialog);
+ break;
+
+ case WM_PAINT:
+ PaintWindow();
+ break;
+
+ case WM_TIMER:
+ if (m_AniPack) m_AniPack->ProcessTimerTick(m_hwndDialog);
+ break;
+
+ case WM_DESTROY:
+ KillTimer(m_hwndDialog, 1);
+ if (m_AniPack) delete m_AniPack;
+ m_AniPack = NULL;
+ DestroyWindow(m_hToolTip);
+ PostQuitMessage(0);
+ if (m_Choosing)
+ SetFocus(m_hWndTarget);
+ break;
+
+ case WM_KEYUP:
+ KeyUp(wParam, lParam);
+ break;
+
+ case WM_CREATE:
+ InitDialog(lParam);
+ break;
+
+ case WM_VSCROLL:
+ ScrollV(LOWORD(wParam));
+ break;
+
+ case WM_MOUSEMOVE:
+ MouseMove(LOWORD(lParam), HIWORD(lParam));
+ break;
+
+ case WM_LBUTTONUP:
+ InsertSmiley();
+ break;
+
+ case WM_MOUSEWHEEL:
+ ScrollV(SB_MYMOVE, ((short)HIWORD(wParam))/-120);
+ MouseMove(LOWORD(lParam), HIWORD(lParam));
+ break;
+
+ default:
+ Result = DefWindowProc(m_hwndDialog, msg, wParam, lParam);
+ break;
+ }
+
+ return Result;
+}
+
+struct smlsrvstruct
+{
+ smlsrvstruct(SmileyType *tsml, HANDLE thContact)
+ : sml(tsml), hContact(thContact) {}
+ SmileyType *sml;
+ HANDLE hContact;
+};
+
+void CALLBACK smileyServiceCallback(void* arg)
+{
+ smlsrvstruct* p = (smlsrvstruct*)arg;
+ p->sml->CallSmileyService(p->hContact);
+ delete p;
+}
+
+void SmileyToolWindowType::InsertSmiley(void)
+{
+ if (m_CurrentHotTrack >= 0 && m_hWndTarget != NULL)
+ {
+ SmileyType *sml = m_pSmileyPack->GetSmiley(m_CurrentHotTrack);
+
+ if (sml->IsService())
+ {
+ smlsrvstruct* p = new smlsrvstruct(sml, m_hContact);
+ CallFunctionAsync(smileyServiceCallback, p);
+ }
+ else
+ {
+ bkstring insertText;
+
+ if (opt.SurroundSmileyWithSpaces) insertText = ' ';
+ insertText += sml->GetInsertText();
+ if (opt.SurroundSmileyWithSpaces) insertText += ' ';
+
+ SendMessage(m_hWndTarget, m_TargetMessage, m_TargetWParam, (LPARAM) insertText.c_str());
+ }
+ m_Choosing = true;
+ DestroyWindow(m_hwndDialog);
+ }
+ else if (m_hWndTarget == NULL)
+ DestroyWindow(m_hwndDialog);
+}
+
+void SmileyToolWindowType::SmileySel(int but)
+{
+ if (but != m_CurrentHotTrack)
+ {
+ SCROLLINFO si;
+ si.cbSize = sizeof (si);
+ si.fMask = SIF_POS;
+ si.nPos = 0;
+ GetScrollInfo (m_hwndDialog, SB_VERT, &si);
+
+ HDC hdc = GetDC(m_hwndDialog);
+ if (m_CurrentHotTrack >= 0)
+ {
+ RECT rect = CalculateButtonToCoordinates(m_CurrentHotTrack, si.nPos);
+ DrawFocusRect(hdc, &rect);
+ m_CurrentHotTrack = -1;
+ SendMessage(m_hToolTip, TTM_ACTIVATE, FALSE, 0);
+ }
+ m_CurrentHotTrack = but;
+ if (m_CurrentHotTrack >= 0)
+ {
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.hwnd = m_hwndDialog;
+ ti.uId = (UINT_PTR)m_hwndDialog;
+
+ const bkstring& toolText = m_pSmileyPack->GetSmiley(m_CurrentHotTrack)->GetToolText();
+ ti.lpszText = const_cast<TCHAR*>(toolText.c_str());
+ SendMessage(m_hToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+ SendMessage(m_hToolTip, TTM_ACTIVATE, TRUE, 0);
+
+ RECT rect = CalculateButtonToCoordinates(m_CurrentHotTrack, si.nPos);
+ DrawFocusRect(hdc, &rect);
+ if (m_AniPack) m_AniPack->SetSel(rect);
+ }
+ ReleaseDC(m_hwndDialog, hdc);
+ }
+}
+
+
+void SmileyToolWindowType::ScrollV(int action, int dist)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof (si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo (m_hwndDialog, SB_VERT, &si);
+
+ // Save the position for comparison later on
+ int yPos = si.nPos;
+ switch (action)
+ {
+ // user clicked the HOME keyboard key
+ case SB_TOP:
+ si.nPos = si.nMin;
+ break;
+
+ // user clicked the END keyboard key
+ case SB_BOTTOM:
+ si.nPos = si.nMax;
+ break;
+
+ // user clicked the top arrow
+ case SB_LINEUP:
+ si.nPos -= 1;
+ break;
+
+ // user clicked the bottom arrow
+ case SB_LINEDOWN:
+ si.nPos += 1;
+ break;
+
+ // user clicked the scroll bar shaft above the scroll box
+ case SB_PAGEUP:
+ si.nPos -= si.nPage;
+ break;
+
+ // user clicked the scroll bar shaft below the scroll box
+ case SB_PAGEDOWN:
+ si.nPos += si.nPage;
+ break;
+
+ // user dragged the scroll box
+ case SB_THUMBTRACK:
+ si.nPos = si.nTrackPos;
+ break;
+
+ // user dragged the scroll box
+ case SB_MYMOVE:
+ si.nPos += dist;
+ break;
+ }
+ // Set the position and then retrieve it. Due to adjustments
+ // by Windows it may not be the same as the value set.
+ si.fMask = SIF_POS;
+ SetScrollInfo (m_hwndDialog, SB_VERT, &si, TRUE);
+ GetScrollInfo (m_hwndDialog, SB_VERT, &si);
+ // If the position has changed, scroll window and update it
+ if (si.nPos != yPos)
+ {
+ if (m_AniPack) m_AniPack->SetOffset(si.nPos*GetRowSize());
+
+ ScrollWindowEx(m_hwndDialog, 0, (yPos - si.nPos) * GetRowSize(),
+ NULL, NULL, NULL, NULL, SW_INVALIDATE);
+
+ UpdateWindow (m_hwndDialog);
+ }
+}
+
+
+void SmileyToolWindowType::MouseMove(int xposition, int yposition)
+{
+ if (m_CurrentHotTrack == -2) return; //prevent focussing when not drawn yet!
+ // SetFocus(m_hwndDialog);
+
+
+ SCROLLINFO si;
+ si.cbSize = sizeof (si);
+ si.fMask = SIF_POS;
+ si.nPos = 0;
+ GetScrollInfo (m_hwndDialog, SB_VERT, &si);
+
+ POINT pt = { xposition, yposition };
+ int but = CalculateCoordinatesToButton(pt, si.nPos);
+ SmileySel(but);
+}
+
+
+
+void SmileyToolWindowType::KeyUp(WPARAM wParam, LPARAM lParam)
+{
+ int colSel = -1, numKey = -1;
+ int but = m_CurrentHotTrack;
+
+ switch(wParam)
+ {
+ case VK_END:
+ but = m_NumberOfButtons-1;
+ break;
+
+ case VK_HOME:
+ but = 0;
+ break;
+
+ case VK_LEFT:
+ but -= (opt.IEViewStyle ? 1 : m_NumberOfVerticalButtons) * LOWORD(lParam);
+ break;
+
+ case VK_UP:
+ but -= (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * LOWORD(lParam);
+ break;
+
+ case VK_RIGHT:
+ but += (opt.IEViewStyle ? 1 : m_NumberOfVerticalButtons) * LOWORD(lParam);
+ break;
+
+ case VK_DOWN:
+ but += (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * LOWORD(lParam);
+ break;
+
+ case VK_SPACE:
+ case VK_RETURN:
+ if (but != -1) InsertSmiley();
+ return;
+
+ case VK_ESCAPE:
+ DestroyWindow(m_hwndDialog);
+ return;
+
+ case VK_NUMPAD1:
+ case VK_NUMPAD2:
+ case VK_NUMPAD3:
+ case VK_NUMPAD4:
+ case VK_NUMPAD5:
+ case VK_NUMPAD6:
+ case VK_NUMPAD7:
+ case VK_NUMPAD8:
+ case VK_NUMPAD9:
+ if ((GetKeyState(VK_NUMLOCK) & 1) != 0)
+ numKey = (int)wParam - VK_NUMPAD1;
+ else
+ {
+ rowSel = -1;
+ return;
+ }
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ numKey = (int)wParam - '1';
+ break;
+
+ default:
+ rowSel = -1;
+ return;
+ }
+
+ if (numKey != -1)
+ {
+ if (rowSel == -1)
+ {
+ rowSel = numKey;
+ but = (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * rowSel;
+ }
+ else
+ {
+ colSel = numKey;
+ if (opt.IEViewStyle)
+ but = colSel + m_NumberOfHorizontalButtons * rowSel;
+ else
+ but = rowSel + m_NumberOfVerticalButtons * colSel;
+ }
+ }
+
+ if (but < 0) but = 0;
+ if (but >= (int)m_NumberOfButtons) but = m_NumberOfButtons-1;
+
+ SmileySel(but);
+ if (colSel != -1) InsertSmiley();
+}
+
+
+void SmileyToolWindowType::InitDialog(LPARAM lParam)
+{
+ LPCREATESTRUCT createStruct = (LPCREATESTRUCT)lParam;
+ SmileyToolWindowParam* stwp = (SmileyToolWindowParam*) createStruct->lpCreateParams;
+
+ m_pSmileyPack = stwp->pSmileyPack;
+ m_XPosition = stwp->xPosition;
+ m_YPosition = stwp->yPosition;
+ m_hWndTarget = stwp->hWndTarget;
+ m_TargetMessage = stwp->targetMessage;
+ m_TargetWParam = stwp->targetWParam;
+ m_Direction = stwp->direction;
+ m_hContact = stwp->hContact;
+
+ m_CurrentHotTrack = -2;
+ m_Choosing = false;
+
+ CreateSmileyWinDim();
+
+ int width = m_BitmapWidth.cx;
+ int height = m_BitmapWidth.cy;
+
+ const int colsz = GetRowSize();
+ const int heightn = m_WindowSizeY;
+
+ SCROLLINFO si;
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
+ si.nMin = 0;
+ si.nMax = height / colsz - 1;
+ si.nPage = heightn / colsz;
+ si.nPos = 0;
+ SetScrollInfo(m_hwndDialog, SB_VERT, &si, TRUE);
+
+ if (GetWindowLongPtr(m_hwndDialog, GWL_STYLE) & WS_VSCROLL)
+ width += GetSystemMetrics(SM_CXVSCROLL);
+
+ RECT rc = { 0, 0, width, heightn };
+ AdjustWindowRectEx(&rc, GetWindowLongPtr(m_hwndDialog, GWL_STYLE),
+ FALSE, GetWindowLongPtr(m_hwndDialog, GWL_EXSTYLE));
+
+ width = rc.right - rc.left;
+ height = rc.bottom - rc.top;
+
+ switch (m_Direction)
+ {
+ case 1:
+ m_XPosition-=width;
+ break;
+ case 2:
+ m_XPosition-=width;
+ m_YPosition-=height;
+ break;
+ case 3:
+ m_YPosition-=height;
+ break;
+ }
+
+ // Get screen dimentions
+ int xoScreen = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ int yoScreen = GetSystemMetrics(SM_YVIRTUALSCREEN);
+
+ int xScreen = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ int yScreen = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+
+ if (xScreen == 0) xScreen = GetSystemMetrics(SM_CXSCREEN);
+ if (yScreen == 0) yScreen = GetSystemMetrics(SM_CYSCREEN);
+
+ xScreen += xoScreen;
+ yScreen += yoScreen;
+
+ // Prevent window from opening off-screen
+ if (m_YPosition + height > yScreen) m_YPosition = yScreen - height;
+ if (m_XPosition + width > xScreen) m_XPosition = xScreen - width;
+ if (m_YPosition < yoScreen) m_YPosition = yoScreen;
+ if (m_XPosition < xoScreen) m_XPosition = xoScreen;
+
+ // Move window to desired location
+ SetWindowPos(m_hwndDialog, NULL, m_XPosition, m_YPosition,
+ width, height, SWP_NOZORDER);
+
+ m_AniPack = new AnimatedPack(m_hwndDialog, height, m_ButtonSize, opt.SelWndBkgClr);
+
+ SmileyPackType::SmileyVectorType &sml = m_pSmileyPack->GetSmileyList();
+ for (unsigned i=0; i<m_NumberOfButtons; i++)
+ {
+ if (!sml[i].IsHidden())
+ {
+ m_AniPack->Add(&sml[i], CalculateButtonToCoordinates(i, 0), opt.IEViewStyle);
+ }
+ }
+ m_AniPack->SetOffset(0);
+
+ if (opt.AnimateSel) SetTimer(m_hwndDialog, 1, 100, NULL);
+
+ //add tooltips
+ m_hToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, _T(""),
+ TTS_NOPREFIX | WS_POPUP, 0, 0, 0, 0, m_hwndDialog, NULL, g_hInst, NULL);
+ TOOLINFO ti = {0};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.hwnd = m_hwndDialog;
+ ti.uId = (UINT_PTR)m_hwndDialog;
+ ti.lpszText = TranslateT("d'Oh!");
+ SendMessage(m_hToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+}
+
+
+void SmileyToolWindowType::PaintWindow(void)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = 0;
+ GetScrollInfo(m_hwndDialog, SB_VERT, &si);
+
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(m_hwndDialog, &ps);
+
+ HBITMAP hBmp = CreateCompatibleBitmap(hdc, m_BitmapWidth.cx, m_BitmapWidth.cy);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HANDLE hOld = SelectObject(hdcMem, hBmp);
+
+ CreateSmileyBitmap(hdcMem);
+
+ if (m_AniPack) m_AniPack->Draw(hdcMem);
+
+ BitBlt(hdc, 0, 0, m_BitmapWidth.cx, m_WindowSizeY, hdcMem, 0, 0, SRCCOPY);
+
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hBmp);
+ DeleteDC(hdcMem);
+
+ if (m_CurrentHotTrack == -2) m_CurrentHotTrack = -1;
+
+ EndPaint(m_hwndDialog, &ps);
+}
+
+
+void SmileyToolWindowType::CreateSmileyWinDim(void)
+{
+ m_NumberOfButtons = m_pSmileyPack->VisibleSmileyCount();
+
+ if (m_NumberOfButtons == 0) return;
+
+ // Find largest smiley
+ if (m_pSmileyPack->selec.x == 0 || m_pSmileyPack->selec.y == 0)
+ {
+ if (opt.ScaleAllSmileys)
+ {
+ m_pSmileyPack->GetSmiley(0)->GetSize(m_ButtonSize);
+ ++m_ButtonSize.cx; ++m_ButtonSize.cy;
+ }
+ else
+ {
+ m_ButtonSize.cx = 0;
+ m_ButtonSize.cy = 0;
+ SmileyPackType::SmileyVectorType &sml = m_pSmileyPack->GetSmileyList();
+ for (unsigned i=0; i<m_NumberOfButtons; i++)
+ {
+ SIZE smsz;
+ sml[i].GetSize(smsz);
+
+ if (m_ButtonSize.cx < smsz.cx) m_ButtonSize.cx = smsz.cx;
+ if (m_ButtonSize.cy < smsz.cy) m_ButtonSize.cy = smsz.cy;
+ }
+ }
+ }
+ else
+ {
+ m_ButtonSize = *(SIZE*)&m_pSmileyPack->selec;
+ }
+
+ if (m_pSmileyPack->win.x == 0 || m_pSmileyPack->win.y == 0)
+ {
+ if (opt.IEViewStyle)
+ {
+ // All integer square root
+ unsigned i;
+ for (i=1; i*i<m_NumberOfButtons; i++) ;
+ m_NumberOfHorizontalButtons = min(i, 350 / (m_ButtonSize.cx + m_ButtonSpace));
+
+ m_NumberOfVerticalButtons = m_NumberOfButtons / m_NumberOfHorizontalButtons +
+ (m_NumberOfButtons % m_NumberOfHorizontalButtons != 0);
+ }
+ else
+ {
+ const int nh = min(10u, GetSystemMetrics(SM_CXSCREEN) / ((m_ButtonSize.cx + m_ButtonSpace) * 2));
+
+ m_NumberOfVerticalButtons = m_NumberOfButtons / nh + (m_NumberOfButtons % nh != 0);
+ if (m_NumberOfVerticalButtons < 5) m_NumberOfVerticalButtons = 5;
+
+ m_NumberOfHorizontalButtons = m_NumberOfButtons / m_NumberOfVerticalButtons +
+ (m_NumberOfButtons % m_NumberOfVerticalButtons != 0);
+ }
+ }
+ else
+ {
+ m_NumberOfHorizontalButtons = m_pSmileyPack->win.x;
+ m_NumberOfVerticalButtons = m_NumberOfButtons / m_NumberOfHorizontalButtons +
+ (m_NumberOfButtons % m_NumberOfHorizontalButtons != 0);
+ }
+
+ m_BitmapWidth.cx = m_NumberOfHorizontalButtons * (m_ButtonSize.cx + m_ButtonSpace) + m_ButtonSpace;
+ m_BitmapWidth.cy = m_NumberOfVerticalButtons * (m_ButtonSize.cy + m_ButtonSpace) + m_ButtonSpace;
+
+ const int colsz = m_ButtonSize.cy + m_ButtonSpace;
+ int wndsz = min((int)m_BitmapWidth.cy, GetSystemMetrics(SM_CYSCREEN) / 2);
+ if (opt.IEViewStyle) wndsz = min(wndsz, 250);
+
+ if (m_pSmileyPack->win.x != 0 && m_pSmileyPack->win.y != 0)
+ wndsz = min(wndsz, m_pSmileyPack->win.y * (m_ButtonSize.cy + (int)m_ButtonSpace) + (int)m_ButtonSpace);
+
+ m_WindowSizeY = wndsz - (wndsz % colsz) + m_ButtonSpace;
+}
+
+
+void SmileyToolWindowType::CreateSmileyBitmap(HDC hdc)
+{
+ const RECT rc = { 0, 0, m_BitmapWidth.cx, m_WindowSizeY };
+
+ SetBkColor(hdc, opt.SelWndBkgClr);
+ const HBRUSH hBkgBrush = CreateSolidBrush(opt.SelWndBkgClr);
+ FillRect(hdc, &rc, hBkgBrush);
+ DeleteObject(hBkgBrush);
+
+ if (opt.IEViewStyle)
+ {
+ HPEN hpen = CreatePen(PS_DOT, 1, 0);
+ HGDIOBJ hOldPen = SelectObject(hdc, hpen);
+
+ POINT pts[2] = { {0, 0}, {m_BitmapWidth.cx, 0} };
+
+ for (unsigned i=0; i<=m_NumberOfVerticalButtons; i++)
+ {
+ pts[0].y = pts[1].y = i * (m_ButtonSize.cy + m_ButtonSpace);
+ if (pts[0].y > m_WindowSizeY) break;
+ Polyline(hdc, pts, 2);
+ }
+
+ pts[0].y = 0; pts[1].y = m_BitmapWidth.cy;
+ for (unsigned j=0; j<=m_NumberOfHorizontalButtons; j++)
+ {
+ pts[0].x = pts[1].x = j * (m_ButtonSize.cx + m_ButtonSpace);
+ Polyline(hdc, pts, 2);
+ }
+
+ SelectObject(hdc, hOldPen);
+ DeleteObject(hpen);
+ }
+}
+
+
+RECT SmileyToolWindowType::CalculateButtonToCoordinates(int buttonPosition, int scroll)
+{
+ int row, rowpos;
+
+ if (opt.IEViewStyle)
+ {
+ row = buttonPosition / m_NumberOfHorizontalButtons;
+ rowpos = buttonPosition % m_NumberOfHorizontalButtons;
+ }
+ else
+ {
+ row = buttonPosition % m_NumberOfVerticalButtons;
+ rowpos = buttonPosition / m_NumberOfVerticalButtons;
+ }
+
+ RECT pt;
+ pt.left = rowpos * (m_ButtonSize.cx + m_ButtonSpace) + m_ButtonSpace;
+ pt.top = (row - scroll) * (m_ButtonSize.cy + m_ButtonSpace) + m_ButtonSpace;
+ pt.right = pt.left + m_ButtonSize.cx;
+ pt.bottom = pt.top + m_ButtonSize.cy;
+
+ return pt;
+}
+
+
+int SmileyToolWindowType::CalculateCoordinatesToButton(POINT pt, int scroll)
+{
+ const int rowpos = (pt.x - m_ButtonSpace) / (m_ButtonSize.cx + m_ButtonSpace);
+ const int row = (pt.y - m_ButtonSpace) / (m_ButtonSize.cy + m_ButtonSpace) + scroll;
+
+ int pos;
+ if (opt.IEViewStyle)
+ pos = m_NumberOfHorizontalButtons * row + rowpos;
+ else
+ pos = m_NumberOfVerticalButtons * rowpos + row;
+
+ if (pos >= (int)m_NumberOfButtons) pos = -1;
+
+ return pos;
+}
+
+void __cdecl SmileyToolThread(void *arg)
+{
+ SmileyToolWindowParam* stwp = (SmileyToolWindowParam*)arg;
+ if (stwp->pSmileyPack && stwp->pSmileyPack->VisibleSmileyCount())
+ {
+ WNDCLASSEX wndclass;
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.style = CS_SAVEBITS;
+ wndclass.lpfnWndProc = DlgProcSmileyToolWindow;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 4;
+ wndclass.hInstance = g_hInst;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = CreateSolidBrush(opt.SelWndBkgClr);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = _T("SmileyTool");
+ wndclass.hIconSm = NULL;
+ RegisterClassEx(&wndclass);
+
+ CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOPARENTNOTIFY, _T("SmileyTool"), NULL,
+ WS_BORDER | WS_POPUP | WS_VISIBLE,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ ((SmileyToolWindowParam*)arg)->hWndParent, NULL, g_hInst, arg);
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ UnregisterClass(_T("SmileyTool"), g_hInst);
+ }
+ delete stwp;
+}
diff --git a/plugins/SmileyAdd/src/smltool.h b/plugins/SmileyAdd/src/smltool.h
new file mode 100644
index 0000000000..89fb786862
--- /dev/null
+++ b/plugins/SmileyAdd/src/smltool.h
@@ -0,0 +1,45 @@
+/*
+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/>.
+*/
+
+#ifndef SMILEYADD_SMLTOOL_H_
+#define SMILEYADD_SMLTOOL_H_
+
+class SmileyPackType;
+
+struct SmileyToolWindowParam
+{
+ SmileyPackType* pSmileyPack;
+ int xPosition;
+ int yPosition;
+ int direction;
+ HWND hWndTarget;
+ HWND hWndParent;
+ UINT targetMessage;
+ WPARAM targetWParam;
+ HANDLE hContact;
+};
+
+
+void __cdecl SmileyToolThread(void *arg);
+
+#ifndef min
+#define min(A, B) ((A) < (B) ? (A) : (B))
+#endif
+
+#endif // SMILEYADD_SMLTOOL_H_
diff --git a/plugins/SmileyAdd/src/version.h b/plugins/SmileyAdd/src/version.h
new file mode 100644
index 0000000000..104839a3a2
--- /dev/null
+++ b/plugins/SmileyAdd/src/version.h
@@ -0,0 +1,3 @@
+#define __FILEVERSION_STRING 0,2,3,17
+#define __VERSION_STRING "0.2.3.17"
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 2, 3, 17)