summaryrefslogtreecommitdiff
path: root/plugins/SmileyAdd
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-05-18 22:02:50 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-05-18 22:02:50 +0000
commitf920ef497f3299ae24fe783ce03bdd93b419f764 (patch)
treebdaa9197c08d29ab141a6adfdd6cc0a68ddd3996 /plugins/SmileyAdd
parent6c3b0571f0678da0512069869afaa284c054377e (diff)
plugins folders renaming
git-svn-id: http://svn.miranda-ng.org/main/trunk@60 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/SmileyAdd')
-rw-r--r--plugins/SmileyAdd/AniSmileyObject.cpp450
-rw-r--r--plugins/SmileyAdd/Res/smiley.icobin0 -> 2550 bytes
-rw-r--r--plugins/SmileyAdd/SmileyAdd_10.sln26
-rw-r--r--plugins/SmileyAdd/SmileyAdd_10.vcxproj352
-rw-r--r--plugins/SmileyAdd/SmileyAdd_10.vcxproj.filters148
-rw-r--r--plugins/SmileyAdd/SmileyBase.cpp391
-rw-r--r--plugins/SmileyAdd/SmileyBase.h127
-rw-r--r--plugins/SmileyAdd/anim.cpp184
-rw-r--r--plugins/SmileyAdd/anim.h87
-rw-r--r--plugins/SmileyAdd/bkstring.cpp215
-rw-r--r--plugins/SmileyAdd/bkstring.h270
-rw-r--r--plugins/SmileyAdd/customsmiley.cpp172
-rw-r--r--plugins/SmileyAdd/customsmiley.h85
-rw-r--r--plugins/SmileyAdd/dlgboxsubclass.cpp568
-rw-r--r--plugins/SmileyAdd/docs/smileyadd_msl_specification.txt142
-rw-r--r--plugins/SmileyAdd/docs/smileyadd_readme.txt1136
-rw-r--r--plugins/SmileyAdd/docs/smileyadd_screenshot.jpgbin0 -> 32442 bytes
-rw-r--r--plugins/SmileyAdd/docs/smileyadd_translation.txt125
-rw-r--r--plugins/SmileyAdd/download.cpp292
-rw-r--r--plugins/SmileyAdd/download.h28
-rw-r--r--plugins/SmileyAdd/general.cpp315
-rw-r--r--plugins/SmileyAdd/general.h252
-rw-r--r--plugins/SmileyAdd/imagecache.cpp777
-rw-r--r--plugins/SmileyAdd/imagecache.h166
-rw-r--r--plugins/SmileyAdd/main.cpp261
-rw-r--r--plugins/SmileyAdd/mingw-comp.bat3
-rw-r--r--plugins/SmileyAdd/options.cpp729
-rw-r--r--plugins/SmileyAdd/options.h60
-rw-r--r--plugins/SmileyAdd/regexp/Matcher.cpp178
-rw-r--r--plugins/SmileyAdd/regexp/Matcher.h248
-rw-r--r--plugins/SmileyAdd/regexp/Pattern.cpp1709
-rw-r--r--plugins/SmileyAdd/regexp/Pattern.h1663
-rw-r--r--plugins/SmileyAdd/regexp/WCMatcher.cpp181
-rw-r--r--plugins/SmileyAdd/regexp/WCMatcher.h234
-rw-r--r--plugins/SmileyAdd/regexp/WCPattern.cpp1747
-rw-r--r--plugins/SmileyAdd/regexp/WCPattern.h1663
-rw-r--r--plugins/SmileyAdd/regexp/test.cpp38
-rw-r--r--plugins/SmileyAdd/resource.h49
-rw-r--r--plugins/SmileyAdd/resource.rc214
-rw-r--r--plugins/SmileyAdd/richcall.cpp554
-rw-r--r--plugins/SmileyAdd/services.cpp597
-rw-r--r--plugins/SmileyAdd/services.h48
-rw-r--r--plugins/SmileyAdd/smileyroutines.cpp654
-rw-r--r--plugins/SmileyAdd/smileyroutines.h49
-rw-r--r--plugins/SmileyAdd/smileys.cpp1152
-rw-r--r--plugins/SmileyAdd/smileys.h287
-rw-r--r--plugins/SmileyAdd/smltool.cpp785
-rw-r--r--plugins/SmileyAdd/smltool.h45
-rw-r--r--plugins/SmileyAdd/version.h3
49 files changed, 19459 insertions, 0 deletions
diff --git a/plugins/SmileyAdd/AniSmileyObject.cpp b/plugins/SmileyAdd/AniSmileyObject.cpp
new file mode 100644
index 0000000000..42a63d3549
--- /dev/null
+++ b/plugins/SmileyAdd/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/Res/smiley.ico b/plugins/SmileyAdd/Res/smiley.ico
new file mode 100644
index 0000000000..b6e4190f7c
--- /dev/null
+++ b/plugins/SmileyAdd/Res/smiley.ico
Binary files differ
diff --git a/plugins/SmileyAdd/SmileyAdd_10.sln b/plugins/SmileyAdd/SmileyAdd_10.sln
new file mode 100644
index 0000000000..1010540c7f
--- /dev/null
+++ b/plugins/SmileyAdd/SmileyAdd_10.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SmileyAdd", "SmileyAdd_10.vcxproj", "{5BBA7E51-B494-4462-BC4F-1AE5F57F9857}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Debug|Win32.Build.0 = Debug|Win32
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Debug|x64.ActiveCfg = Debug|x64
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Debug|x64.Build.0 = Debug|x64
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Release|Win32.ActiveCfg = Release|Win32
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Release|Win32.Build.0 = Release|Win32
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Release|x64.ActiveCfg = Release|x64
+ {5BBA7E51-B494-4462-BC4F-1AE5F57F9857}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/plugins/SmileyAdd/SmileyAdd_10.vcxproj b/plugins/SmileyAdd/SmileyAdd_10.vcxproj
new file mode 100644
index 0000000000..4828e65473
--- /dev/null
+++ b/plugins/SmileyAdd/SmileyAdd_10.vcxproj
@@ -0,0 +1,352 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>SmileyAdd</ProjectName>
+ <ProjectGuid>{5BBA7E51-B494-4462-BC4F-1AE5F57F9857}</ProjectGuid>
+ <RootNamespace>SmileyAdd</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <UseOfAtl>false</UseOfAtl>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</IgnoreImportLibrary>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)W</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)W</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)W</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)W</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/help.tlb</TypeLibraryName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;regexp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_WIN32_WINDOWS=0x0410;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <WarningLevel>Level4</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;Delayimp.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <Version>0.2.3.16</Version>
+ <DelayLoadDLLs>gdiplus.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress>0x28110000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <SupportUnloadOfDelayLoadedDLL>true</SupportUnloadOfDelayLoadedDLL>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake />
+ <Bscmake>
+ <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Debug/help.tlb</TypeLibraryName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;regexp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <WarningLevel>Level4</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;Delayimp.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <Version>0.2.3.16</Version>
+ <DelayLoadDLLs>gdiplus.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x28110000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <SupportUnloadOfDelayLoadedDLL>true</SupportUnloadOfDelayLoadedDLL>
+ <TargetMachine>MachineX64</TargetMachine>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake />
+ <Bscmake>
+ <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release/help.tlb</TypeLibraryName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <OmitFramePointers>false</OmitFramePointers>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;regexp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_WIN32_WINDOWS=0x0410;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>
+ </AssemblerOutput>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <CallingConvention>Cdecl</CallingConvention>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;Delayimp.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <Version>0.2.3.16</Version>
+ <DelayLoadDLLs>gdiplus.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <MapExports>false</MapExports>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress>0x28110000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TurnOffAssemblyGeneration>true</TurnOffAssemblyGeneration>
+ <SupportUnloadOfDelayLoadedDLL>true</SupportUnloadOfDelayLoadedDLL>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <MapFileName>
+ </MapFileName>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ <Bscmake />
+ <Bscmake>
+ <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Release/help.tlb</TypeLibraryName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;regexp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_WIN32_WINDOWS=0x0410;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <CallingConvention>Cdecl</CallingConvention>
+ <CompileAs>Default</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;Delayimp.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <Version>0.2.3.16</Version>
+ <DelayLoadDLLs>gdiplus.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress>0x28110000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <SupportUnloadOfDelayLoadedDLL>true</SupportUnloadOfDelayLoadedDLL>
+ <TargetMachine>MachineX64</TargetMachine>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <MapFileName>
+ </MapFileName>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <Bscmake />
+ <Bscmake>
+ <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="anim.cpp" />
+ <ClCompile Include="AniSmileyObject.cpp" />
+ <ClCompile Include="bkstring.cpp" />
+ <ClCompile Include="customsmiley.cpp" />
+ <ClCompile Include="dlgboxsubclass.cpp" />
+ <ClCompile Include="download.cpp" />
+ <ClCompile Include="general.cpp" />
+ <ClCompile Include="imagecache.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="richcall.cpp" />
+ <ClCompile Include="services.cpp" />
+ <ClCompile Include="SmileyBase.cpp" />
+ <ClCompile Include="smileyroutines.cpp" />
+ <ClCompile Include="smileys.cpp" />
+ <ClCompile Include="smltool.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="anim.h" />
+ <ClInclude Include="bkstring.h" />
+ <ClInclude Include="customsmiley.h" />
+ <ClInclude Include="download.h" />
+ <ClInclude Include="general.h" />
+ <ClInclude Include="imagecache.h" />
+ <ClInclude Include="m_folders.h" />
+ <ClInclude Include="m_metacontacts.h" />
+ <ClInclude Include="m_smileyadd.h" />
+ <ClInclude Include="m_smileyadd_deprecated.h" />
+ <ClInclude Include="m_updater.h" />
+ <ClInclude Include="options.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="services.h" />
+ <ClInclude Include="SmileyBase.h" />
+ <ClInclude Include="smileyroutines.h" />
+ <ClInclude Include="smileys.h" />
+ <ClInclude Include="smltool.h" />
+ <ClInclude Include="version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="RES\smiley.ico" />
+ <None Include="docs\smileyadd_msl_specification.txt" />
+ <None Include="docs\smileyadd_readme.txt" />
+ <None Include="docs\smileyadd_translation.txt" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/SmileyAdd/SmileyAdd_10.vcxproj.filters b/plugins/SmileyAdd/SmileyAdd_10.vcxproj.filters
new file mode 100644
index 0000000000..a78673b586
--- /dev/null
+++ b/plugins/SmileyAdd/SmileyAdd_10.vcxproj.filters
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{293a7f44-7469-4a9f-8f6e-5615ff66771a}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{bf1493ea-a6cc-4923-befa-2586a750c0fd}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{f30ee13d-9b85-424d-bdb9-c991748ed97c}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ <Filter Include="Documents">
+ <UniqueIdentifier>{8efee3ee-a59a-4e9b-a0a4-b82a7481a40f}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="anim.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AniSmileyObject.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="bkstring.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="customsmiley.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dlgboxsubclass.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="download.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="general.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="imagecache.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="richcall.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="services.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SmileyBase.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smileyroutines.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smileys.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smltool.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="anim.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="bkstring.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="customsmiley.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="download.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="general.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="imagecache.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_folders.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_metacontacts.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_smileyadd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_smileyadd_deprecated.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_updater.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="options.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="services.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SmileyBase.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smileyroutines.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smileys.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smltool.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="RES\smiley.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="docs\smileyadd_msl_specification.txt">
+ <Filter>Documents</Filter>
+ </None>
+ <None Include="docs\smileyadd_readme.txt">
+ <Filter>Documents</Filter>
+ </None>
+ <None Include="docs\smileyadd_translation.txt">
+ <Filter>Documents</Filter>
+ </None>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/SmileyAdd/SmileyBase.cpp b/plugins/SmileyAdd/SmileyBase.cpp
new file mode 100644
index 0000000000..28bd767153
--- /dev/null
+++ b/plugins/SmileyAdd/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/SmileyBase.h b/plugins/SmileyAdd/SmileyBase.h
new file mode 100644
index 0000000000..3cdf244007
--- /dev/null
+++ b/plugins/SmileyAdd/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/anim.cpp b/plugins/SmileyAdd/anim.cpp
new file mode 100644
index 0000000000..5352383821
--- /dev/null
+++ b/plugins/SmileyAdd/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/anim.h b/plugins/SmileyAdd/anim.h
new file mode 100644
index 0000000000..7d81afa689
--- /dev/null
+++ b/plugins/SmileyAdd/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/bkstring.cpp b/plugins/SmileyAdd/bkstring.cpp
new file mode 100644
index 0000000000..2973783abc
--- /dev/null
+++ b/plugins/SmileyAdd/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/bkstring.h b/plugins/SmileyAdd/bkstring.h
new file mode 100644
index 0000000000..eb164f74e2
--- /dev/null
+++ b/plugins/SmileyAdd/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/customsmiley.cpp b/plugins/SmileyAdd/customsmiley.cpp
new file mode 100644
index 0000000000..f125f015db
--- /dev/null
+++ b/plugins/SmileyAdd/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/customsmiley.h b/plugins/SmileyAdd/customsmiley.h
new file mode 100644
index 0000000000..ce38803c4a
--- /dev/null
+++ b/plugins/SmileyAdd/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/dlgboxsubclass.cpp b/plugins/SmileyAdd/dlgboxsubclass.cpp
new file mode 100644
index 0000000000..8da22c6cb1
--- /dev/null
+++ b/plugins/SmileyAdd/dlgboxsubclass.cpp
@@ -0,0 +1,568 @@
+/*
+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 "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 ((GetWindowLong(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) != 0);
+ doSmileyReplace &= ((cap & PF1_IMRECV) != 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, 0, 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/docs/smileyadd_msl_specification.txt b/plugins/SmileyAdd/docs/smileyadd_msl_specification.txt
new file mode 100644
index 0000000000..55399e3c93
--- /dev/null
+++ b/plugins/SmileyAdd/docs/smileyadd_msl_specification.txt
@@ -0,0 +1,142 @@
+
+MSL (ASL) - Miranda Smiley Library specification
+Used for Smiley definitions
+Authors: Peacow, borkra
+
+This file defines relationships between text smiley codes and images
+Images could be packed into library (dll, icl) or could be standalone
+images of ico, gif, png, tiff, jpeg, bmp formats
+
+To support Unicode definitions for smileys, smiley pack should be stored as Unicode.
+Supported text formats for smiley pack file are: ANSI, UTF-16(Unicode) and UTF-8
+
+The simplest way to do that in Notepad (for instance) select Save As and then choose Unicode encoding
+
+Format:
+
+';' - any line that starts from this character considered comment
+
+Tags you could use in the smiley pack:
+
+"Name" - string, describes name of the smiley pack
+"Author" - string, describes author(s) of the smiley pack
+"Version" - string, describes version string for smiley pack
+"Date" - string, describes creation date for smiley pack
+"Smiley" - describes smiley file and text code(s) correcsponding to it
+ See description below for content
+"Smiley*" - describes hidden smiley (not visible in selector window)
+ Format is the same as for "Smiley". See description below for content
+"ButtonSmiley" - string, describes text of the smiley that should be drawn on the button
+ that opens smiley selection window
+SelectionSize - describes size of the image in the selection window
+ the first parameter is width and the second height.
+"WindowSize" - describes size of the selection window in number of images
+ the first parameter is width and the second height.
+
+Smiley definition guide:
+
+There are 2 styles of smiley definition string:
+
+1) Smileys with plain text replacement
+
+Smiley = "resourcefile", recourceindex, "smiley1 smiley2 ...", "tooltip text"
+
+2) Smileys with regular expression replacement
+
+Smiley = "resourcefile", recourceindex, R"smiley regular expression", "insert text", "tooltip text"
+
+3) Smileys as services
+
+Smiley = "resourcefile", recourceindex, S"service name|parmeter1|parameter2", "tooltip text"
+
+4) Smileys as text (this is pseudo smiley, just text replacement occurs)
+
+Smiley = "", 0, "smiley1 smiley2 ...", "replacement text"
+Smiley = "", 0, R"smiley regular expression", "insert text", "replacement text"
+
+
+Description of fields:
+
+ - resourcefile - path or URL to the file containign the image (it could be absolute or relative)
+
+ - recourceindex - positive value: relative index in the file
+ negative value: resouridentifier.. -100 is resID 100, see ExtractIcon() on the MSDN,
+ for gif images it's a frame number, that will be displayed if animation is disabled or not available
+
+ - smiley? - defines space seprated list character sequencies, that should be interpreted as smiley
+
+ - smiley regular expression - defines regular expression that should be used for parsing smileys
+
+ - insert text - defines text that will be inserted when smiley clicked in smiley selector,
+ for non regular expression smiley this will be the first smiley in smiley? field
+
+ - tooltip text (optional).
+
+ - service name - name of any Miranda service, if service name starts with '/' protocol service for the contact assumed
+
+ - parmeter1 & parameter2 - could be any number, hContact (will be resolved into contact handle) or any string
+
+ Spaces and " require special handling in the smiley definition file
+
+ In all fields except regular expression fields:
+
+ %%__%% or %%_%% character sequence should be used in place of space
+ %%''%% character sequence should be used in place of " (double quote)
+
+ In regular expression fields:
+
+ spaces should be defined as \s (as usual)
+ double quotes should be defined as \x22
+
+
+
+For icons in smiley pack the following rules apply:
+
+If the resourcefile contains multiple icons for one id, the
+best color/resolution one will be selected, this allows to
+auto select the 32bit alphablend on WinXP and lower quality on others
+NOTE on the icon size: only in .dll and .ico the size of the resource will
+be taken. All other .icl, etc will scale everything to 16x16.
+
+
+Here are some examples of wrriting smiley pack:
+
+;
+; examples
+;Smiley = ".\plugins\smileyadd.dll", -112, ";P ;-P" ->from dll
+;Smiley = ".\4peacow.ico", 0, "(peacow)", "The Author" ->from ico
+;Smiley = ".\blabla.exe", 1, ":)" ->from exe
+;Smiley = ".\bla.icl", 3, ":)", "Happy face :)" ->from icl;
+;Smiley = ".\blabla.gif", 0, ":)" ->from gif
+;Smiley = ".\blabla.png", 0, ":)" ->from png
+;Smiley = ".\bla.dll', 5, " *HELP%%__%%ME* DO%%''%%IT%%''%% " ->with special characters
+; will be used as *HELP ME* and DO"IT"
+;Smiley = ".\blabla.png", 0, R":-*\)", ":)", "Happy face :)" ->with regular expression
+
+;Smiley = "abc.ico", 0, S"/SendtZer|hContact|4", "Kisses" -> sends ICQ tZer
+;Smiley = "nudge.ico", 0, S"/SendNudge|hContact|0", "Nudge" -> sends Nudge
+
+
+; smiley on the button
+ButtonSmiley = ":-P"
+
+; info
+Name = "Standard Icon Pack 4 SmileyAdd"
+Author = "Peacow"
+Date = "februari 2003"
+Version = "1.0"
+SelectionSize = 25, 25
+WindowSize = 5, 7
+
+; The following are the standard "built in" smileys
+Smiley = ".\plugins\smileyadd.dll", -113, ":) :-) (:", "SmileyFace :)"
+Smiley = ".\plugins\smileyadd.dll", -114, ";) ;-) (;"
+
+; hidden smiley:
+Smiley* = ".\plugins\smileyadd.dll", -115, ":( :-( ):"
+
+Smiley = ".\plugins\smileyadd.dll", -116, ":P :-P"
+Smiley = ".\plugins\smileyadd.dll", -112, ";P ;-P"
+
+Smiley = "abc.ico", 0, S"/SendtZer|hContact|4", "Kisses"
+Smiley = "nudge.ico", 0, S"/SendNudge|hContact|0", "Nudge"
diff --git a/plugins/SmileyAdd/docs/smileyadd_readme.txt b/plugins/SmileyAdd/docs/smileyadd_readme.txt
new file mode 100644
index 0000000000..27aa54ca61
--- /dev/null
+++ b/plugins/SmileyAdd/docs/smileyadd_readme.txt
@@ -0,0 +1,1136 @@
+SmileyAdd plugin for Miranda IM
+=====================================================================
+A plugin that adds smileys to the standard message dialog.
+Features:
+- Emoticons in all message dialogs (SRMM, TabSRMM, Scriver and others),
+ IEView logs, History++ (History++ v.1.5.1.2 or later required)
+ logs static and dynamic images for smileys in pretty much any format (png, gif, jpg, ico, etc.)
+ as well as images packaged in DLL
+- Animated gif emoticons and provide animation for all message logs
+- Emoticons animation in a smiley selector window
+- Emoticons on contact list, in Popup+ popups, Tipper YM tooltips, etc.
+- Smiley packs per protocol
+- Smiley pack per transport (gateway between protocols)
+- Smiley packs per contact
+- Graphical emoticons in the message input area, thus it will allow you to see these images while you are typing a message
+- MSN Custom Emoticons (smileys)
+- Supports plain text and regular expression smiley codes
+- Supports sending ICQ tZers, Nudges and/or call any Miranda service from selector window.
+- Unicode emoticons
+- Translation support
+
+Author: Boris Krasnovskiy, previous: Rein-Peter de Boer (peacow), bid,
+Email: borkra@miranda-im.org
+
+Emoticon Images: Angeli-Ka
+Regular expression library: Jeff Stuart
+Hashing algorithm: Austin Appleby
+
+Date: November, 2010
+Current version: 0.2.3.16
+
+Licensing terms could be found below.
+
+
+Installation Instructions
+=========================
+Extract the zip file in the Miranda home directory. This will create a directory
+'Smileys' with the default smiley pack. And the smileyadd.dll will go in the
+plugins directory.
+Or you can copy this by hand from the archive. Note: to make the default work,
+the default smiley pack HAS to be in the 'Smileys' directory.
+
+GDI+ installation required to use non icon smiley packs
+GDI+ come pre-installed on Windows XP, Office XP and later, for everybody else you can get it here:
+http://www.microsoft.com/downloads/details.aspx?FamilyID=6a63ab9c-df12-4d41-933c-be590feaa05a&DisplayLang=en
+
+
+Smiley Packs
+================
+SmileyAdd supports *.msl, *.asl and *.xep Smiley Packs.
+For syntax of in *.msl or *.asl files check the example file "smileyadd_example_libfile.msl".
+
+Note SmileyAdd provide better validation for malformed Regular Expressions then nConvers.
+As a result some Smiley Packs working under nConvers will give errors under SmileyAdd.
+Although if these errors are fixed resulting Smiley Pack will work correctly under both plugins.
+
+Pre-packaged Smiley Packs could be found here:
+http://addons.miranda-im.org/index.php?action=display&id=41
+
+Any Smiley Pack from SmileyAdd, IEView or nConvers sections is usable with SmileyAdd
+
+Per Contact Smiley Packs
+========================
+At first you need to create custom smiley category. To do that type the name of the new category
+into the text input field in Smiley Category part of option dialog and press "+".
+Now you can select this new Smiley Category for the contact. You need to right click on the
+contact select "Assign Smiley Category" and then select from the menu appeared category you want.
+To delete custom Smiley Category select in in the list in option dialog and press "-".
+
+Limitations
+===========
+Non Unicode version does not support DBCS characters in smiley codes and file names.
+DBCS character sets are Chinese, Japanese, Korean, etc.
+It's recommended to use Unicode version in this case.
+
+For true Unicode operation of Unicode SmileyAdd Unicode Smiley Packs required
+
+SmileyAdd will work only with Miranda 0.6 or later.
+Unicode SmileyAdd will only work with Unicode Miranda and
+Windows 2000 or later.
+
+GIF with "Disposal method" "To Previous" not supported
+
+When SmileyAdd is used with IEView only smiley packs with each smiley stored
+in separate image file (phg, gif, ico, jpg, etc.) could be used
+
+Bugs
+====
+No guarantees, but probably there are some bugs. If you find something, and would
+like to see this fixed then post on the Miranda forum as detailed as possible report
+on the bug (version numbers, OS, how to reproduce it) and I will get back on that.
+
+
+Source
+======
+Source is published on my Google Code site.
+http://code.google.com/p/mirandaimplugins/downloads/list
+
+
+Developers
+==========
+There is also some smiley support for plugins... There is something remotely
+resembling documentation in "m_smileyadd.h". Contact me if you have questions.
+At this moment you can:
+ - replace smileys in a RichEdit with only supplying the RichEdit handle
+ - retrieve an icon by supplying the emoticon text, i.e. ":)"
+ - parse text and retrieve icons for each emoticon found
+ - show a smiley select window
+ - contact me if you need more support (see the developer.txt also)
+
+
+
+Thanks to:
+==========
+- Angeli-Ka For excellent swmiley pack
+- Jeff Stuart For excelent regular expression library
+- MatriX For helping out with the icons and other stuff!
+- Pixador For some bug tracking :)
+- Wesslén For even more enhancing the standard message dialog and
+ fine tuning my subclass routines.. :)
+- Various forum people tracking bugs for me
+- Miranda crew for Miranda IM... :)
+- Everyone pointing out bugs to me!
+- Creators of all the smiley packs for SmileyAdd :)
+
+
+ChangeLog
+=========
+
+0.2.3.16
+---------
+ Added WLM 2011 smileys
+ Added ability to disable smiley for contact
+ Miranda 0.9 compatibility
+ Improved support for accounts
+
+0.2.3.15
+---------
+ Prevented ANSI SmileyAdd from loading on Unicode Miranda
+ Fixed duplicate smiley categories
+ Fixed active account determination
+
+0.2.3.14
+---------
+ Fixed crash with smiley scaling
+ Added x64 portability
+ Fixed crashes with broken web packs
+ Fixed memory leaks with bogus custom smileys
+ Added Folders plugin support for web packs
+
+0.2.3.13
+---------
+ Fixed one more crash
+
+0.2.3.12
+---------
+ Fixed crash with bogus custom smileys
+ Removed plugin smiley categories from per-contact menu
+ Allow plugin smiley category in Use one for all mode
+
+0.2.3.11
+---------
+ Added abilty to define and use user defined smiley categories in all modes
+ Added optional high quality image scaling for icon and png smileys (GDI+ required)
+ Added ability to specify minimum smiley height
+
+0.2.3.10
+---------
+ Fixed smiley jumping in some cases with smileys of different sizes in one line
+
+0.2.3.9
+---------
+ Added support for zoom
+ Fixed text/smileys in input area on options change
+ Smarter operation (do not touch input area if no need)
+
+0.2.3.8
+---------
+ Fixed custom smileys with Metacontacts
+ Fixed memory leak
+
+0.2.3.7
+---------
+ Performance improvements
+
+0.2.3.5
+---------
+ Fixed super fast animation
+ Fixed window background draw
+
+0.2.3.4
+---------
+ Use GDI+ to display TIFF smileys
+ Unloading smileys after 3 min of non-display
+
+0.2.3.3
+---------
+ Added protection for bogus frame numbers in smiley pack
+ Web packs downloading in background now
+ Redraw Message windows when packs reloaded through API
+
+0.2.3.2
+---------
+ Fixed error message with text-only "smileys"
+ Fixed crash on exit
+ Fixed smiley display in Tipper YM
+ Non gif smileys now handled through advimg if possible (no GDI+ required)
+
+0.2.3.1
+---------
+ Large reduction in GDI resources and memory usage
+ Added irc url to exclusions from parsing
+ Added support for animated xep smiley packs
+ Fixed Apply button behavior in Options dialog
+
+0.2.3.0
+---------
+ Fixed text typing in input area
+ Fixed drawing of selected animated smileys
+ Added skipping timestamp during smiley parsing
+
+ Reduction of memory used with animated smileys in message dialog and pre-selector
+
+0.2.2.28
+---------
+ Fixed typing in the input area
+ Fixed Metacontacts protocol detection
+
+0.2.2.27
+---------
+ Fixed Popup+ operation
+
+0.2.2.26
+---------
+ Fixes for Max custom smiley size option
+ Fixed animated smiley drawing issues
+ Fixed input area smiley parsing
+
+0.2.2.25
+---------
+ Fixed text replacement smiley
+ Added ability to specify max size for custom smiley
+
+0.2.2.24
+---------
+ Added text smiley capability
+ Fixed Metacontacts and per contact smiley pack
+ Removed internal path conversion logic (should fix crashes)
+ Lots of internal changes
+
+0.2.2.23
+---------
+ Fixed crash
+ Fixed hidden smileys
+ Fixed assigning contact smileys when no protocol pack defined
+ Renamed default menu item
+
+0.2.2.22
+---------
+ Redesigned smiley pack selection menu
+ Reduced plugin size
+
+0.2.2.21
+---------
+ Fixed problem with ANSI SmileyAdd and smileys with '?'
+ Improvements for custom smileys
+
+0.2.2.20
+---------
+ Fixed sending ICQ tZers
+ Added ability to send Nudges through Nudge plugin
+ Slight reduction in resources used
+
+0.2.2.19
+---------
+ Added ability to add service calls to smiley pack
+ (allows adding ICQ tZers, Nudges, etc. to smiley selection window)
+
+0.2.2.18
+---------
+ Fixed crash with Miranda 0.7 and Jabber transports
+ Reduced amount of error notifications in option dialog
+
+0.2.2.17
+---------
+ Fixed extention of downloaded smiley
+ Fixed crash when loaded without initialization
+
+0.2.2.16
+---------
+ Fixed https type urls
+ Fixed crash on exit
+
+0.2.2.15
+---------
+ Added ability to provide http url as smiley path
+
+0.2.2.14
+---------
+ Added support for not adding custom smileys to outgoing messages
+ Added support for Transports
+ Fixed crash with language packs
+
+0.2.2.13
+---------
+ Fixed custom category name in option dialog
+ Fixed smiley definitions in smiley pack
+
+0.2.2.12
+---------
+ Fixed Ctrl-Up/Down operation in chat
+ Rewrote smiley pack file specification
+
+0.2.2.11
+---------
+ Fixed crash with \b regex clause
+ Fixed crash with Miranda 0.8 build #8
+ Fixed smiley pack definitions
+ Increased size of smiley prelookup in input area
+ Changed screenshot
+
+0.2.2.10
+---------
+ Added new default smiley pack(s) by Angeli-Ka (has default smiley for most protocols)
+ Added auto selection of default smiley pack for protocol based on protocol type
+ Fixed problems with Tab key and input area lockup
+ Fixed smiley quoting in TabSRMM
+ Fixed decoding special chars (double quote and spaces) in insert text and tool text
+
+0.2.2.9
+---------
+ Fixed Tab expansion in Scriver chat
+ Fixed input area smileys after changing options
+ Fixed selection preservation after copy
+
+0.2.2.8
+---------
+ Fixed smiley animation with Popup+ opening message window
+ Fixed Tab expansion in Chat
+ Fixed alpha transparent icon creation
+ Fixed deleting custom smileys after Miranda loaded
+ Fixed resource leak with custom smileys
+
+0.2.2.7
+---------
+ Fixed inability to type after choosing smiley through selector in Win98
+ Fixed crash with Miranda 0.8 #10
+ Fixed loosing smileys after deleting an account
+
+0.2.2.6
+---------
+ Fixed positioning of controls in option dialog
+ Put smiley selector window computation back
+
+0.2.2.5
+---------
+ Added support for Miranda 0.8 Accounts
+ Reduced resource utilization with png smileys
+ Fixed few crashes in obscure conditions
+
+0.2.2.4
+---------
+ Added option to disable custom smileys
+ Fixed custom smiley operation with SRMM
+ Added Updater plugin Beta site
+
+0.2.2.3
+---------
+ Fixed freezing after cut in input area
+ Fixed smiley selector covered by status bar
+ Slight typing performance improvements
+
+0.2.2.2
+---------
+ Fixed freezing with History++ pseudo edit
+
+0.2.2.1
+---------
+ Fixed lazy animation in selector window
+ Fixed input area freeze with no smiley pack defined
+ Fixed no scroll bar with preset selector window size
+ Fixed custom smiley parsing when no smiley pack defined
+
+0.2.2.0
+---------
+ Added MSN custom smiley support
+ Fixed Scriver All tabs message send
+ Redesigned animation in selection window (for better or worse)
+
+0.2.1.10
+---------
+ Fixed cut & paste issue
+
+0.2.1.9
+---------
+ Fix for the crash on some computers
+
+0.2.1.8
+---------
+ Restored History++ compatibility
+
+0.2.1.7
+---------
+ Fixed operation with input area smileys disabled
+
+0.2.1.6
+---------
+ Added correct display of selected animated smileys
+ Added ability to cut&paste animated smileys
+ Large number of fixes for cut&paste and drawing issues
+
+0.2.1.5
+---------
+ Fixed input area smileys with surround spaces
+ Fixed smiley background color
+
+0.2.1.4
+---------
+ Fixed input area smiley replacement
+
+0.2.1.3
+---------
+ Added support for "mutating" smileys in input area
+ Added correct handling of cut and copy operations
+ Fixed selection in input area during smiley replacement
+
+0.2.1.2
+---------
+ Fixed URL detection
+ Fixed isolated smiley replacing in input area
+
+0.2.1.1
+---------
+ Fixed non animated smiley packs
+ Fixed sending on "Send" button with Input area smileys
+ Fixed TabSRMM saved message
+
+0.2.1.0
+---------
+ Added support for input area smileys
+ Added support for Metacontacts with SRMM
+ Fixed empty row/col in selection window
+
+0.2.0.8
+---------
+ Fixes for History++ animation
+ Fixes for smiley positioning and scaling
+
+0.2.0.7
+---------
+ Added History++ animation
+ Fixed memory leaks
+
+0.2.0.6
+---------
+ Changed horizontal smiley scaling
+ Fixed TabSRMM animation
+
+0.2.0.5
+---------
+ Fixed smiley jump when scrolling
+
+0.2.0.4
+---------
+ Fixed tooltips for close smileys
+ Fixed crash on exit
+
+0.2.0.3
+---------
+ Added smiley tooltips
+
+0.2.0.2
+---------
+ Fixed animated smileys positioning problems
+
+0.2.0.0
+---------
+ Animated smileys in RichEdit support
+
+0.1.12.17
+---------
+ Fixed xep parameters display
+ Added service to reload smiley packs
+
+0.1.12.16
+---------
+ Fixed XEP file parsing
+
+0.1.12.15
+---------
+ Fixed crash in ANSI version with non ASCII regex smileys
+
+0.1.12.14
+---------
+ Fixed problem when parsing some xep files
+ Fixed an error message
+ Added new Miranda interface support
+
+0.1.12.13
+---------
+ Fixed bogus error message in option dialog
+ Fixed translation issues
+ Fixed services destruction
+
+0.1.12.12
+---------
+ Fixed few resource leaks
+ Minor code improvements
+
+0.1.12.11
+---------
+ Added ability to select smiley by typing row and column
+ Restored Windows 95 compatibility
+ Changed xep smiley pack parsing
+ Fixed protocol name translation
+ Removed popup support due to non displayed errors in some cases
+ Moved reporting broken smileys in a pack to Network Log
+ Fix for BoundsChecker (thanks ghazan)
+
+0.1.12.10
+---------
+ Fixed smiley duplication
+ Added URL and file path exclusion logic for smiley parsing
+
+0.1.12.9
+--------
+ Fixed full path to relative path conversion logic
+ Improved smiley scaling algorithm
+
+0.1.12.8
+--------
+ Added support for Spell Checker plugin
+ Fixed a crash in some rear cases
+
+0.1.12.7
+--------
+ Fixed regular expressions with non-English alphabets
+ Attempt to fix double size smileys with History++
+
+0.1.12.6
+--------
+ Fixed crash caused by other plugins registering smiley category
+
+0.1.12.5
+--------
+ Fixed non-Unicode SmileyAdd and DBCS character sets
+ Fixed scale to height option with History++
+ Disabled "Assign Smiley Category" menu if per protocol smileys disabled
+ Added support for (?imsxr-imsxr) clause in regular expressions
+
+0.1.12.4
+--------
+ Fixed random crash
+ Fixed ability to disable smiley category
+ Added support for new xep smiley packs
+ Added ability to specify size of smiley selection window in smiley pack
+ Added ability to specify tooltip text for regular expression smileys
+ Changed to use popup plugin for error messages
+
+0.1.12.3
+--------
+ Fixed 100% CPU with custom smiley categories defined
+ Fixed contact specific smileys and Specify Smiley pack for each protocol
+ Naming fixes
+ Changed default button smiley icon
+
+0.1.12.2
+--------
+ Fixed IEView smileys
+
+0.1.12.1
+--------
+ Fixed crash on removal of custom smiley categories
+ Fixed some backwards compatibilty issues
+ Added ability to assign protocol smileys for the contact
+ Added message dialog redraw on changing contact smileys
+
+0.1.12.0
+--------
+ Added per contact smiley packs
+ Improvements in error notifications
+ Removed deprecated API from header file
+
+0.1.11.15
+---------
+ Modifier support for regular expressions
+ Performance improvements in animated preview window closing
+
+0.1.11.14
+---------
+ Fixed missing last smiley code (no smiley insert)
+
+0.1.11.13
+---------
+ Added IcoLib support
+ Moved Options into "Customize" group
+ More dll size reductions
+
+0.1.11.12
+---------
+ Fixed crash with malformed smiley packs
+
+0.1.11.11
+---------
+ New Regular expression engine for smaller size
+ Button Smiley behaivior changed
+ (now if Button Smiley explicitly not specified in smiley pack SmileyAdd built-in icon is used (the same as TabSRMM))
+ Fixed crash in selector window with no visible smileys
+ Fixed option dialog for smiley pack with no visible smileys
+
+0.1.11.10
+---------
+ Attempt to fix high CPU usage on/after smiley selection on some computers
+
+0.1.11.9
+--------
+ Attempt to fix desktop redraw issue
+ Fixed high cpu usage
+
+0.1.11.8
+--------
+ Attempt to fix desktop redraw issue
+ Fixed random crash
+
+0.1.11.7
+--------
+ Attempt to fix desktop redraw issue
+
+0.1.11.6
+--------
+ Animation antilockup logic
+ Less invasive window subclassing
+
+0.1.11.5
+--------
+ Animation performance improvemnets
+ Animation antilockup logic
+
+0.1.11.4
+--------
+ Fixed crash with hidden animated smiley
+ Fixed smiley corruption in scale to text mode
+
+0.1.11.3
+--------
+ Fixed crash
+
+0.1.11.2
+--------
+ Fixed improper delay on animated GIFs
+ Fixed png smiley corruption
+ Fixed smiley corruption with EMF
+ Fixed smileys do not animate after scroll up
+
+0.1.11.1
+--------
+ Fixed smileys do not animate sometimes in selector window
+ Added mouse wheel scrolling in selector window
+ Animation performance improvements
+ Fixed smiley no show with TabSRMM
+
+0.1.11.0
+--------
+ Added ability to show animated smileys in selector window
+ Added API to insert EMF into RichEdit (allows "true" transparency)
+ Fixed crash on exit with Win98
+ Fixed error reporting for not found smileys
+
+0.1.10.9
+--------
+ Fixed smiley selector window with large number of smileys
+ Do not allow smiley selector window to cover more then 1/4 of the screen area
+
+0.1.10.8
+--------
+ Fixed grid lines in smiley selector
+
+0.1.10.7
+--------
+ Fixed issue with smiley replacement when "Replace only isolated smileys" selected
+ Added grid lines to IEView smiley selector
+
+0.1.10.6
+--------
+ Fixed smiley replacement at the beginning of the line
+
+0.1.10.5
+--------
+ More IEView like selector window (in IEView mode) clipping, scroll bar
+ Fixed non-Unicode SmileyAdd with DBCS character sets (Chinese, Japanese, Korean, etc.)
+
+0.1.10.4
+--------
+ Fixed insertion code when regular expressions used in msl/asl smiley pack
+ IEView style selection window now selectable option (default SmileyAdd one)
+ Fixed preview window update on option change in Option dialog
+ Added ability to select gif frame to display as static smiley
+
+0.1.10.3
+--------
+ Added Regular Expression support to msl/asl Smiley Packs
+ Attempt to correct message log repainting problems
+
+0.1.10.2
+--------
+ More IEView like selector window
+ Attempt to correct message log repainting problems
+
+0.1.10.1
+--------
+ Fixed Preview window for xep and asl smiley packs
+ Attempt to fix "jumping"
+
+0.1.10.0
+--------
+ Added batch parsing API (to support IEView)
+ Fixed checkbox operation in Option Dialog
+ Added support for SelectionSize in asl/msl packs
+
+0.1.9.6
+--------
+ Added ability to paste smiley codes from message log
+ (This feature works only if RichEdit v3 or later is used)
+ Perfomance improvements in smiley drawing
+ Fixed "Surround Smiley with spaces" checkbox in Option dialog
+
+0.1.9.5
+--------
+ Fixed Smiley Pack information display in Option dialog
+
+0.1.9.4
+--------
+ Fixed inadvertent smiley pack change in Option dialog
+ Fixed random crash in Message Dialog smiley parsing
+ Perfomance improvements in smiley drawing
+ Support for Unicode file names in Unicode version
+
+0.1.9.3
+--------
+ Fixed crash on Disable Message Dialog Support
+ Fixed resource leak
+ Added GETINFO2 API where deleting Smiley handle is user responsibility
+ (GETINFO API should not be used any longer)
+ Added protection against multiple SmileyAdd instances execution
+
+0.1.9.2
+--------
+ Fixed Smiley Pack info display for Unicode version
+ A lot of robustness updates for Unicode version
+ Added option to scale all smileys in selector window
+ Fixed translation file
+ Error messages in Unicode
+ Fixed crash with nonexistent xep smiley pack
+
+0.1.9.0
+--------
+ Fixed xep smiley replacement (longer smiley have precedence over shorter ones)
+ Fixed button smiley for xep smileys (works now by the same rule as msl one)
+ Added unloading GDI+ when it's no longer needed
+ Improved smiley parsing performance by at least 20%
+ Final resolution for SmileyAdd related flickering
+ Updates for Unicode version
+ Added support for Unicode (UTF8 and UTF16) msl, asl and xep files
+ Added Unicode version for ParseText API
+
+0.1.8.8
+--------
+ Added workaround for crashing ISee being reported as SmileyAdd crash
+ Smiley Preview Window now shows smileys in it's original size (window might get pretty big)
+
+0.1.8.7
+--------
+ Fixed unicode conversion problem
+ (now Windows language for non-Unicode programs always used)
+
+0.1.8.6
+--------
+ Improved performance for smiley parsing
+ Fixed flickering durig smiley replacement
+ Fixed for rare crashes in non message log window
+
+0.1.8.4
+--------
+ Improved performance for smiley parsing
+ Added workarounds for 32bit_Icons and '&' problems in xep smileys
+
+0.1.8.3
+--------
+ Improved performance for msl smiley parsing
+ Fixed selection restore in message log
+
+0.1.8.2
+--------
+ Fixed Message Log position restore after smiley insertion
+ Fixed cursor flickering durig smiley replacement
+ Improved performance on smiley replacement
+ Fixed smiley replacement issues for xep smiley pack
+ Added workaround for non compliant XML node name
+
+0.1.8.1
+--------
+ Fixed smiley replacement at the end of the line
+ A lot of error checking and notifications for xep Smiley Pack
+ Some perfomance improvements
+
+0.1.8.0
+--------
+ Added support for nConvers (xep) Smiley Packs
+ Fixed few potential problems with unicode
+
+0.1.7.6
+--------
+ Added support for environment variables in the path
+ Fixed SmileyAdd operation with RSS plugin
+ Utilizing Unicode hooks for Unicode SRMMs
+ Added line number display for broken smiley
+ Added translation capability for few error messages
+ Fixed loading Smiley Pack once when used by multiple smiley categories
+ Fixed default Smiley Pack for RegisterCategory API
+ Fixed Option Dialog icon for category registered with RegisterCategory API
+ Fixed crash when call RegisterCategory API at power-up
+ Changed ParseText API to support iterative operation
+ Fixed icon retrieval by ParseText API
+
+0.1.7.5
+--------
+ Fixed smiley detection and replacement issues
+
+0.1.7.4
+--------
+ Fixed rare smiley detection and replacement issues
+ Fixed "Use one for all" checkbox handling in Option Dialog
+
+0.1.7.3
+--------
+ Fixed rare smiley detection and replacement issues
+
+0.1.7.2
+--------
+ Fixed smiley replacement with no button bar
+ Fixed images in Option Dialog for systems with Commmon Controls 5 or earlier
+ Added color selection in Option Dialog for smiley selector window
+ Spelling fixes in Register Category API
+
+0.1.7.1
+--------
+ Fixed crash with massage dialog with no contact
+ Fixed crash with PopUp++
+ Attempt to fix Win9x icon size problem in Option Dialog
+ Much reduced GDI resource utilization
+ Added API to register a Smiley Category
+ Many changes to API to Find Smiley in Text
+
+0.1.7.0
+--------
+ Added support for non icon smileys (png, gif, etc.)
+ GDI+ required for this feature to work
+ Added support for IEView .asl Smiley Packs
+ Added error notification on missing smileys from the pack
+ Better handling for relative paths in Smiley Pack
+ Added API to find smileys in a text string (pescuma request for use in modern contact list)
+
+0.1.6.2
+--------
+ Fixed URL corruption regression
+
+0.1.6.1
+--------
+ Fixed Option Dialog for systems with many protocols
+ Surround smiley with spaces option now make sure that any smiley
+ detected have spaces on both sides
+
+ At this point I am not aware of any problems with SmileyAdd.
+ If you encounter some you want to report it together with detailed steps to reproduce it
+ and software versions (Miranda, plugins, OS, etc.) you are using.
+
+0.1.6.0
+--------
+ API change, hookable event provided to notify plugins with "native"
+ SmileyAdd support that options have changed, so they could redraw
+ Fixed smiley selection window display location on multi-monitor systems
+ Fixed protocol icon display in Option Dialog for protocols which don't
+ provide "small" icons (i.e MetaContacts)
+ Increased size of controls in Option Dialog to aid translation
+ Fixed string in translation file
+ Fixed changing checkbox "Specify smiley pack ..." would not enable Apply button
+ Fixed translation of the protocol name could result in smiley not showing
+ Caution: This fix could result in having to reselect smiley pack for protocol!
+
+ At this point I am not aware of any problems with SmileyAdd.
+ If you encounter some you want to report it together with detailed steps to reproduce it
+ and software versions (Miranda, plugins, OS, etc.) you are using.
+
+0.1.5.3
+--------
+ Option Dialog redesigned. It's hopefully more understandable now.
+ (Thanks to HikoH for lot's of suggestions)
+ Unfortunatly all translations will have to change with this release.
+ Changing Options in option dialog no longer require restart for them to take effect.
+
+ At this point I am not aware of any problems with SmileyAdd.
+ If you encounter some you want to report it together with detailed steps to reproduce it
+ and software versions (Miranda, plugins, OS, etc.) you are using.
+
+0.1.5.2
+--------
+ Fixed smiley replace in history
+ Fixed smiley selection tool sometimes open in strange location
+ Fixed Option dialog filename problem when you browse for it
+
+0.1.5.1
+--------
+ Fixed overwrite of "Add Contact to buddy list" button (for not listed contacts)
+ Fixed "Use One for all" radio buttons problem in Option Dialog
+
+0.1.5.0
+--------
+ API change to support TabSRMM 0.9.9.96 and later (per Nightwish request)
+ (the parent window for smiley selection dialog could be specified now)
+ Fixed smiley scaling issue with some smiley packs on Miranda Button.
+
+0.1.4.14
+--------
+ Fixed File Open Dialog for empty or broken file names
+
+0.1.4.13
+--------
+ Better handling of broken msl files (Button S on message dialog instead of icon)
+ Attempt to fix no File Open dialog on Win9x.
+ Error messages are now using balloon notification, when it works
+ Force focus to entry dialog after selecting smiley
+ Code cleanup
+
+0.1.4.12
+--------
+ Implemented much more robust algorithm for detecting
+ SRMM 2.x interface supporting plugins versa
+ SRMM 1.x interface supporting plugins
+
+ This release have been tested to work with:
+ SRMM 2.4.0.0 and 1.0.4.2 (2.x and 1.x)
+ TabSRMM 0.9.9.95 and 0.0.8.0 (built-in support and 1.x)
+ SRAMM 0.3.2.2 (1.x)
+ SRMM Mod 0.5.8.0 (1.x)
+ Scriver 2.2.2.2 (built-in support)
+ Single Mode SRMM 0.1 have built-in text-only SmileyAdd support and it's working as such
+
+ This will fix "smiley button does not show" problems.
+
+0.1.4.11
+--------
+ Fixed latest SRMM support regression
+ Improved relative path support
+ Smiley packs disabled in the Option dialog no longer loaded
+
+0.1.4.9
+-------
+ Fixed handling of SRMM interface (interface changed between Miranda 3.3.1 and 4.0)
+ Bottom smiley button option is not available any more for Miranda later then 3.3.1
+ Smiley packs which specified but don't exist now give error messages (at power up too)
+ Non IM protocols (i.e. weather, time, etc.) are ignored now
+ Fixed Option dialog translation
+ Fixed Option dialog handling of missing Smiley Packs
+ Fixed Option dialog Cancel/Apply functions
+ Option dialog accepts relative paths now (if you type them in)
+ Changing settings in Option dialog will affect all open windows on Apply
+ Fixed numerous resource leaks
+ A lot of general code robustness and performance updates
+ Removed Windows API calls that don't exist on Win95
+ MS VC++ 6 portability updates (dsp file provided)
+ Fixed older TabSRMM support
+
+ Extensive testing have been performed on plugin for this release.
+ Plugin have been tested to work with Windows XP, 2000, 95, Miranda 4.0.1 and 3.3.1
+ SRMM and TabSRMM
+
+0.1.4.8
+-------
+ Fixed transparency with TabSRMM
+ Fixed losing focus after entering smiley in SRMM
+ Fixed memory leak in smiley dialog
+ GCC portability updates
+
+0.1.4.7
+-------
+ Fixed crashes and option dialog.
+
+0.1.4.6
+-------
+ More crash fixes.
+
+0.1.4.5a
+-------
+ Fixed back merge
+
+0.1.4.5
+-------
+ OLE resource leak fixed (by NightWish)
+ Includes TabSRMM version (by NightWish)
+
+0.1.4.4a
+-------
+ Compiled in .NET studio as before.
+
+0.1.4.4
+-------
+ Multiple crash fixes with invalid files (by bidyut)
+
+0.1.4.3
+-------
+ Fixed incompatibility with Jabber plugin
+ Plugin support for showing a smiley selection window
+ Internal changes, better memory/resource management
+ Order of the smileys in the smiley pack doesn't matter anymore
+
+0.1.3.2
+-------
+ Minor fixes:
+ - Smiley button scales the image now correctly
+ - Fixed problem with spaces and " in smiley text when used with smiley select window
+
+0.1.3.1
+-------
+ Minor fixes:
+ - Smiley selection window placement is better
+ - Button is better shown when quote button is disabled
+
+0.1.3.0
+-------
+- Works now with Miranda version 0.3.3
+- Fixed 4 character smiley bug
+- Smileybutton can now be on top row buttons (default)
+- More than 2 consecutive smileys without spaces are now
+ recognized in 'isolated smiley' mode
+- Support for spaces or " in smileys (see sample .msl file)
+
+0.1.2.3
+-------
+- Fixed incompatibility (2) with message dialog in nightly build of 30-06-2003
+
+0.1.2.2
+-------
+- Fixed incompatibility with message dialog in nightly build of 30-06-2003
+
+0.1.2.1
+-------
+- (Small) bug with smiley pack loading fixed
+
+0.1.2.0
+-------
+- Ability for hidden smileys (not shown in select window)
+- Ability for separate tooltip text in select window
+- Fixed display bug in select window with large smiley packs
+- Fixed bug with random smileys not being replaced (at least i hope so)
+- Fixed bug with smiley replacement if some text was selected
+- Fixed bug when using large smiley texts
+- Fixed bug with (non) case sensitivity (now case sensitive)
+- Fixed some buffer overflow bugs
+- Other minor bug fixes
+
+
+0.1.1.0
+-------
+- Added Support for smiley sizes other than 16x16
+- Added SmileySelectWindow button
+- Fixed (disabled) display of smileys in hyperlinks
+- Added 'scale to text height' option
+- Fixed GDI leakage bugs
+- Other minor bugfixes
+
+0.1.0.0
+-------
+Official 1.0 release
+- Support for customizable icon packs
+- Standard shipment with full OG smiley pack of MatriX
+- Better smiley parsing (support for 'isolated' smileys)
+- Specify smileys on a 'per protocol' basis
+- Smiley replacementsupport for plugins
+
+0.0.2.0
+-------
+Second alpha release with OG smileys of MatriX
+
+0.0.1.0
+-------
+Initial alpha (test) release with beautyfully :) styled greenish icons.
+
+
+Miranda IM
+==========
+Miranda IM is an open source instant messenger framework that
+support plugins for many different networks; ICQ, MSN, YAHOO
+AIM to mention a few (but not all). Get it at:
+http://www.miranda-im.org/
+
+
+Copyright and license
+=====================
+
+Software:
+
+Copyright (C) 2005 - 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; either version 2
+of the License, or (at your option) any later version.
+
+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/>.
+
+Emoticon Images:
+
+Copyright (C) 2008 Angeli-Ka All Rights Reserved
+
+Emoticon images are published under
+Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 license
+
+Under this license:
+
+You are free:
+to Share — to copy, distribute and transmit the work
+
+Under the following conditions:
+Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
+Noncommercial. You may not use this work for commercial purposes.
+No Derivative Works. You may not alter, transform
+
+The complete text of the license, translation and legal text can be found here:
+http://creativecommons.org/licenses/by-nc-nd/3.0/
+
diff --git a/plugins/SmileyAdd/docs/smileyadd_screenshot.jpg b/plugins/SmileyAdd/docs/smileyadd_screenshot.jpg
new file mode 100644
index 0000000000..a0c8ca4fa6
--- /dev/null
+++ b/plugins/SmileyAdd/docs/smileyadd_screenshot.jpg
Binary files differ
diff --git a/plugins/SmileyAdd/docs/smileyadd_translation.txt b/plugins/SmileyAdd/docs/smileyadd_translation.txt
new file mode 100644
index 0000000000..eec2d0c1e6
--- /dev/null
+++ b/plugins/SmileyAdd/docs/smileyadd_translation.txt
@@ -0,0 +1,125 @@
+
+; This file contains the list of translatable strings in the
+; Miranda SmileyAdd plugin.
+; Author Rein-Peter de Boer (peacow), borkra
+
+; Last updated: July, 2005
+; SmileyAdd 0.1.12.3
+
+
+;
+
+
+
+;end of new section 0.1.12.3
+
+[Only replace isolated smileys (spaces on both sides)]
+trans!
+
+[Scale smiley to textheight]
+trans!
+
+[Off]
+trans!
+
+[Top]
+trans!
+
+[Bottom]
+trans!
+
+[Disable]
+trans!
+
+[Surround inserted smiley with spaces]
+trans!
+
+[Specify Smiley Pack for each category]
+trans!
+
+[Smiley Packs]
+trans!
+
+[smileys]
+trans!
+
+[Standard]
+trans!
+
+[Name:]
+trans!
+
+[Author:]
+trans!
+
+[Version:]
+trans!
+
+[Show Smiley Selection Window]
+trans!
+
+[Smileys]
+trans!
+
+[Events]
+trans!
+
+[All Files]
+trans!
+
+[d'Oh!]
+trans!
+
+[Nothing loaded]
+trans!
+
+[SmileyAdd Built-In Message Dialog Support]
+trans!
+
+[Preview]
+trans!
+
+[Smiley Button]
+trans!
+
+[Smiley Selector]
+trans!
+
+[Display]
+trans!
+
+[GDI+ not installed. Smiley Pack %s could not be loaded.\nGDI+ can be downloaded here: http://www.microsoft.com/downloads]
+trans!
+
+[Smiley #%u in file %s for Smiley Pack %s not found. Correct Smiley Pack implementation.]
+trans!
+
+[Smiley Pack %s not found.\nSelect correct Smiley Pack in the Miranda Options | Events | Smileys.]
+trans!
+
+[MS XML parser not installed. Smiley Pack %s could not be loaded.\nMS XML parser can be downloaded here: http://www.microsoft.com/downloads]
+trans!
+
+[Smiley Pack malformed: %s on line %u position: %u Correct Smiley Pack implementation.\n XML spec could found here: http://www.w3.org/TR/xml11/]
+trans!
+
+[Regular Expression "%s" in smiley pack "%s" malformed.\nCorrect Smiley Pack implementation.]
+trans!
+
+[Regular Expression "%s" in smiley pack "%s" could produce "empty matches".\nCorrect Smiley Pack implementation.]
+trans!
+
+[Only one instance of SmileyAdd could be executed.\nRemove duplicate instances from 'Plugins' directory]
+trans!
+
+[Use first smiley for selection size]
+trans!
+
+[IEView style window]
+trans!
+
+[Animate]
+trans!
+
+[Assign Smiley Category]
+trans!
diff --git a/plugins/SmileyAdd/download.cpp b/plugins/SmileyAdd/download.cpp
new file mode 100644
index 0000000000..39de234634
--- /dev/null
+++ b/plugins/SmileyAdd/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/download.h b/plugins/SmileyAdd/download.h
new file mode 100644
index 0000000000..bb83926acc
--- /dev/null
+++ b/plugins/SmileyAdd/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/general.cpp b/plugins/SmileyAdd/general.cpp
new file mode 100644
index 0000000000..0e1c6e7131
--- /dev/null
+++ b/plugins/SmileyAdd/general.cpp
@@ -0,0 +1,315 @@
+/*
+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 "m_metacontacts.h"
+
+#ifdef _MSC_VER
+#include <delayimp.h>
+#endif
+
+/*
+#include "m_popup.h"
+#if defined _UNICODE || defined UNICODE
+#include "m_popupw.h"
+#endif
+*/
+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);
+}
+
+void ReportError(const TCHAR* errmsg)
+{
+ static const TCHAR title[] = _T("Miranda SmileyAdd");
+/*
+#if defined _UNICODE || defined UNICODE
+ POPUPDATAW pd = {0};
+#else
+ POPUPDATAEX pd = {0};
+#endif
+
+ _tcscpy(pd.lpwzContactName, title);
+ _tcscpy(pd.lpwzText, errmsg);
+
+ pd.iSeconds = -1;
+
+#if defined _UNICODE || defined UNICODE
+ bool popupFail = PUAddPopUpW(&pd) != CALLSERVICE_NOTFOUND;
+#else
+ bool popupFail = PUAddPopUpEx(&pd) != CALLSERVICE_NOTFOUND;
+#endif
+ if (popupFail)
+*/
+ MessageBox(NULL, errmsg, title, MB_OK | MB_ICONWARNING | MB_TOPMOST);
+}
+
+#pragma warning( disable : 4786 )
+#undef _MT
+
+#if defined _UNICODE || defined UNICODE
+
+#include <wcpattern.cpp>
+#include <wcmatcher.cpp>
+
+#else
+
+#include <pattern.cpp>
+#include <matcher.cpp>
+
+#endif
diff --git a/plugins/SmileyAdd/general.h b/plugins/SmileyAdd/general.h
new file mode 100644
index 0000000000..eedd1e1489
--- /dev/null
+++ b/plugins/SmileyAdd/general.h
@@ -0,0 +1,252 @@
+/*
+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 )
+
+#if defined _UNICODE || defined UNICODE
+
+#include <wcpattern.h>
+#include <wcmatcher.h>
+
+typedef WCPattern _TPattern;
+typedef WCMatcher _TMatcher;
+#define createTMatcher createWCMatcher
+
+#else
+
+#include <pattern.h>
+#include <matcher.h>
+
+typedef Pattern _TPattern;
+typedef Matcher _TMatcher;
+#define createTMatcher createMatcher
+
+#endif
+
+#pragma warning( pop )
+
+#define _MT
+
+#include "resource.h"
+
+#pragma warning( push )
+#pragma warning( disable : 4100 )
+#define MIRANDA_VER 0x0A00
+#define NETLIB_NOLOGGING
+
+#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; }
+};
+
+#if defined _UNICODE || defined UNICODE
+#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
+#else
+#define T2A_SM(p1) (char*)p1
+#define T2W_SM A2W_SM
+#define A2T_SM(p1) (TCHAR*)p1
+#define W2T_SM W2A_SM
+#endif
+
+
+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/imagecache.cpp b/plugins/SmileyAdd/imagecache.cpp
new file mode 100644
index 0000000000..b771c4941b
--- /dev/null
+++ b/plugins/SmileyAdd/imagecache.cpp
@@ -0,0 +1,777 @@
+/*
+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;
+#if (defined _UNICODE || defined UNICODE)
+ lastmodule = LoadLibraryEx(file.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE);
+#else
+ lastmodule = LoadLibraryEx(file.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES);
+#endif
+ }
+ 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/imagecache.h b/plugins/SmileyAdd/imagecache.h
new file mode 100644
index 0000000000..aa13e4981c
--- /dev/null
+++ b/plugins/SmileyAdd/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/main.cpp b/plugins/SmileyAdd/main.cpp
new file mode 100644
index 0000000000..8af68d3d8a
--- /dev/null
+++ b/plugins/SmileyAdd/main.cpp
@@ -0,0 +1,261 @@
+/*
+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_updater.h"
+#include "m_metacontacts.h"
+
+//globals
+HINSTANCE g_hInst;
+HANDLE hEvent1, hContactMenuItem;
+extern LIST<HANDLE> menuHandleArray;
+
+char* metaProtoName;
+
+PLUGINLINK *pluginLink;
+
+//static globals
+static HANDLE hHooks[7];
+static HANDLE hService[13];
+int hLangpack;
+
+MM_INTERFACE mmi;
+LIST_INTERFACE li;
+UTF8_INTERFACE utfi;
+
+static const PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+#if defined(_WIN64)
+ "SmileyAdd x64",
+#elif defined(UNICODE) | defined(_UNICODE)
+ "SmileyAdd Unicode",
+#else
+ "SmileyAdd",
+#endif
+ __VERSION_DWORD,
+ "Smiley support for Miranda Instant Messanger",
+ "Peacow, nightwish, bid, borkra",
+ "borkra@miranda-im.org",
+ "Copyright© 2004 - 2011 Boris Krasnovskiy, portions by Rein-Peter de Boer",
+ "http://code.google.com/p/mirandaimplugins/downloads/list",
+ // "http://addons.miranda-im.org/index.php?action=display&id=2152",
+#if defined(UNICODE) | defined(_UNICODE)
+ UNICODE_AWARE, //not transient
+#else
+ 0,
+#endif
+ 0, //doesn't replace anything built-in
+#if defined(UNICODE) | defined(_UNICODE)
+ // {BD542BB4-5AE4-4d0e-A435-BA8DBE39607F}
+ { 0xbd542bb4, 0x5ae4, 0x4d0e, { 0xa4, 0x35, 0xba, 0x8d, 0xbe, 0x39, 0x60, 0x7f } }
+#else
+ // {9817B76A-603A-4616-806C-86ECB97EA05B}
+ { 0x9817b76a, 0x603a, 0x4616, { 0x80, 0x6c, 0x86, 0xec, 0xb9, 0x7e, 0xa0, 0x5b } }
+#endif
+
+};
+
+static SKINICONDESC skinDesc =
+{
+ SKINICONDESC_SIZE_V1, "SmileyAdd", NULL,
+ "SmileyAdd_ButtonSmiley", NULL, -IDI_SMILINGICON
+};
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD /* mirandaVersion */)
+{
+ return (PLUGININFOEX*)&pluginInfoEx;
+}
+
+// MirandaPluginInterfaces - returns the protocol interface to the core
+static const MUUID interfaces[] = {MIID_SMILEY, MIID_LAST};
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+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 = (HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&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 = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&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(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ mir_getLI(&li);
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+ 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);
+
+#ifdef _UNICODE
+ if (strstr(temp, "Unicode") == NULL)
+ {
+ ReportError(TranslateT("Please update SmileyAdd to ANSI Version"));
+ return 1;
+ }
+#else
+ if (strstr(temp, "Unicode") != NULL)
+ {
+ ReportError(Translate("Please update SmileyAdd to Unicode Version"));
+ return 1;
+ }
+#endif
+
+ 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);
+
+#if defined(UNICODE) | defined(_UNICODE)
+ hService[12] = CreateServiceFunction(MS_SMILEYADD_PARSEW, ParseTextW);
+#endif
+
+ 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/mingw-comp.bat b/plugins/SmileyAdd/mingw-comp.bat
new file mode 100644
index 0000000000..6b09bfdf1b
--- /dev/null
+++ b/plugins/SmileyAdd/mingw-comp.bat
@@ -0,0 +1,3 @@
+c:\mingw\bin\windres -D_WIN32_IE=0x0501 resource.rc resource.o
+c:\mingw\bin\gcc -shared -Os -s -o smileyadd.dll -I..\..\include -I..\..\include\mingw -I. -Iregexp -D_WIN32_IE=0x0501 bkstring.cpp dlgboxsubclass.cpp general.cpp main.cpp options.cpp services.cpp smileyroutines.cpp smileys.cpp smltool.cpp customsmiley.cpp download.cpp anim.cpp imagecache.cpp SmileyBase.cpp AniSmileyObject.cpp resource.o -lkernel32 -lstdc++ -lgdi32 -lole32 -lcomdlg32 -luuid -lcomctl32 2> log
+c:\mingw\bin\gcc -shared -Os -s -o smileyaddw.dll -I..\..\include -I..\..\include\mingw -I. -Iregexp -D_WIN32_IE=0x0501 -DUNICODE -D_UNICODE bkstring.cpp dlgboxsubclass.cpp general.cpp main.cpp options.cpp services.cpp smileyroutines.cpp smileys.cpp smltool.cpp customsmiley.cpp download.cpp anim.cpp imagecache.cpp SmileyBase.cpp AniSmileyObject.cpp resource.o -lkernel32 -lstdc++ -lgdi32 -lole32 -lcomdlg32 -luuid -lcomctl32 2> logu
diff --git a/plugins/SmileyAdd/options.cpp b/plugins/SmileyAdd/options.cpp
new file mode 100644
index 0000000000..e2b63f3be9
--- /dev/null
+++ b/plugins/SmileyAdd/options.cpp
@@ -0,0 +1,729 @@
+/*
+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 = "Smileys";
+ odp.pszGroup = "Customize";
+ odp.pfnDlgProc = DlgProcSmileysOptions;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE, addInfo, (LPARAM)&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/options.h b/plugins/SmileyAdd/options.h
new file mode 100644
index 0000000000..3b83b0dc8f
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/Matcher.cpp b/plugins/SmileyAdd/regexp/Matcher.cpp
new file mode 100644
index 0000000000..ebd1f57850
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/Matcher.h b/plugins/SmileyAdd/regexp/Matcher.h
new file mode 100644
index 0000000000..621184d8c2
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/Pattern.cpp b/plugins/SmileyAdd/regexp/Pattern.cpp
new file mode 100644
index 0000000000..4487dddd4b
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/Pattern.h b/plugins/SmileyAdd/regexp/Pattern.h
new file mode 100644
index 0000000000..bb16ad90fa
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/WCMatcher.cpp b/plugins/SmileyAdd/regexp/WCMatcher.cpp
new file mode 100644
index 0000000000..bed5a1944b
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/WCMatcher.h b/plugins/SmileyAdd/regexp/WCMatcher.h
new file mode 100644
index 0000000000..23cc49e41f
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/WCPattern.cpp b/plugins/SmileyAdd/regexp/WCPattern.cpp
new file mode 100644
index 0000000000..d396bd187d
--- /dev/null
+++ b/plugins/SmileyAdd/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());
+ *std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out) = 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/regexp/WCPattern.h b/plugins/SmileyAdd/regexp/WCPattern.h
new file mode 100644
index 0000000000..3d52a7fd2e
--- /dev/null
+++ b/plugins/SmileyAdd/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/regexp/test.cpp b/plugins/SmileyAdd/regexp/test.cpp
new file mode 100644
index 0000000000..bb41566f96
--- /dev/null
+++ b/plugins/SmileyAdd/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/resource.h b/plugins/SmileyAdd/resource.h
new file mode 100644
index 0000000000..bdf799fe2c
--- /dev/null
+++ b/plugins/SmileyAdd/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/resource.rc b/plugins/SmileyAdd/resource.rc
new file mode 100644
index 0000000000..8c53c504bf
--- /dev/null
+++ b/plugins/SmileyAdd/resource.rc
@@ -0,0 +1,214 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <windows.h>
+#include "version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_SMILEYS DIALOGEX 0, 0, 320, 256
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Smiley Categories",IDC_STATIC,6,3,310,102
+ CONTROL "",IDC_CATEGORYLIST,"SysTreeView32",TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | TVS_CHECKBOXES | WS_BORDER | WS_HSCROLL | WS_TABSTOP,14,13,104,83
+ CONTROL "Specify Smiley Pack for each category",IDC_USESTDPACK,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,124,11,165,11
+ EDITTEXT IDC_FILENAME,124,25,185,12,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES
+ PUSHBUTTON "...",IDC_BROWSE,296,15,13,11
+ LTEXT "Name:",IDC_STATIC,125,42,30,8
+ LTEXT "",IDC_LIBNAME,159,41,150,11,SS_SUNKEN | SS_PATHELLIPSIS
+ LTEXT "Author:",IDC_STATIC,125,56,30,8
+ LTEXT "",IDC_LIBAUTHOR,159,55,104,11,SS_SUNKEN | SS_PATHELLIPSIS
+ LTEXT "Version:",IDC_STATIC,125,70,30,8
+ LTEXT "",IDC_LIBVERSION,159,70,104,11,SS_SUNKEN | SS_PATHELLIPSIS
+ PUSHBUTTON "Preview",IDC_SMLOPTBUTTON,270,55,37,25
+ EDITTEXT IDC_NEWCATEGORY,126,85,129,13,ES_AUTOHSCROLL
+ PUSHBUTTON "+",IDC_ADDCATEGORY,263,84,17,15
+ PUSHBUTTON "-",IDC_DELETECATEGORY,290,84,16,14
+ GROUPBOX "Display",IDC_STATIC,6,106,310,54
+ CONTROL "Replace only smileys surrounded by spaces",IDC_SPACES,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,16,115,198,12,WS_EX_RTLREADING
+ CONTROL "Scale smiley to textheight",IDC_SCALETOTEXTHEIGHT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,128,147,10
+ CONTROL "Animate",IDC_ANIMATEDLG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,218,116,98,10
+ CONTROL "Disable custom smileys",IDC_DISABLECUSTOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,138,147,10
+ GROUPBOX "Smiley Selector",IDC_STATIC,6,160,310,46
+ CONTROL "Surround inserted smiley with spaces",IDC_APPENDSPACES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,171,235,10
+ CONTROL "Use first smiley for selection size",IDC_SCALEALLSMILEYS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,182,209,10
+ CONTROL "IEView style window",IDC_IEVIEWSTYLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,193,198,10
+ CONTROL "",IDC_SELCLR,"ColourPicker",WS_TABSTOP,260,170,50,11
+ CONTROL "Animate",IDC_ANIMATESEL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,218,193,98,10
+ GROUPBOX "Input Area",IDC_STATIC,6,208,145,42
+ CONTROL "Enable Smileys",IDC_INPUTSMILEYS,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,15,219,130,12
+ CONTROL "Don't replace at cursor",IDC_DCURSORSMILEY,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,15,231,130,12
+ GROUPBOX "Built-In Message Dialog Support",IDC_STATIC,154,208,162,42
+ CONTROL "Disable",IDC_PLUGENABLED,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,240,217,69,12
+ COMBOBOX IDC_SMLBUT,159,230,55,35,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Smiley Button",IDC_STATIC,161,219,72,8
+ EDITTEXT IDC_MAXCUSTSMSZ,165,128,30,15,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_BORDER,WS_EX_CLIENTEDGE | WS_EX_STATICEDGE
+ CONTROL "",IDC_MAXCUSTSPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,182,129,11,12
+ LTEXT "Max ""Custom Smiley"" height",IDC_STATIC,198,132,118,8
+ CONTROL "High Quality smiley scaling",IDC_HQSCALING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,148,148,10
+ EDITTEXT IDC_MINSMSZ,165,144,30,15,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_BORDER,WS_EX_CLIENTEDGE | WS_EX_STATICEDGE
+ CONTROL "",IDC_MINSPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,182,145,11,12
+ LTEXT "Min smiley height",IDC_STATIC,198,148,118,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_OPT_SMILEYS, DIALOG
+ BEGIN
+ LEFTMARGIN, 6
+ RIGHTMARGIN, 316
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 250
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION __FILEVERSION_STRING
+ PRODUCTVERSION __FILEVERSION_STRING
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Boris Krasnovskiy"
+ VALUE "FileDescription", "SmileyAdd Miranda Plugin"
+ VALUE "FileVersion", __VERSION_STRING
+ VALUE "InternalName", "SmileyAdd"
+ VALUE "LegalCopyright", "Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved"
+ VALUE "OriginalFilename", "SmileyAdd.dll"
+ VALUE "ProductName", " SmileyAdd Miranda Plugin"
+ VALUE "ProductVersion", __VERSION_STRING
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_SMILINGICON ICON "RES\\smiley.ICO"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include <windows.h>\r\n"
+ "#include ""version.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/SmileyAdd/richcall.cpp b/plugins/SmileyAdd/richcall.cpp
new file mode 100644
index 0000000000..72830400a5
--- /dev/null
+++ b/plugins/SmileyAdd/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 = (GetWindowLong(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/services.cpp b/plugins/SmileyAdd/services.cpp
new file mode 100644
index 0000000000..71b43145dc
--- /dev/null
+++ b/plugins/SmileyAdd/services.cpp
@@ -0,0 +1,597 @@
+/*
+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<HANDLE> 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;
+}
+
+#if defined(UNICODE) | defined(_UNICODE)
+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;
+}
+#endif
+
+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();
+#if defined(UNICODE) | defined(_UNICODE)
+ smre->oflag = smre->flag | SAFL_UNICODE;
+#else
+ smre->oflag = smre->flag & ~SAFL_UNICODE;
+#endif
+
+ 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;
+
+#if (defined _UNICODE || defined UNICODE)
+ unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
+ if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP;
+#endif
+
+#if (defined _UNICODE || defined UNICODE)
+ bkstring nmd(A2W_SM(smre->dispname, lpcp));
+#else
+ bkstring nmd(smre->dispname);
+#endif
+
+ 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;
+ HANDLE* 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 = (HANDLE*)CallService(MS_CLIST_ADDCONTACTMENUITEM, ind, (LPARAM)&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 = (HANDLE*)CallService(MS_CLIST_ADDCONTACTMENUITEM, 1, (LPARAM)&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 = (HANDLE*)CallService(MS_CLIST_ADDCONTACTMENUITEM, 2, (LPARAM)&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/services.h b/plugins/SmileyAdd/services.h
new file mode 100644
index 0000000000..9311e3637d
--- /dev/null
+++ b/plugins/SmileyAdd/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/smileyroutines.cpp b/plugins/SmileyAdd/smileyroutines.cpp
new file mode 100644
index 0000000000..059f0d6e15
--- /dev/null
+++ b/plugins/SmileyAdd/smileyroutines.cpp
@@ -0,0 +1,654 @@
+/*
+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);
+
+
+#ifdef _UNICODE
+bool g_HiddenTextSupported = true;
+#else
+int RichEditVersion(void);
+bool g_HiddenTextSupported = (RichEditVersion() == 3);
+#endif
+
+// {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;
+
+#if !defined(_UNICODE) && !defined(UNICODE)
+ size_t len = SysStringLen(btxt);
+ for (unsigned i=0; i<len; ++i)
+ if (btxt[i] == 65532) btxt[i] = L' ';
+#endif
+
+ 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 = (GetWindowLong(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 = (GetWindowLong(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();
+}
+
+
+#if !defined(_UNICODE) && !defined(UNICODE)
+int RichEditVersion(void)
+{
+ int ret = -1;
+
+ HMODULE hLib = GetModuleHandle(_T("riched20.dll"));
+ HRSRC hVersion = FindResource(hLib, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
+ if (hVersion != NULL)
+ {
+ HGLOBAL hGlobal = LoadResource(hLib, hVersion);
+ if (hGlobal != NULL)
+ {
+ LPVOID versionInfo = LockResource(hGlobal);
+ if (versionInfo != NULL)
+ {
+ int vl = *(unsigned short*)versionInfo;
+ unsigned *res = (unsigned*)versionInfo;
+ while (*res != 0xfeef04bd && ((char*)res - (char*)versionInfo) < vl) ++res;
+
+ if (((char*)res - (char*)versionInfo) < vl)
+ {
+ VS_FIXEDFILEINFO *vsInfo = (VS_FIXEDFILEINFO*)res;
+ ret = LOWORD(vsInfo->dwFileVersionMS) ? 3 : 2;
+ }
+ }
+ UnlockResource(hGlobal);
+ FreeResource(hGlobal);
+ }
+ }
+ return ret;
+}
+#endif
+
+
diff --git a/plugins/SmileyAdd/smileyroutines.h b/plugins/SmileyAdd/smileyroutines.h
new file mode 100644
index 0000000000..6b12ee6ead
--- /dev/null
+++ b/plugins/SmileyAdd/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/smileys.cpp b/plugins/SmileyAdd/smileys.cpp
new file mode 100644
index 0000000000..c23467812d
--- /dev/null
+++ b/plugins/SmileyAdd/smileys.cpp
@@ -0,0 +1,1152 @@
+/*
+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();
+
+#if (defined _UNICODE || defined UNICODE)
+ unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
+ if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP;
+#endif
+
+ 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);
+
+#if (defined _UNICODE || defined UNICODE)
+ displayName = A2W_SM(protoName, lpcp);
+#else
+ displayName = protoName;
+#endif
+ 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/smileys.h b/plugins/SmileyAdd/smileys.h
new file mode 100644
index 0000000000..2b127eb225
--- /dev/null
+++ b/plugins/SmileyAdd/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/smltool.cpp b/plugins/SmileyAdd/smltool.cpp
new file mode 100644
index 0000000000..79030076c8
--- /dev/null
+++ b/plugins/SmileyAdd/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 (GetWindowLong(m_hwndDialog, GWL_STYLE) & WS_VSCROLL)
+ width += GetSystemMetrics(SM_CXVSCROLL);
+
+ RECT rc = { 0, 0, width, heightn };
+ AdjustWindowRectEx(&rc, GetWindowLong(m_hwndDialog, GWL_STYLE),
+ FALSE, GetWindowLong(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/smltool.h b/plugins/SmileyAdd/smltool.h
new file mode 100644
index 0000000000..89fb786862
--- /dev/null
+++ b/plugins/SmileyAdd/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/version.h b/plugins/SmileyAdd/version.h
new file mode 100644
index 0000000000..c6960ff94c
--- /dev/null
+++ b/plugins/SmileyAdd/version.h
@@ -0,0 +1,3 @@
+#define __FILEVERSION_STRING 0,2,3,16
+#define __VERSION_STRING "0.2.3.16"
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 2, 3, 16)