From a33833212f040272fc6c97047c8cb335b6f5405a Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 24 Jul 2012 06:41:19 +0000 Subject: SimpleAR, SimpleStatusMsg, SmileyAdd, SpellChecker: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1149 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/SmileyAdd/src/AniSmileyObject.cpp | 450 +++++++ plugins/SmileyAdd/src/SmileyBase.cpp | 391 +++++++ plugins/SmileyAdd/src/SmileyBase.h | 127 ++ plugins/SmileyAdd/src/anim.cpp | 184 +++ plugins/SmileyAdd/src/anim.h | 87 ++ plugins/SmileyAdd/src/bkstring.cpp | 215 ++++ plugins/SmileyAdd/src/bkstring.h | 270 +++++ plugins/SmileyAdd/src/customsmiley.cpp | 172 +++ plugins/SmileyAdd/src/customsmiley.h | 85 ++ plugins/SmileyAdd/src/dlgboxsubclass.cpp | 568 +++++++++ plugins/SmileyAdd/src/download.cpp | 292 +++++ plugins/SmileyAdd/src/download.h | 28 + plugins/SmileyAdd/src/general.cpp | 299 +++++ plugins/SmileyAdd/src/general.h | 232 ++++ plugins/SmileyAdd/src/imagecache.cpp | 775 ++++++++++++ plugins/SmileyAdd/src/imagecache.h | 166 +++ plugins/SmileyAdd/src/main.cpp | 225 ++++ plugins/SmileyAdd/src/options.cpp | 728 ++++++++++++ plugins/SmileyAdd/src/options.h | 60 + plugins/SmileyAdd/src/regexp/Matcher.cpp | 178 +++ plugins/SmileyAdd/src/regexp/Matcher.h | 248 ++++ plugins/SmileyAdd/src/regexp/Pattern.cpp | 1709 +++++++++++++++++++++++++++ plugins/SmileyAdd/src/regexp/Pattern.h | 1663 ++++++++++++++++++++++++++ plugins/SmileyAdd/src/regexp/WCMatcher.cpp | 181 +++ plugins/SmileyAdd/src/regexp/WCMatcher.h | 234 ++++ plugins/SmileyAdd/src/regexp/WCPattern.cpp | 1747 ++++++++++++++++++++++++++++ plugins/SmileyAdd/src/regexp/WCPattern.h | 1663 ++++++++++++++++++++++++++ plugins/SmileyAdd/src/regexp/test.cpp | 38 + plugins/SmileyAdd/src/resource.h | 49 + plugins/SmileyAdd/src/richcall.cpp | 554 +++++++++ plugins/SmileyAdd/src/services.cpp | 590 ++++++++++ plugins/SmileyAdd/src/services.h | 48 + plugins/SmileyAdd/src/smileyroutines.cpp | 610 ++++++++++ plugins/SmileyAdd/src/smileyroutines.h | 49 + plugins/SmileyAdd/src/smileys.cpp | 1146 ++++++++++++++++++ plugins/SmileyAdd/src/smileys.h | 287 +++++ plugins/SmileyAdd/src/smltool.cpp | 785 +++++++++++++ plugins/SmileyAdd/src/smltool.h | 45 + plugins/SmileyAdd/src/version.h | 3 + 39 files changed, 17181 insertions(+) create mode 100644 plugins/SmileyAdd/src/AniSmileyObject.cpp create mode 100644 plugins/SmileyAdd/src/SmileyBase.cpp create mode 100644 plugins/SmileyAdd/src/SmileyBase.h create mode 100644 plugins/SmileyAdd/src/anim.cpp create mode 100644 plugins/SmileyAdd/src/anim.h create mode 100644 plugins/SmileyAdd/src/bkstring.cpp create mode 100644 plugins/SmileyAdd/src/bkstring.h create mode 100644 plugins/SmileyAdd/src/customsmiley.cpp create mode 100644 plugins/SmileyAdd/src/customsmiley.h create mode 100644 plugins/SmileyAdd/src/dlgboxsubclass.cpp create mode 100644 plugins/SmileyAdd/src/download.cpp create mode 100644 plugins/SmileyAdd/src/download.h create mode 100644 plugins/SmileyAdd/src/general.cpp create mode 100644 plugins/SmileyAdd/src/general.h create mode 100644 plugins/SmileyAdd/src/imagecache.cpp create mode 100644 plugins/SmileyAdd/src/imagecache.h create mode 100644 plugins/SmileyAdd/src/main.cpp create mode 100644 plugins/SmileyAdd/src/options.cpp create mode 100644 plugins/SmileyAdd/src/options.h create mode 100644 plugins/SmileyAdd/src/regexp/Matcher.cpp create mode 100644 plugins/SmileyAdd/src/regexp/Matcher.h create mode 100644 plugins/SmileyAdd/src/regexp/Pattern.cpp create mode 100644 plugins/SmileyAdd/src/regexp/Pattern.h create mode 100644 plugins/SmileyAdd/src/regexp/WCMatcher.cpp create mode 100644 plugins/SmileyAdd/src/regexp/WCMatcher.h create mode 100644 plugins/SmileyAdd/src/regexp/WCPattern.cpp create mode 100644 plugins/SmileyAdd/src/regexp/WCPattern.h create mode 100644 plugins/SmileyAdd/src/regexp/test.cpp create mode 100644 plugins/SmileyAdd/src/resource.h create mode 100644 plugins/SmileyAdd/src/richcall.cpp create mode 100644 plugins/SmileyAdd/src/services.cpp create mode 100644 plugins/SmileyAdd/src/services.h create mode 100644 plugins/SmileyAdd/src/smileyroutines.cpp create mode 100644 plugins/SmileyAdd/src/smileyroutines.h create mode 100644 plugins/SmileyAdd/src/smileys.cpp create mode 100644 plugins/SmileyAdd/src/smileys.h create mode 100644 plugins/SmileyAdd/src/smltool.cpp create mode 100644 plugins/SmileyAdd/src/smltool.h create mode 100644 plugins/SmileyAdd/src/version.h (limited to 'plugins/SmileyAdd/src') diff --git a/plugins/SmileyAdd/src/AniSmileyObject.cpp b/plugins/SmileyAdd/src/AniSmileyObject.cpp new file mode 100644 index 0000000000..42a63d3549 --- /dev/null +++ b/plugins/SmileyAdd/src/AniSmileyObject.cpp @@ -0,0 +1,450 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "SmileyBase.h" +#include "m_smileyadd.h" +#include "smileys.h" +#include "options.h" + +#include + + +class CAniSmileyObject; + +static int CompareAniSmiley(const CAniSmileyObject* p1, const CAniSmileyObject* p2) +{ + return (int)((char*)p2 - (char*)p1); +} + +static LIST 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; iProcessTimerTick(); +} + +void DestroyAniSmileys(void) +{ + if (timerId && (timerId+1)) + { + KillTimer(NULL, timerId); + timerId = 0; + } + regAniSmileys.destroy(); +} + diff --git a/plugins/SmileyAdd/src/SmileyBase.cpp b/plugins/SmileyAdd/src/SmileyBase.cpp new file mode 100644 index 0000000000..28bd767153 --- /dev/null +++ b/plugins/SmileyAdd/src/SmileyBase.cpp @@ -0,0 +1,391 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "SmileyBase.h" + +#include + +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 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(this); + else if (iid == IID_IViewObject) + *ppvObject = static_cast(this); + else if (iid == IID_IOleObject) + *ppvObject = static_cast(this); + else if (iid == IID_IUnknown) + *ppvObject = this; + else if (iid == IID_IViewObject2) + *ppvObject = static_cast(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; iQueryHitPointSpecial(x, y, hwnd, smltxt)) return i; + + return -1; +} + +void DestroySmileyBase(void) +{ + regSmileys.destroy(); +} + diff --git a/plugins/SmileyAdd/src/SmileyBase.h b/plugins/SmileyAdd/src/SmileyBase.h new file mode 100644 index 0000000000..3cdf244007 --- /dev/null +++ b/plugins/SmileyAdd/src/SmileyBase.h @@ -0,0 +1,127 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include + +#define OLEIVERB_SETOWNER (-24) + +EXTERN_C const IID IID_ITooltipData; + +class ITooltipData : public IUnknown +{ +public: + STDMETHOD(SetTooltip) (BSTR bstrHint) PURE; + STDMETHOD(GetTooltip) (BSTR * bstrHint) PURE; +}; + + +EXTERN_C const IID IID_ISmileyAddSmiley; + +class ISmileyBase : + public IOleObject, public IViewObject2, public ITooltipData +{ +protected: + IOleAdviseHolder* m_spAdviseHolder; + IAdviseSink* m_spAdviseSink; + IOleClientSite* m_spClientSite; + DWORD m_advf; + LONG m_lRefCount; + + SIZEL m_sizeExtent; + SIZEL m_sizeExtentHiM; + RECT m_orect; + + TCHAR* m_smltxt; + HWND m_hwnd; + + bool m_visible; + bool m_dirAniAllow; + +public: + ISmileyBase(void); + virtual ~ISmileyBase(void); + + void OnClose(void); + void SendOnViewChange(void); + + bool QueryHitPointSpecial(int x, int y, HWND hwnd, TCHAR** smltxt); + void SetHint(TCHAR* smltxt); + + virtual void SetPosition(HWND hwnd, LPCRECT lpRect); + + // + // IUnknown members + // + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject); + + // + // IOleObject members + // + STDMETHOD(SetClientSite)(IOleClientSite *pClientSite); + STDMETHOD(GetClientSite)(IOleClientSite **ppClientSite); + STDMETHOD(SetHostNames)(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */); + STDMETHOD(Close)(DWORD /* dwSaveOption */); + STDMETHOD(SetMoniker)(DWORD /* dwWhichMoniker */, IMoniker* /* pmk */); + STDMETHOD(GetMoniker)(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */, IMoniker** /* ppmk */); + STDMETHOD(InitFromData)(IDataObject* /* pDataObject */, BOOL /* fCreation */, DWORD /* dwReserved */); + STDMETHOD(GetClipboardData)(DWORD /* dwReserved */, IDataObject** /* ppDataObject */); + STDMETHOD(DoVerb)(LONG /*iVerb*/, LPMSG /* pMsg */, IOleClientSite* /* pActiveSite */, LONG /* lindex */, + HWND /*hwndParent*/, LPCRECT /*lprcPosRect*/); + STDMETHOD(EnumVerbs)(IEnumOLEVERB** /*ppEnumOleVerb*/); + STDMETHOD(Update)(void); + STDMETHOD(IsUpToDate)(void); + STDMETHOD(GetUserClassID)(CLSID *pClsid); + STDMETHOD(GetUserType)(DWORD /*dwFormOfType*/, LPOLESTR* /*pszUserType*/); + STDMETHOD(SetExtent)(DWORD /*dwDrawAspect*/, SIZEL* /*psizel*/); + STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL *psizel); + STDMETHOD(Advise)(IAdviseSink *pAdvSink, DWORD *pdwConnection); + STDMETHOD(Unadvise)(DWORD dwConnection); + STDMETHOD(EnumAdvise)(IEnumSTATDATA **ppEnumAdvise); + STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus); + STDMETHOD(SetColorScheme)(LOGPALETTE* /* pLogpal */); + + // + // IViewObject members + // + STDMETHOD(SetAdvise)(DWORD aspect, DWORD advf, IAdviseSink* pAdvSink); + STDMETHOD(GetAdvise)(DWORD* /*pAspects*/, DWORD* /*pAdvf*/, IAdviseSink** ppAdvSink); + STDMETHOD(Freeze)(DWORD, long, void*, DWORD*); + STDMETHOD(Unfreeze)(DWORD); + STDMETHOD(GetColorSet)(DWORD, long, void*, DVTARGETDEVICE*, HDC, + LOGPALETTE**); + + // + // IViewObject2 members + // + STDMETHOD(GetExtent)(DWORD aspect, long, DVTARGETDEVICE*, SIZEL* pSize); + + // + // ITooltipData members + // + STDMETHOD(SetTooltip)(BSTR bstrHint); + STDMETHOD(GetTooltip)(BSTR * bstrHint); + +}; + +int CheckForTip(int x, int y, HWND hwnd, TCHAR** smltxt); +void CloseSmileys(void); + diff --git a/plugins/SmileyAdd/src/anim.cpp b/plugins/SmileyAdd/src/anim.cpp new file mode 100644 index 0000000000..5352383821 --- /dev/null +++ b/plugins/SmileyAdd/src/anim.cpp @@ -0,0 +1,184 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2006 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "anim.h" + +#include + +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. +*/ + +#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 m_AniList; + + HWND m_hwnd; + int m_wsize; + + HBRUSH m_hbr; + HBITMAP m_hBmp; + HDC m_hdcMem; + HBITMAP m_hOld; + + static unsigned CALLBACK AnimateThreadFunc ( void* arg ); + +public: + AnimatedPack(HWND hwnd, int wsize, SIZE& sel, COLORREF bkg); + ~AnimatedPack(); + + void Add(SmileyType *sml, RECT rect, bool clip); + void Draw(HDC hdc); + void SetOffset(int off); + void SetSel(RECT& rect); + + void ProcessTimerTick(HWND hwnd); +}; + +#endif diff --git a/plugins/SmileyAdd/src/bkstring.cpp b/plugins/SmileyAdd/src/bkstring.cpp new file mode 100644 index 0000000000..2973783abc --- /dev/null +++ b/plugins/SmileyAdd/src/bkstring.cpp @@ -0,0 +1,215 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef __GNUC__ +# ifdef _DEBUG +# define _CRTDBG_MAP_ALLOC +# include +# include +# else +# include +# endif +#endif + +#include +#include + +#include "bkstring.h" + + +bkstring::~bkstring() { if (sizeAlloced) free(buf); } + +void bkstring::reserve(size_type len) +{ + if (len >= sizeAlloced || sizeAlloced == 0) + { + if (sizeAlloced == 0) buf = NULL; + buf = (value_type*)realloc(buf, (len+1) * sizeof(value_type)); + if (sizeAlloced == 0) buf[0] = 0; + sizeAlloced = len+1; + } +} + +void bkstring::appendfmt(const value_type *fmt, ...) +{ + areserve(_tcslen(fmt)*2); + + va_list vararg; + va_start(vararg, fmt); + for (;;) + { + int len = _vsntprintf(buf + lenBuf, sizeAlloced - lenBuf - 1, fmt, vararg); + if (len < 0) + reserve(sizeAlloced + 256); + else + { + lenBuf += len; + buf[lenBuf] = 0; + break; + } + } + va_end(vararg); +} + +bkstring& bkstring::append(const value_type* _Ptr) +{ + size_type len = _tcslen(_Ptr); + areserve(len); + memcpy(buf+lenBuf, _Ptr, (len+1)*sizeof(value_type)); + lenBuf += len; + return *this; +} + +bkstring& bkstring::append(const value_type* _Ptr, size_type _Count) +{ + size_type len = min(_tcslen(_Ptr), _Count); + areserve(len); + memcpy(buf+lenBuf, _Ptr, len*sizeof(value_type)); + lenBuf += len; + buf[lenBuf] = 0; + return *this; +} + +bkstring& bkstring::append(const bkstring& _Str, size_type _Off, size_type _Count) +{ + size_type len = min(_Count, _Str.size() - _Off); + areserve(len); + memcpy(buf+lenBuf, _Str.c_str()+_Off, len*sizeof(value_type)); + lenBuf += len; + buf[lenBuf] = 0; + return *this; +} + +bkstring& bkstring::append(const bkstring& _Str) +{ + size_type len = _Str.size(); + areserve(len); + memcpy(buf+lenBuf, _Str.c_str(), len*sizeof(value_type)); + lenBuf += len; + buf[lenBuf] = 0; + return *this; +} + +bkstring& bkstring::append(size_type _Count, value_type _Ch) +{ + areserve(_Count); + for(size_type i=0; i<_Count; ++i) buf[lenBuf+i] = _Ch; + lenBuf += _Count; + buf[lenBuf] = 0; + return *this; +} + + +bkstring& bkstring::assign(const value_type* _Ptr, size_type _Count) +{ + if (_Count == 0 && sizeAlloced == 0) + { + buf = (TCHAR*)_T(""); + } + else + { + reserve(_Count); + memcpy(buf, _Ptr, _Count*sizeof(value_type)); + buf[_Count] = 0; + lenBuf = _Count; + } + return *this; +} + +bkstring& bkstring::assign(const bkstring& _Str, size_type _Off, size_type _Count) +{ + size_type len = min(_Count, _Str.size() - _Off); + if (len == 0 && sizeAlloced == 0) + { + buf = (TCHAR*)_T(""); + } + else + { + reserve(len); + memcpy(buf, _Str.c_str() + _Off, len*sizeof(value_type)); + lenBuf = len; + buf[len] = 0; + } + return *this; +} + +bkstring& bkstring::assign(size_type _Count, value_type _Ch) +{ + reserve(_Count); + for(size_type i=0; i<_Count; ++i) buf[i] = _Ch; + buf[_Count] = 0; + lenBuf = _Count; + return *this; +} + +bkstring::size_type bkstring::find(value_type _Ch, size_type _Off) const +{ + for (size_type i=_Off; i<=lenBuf; ++i) + if (buf[i] == _Ch) return i; + return (size_type)npos; +} + +bkstring::size_type bkstring::find(const value_type* _Ptr, size_type _Off) const +{ + if (_Off > lenBuf) return (size_type)npos; + + value_type* pstr = _tcsstr(buf+_Off, _Ptr); + return pstr ? pstr - buf : npos; +} + +bkstring::size_type bkstring::find_last_of(value_type _Ch, size_type _Off) const +{ + for (size_type i=(_Off == npos ? lenBuf : _Off); i--;) + if (buf[i] == _Ch) return i; + return (size_type)npos; +} + +bkstring& bkstring::insert(size_type _P0, const value_type* _Ptr, size_type _Count) +{ + size_type len = _tcslen(_Ptr); + if (_Count < len) len = _Count; + areserve(len); + value_type *p = buf + _P0; + memmove(p+len, p, (lenBuf-_P0+1)*sizeof(value_type)); + memcpy(p, _Ptr, _Count*sizeof(value_type)); + lenBuf += len; + return *this; +} + +bkstring& bkstring::insert(size_type _P0, size_type _Count, value_type _Ch) +{ + areserve(_Count); + value_type *p = buf + _P0; + memmove(p+_Count, p, (lenBuf-_P0+1)*sizeof(value_type)); + for(size_type i=0; i<_Count; ++i) p[i] = _Ch; + lenBuf += _Count; + return *this; +} + +bkstring& bkstring::erase(size_type _Pos, size_type _Count) +{ + if (_Pos < lenBuf) + { + const size_type len = min(lenBuf - _Pos, _Count); + value_type *p = buf + _Pos; + lenBuf -= len; + memmove(p, p+len, (lenBuf - _Pos)*sizeof(value_type)); + buf[lenBuf] = 0; + } + return *this; +} diff --git a/plugins/SmileyAdd/src/bkstring.h b/plugins/SmileyAdd/src/bkstring.h new file mode 100644 index 0000000000..eb164f74e2 --- /dev/null +++ b/plugins/SmileyAdd/src/bkstring.h @@ -0,0 +1,270 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include + +#ifndef min +#define min(A, B) ((A) < (B) ? (A) : (B)) +#endif + +class bkstring +{ +public: + typedef size_t size_type; + typedef TCHAR value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + enum { npos = -1 }; +#else + static const size_type npos = size_type(-1); +#endif + +private: + value_type* buf; + size_type sizeAlloced; + size_type lenBuf; + + void areserve(size_type len) { reserve(lenBuf + len); } + +public: + + explicit bkstring() : buf((TCHAR*)_T("")), sizeAlloced(0), lenBuf(0) + {} + + bkstring(const value_type* _Ptr, size_type _Count) : sizeAlloced(0), lenBuf(0) + { assign(_Ptr, _Count); } + + bkstring(const value_type* _Ptr) : sizeAlloced(0), lenBuf(0) + { assign(_Ptr); } + + bkstring(size_type _Count, value_type _Ch) : sizeAlloced(0), lenBuf(0) + { assign(_Count, _Ch); } + + bkstring(const bkstring& _Str) : sizeAlloced(0), lenBuf(0) + { assign(_Str); } + + bkstring(const bkstring& _Str, size_type _Off, size_type _Count) : sizeAlloced(0), lenBuf(0) + { assign(_Str, _Off, _Count); } + + ~bkstring(); + + size_type size(void) const { return lenBuf; } + const value_type* c_str(void) const { return buf; } + + void clear(void) { if (lenBuf) { lenBuf = 0; buf[0] = 0; } } + void insert(const value_type *txt); + void reserve(size_type len); + + bkstring& assign(const value_type* _Ptr) + { return assign(_Ptr, _tcslen(_Ptr)); } + + bkstring& assign(const bkstring& _Str) + { return assign(_Str, 0, (size_type)npos); } + + bkstring& assign(const value_type* _Ptr, size_type _Count); + bkstring& assign(const bkstring& _Str, size_type off, size_type _Count); + bkstring& assign(size_type _Count, value_type _Ch); + + bkstring& append(const value_type* _Ptr); + bkstring& append(const value_type* _Ptr, size_type _Count); + bkstring& append(const bkstring& _Str, size_type _Off, size_type _Count); + bkstring& append(const bkstring& _Str); + bkstring& append(size_type _Count, value_type _Ch); + + int compare(const bkstring& _Str) const + { return _tcscmp(buf, _Str.c_str()); } + + int compare(size_type _Pos1, size_type _Num1, const bkstring& _Str) const + { return _tcsncmp(&buf[_Pos1], _Str.c_str(), _Num1); } + + int compare(size_type _Pos1, size_type _Num1, const bkstring& _Str, size_type _Off, size_type _Count) const + { return _tcsncmp(&buf[_Pos1], _Str.c_str()+_Off, min(_Num1, _Count)); } + + int compare(const value_type* _Ptr) const + { return _tcscmp(buf, _Ptr); } + + int compare(size_type _Pos1, size_type _Num1, const value_type* _Ptr) const + { return _tcsncmp(&buf[_Pos1], _Ptr, _Num1); } + + int compare(size_type _Pos1, size_type _Num1, const value_type* _Ptr, size_type _Num2) const + { return _tcsncmp(&buf[_Pos1], _Ptr, min(_Num1, _Num2)); } + + int comparei(const bkstring& _Str) const + { return _tcsicmp(buf, _Str.c_str()); } + + int comparei(size_type _Pos1, size_type _Num1, const bkstring& _Str) const + { return _tcsnicmp(&buf[_Pos1], _Str.c_str(), _Num1); } + + int comparei(size_type _Pos1, size_type _Num1, const bkstring& _Str, size_type _Off, size_type _Count) const + { return _tcsnicmp(&buf[_Pos1], _Str.c_str()+_Off, min(_Num1, _Count)); } + + int comparei(const value_type* _Ptr) const + { return _tcsicmp(buf, _Ptr); } + + int comparei(size_type _Pos1, size_type _Num1, const value_type* _Ptr) const + { return _tcsnicmp(&buf[_Pos1], _Ptr, _Num1); } + + int comparei(size_type _Pos1, size_type _Num1, const value_type* _Ptr, size_type _Num2) const + { return _tcsnicmp(&buf[_Pos1], _Ptr, min(_Num1, _Num2)); } + + bool empty(void) const { return lenBuf == 0; }; + bkstring& erase(size_type _Pos = 0, size_type _Count = npos); + + size_type find(value_type _Ch, size_type _Off = 0) const; + size_type find(const value_type* _Ptr, size_type _Off = 0) const; + size_type find(bkstring& _Str, size_type _Off = 0) const + { return find(_Str.c_str(), _Off); } + + size_type find_last_of(value_type _Ch, size_type _Off = npos) const; + + bkstring& insert(size_type _P0, const value_type* _Ptr) + { return insert(_P0, _Ptr, _tcslen(_Ptr)); } + + bkstring& insert(size_type _P0, const bkstring& _Str) + { return insert(_P0, _Str.c_str(), _Str.size()); }; + + bkstring& insert(size_type _P0, const value_type* _Ptr, size_type _Count); + bkstring& insert(size_type _P0, size_type _Count, value_type _Ch); + + bkstring substr(size_type _Off = 0, size_type _Count = npos) const + { return bkstring(*this, _Off, _Count); } + + bkstring& operator = (const bkstring& _Str) + { return assign(_Str); } + + bkstring& operator =(const value_type* _Ptr) + { return assign(_Ptr); } + + bkstring& operator = (const value_type _Ch) + { return assign(1, _Ch); } + + bkstring& operator +=(const bkstring& _Str) + { return append(_Str); } + + bkstring& operator += (const value_type* _Ptr) + { return append(_Ptr); } + + bkstring& operator += (const value_type _Ch) + { return append(1, _Ch); } + + value_type& operator[] (int ind) const + { return buf[ind]; } + + friend bkstring operator+ (const bkstring& _Str1, const bkstring& _Str2) + { bkstring s(_Str1); return s.append(_Str2); } + + friend bkstring operator+ (const bkstring& _Str1, const value_type* _Ptr2) + { bkstring s(_Str1); return s.append(_Ptr2); } + + friend bkstring operator+(const value_type* _Ptr1, const bkstring& _Str2) + { bkstring s(_Ptr1); return s.append(_Str2); } + + friend bkstring operator+ (const bkstring& _Str1, const value_type _Ch) + { bkstring s(_Str1); return s.append(1, _Ch); } + + friend bool operator==(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) == 0; } + + friend bool operator==(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) == 0; } + + friend bool operator==(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) == 0; } + + friend bool operator!=(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) != 0; } + + friend bool operator!=(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) != 0; } + + friend bool operator!=(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) != 0; } + + friend bool operator<(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) < 0; } + + friend bool operator<(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) < 0; } + + friend bool operator<(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) > 0; } + + friend bool operator>(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) > 0; } + + friend bool operator>(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) > 0; } + + friend bool operator>(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) < 0; } + + friend bool operator<=(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) <= 0; } + + friend bool operator<=(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) <= 0; } + + friend bool operator<=(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) >= 0; } + + friend bool operator>=(const bkstring& _Str1, const bkstring& _Str2) + { return _Str1.compare(_Str2) >= 0; } + + friend bool operator>=(const bkstring& _Str1, const value_type* _Ptr2) + { return _Str1.compare(_Ptr2) >= 0; } + + friend bool operator>=(const value_type* _Ptr1, const bkstring& _Str2) + { return _Str2.compare(_Ptr1) <= 0; } + + friend bool operator==(const value_type _Ch1, const bkstring& _Str2) + { return (_Str2.size() == 1) && (_Str2[0] == _Ch1); } + + friend bool operator==(const bkstring& _Str1, const value_type _Ch2) + { return (_Str1.size() == 1) && (_Str1[0] == _Ch2); } + + friend bool operator!=(const value_type _Ch1, const bkstring& _Str2) + { return (_Str2.size() != 1) || (_Str2[0] != _Ch1); } + + friend bool operator!=(const bkstring& _Str1, const value_type _Ch2) + { return (_Str1.size() != 1) || (_Str1[0] != _Ch2); } + + iterator begin(void) + { return buf; } + + const_iterator begin(void) const + { return buf; } + + iterator end(void) + { return buf + lenBuf; } + + const_iterator end(void) const + { return buf + lenBuf; } + + // Custom extentions + + void appendfmt(const value_type *fmt, ...); + + size_type sizebytes(void) const { return lenBuf * sizeof(value_type); } +}; + +//const bkstring::size_type bkstring::npos = -1; diff --git a/plugins/SmileyAdd/src/customsmiley.cpp b/plugins/SmileyAdd/src/customsmiley.cpp new file mode 100644 index 0000000000..f125f015db --- /dev/null +++ b/plugins/SmileyAdd/src/customsmiley.cpp @@ -0,0 +1,172 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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. +*/ + +#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 SmileyVectorType; + typedef SMOBJLIST 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 SmileyPackVectorType; + +private: + SmileyPackVectorType m_SmileyPacks; + +public: + int NumberOfSmileyPacks(void) { return m_SmileyPacks.getCount(); } + + bool AddSmileyPack(HANDLE hContact, TCHAR* dir); + bool AddSmiley(HANDLE hContact, TCHAR* path); + + void ClearAndFreeAll(void) { m_SmileyPacks.destroy(); } + + SmileyPackCType* GetSmileyPack(HANDLE id); +}; + +extern SmileyPackCListType g_SmileyPackCStore; + +#endif diff --git a/plugins/SmileyAdd/src/dlgboxsubclass.cpp b/plugins/SmileyAdd/src/dlgboxsubclass.cpp new file mode 100644 index 0000000000..03a0a6e5fd --- /dev/null +++ b/plugins/SmileyAdd/src/dlgboxsubclass.cpp @@ -0,0 +1,568 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2012 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "smileyroutines.h" +#include "services.h" +#include "options.h" + +//***************************************************// +// DISCLAIMER!!! +// we are not supposed to use this object, so be aware +typedef struct NewMessageWindowLParam +{ + HANDLE hContact; + int isSend; + const char *szInitialText; +} +msgData; +// this is an undocumented object!!!!!!! +// subject to change in miranda versions...!!!!!! +// DISCLAIMER!!! +//***************************************************// + +extern HINSTANCE g_hInst; + +static HHOOK g_hMessageHookPre = NULL; +static HANDLE g_hMutex = NULL; +static HANDLE g_hHookMsgWnd = NULL; + +static LRESULT CALLBACK MessageDlgSubclas(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +//type definitions +class MsgWndData +{ +public: + HWND hwnd; + char ProtocolName[52]; + HWND REdit; + HWND QuoteB; + HWND MEdit; + HWND MOK; + HWND LButton; + mutable HWND hSmlButton; + mutable HBITMAP hSmlBmp; + mutable HICON hSmlIco; + int idxLastChar; + WNDPROC wpOrigWndProc; + HANDLE hContact; + bool doSmileyReplace; + bool doSmileyButton; + bool OldButtonPlace; + bool isSplit; + bool isSend; + + MsgWndData() + { + ProtocolName[0] = 0; + REdit = NULL; + QuoteB = NULL; + MEdit = NULL; + MOK = NULL; + LButton = NULL; + hSmlButton = NULL; + hSmlBmp = NULL; + hSmlIco = NULL; + idxLastChar = 0; + hContact = NULL; + doSmileyReplace = false; + doSmileyButton = false; + OldButtonPlace = false; + isSplit = false; + isSend = false; + wpOrigWndProc = NULL; + } + + MsgWndData(const MsgWndData &dsb) + { + *this = dsb; + dsb.hSmlBmp = NULL; + dsb.hSmlIco = NULL; + dsb.hSmlButton = NULL; + } + + + ~MsgWndData() + { + clear(); + } + + void clear(void) + { + if (hSmlBmp != NULL) DeleteObject(hSmlBmp); + if (hSmlIco != NULL) DestroyIcon(hSmlIco); + if (hSmlButton != NULL) DestroyWindow(hSmlButton); + hSmlBmp = NULL; + hSmlIco = NULL; + hSmlButton = NULL; + } + + RECT CalcSmileyButtonPos(void) + { + RECT rect; + POINT pt; + + if (OldButtonPlace) + { + if (isSplit && DBGetContactSettingByte(NULL, "SRMsg", "ShowQuote", FALSE)) + { + GetWindowRect(QuoteB, &rect); + pt.x = rect.right + 12; + } + else + { + GetWindowRect(MEdit, &rect); + pt.x = rect.left; + } + GetWindowRect(MOK, &rect); + pt.y = rect.top; + } + else + { + GetWindowRect(LButton, &rect); + pt.y = rect.top; + + if ((GetWindowLongPtr(LButton, GWL_STYLE) & WS_VISIBLE) != 0) + pt.x = rect.left - 28; + else + pt.x = rect.left; + } + + ScreenToClient(GetParent(LButton), &pt); + rect.bottom += pt.y - rect.top; + rect.right += pt.x - rect.left; + rect.top = pt.y; + rect.left = pt.x; + + return rect; + } + + //helper function + //identifies the message dialog + bool IsMessageSendDialog(HWND hwnd) + { + TCHAR szClassName[32] = _T(""); + + GetClassName(hwnd, szClassName, SIZEOF(szClassName)); + if (_tcscmp(szClassName, _T("#32770"))) return false; + + if ((REdit = GetDlgItem(hwnd, MI_IDC_LOG)) != NULL) + { + GetClassName(REdit, szClassName, SIZEOF(szClassName)); + if (_tcscmp(szClassName, _T("RichEdit20A")) != 0 && + _tcscmp(szClassName, _T("RichEdit20W")) != 0 && + _tcscmp(szClassName, _T("RICHEDIT50W")) != 0) return false; + } + else return false; + + if ((MEdit = GetDlgItem(hwnd, MI_IDC_MESSAGE)) != NULL) + { + GetClassName(MEdit, szClassName, SIZEOF(szClassName)); + if (_tcscmp(szClassName, _T("Edit")) != 0 && + _tcscmp(szClassName, _T("RichEdit20A")) != 0 && + _tcscmp(szClassName, _T("RichEdit20W")) != 0 && + _tcscmp(szClassName, _T("RICHEDIT50W")) != 0) return false; + } + else return false; + + QuoteB = GetDlgItem(hwnd, MI_IDC_QUOTE); + + if ((LButton = GetDlgItem(hwnd, MI_IDC_ADD)) == NULL) + return false; + + if (GetDlgItem(hwnd, MI_IDC_NAME) == NULL) + return false; + if ((MOK = GetDlgItem(hwnd, IDOK)) == NULL) + return false; + + return true; + } + + void CreateSmileyButton(void) + { + doSmileyButton = opt.ButtonStatus != 0; + OldButtonPlace = opt.ButtonStatus == 2; + + SmileyPackType* SmileyPack = GetSmileyPack(ProtocolName, hContact); + doSmileyButton &= SmileyPack != NULL && SmileyPack->VisibleSmileyCount() != 0; + + bool showButtonLine; + if (IsOldSrmm()) + { + isSplit = DBGetContactSettingByte(NULL,"SRMsg","Split", TRUE) != 0; + + doSmileyReplace = (isSplit || !isSend); + doSmileyButton &= isSplit || isSend; + showButtonLine = DBGetContactSettingByte(NULL, "SRMsg", "ShowButtonLine", TRUE) != 0; + } + else + { + doSmileyReplace = true; + OldButtonPlace = false; + showButtonLine = DBGetContactSettingByte(NULL, "SRMM", "ShowButtonLine", TRUE) != 0; + } + + doSmileyButton &= OldButtonPlace || showButtonLine; + + if (ProtocolName[0] != 0) + { + INT_PTR cap = CallProtoService(ProtocolName, PS_GETCAPS, PFLAGNUM_1, 0); + doSmileyButton &= ((cap & (PF1_IMSEND | PF1_CHAT)) != 0); + doSmileyReplace &= ((cap & (PF1_IMRECV | PF1_CHAT)) != 0); + } + + if (doSmileyButton && opt.PluginSupportEnabled) + { + //create smiley button + RECT rect = CalcSmileyButtonPos(); + + hSmlButton = CreateWindowEx( + WS_EX_LEFT | WS_EX_NOPARENTNOTIFY | WS_EX_TOPMOST, + MIRANDABUTTONCLASS, + _T("S"), + WS_CHILD|WS_VISIBLE|WS_TABSTOP, // window style + rect.left, // horizontal position of window + rect.top, // vertical position of window + rect.bottom - rect.top + 1, // window width + rect.bottom - rect.top + 1, // window height + GetParent(LButton), // handle to parent or owner window + (HMENU) IDC_SMLBUTTON, // menu handle or child identifier + NULL, // handle to application instance + NULL); // window-creation data + + // Conversion to bitmap done to prevent Miranda from scaling the image + SmileyType* sml = FindButtonSmiley(SmileyPack); + if (sml != NULL) + { + hSmlBmp = sml->GetBitmap(GetSysColor(COLOR_BTNFACE), 0, 0); + SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hSmlBmp); + } + else + { + hSmlIco = GetDefaultIcon(); + SendMessage(hSmlButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hSmlIco); + } + + SendMessage(hSmlButton, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Show Smiley Selection Window"), 0); + SendMessage(hSmlButton, BUTTONSETASFLATBTN, TRUE, 0); + } + } +}; + +static int CompareMsgWndData(const MsgWndData* p1, const MsgWndData* p2) +{ + return (int)((INT_PTR)p1->hwnd - (INT_PTR)p2->hwnd); +} +static LIST 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; ihContact == (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) >l, 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. +*/ + +#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 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; iheadersCount; i++) + { + if (!strcmp(nlhrReply->headers[i].szName, "Location")) + { + size_t rlen = 0; + if (nlhrReply->headers[i].szValue[0] == '/') + { + const char* szPath; + const char* szPref = strstr(szUrl, "://"); + szPref = szPref ? szPref + 3 : szUrl; + szPath = strchr(szPref, '/'); + rlen = szPath != NULL ? szPath - szUrl : strlen(szUrl); + } + + szRedirUrl = (char*)mir_realloc(szRedirUrl, + rlen + strlen(nlhrReply->headers[i].szValue)*3 + 1); + + strncpy(szRedirUrl, szUrl, rlen); + strcpy(szRedirUrl+rlen, nlhrReply->headers[i].szValue); + + nlhr.szUrl = szRedirUrl; + break; + } + } + } + else + result = 1; + } + else + { + hHttpDwnl = NULL; + result = 1; + } + + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)nlhrReply); + } + + mir_free(szRedirUrl); + + return result == 0; +} + + +void __cdecl SmileyDownloadThread(void*) +{ + bool needext = false; + HANDLE hHttpDwnl = NULL; + WaitForSingleObject(g_hDlMutex, 3000); + while (!Miranda_Terminated() && dlQueue.getCount()) + { + ReleaseMutex(g_hDlMutex); + if (_taccess(dlQueue[0].fname.c_str(), 0) != 0) + { + InternetDownloadFile(T2A_SM(dlQueue[0].url.c_str()), T2A_SM(dlQueue[0].fname.c_str()), hHttpDwnl); + WaitForSingleObject(g_hDlMutex, 3000); + + bkstring fname(dlQueue[0].fname); + if (dlQueue[0].needext) { fname += GetImageExt(fname); needext = true; } + _trename(dlQueue[0].fname.c_str(), fname.c_str()); + } + else + WaitForSingleObject(g_hDlMutex, 3000); + + dlQueue.remove(0); + } + dlQueue.destroy(); + if (hHttpDwnl) Netlib_CloseHandle(hHttpDwnl); + threadRunning = false; + ReleaseMutex(g_hDlMutex); + + if (!Miranda_Terminated()) + { + if (needext) + CallServiceSync(MS_SMILEYADD_RELOAD, 0, 0); + else + NotifyEventHooks(hEvent1, 0, 0); + } +} + + +bool GetSmileyFile(bkstring& url, const bkstring& packstr) +{ + _TPattern * urlsplit = _TPattern::compile(_T(".*/(.*)")); + _TMatcher * m0 = urlsplit->createTMatcher(url); + + m0->findFirstMatch(); + + bkstring filename; + filename.appendfmt(_T("%s\\%s\\"), cachepath, packstr.c_str()); + size_t pathpos = filename.size(); + filename += m0->getGroup(1); + + delete m0; + delete urlsplit; + + bool needext = filename.find('.') == filename.npos; + if (needext) filename += _T(".*"); + + _tfinddata_t c_file; + INT_PTR hFile = _tfindfirst((TCHAR*)filename.c_str(), &c_file); + if (hFile > -1) + { + _findclose(hFile); + filename.erase(pathpos); + filename += c_file.name; + url = filename; + return false; + } + if (needext) filename.erase(filename.size()-1); + + WaitForSingleObject(g_hDlMutex, 3000); + dlQueue.insert(new QueueElem(url, filename, needext)); + ReleaseMutex(g_hDlMutex); + + if (!threadRunning) + { + threadRunning = true; + mir_forkthread(SmileyDownloadThread, NULL); + } + + url = filename; + return false; +} + +void GetDefaultSmileyCacheFolder(TCHAR* szPath, size_t cbLen) +{ + TCHAR *tmpPath = Utils_ReplaceVarsT(_T("%miranda_userdata%\\SmileyCache")); + if ((INT_PTR)tmpPath != CALLSERVICE_NOTFOUND) + { + mir_sntprintf(szPath, cbLen, _T("%s"), tmpPath); + mir_free(tmpPath); + } + else + { + char dbPath[MAX_PATH]; + CallService(MS_DB_GETPROFILEPATH, SIZEOF(dbPath), (LPARAM)dbPath); + mir_sntprintf(szPath, cbLen, _T("%s\\SmileyCache"), A2T_SM(dbPath)); + } +} + +int FolderChanged(WPARAM, LPARAM) +{ + FOLDERSGETDATA fgd = {0}; + fgd.cbSize = sizeof(FOLDERSGETDATA); + fgd.nMaxPathSize = SIZEOF(cachepath); + fgd.szPathT = cachepath; + if (CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolder, (LPARAM) &fgd)) + { + GetDefaultSmileyCacheFolder(cachepath, SIZEOF(cachepath)); + } + + return 0; +} + +void GetSmileyCacheFolder(void) +{ + TCHAR defaultPath[MAX_PATH]; + + GetDefaultSmileyCacheFolder(defaultPath, SIZEOF(defaultPath)); + + FOLDERSDATA fd = {0}; + fd.cbSize = sizeof(FOLDERSDATA); + strcpy(fd.szSection, "SmileyAdd"); + strcpy(fd.szName,"Smiley Cache"); + fd.szFormatT = defaultPath; + fd.flags = FF_TCHAR; + hFolder = (HANDLE)CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd); + + FolderChanged(0, 0); + + hFolderHook = HookEvent(ME_FOLDERS_PATH_CHANGED, FolderChanged); +} + +void DownloadInit(void) +{ + NETLIBUSER nlu = {0}; + nlu.cbSize = sizeof(nlu); + nlu.flags = NUF_OUTGOING|NUF_HTTPCONNS|NUF_NOHTTPSOPTION; + nlu.szSettingsModule = "SmileyAdd"; + nlu.szDescriptiveName = Translate("SmileyAdd HTTP connections"); + hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); + + GetSmileyCacheFolder(); + g_hDlMutex = CreateMutex(NULL, FALSE, NULL); +} + +void DownloadClose(void) +{ + UnhookEvent(hFolderHook); + CloseHandle(g_hDlMutex); + Netlib_CloseHandle(hNetlibUser); +} diff --git a/plugins/SmileyAdd/src/download.h b/plugins/SmileyAdd/src/download.h new file mode 100644 index 0000000000..bb83926acc --- /dev/null +++ b/plugins/SmileyAdd/src/download.h @@ -0,0 +1,28 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef SMILEYADD_DOWNLOAD_H_ +#define SMILEYADD_DOWNLOAD_H_ + +#include "general.h" + +bool GetSmileyFile(bkstring& url, const bkstring& packstr); +void DownloadInit(void); +void DownloadClose(void); + +#endif diff --git a/plugins/SmileyAdd/src/general.cpp b/plugins/SmileyAdd/src/general.cpp new file mode 100644 index 0000000000..9e97737f8c --- /dev/null +++ b/plugins/SmileyAdd/src/general.cpp @@ -0,0 +1,299 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2012 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "m_metacontacts.h" + +#ifdef _MSC_VER +#include +#endif + +/* +#include "m_popup.h" +*/ +static ULONG_PTR g_gdiplusToken = 0; +static bool gdiPlusFail = false; + +// +// General functions +// +int CalculateTextHeight(HDC hdc, CHARFORMAT2* chf) +{ + HDC hcdc = CreateCompatibleDC(hdc); + + int logPixelsY = GetDeviceCaps(hdc, LOGPIXELSY); + HFONT hFont = CreateFont(-(chf->yHeight * logPixelsY / 1440), 0, 0, 0, + chf->wWeight, chf->dwEffects & CFE_ITALIC ? 1 : 0, 0, 0, + chf->bCharSet, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, + chf->szFaceName); + SelectObject(hcdc, hFont); + + SIZE fontSize; + GetTextExtentPoint32(hcdc, _T(")"), 1, &fontSize); + + DeleteObject(hFont); + DeleteDC(hcdc); + + return fontSize.cy; +} + + +HICON GetDefaultIcon(bool copy) +{ + HICON resIco = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"SmileyAdd_ButtonSmiley"); + if ( resIco == NULL || resIco == (HICON)CALLSERVICE_NOTFOUND ) + { + resIco = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SMILINGICON), + IMAGE_ICON, 0, 0, copy ? 0 : LR_SHARED); + } + else + { + if (copy) + { + resIco = (HICON)CopyImage(resIco, IMAGE_ICON, 0, 0, 0); + CallService(MS_SKIN2_RELEASEICON, 0, (LPARAM)"SmileyAdd_ButtonSmiley"); + } + } + + return resIco; +} + + +const TCHAR* GetImageExt(bkstring &fname) +{ + const TCHAR* ext = _T(""); + + int fileId = _topen(fname.c_str(), O_RDONLY | _O_BINARY); + if (fileId != -1) + { + BYTE buf[6]; + + int bytes = _read(fileId, buf, sizeof(buf)); + if (bytes > 4) + { + if ( *(unsigned short*)buf == 0xd8ff ) + ext = _T("jpg"); + else if ( *(unsigned short*)buf == 0x4d42 ) + ext = _T("bmp"); + else if ( *(unsigned*)buf == 0x474e5089 ) + ext = _T("png"); + else if ( *(unsigned*)buf == 0x38464947 ) + ext = _T("gif"); + } + _close(fileId); + } + return ext; +} + + + +HICON ImageList_GetIconFixed (HIMAGELIST himl, INT i, UINT fStyle) +{ + ICONINFO ii; + HICON hIcon; + HBITMAP hOldDstBitmap; + HDC hdcDst; + + int cx, cy; + ImageList_GetIconSize(himl, &cx, &cy); + + hdcDst = CreateCompatibleDC(NULL); + + ii.fIcon = TRUE; + ii.xHotspot = 0; + ii.yHotspot = 0; + + /* draw mask*/ + ii.hbmMask = CreateBitmap (cx, cy, 1, 1, NULL); + hOldDstBitmap = (HBITMAP)SelectObject (hdcDst, ii.hbmMask); + PatBlt(hdcDst, 0, 0, cx, cy, WHITENESS); + ImageList_Draw(himl, i, hdcDst, 0, 0, fStyle | ILD_MASK); + + /* draw image*/ + ii.hbmColor = CreateBitmap (cx, cy, 1, 32, NULL); + SelectObject (hdcDst, ii.hbmColor); + PatBlt (hdcDst, 0, 0, cx, cy, BLACKNESS); + ImageList_Draw(himl, i, hdcDst, 0, 0, fStyle | ILD_TRANSPARENT); + + /* + * CreateIconIndirect requires us to deselect the bitmaps from + * the DCs before calling + */ + SelectObject(hdcDst, hOldDstBitmap); + + hIcon = CreateIconIndirect (&ii); + + DeleteObject (ii.hbmMask); + DeleteObject (ii.hbmColor); + DeleteDC (hdcDst); + + return hIcon; +} + +void pathToRelative(const bkstring& pSrc, bkstring& pOut) +{ + TCHAR szOutPath[MAX_PATH]; + CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)pSrc.c_str(), (LPARAM)szOutPath); + pOut = szOutPath; +} + +void pathToAbsolute(const bkstring& pSrc, bkstring& pOut) +{ + TCHAR szOutPath[MAX_PATH]; + + TCHAR* szVarPath = Utils_ReplaceVarsT(pSrc.c_str()); + if (szVarPath == (TCHAR*)CALLSERVICE_NOTFOUND || szVarPath == NULL) + { + TCHAR szExpPath[MAX_PATH]; + ExpandEnvironmentStrings(pSrc.c_str(), szExpPath, SIZEOF(szExpPath)); + CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szExpPath, (LPARAM)szOutPath); + } + else + { + CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szVarPath, (LPARAM)szOutPath); + mir_free(szVarPath); + } + pOut = szOutPath; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// UrlDecode - converts URL chars like %20 into printable characters + +static int __fastcall SingleHexToDecimal(char c) +{ + if (c >= '0' && c <= '9') return c-'0'; + if (c >= 'a' && c <= 'f') return c-'a'+10; + if (c >= 'A' && c <= 'F') return c-'A'+10; + return -1; +} + +void UrlDecode(char* str) +{ + char* s = str, *d = str; + + while(*s) + { + if (*s == '%') + { + int digit1 = SingleHexToDecimal(s[1]); + if ( digit1 != -1 ) + { + int digit2 = SingleHexToDecimal(s[2]); + if ( digit2 != -1 ) + { + s += 3; + *d++ = (char)((digit1 << 4) | digit2); + continue; + } + } + } + *d++ = *s++; + } + + *d = 0; +} + + +bool InitGdiPlus(void) +{ + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + + static const TCHAR errmsg[] = _T("GDI+ not installed.\n") + _T("GDI+ can be downloaded here: http://www.microsoft.com/downloads"); + +#ifdef _MSC_VER + __try +#endif + { + if (g_gdiplusToken == 0 && !gdiPlusFail) + { + Gdiplus::GdiplusStartup(&g_gdiplusToken, &gdiplusStartupInput, NULL); + } + } +#ifdef _MSC_VER + __except ( EXCEPTION_EXECUTE_HANDLER ) + { + gdiPlusFail = true; + ReportError(errmsg); + } +#endif + + return !gdiPlusFail; +} + +void DestroyGdiPlus(void) +{ + if (g_gdiplusToken != 0) + { + Gdiplus::GdiplusShutdown(g_gdiplusToken); +#ifdef _MSC_VER +#if 1200 < _MSC_VER + __FUnloadDelayLoadedDLL2("gdiplus.dll"); +#else + __FUnloadDelayLoadedDLL("gdiplus.dll"); +#endif +#endif + g_gdiplusToken = 0; + } +} + +HANDLE DecodeMetaContact(HANDLE hContact) +{ + if (hContact == NULL) return NULL; + HANDLE hReal = (HANDLE) CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM) hContact, 0); + if (hReal == NULL || hReal == (HANDLE)CALLSERVICE_NOTFOUND) + hReal = hContact; + + return hReal; +} + +bool IsSmileyProto(char* proto) +{ + return proto && (!metaProtoName || strcmp(proto, metaProtoName)) && + (CallProtoService(proto, PS_GETCAPS, PFLAGNUM_1, 0) & (PF1_IM | PF1_CHAT)); +} + +void ReportError(const TCHAR* errmsg) +{ + static const TCHAR title[] = _T("Miranda SmileyAdd"); +/* + + POPUPDATAW pd = {0}; + + + _tcscpy(pd.lpwzContactName, title); + _tcscpy(pd.lpwzText, errmsg); + + pd.iSeconds = -1; + + + bool popupFail = PUAddPopUpW(&pd) != CALLSERVICE_NOTFOUND; + + if (popupFail) +*/ + MessageBox(NULL, errmsg, title, MB_OK | MB_ICONWARNING | MB_TOPMOST); +} + +#pragma warning( disable : 4786 ) +#undef _MT + +#include +#include diff --git a/plugins/SmileyAdd/src/general.h b/plugins/SmileyAdd/src/general.h new file mode 100644 index 0000000000..8cc13c5619 --- /dev/null +++ b/plugins/SmileyAdd/src/general.h @@ -0,0 +1,232 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "bkstring.h" + +#undef _MT + +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4275 ) +#pragma warning( disable : 4390 ) + +#include +#include + +typedef WCPattern _TPattern; +typedef WCMatcher _TMatcher; +#define createTMatcher createWCMatcher + +#pragma warning( pop ) + +#define _MT + +#include "resource.h" + +#pragma warning( push ) +#pragma warning( disable : 4100 ) +#define MIRANDA_VER 0x0A00 +#define NETLIB_NOLOGGING +#define MIRANDA_CUSTOM_LP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma warning( pop ) + +//globals, defined int main.cpp +extern HINSTANCE g_hInst; +extern char* metaProtoName; + +//some system and NT stuff... +#ifndef OPENFILENAME_SIZE_VERSION_400 +#define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAME) +#endif + + +#define IDC_SMLBUTTON (WM_USER + 33) + +/////////////////////////////////////////////////// +// +//defines from miranda im sources (undocumented!) +// +#define MI_IDC_LOG 1001 //rich edit +#define MI_IDC_MESSAGE 1002 //edit control +#define MI_IDC_QUOTE 1034 //button control +#define MI_IDC_NAME 1009 //text control +#define MI_IDC_ADD 1070 //Add button. + +#define DM_REMAKELOG (WM_USER + 11) +#define DM_OPTIONSAPPLIED (WM_USER + 14) +#define DM_APPENDTOLOG (WM_USER + 17) + +#define WM_REMAKERICH (WM_USER + 0x3457) + +///////////////////////////////////////////////////// + +#define MAX_SMILEY_LENGTH 96 + +class A2W_SM +{ +public: + wchar_t* m_psz; + + A2W_SM(const char* psz, unsigned nCodePage = CP_ACP) + { + const int nLength = MultiByteToWideChar(nCodePage, 0, psz, -1, NULL, 0); + m_psz = new wchar_t[nLength]; + MultiByteToWideChar(nCodePage, 0, psz, -1, m_psz, nLength); + } + ~A2W_SM() { delete [] m_psz; } + operator wchar_t*() const { return m_psz; } +}; + + +class W2A_SM +{ +public: + char* m_psz; + + W2A_SM(const wchar_t* psz, unsigned nCodePage = CP_ACP) + { + const int nLength = WideCharToMultiByte(nCodePage, 0, psz, -1, NULL, 0, NULL, NULL); + m_psz = new char[nLength]; + WideCharToMultiByte(nCodePage, 0, psz, -1, m_psz, nLength, NULL, NULL); + } + ~W2A_SM() { delete [] m_psz; } + operator char*() const { return m_psz; } +}; + +#define T2A_SM (char*)W2A_SM +#define T2W_SM(p1) (wchar_t*)p1 +#define A2T_SM (wchar_t*)A2W_SM +#define W2T_SM(p1) (TCHAR*)p1 + +template struct SMOBJLIST : public OBJLIST +{ + SMOBJLIST() : OBJLIST(5) {}; + + SMOBJLIST& operator = (const SMOBJLIST& lst) + { + OBJLIST::destroy(); + return operator += (lst); + } + + SMOBJLIST& operator += (const SMOBJLIST& lst) + { + for (int i=0; i& lst) + { + for (int i=0; i::destroy(); + } +}; + +inline unsigned short GetWinVer(void) +{ + unsigned short ver = LOWORD(GetVersion()); + ver = (ver & 0xFF) << 8 | (ver >> 8); + return ver; +} + + +// init functions +void InstallDialogBoxHook(void); +void RemoveDialogBoxHook(void); +int UpdateSrmmDlg(WPARAM wParam, LPARAM lParam); +bool IsOldSrmm(void); + +//functions for general use (defined in general.cpp) +int CalculateTextHeight(HDC hdc, CHARFORMAT2* chf); +const TCHAR* GetImageExt(bkstring &fname); + +HANDLE DecodeMetaContact(HANDLE hContact); +bool IsSmileyProto(char* proto); + +HICON ImageList_GetIconFixed (HIMAGELIST himl, INT i, UINT fStyle); + +void pathToRelative(const bkstring& pSrc, bkstring& pOut); +void pathToAbsolute(const bkstring& pSrc, bkstring& pOut); + +bool InitGdiPlus(void); +void DestroyGdiPlus(void); + +void ReportError(const TCHAR* errmsg); +HICON GetDefaultIcon(bool copy = true); + +void CloseRichCallback(HWND hwnd, bool force); +void CloseRichOwnerCallback(HWND hwnd, bool force); +bool SetRichCallback(HWND hwnd, HANDLE hContact, bool subany, bool subnew); +void SetRichOwnerCallback(HWND hwnd, HWND hwndInput, HWND hwndLog); +void ProcessAllInputAreas(bool restoreText); +void RichEditData_Destroy(void); + +void CloseSmileys(void); +void DestroySmileyBase(void); +void DestroyAniSmileys(void); + +void UrlDecode(char* str); + +#endif diff --git a/plugins/SmileyAdd/src/imagecache.cpp b/plugins/SmileyAdd/src/imagecache.cpp new file mode 100644 index 0000000000..8fdcea9003 --- /dev/null +++ b/plugins/SmileyAdd/src/imagecache.cpp @@ -0,0 +1,775 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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 g_imagecache(25, ImageType::CompareImg); + +static bkstring lastdllname; +static HMODULE lastmodule; +static time_t laststamp; +static UINT_PTR timerId; + +static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD) +{ + WaitForSingleObject(g_hMutexIm, 3000); + const time_t ts = time(NULL) - 10; + if ( lastmodule && ts > laststamp) + { + FreeLibrary(lastmodule); + lastmodule = NULL; + lastdllname.clear(); + } + + for (int i=g_imagecache.getCount(); i--; ) + g_imagecache[i].ProcessTimerTick(ts); + + if (g_imagecache.getCount() == 0) + { + g_imagecache.destroy(); + if (timerId && (timerId+1) && lastmodule == NULL) + { + KillTimer(NULL, timerId); + timerId = 0; + } + } + + ReleaseMutex(g_hMutexIm); +} + + +static void CALLBACK sttMainThreadCallback( PVOID ) +{ + if (timerId == 0xffffffff) + timerId = SetTimer(NULL, 0, 10000, (TIMERPROC)timerProc); +} + + +static HMODULE LoadDll(const bkstring& file) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + if (lastdllname != file) + { + FreeLibrary(lastmodule); + lastdllname = file; + + lastmodule = LoadLibraryEx(file.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE); + + } + laststamp = time(NULL); + + ReleaseMutex(g_hMutexIm); + return lastmodule; +} + + + +ImageBase::ImageBase(unsigned id) +{ + m_id = id; + m_lRefCount = 1; + m_timestamp = 0; +} + +long ImageBase::AddRef(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + long cnt = ++m_lRefCount; + ReleaseMutex(g_hMutexIm); + return cnt; +} + +long ImageBase::Release(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + long cnt = m_lRefCount; + if (cnt) m_lRefCount = --cnt; + if (cnt == 0) m_timestamp = time(NULL); + + ReleaseMutex(g_hMutexIm); + return cnt; +} + +void ImageBase::ProcessTimerTick(time_t ts) +{ + WaitForSingleObject(g_hMutexIm, 3000); + if (m_lRefCount == 0 && m_timestamp < ts ) + { + if (!g_imagecache.remove(this)) + delete this; + } + ReleaseMutex(g_hMutexIm); +} + +int ImageBase::CompareImg(const ImageBase* p1, const ImageBase* p2) +{ + unsigned id1 = p1->m_id; + unsigned id2 = p2->m_id; + + if (id1 == id2) return 0; + else return id1 < id2 ? -1 : 1; +} + +void ImageBase::Draw(HDC hdc, RECT& rc, bool clip) +{ + HRGN hrgn = NULL; + if (clip) + { + hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); + SelectClipRgn(hdc, hrgn); + } + + SIZE iSize; + GetSize(iSize); + + const int sizeX = rc.right - rc.left; + const int sizeY = rc.bottom - rc.top; + + const int x = rc.left + (sizeX > iSize.cx || clip ? (sizeX - iSize.cx) / 2 : 0); + const int y = rc.top + (sizeY > iSize.cy || clip ? (sizeY - iSize.cy) / 2 : 0); + + const int scaleX = sizeX > iSize.cx || clip ? iSize.cx : sizeX; + const int scaleY = sizeY > iSize.cy || clip ? iSize.cy : sizeY; + + DrawInternal(hdc, x, y, scaleX, scaleY); + + if (clip) + { + SelectClipRgn(hdc, NULL); + DeleteObject(hrgn); + } +} + + +HBITMAP ImageBase::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY) +{ + RECT rc = { 0, 0, sizeX, sizeY }; + + if (sizeX == 0 || sizeY == 0) + { + SIZE iSize; + GetSize(iSize); + + if (sizeX == 0) rc.right = iSize.cx; + if (sizeY == 0) rc.bottom = iSize.cy; + } + + HBRUSH hBkgBrush = CreateSolidBrush(bkgClr); + HDC hdc = GetDC(NULL); + HBITMAP hBmp = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); + HDC hdcMem = CreateCompatibleDC(hdc); + SelectObject(hdcMem, hBmp); + + FillRect(hdcMem, &rc, hBkgBrush); + + Draw(hdcMem, rc, false); + + DeleteDC(hdcMem); + ReleaseDC(NULL, hdc); + DeleteObject(hBkgBrush); + + return hBmp; +} + +int ImageBase::SelectNextFrame(const int frame) +{ + int res = (frame + 1) % GetFrameCount(); + SelectFrame(res); + return res; +} + + + +IconType::IconType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type) + : ImageBase(id) +{ + m_SmileyIcon = NULL; + + switch (type) + { + case icoDll: + { + const HMODULE hModule = LoadDll(file); + if (hModule != NULL) + m_SmileyIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0); + } + break; + + case icoFile: + m_SmileyIcon = (HICON) LoadImage(NULL, file.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + break; + + default: + ExtractIconEx(file.c_str(), index, NULL, &m_SmileyIcon, 1); + break; + } +} + +IconType::~IconType() +{ + DestroyIcon(m_SmileyIcon); +} + +void IconType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_SmileyIcon != NULL) + DrawIconEx(hdc, x, y, m_SmileyIcon, sizeX, sizeY, 0, NULL, DI_NORMAL); +} + +HICON IconType::GetIcon(void) +{ + return (HICON)CopyImage(m_SmileyIcon, IMAGE_ICON, 0, 0, 0); +} + +void IconType::GetSize(SIZE& size) +{ + if (m_SmileyIcon != NULL) + { + ICONINFO ii; + BITMAP bm; + GetIconInfo(m_SmileyIcon, &ii); + GetObject(ii.hbmColor, sizeof(bm), &bm); + size.cx = bm.bmWidth; + size.cy = bm.bmHeight; + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + } +} + + + +ImageListItemType::ImageListItemType(const unsigned id, HIMAGELIST hImList, int index) + : ImageBase(id) +{ + m_index = index; + m_hImList = hImList; +} + +void ImageListItemType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + SIZE iSize; + GetSize(iSize); + + if (sizeX >= iSize.cx && sizeY >= iSize.cy) + ImageList_Draw(m_hImList, m_index, hdc, x, y, ILD_TRANSPARENT); + else + { + HICON hIcon = ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT); + DrawIconEx(hdc, x, y, hIcon, sizeX, sizeY, 0, NULL, DI_NORMAL); + DestroyIcon(hIcon); + } +} + +HICON ImageListItemType::GetIcon(void) +{ + return ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT); +} + +void ImageListItemType::GetSize(SIZE& size) +{ + ImageList_GetIconSize(m_hImList, (int*)&size.cx, (int*)&size.cy); +} + +ImageType::ImageType(const unsigned id, const bkstring& file, IStream* pStream) + : ImageBase(id) +{ + m_bmp = NULL; + m_pPropertyItem = NULL; + m_nCurrentFrame = 0; + m_nFrameCount = 0; + + if (!InitGdiPlus()) return; + + if (pStream) + m_bmp = new Gdiplus::Bitmap(pStream); + else + m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str())); + + if (m_bmp->GetLastStatus() != Gdiplus::Ok) + { + delete m_bmp; + m_bmp = NULL; + return; + } + + GUID pageGuid = Gdiplus::FrameDimensionTime; + m_nFrameCount = m_bmp->GetFrameCount(&pageGuid); + + if (IsAnimated()) + { + int nSize = m_bmp->GetPropertyItemSize(PropertyTagFrameDelay); + m_pPropertyItem = (Gdiplus::PropertyItem*) new char[nSize]; + m_bmp->GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem); + } +} + +ImageType::ImageType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type) + : ImageBase(id) +{ + m_bmp = NULL; + m_pPropertyItem = NULL; + m_nCurrentFrame = 0; + m_nFrameCount = 0; + + if (!InitGdiPlus()) return; + + switch (type) + { + case icoDll: + { + const HMODULE hModule = LoadDll(file); + if (hModule != NULL) + { + HICON hIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0); + m_bmp = new Gdiplus::Bitmap(hIcon); + DestroyIcon(hIcon); + } + } + break; + + case icoFile: + m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str())); + break; + + default: + HICON hIcon = NULL; + ExtractIconEx(file.c_str(), index, NULL, &hIcon, 1); + m_bmp = new Gdiplus::Bitmap(hIcon); + DestroyIcon(hIcon); + break; + } + + if (m_bmp->GetLastStatus() != Gdiplus::Ok) + { + delete m_bmp; + m_bmp = NULL; + return; + } +} + + +ImageType::~ImageType(void) +{ + if (m_pPropertyItem) delete[] m_pPropertyItem; + if (m_bmp) delete m_bmp; +} + +void ImageType::SelectFrame(int frame) +{ + if ((unsigned)frame >= (unsigned)m_nFrameCount) frame = 0; + if (IsAnimated() && frame != m_nCurrentFrame) + { + m_nCurrentFrame = frame; + GUID pageGuid = Gdiplus::FrameDimensionTime; + m_bmp->SelectActiveFrame(&pageGuid, frame); + } +} + + +void ImageType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_bmp == NULL) return; + + WaitForSingleObject(g_hMutexIm, 3000); + { + Gdiplus::Graphics grp(hdc); +// if (opt.HQScaling) grp.SetInterpolationMode(Gdiplus::InterpolationModeBicubic); + grp.DrawImage(m_bmp, x, y, sizeX, sizeY); + } + ReleaseMutex(g_hMutexIm); +} + +int ImageType::GetFrameDelay(void) const +{ + return ((long*) m_pPropertyItem->value)[m_nCurrentFrame]; +} + +HICON ImageType::GetIcon(void) +{ + if (m_bmp == NULL) return NULL; + + HICON hIcon = NULL; + WaitForSingleObject(g_hMutexIm, 3000); + + m_bmp->GetHICON(&hIcon); + + ReleaseMutex(g_hMutexIm); + return hIcon; +} + + +void ImageType::GetSize(SIZE& size) +{ + if (m_bmp) + { + size.cx = m_bmp->GetWidth(); + size.cy = m_bmp->GetHeight(); + } + else + { + size.cx = 0; + size.cy = 0; + } +} + + +ImageFType::ImageFType(const unsigned id) + : ImageBase(id) +{ + m_bmp = NULL; +} + +ImageFType::ImageFType(const unsigned id, const bkstring& file) + : ImageBase(id) +{ + m_bmp = NULL; + + FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0); + if (fif == FIF_UNKNOWN) + fif = fei->FI_GetFIFFromFilenameT(file.c_str()); + if (fif == FIF_UNKNOWN) return; + + FIBITMAP *dib = fei->FI_LoadT(fif, file.c_str(), 0); + if (dib == NULL) return; + + bool transp = fei->FI_IsTransparent(dib) != 0; + FREE_IMAGE_TYPE imt = fei->FI_GetImageType(dib); + unsigned bpp = fei->FI_GetBPP(dib); + + if (transp && bpp != 32 || imt == FIT_RGBA16) + { + FIBITMAP *tdib = fei->FI_ConvertTo32Bits(dib); + fei->FI_Unload(dib); + dib = tdib; + } + else if (!transp && bpp > 24) + { + FIBITMAP *tdib = fei->FI_ConvertTo24Bits(dib); + fei->FI_Unload(dib); + dib = tdib; + } + + m_bmp = fei->FI_CreateHBITMAPFromDIB(dib); + fei->FI_Unload(dib); + + if (transp) + fei->FI_Premultiply(m_bmp); +} + +ImageFType::~ImageFType() +{ + DeleteObject(m_bmp); +} + +void ImageFType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_bmp == NULL) return; + + HDC hdcImg = CreateCompatibleDC(hdc); + HBITMAP oldBmp = (HBITMAP) SelectObject(hdcImg, m_bmp); + + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + + if (bm.bmBitsPixel == 32 && pAlphaBlend) + { + BLENDFUNCTION bf = {0}; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + pAlphaBlend(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, bm.bmWidth, bm.bmHeight, bf); + } + else + { + BitBlt(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, SRCCOPY); + } + + SelectObject(hdcImg, oldBmp); + DeleteDC(hdcImg); +} + +HICON ImageFType::GetIcon(void) +{ + if (m_bmp == NULL) return NULL; + + HICON hIcon; + + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + + ICONINFO ii; + ii.fIcon = TRUE; + ii.xHotspot = 0; + ii.yHotspot = 0; + + if (bm.bmBitsPixel == 32 && GetWinVer() < 0x0501) + { + int slen = bm.bmWidth * 4; + int len = bm.bmHeight * slen; + + BYTE* p = (BYTE*)mir_alloc(len); + BYTE* maskBits = (BYTE*)mir_calloc(len); + BYTE* colorBits = (BYTE*)mir_calloc(len); + + GetBitmapBits(m_bmp, len, p); + + for (int y = 0; y < bm.bmHeight; ++y) + { + int shift = y * slen; + BYTE *px = p + shift; + BYTE *color = colorBits + shift; + BYTE *mask = maskBits + shift; + + for (int x = 0; x < bm.bmWidth; ++x) + { + for(int i = 0; i < 4; i++) + { + mask[i] = px[3]; + color[i] = px[i]; + } + + px += 4; + mask += 4; + color += 4; + } + } + + ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, maskBits); + ii.hbmColor = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, colorBits); + + hIcon = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + + mir_free(p); + mir_free(maskBits); + mir_free(colorBits); + } + else + { + ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL); + ii.hbmColor = m_bmp; + hIcon = CreateIconIndirect(&ii); + DeleteObject(ii.hbmMask); + } + return hIcon; +} + +void ImageFType::GetSize(SIZE& size) +{ + if (m_bmp) + { + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + size.cx = bm.bmWidth; + size.cy = bm.bmHeight; + } + else + { + size.cx = 0; + size.cy = 0; + } +} +/* +ImageFAniType::ImageFAniType(const unsigned id, const bkstring& file) +: ImageFType(id) +{ + m_fmbmp = NULL; + m_nCurrentFrame = -1; + m_FrameDelay = NULL; + + FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0); + if (fif == FIF_UNKNOWN) + fif = fei->FI_GetFIFFromFilenameT(file.c_str()); + + m_fmbmp = fei->FI_OpenMultiBitmap(fif, T2A_SM(file.c_str()), FALSE, TRUE, TRUE, GIF_PLAYBACK); + if (m_fmbmp == NULL) return; + + m_nFrameCount = fei->FI_GetPageCount(m_fmbmp); + m_bmpl = (HBITMAP*)mir_calloc(m_nFrameCount*sizeof(HBITMAP)); + m_FrameDelay = (int*)mir_calloc(m_nFrameCount*sizeof(int)); + SelectFrame(0); +} + +ImageFAniType::~ImageFAniType() +{ + if (m_fmbmp) fei->FI_CloseMultiBitmap(m_fmbmp, 0); + for (int i=0; i= (unsigned)m_nFrameCount) frame = 0; + if (frame == m_nCurrentFrame) return; + m_nCurrentFrame = frame; + + if (m_bmpl[frame]) + { + m_bmp = m_bmpl[frame]; + return; + } + + FITAG *tag = NULL; + + FIBITMAP *dib = fei->FI_LockPage(m_fmbmp, frame); + if (dib == NULL) + return; + + if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameTime", &tag)) + m_FrameDelay[frame] = *(LONG *)fei->FI_GetTagValue(tag) / 10; + + m_bmpl[frame] = m_bmp = fei->FI_CreateHBITMAPFromDIB(dib); + + if (fei->FI_IsTransparent(dib)) + fei->FI_Premultiply(m_bmp); + + fei->FI_UnlockPage(m_fmbmp, dib, FALSE); +} +*/ + +#pragma optimize("gt", on) + +// MurmurHash2, by Austin Appleby +unsigned int __fastcall hash( const void * key, unsigned int len ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + unsigned int h = len; + + // Mix 4 bytes at a time into the hash + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + unsigned int k = *(unsigned int *)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} +#pragma optimize("", on) + + +void InitImageCache(void) +{ + g_hMutexIm = CreateMutex(NULL, FALSE, NULL); + CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM) &fei); + + pAlphaBlend = (tAlphaBlend) GetProcAddress(GetModuleHandleA("gdi32"), "GdiAlphaBlend"); + if (pAlphaBlend == NULL) + pAlphaBlend = (tAlphaBlend) GetProcAddress(LoadLibraryA("msimg32"), "AlphaBlend"); +} + +void DestroyImageCache(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + if (timerId) KillTimer(NULL, timerId); + if (lastmodule) FreeLibrary(lastmodule); + + g_imagecache.destroy(); + + ReleaseMutex(g_hMutexIm); + CloseHandle(g_hMutexIm); +} + + +ImageBase* AddCacheImage(const bkstring& file, int index) +{ + bkstring tmpfile(file); tmpfile.appendfmt(_T("#%d"), index); + unsigned id = hash(tmpfile.c_str(), (unsigned int)tmpfile.size() * sizeof(TCHAR)); + + WaitForSingleObject(g_hMutexIm, 3000); + + ImageBase srch(id); + ImageBase *img = g_imagecache.find(&srch); + if (img == NULL) + { + bkstring::size_type ind = file.find_last_of('.'); + if (ind == file.npos) return NULL; + bkstring ext = file.substr(ind+1); + + if (ext.comparei(_T("dll")) == 0 || ext.comparei(_T("exe")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoDll) : (ImageBase*)new IconType(id, file, index, icoDll); + else if (ext.comparei(_T("ico")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, 0, icoFile) : (ImageBase*)new IconType(id, file, 0, icoFile); + else if (ext.comparei(_T("icl")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoIcl) : (ImageBase*)new IconType(id, file, index, icoIcl); + else if (ext.comparei(_T("gif")) == 0) + img = new ImageType(id, file, NULL); + else if (fei == NULL || ext.comparei(_T("tif")) == 0 || ext.comparei(_T("tiff")) == 0) + img = new ImageType(id, file, NULL); + else + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, NULL) : (ImageBase*)new ImageFType(id, file); + + g_imagecache.insert(img); + + if (timerId == 0) + { + timerId = 0xffffffff; + CallFunctionAsync(sttMainThreadCallback, NULL); + } + } + else + img->AddRef(); + + ReleaseMutex(g_hMutexIm); + + return img; +} diff --git a/plugins/SmileyAdd/src/imagecache.h b/plugins/SmileyAdd/src/imagecache.h new file mode 100644 index 0000000000..aa13e4981c --- /dev/null +++ b/plugins/SmileyAdd/src/imagecache.h @@ -0,0 +1,166 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef SMILEYADD_IMAGECACHE_H_ +#define SMILEYADD_IMAGECACHE_H_ + +#include "general.h" + + +class ImageBase +{ +protected: + unsigned m_id; + long m_lRefCount; + time_t m_timestamp; + +public: + + ImageBase(unsigned id); + virtual ~ImageBase() {} + + long AddRef(void); + long Release(void); + + void ProcessTimerTick(time_t ts); + + virtual void GetSize(SIZE& /* size */) {}; + virtual int GetFrameCount(void) const { return 0; } + virtual int GetFrameDelay(void) const { return 0; } + virtual HICON GetIcon(void) { return NULL; }; + virtual void DrawInternal(HDC /* dc */, int /* x */, int /* y */, int /* sizeX */, int /* sizeY */) {}; + virtual void SelectFrame(int /* frame */) {} + + bool IsAnimated(void) const { return GetFrameCount() > 1; } + HBITMAP GetBitmap(COLORREF bkgClr, int sizeX, int sizeY); + void Draw(HDC dc, RECT &rc, bool clip); + int SelectNextFrame(const int frame); + + static int CompareImg(const ImageBase* p1, const ImageBase* p2); +}; + + +typedef enum +{ + icoDll, + icoFile, + icoIcl +} +IcoTypeEnum; + + +class IconType : public ImageBase +{ +private: + HICON m_SmileyIcon; + +public: + IconType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type); + ~IconType(); + + void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY); + HICON GetIcon(void); + void GetSize(SIZE& size); +}; + + +class ImageListItemType : public ImageBase +{ +private: + int m_index; + HIMAGELIST m_hImList; + +public: + ImageListItemType(const unsigned id, HIMAGELIST hImList, int index); + + void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY); + HICON GetIcon(void); + void GetSize(SIZE& size); +}; + + +class ImageType : public ImageBase +{ +private: + int m_nCurrentFrame; + int m_nFrameCount; + + Gdiplus::Bitmap* m_bmp; + Gdiplus::PropertyItem* m_pPropertyItem; + +public: + + ImageType(const unsigned id, const bkstring& file, IStream* pStream); + ImageType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type); + ~ImageType(); + + void SelectFrame(int frame); + + void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY); + HICON GetIcon(void); + void GetSize(SIZE& size); + + int GetFrameDelay(void) const; + int GetFrameCount(void) const { return m_nFrameCount; } +}; + +class ImageFType : public ImageBase +{ +protected: + HBITMAP m_bmp; + +public: + + ImageFType(const unsigned id); + ImageFType(const unsigned id, const bkstring& file); + ~ImageFType(); + + void DrawInternal(HDC dc, int x, int y, int sizeX, int sizeY); + HICON GetIcon(void); + void GetSize(SIZE& size); +}; +/* +class ImageFAniType : public ImageFType +{ +private: + int m_nCurrentFrame; + int m_nFrameCount; + int *m_FrameDelay; + + FIMULTIBITMAP *m_fmbmp; + HBITMAP* m_bmpl; + +public: + + ImageFAniType(const unsigned id, const bkstring& file); + ~ImageFAniType(); + + void SelectFrame(int frame); + + int GetFrameDelay(void) const { return m_FrameDelay[m_nCurrentFrame]; } + int GetFrameCount(void) const { return m_nFrameCount; } +}; +*/ + + +ImageBase* AddCacheImage(const bkstring& file, int index); + +void InitImageCache(void); +void DestroyImageCache(void); + +#endif + diff --git a/plugins/SmileyAdd/src/main.cpp b/plugins/SmileyAdd/src/main.cpp new file mode 100644 index 0000000000..a2deb4399a --- /dev/null +++ b/plugins/SmileyAdd/src/main.cpp @@ -0,0 +1,225 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "smileys.h" +#include "customsmiley.h" +#include "services.h" +#include "options.h" +#include "download.h" +#include "imagecache.h" +#include "version.h" +#include "m_metacontacts.h" + +//globals +HINSTANCE g_hInst; +HANDLE hEvent1, hContactMenuItem; +extern LIST menuHandleArray; + +char* metaProtoName; + + + +//static globals +static HANDLE hHooks[7]; +static HANDLE hService[13]; +int hLangpack; + + +static const PLUGININFOEX pluginInfoEx = +{ + sizeof(PLUGININFOEX), + "SmileyAdd", + __VERSION_DWORD, + "Smiley support for Miranda Instant Messanger", + "Peacow, nightwish, bid, borkra", + "borkra@miranda-im.org", + "Copyright© 2004 - 2012 Boris Krasnovskiy, portions by Rein-Peter de Boer", + "http://nightly.miranda.im/", + // "http://nightly.miranda.im/", + UNICODE_AWARE, + // {BD542BB4-5AE4-4d0e-A435-BA8DBE39607F} + { 0xbd542bb4, 0x5ae4, 0x4d0e, { 0xa4, 0x35, 0xba, 0x8d, 0xbe, 0x39, 0x60, 0x7f } } +}; + +static SKINICONDESC skinDesc = +{ + sizeof(SKINICONDESC), "SmileyAdd", NULL, + "SmileyAdd_ButtonSmiley", NULL, -IDI_SMILINGICON +}; + + +extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD /* mirandaVersion */) +{ + return (PLUGININFOEX*)&pluginInfoEx; +} + +// MirandaInterfaces - returns the protocol interface to the core +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_SMILEY, MIID_LAST}; + +static int ModulesLoaded(WPARAM, LPARAM) +{ + char path[MAX_PATH]; + GetModuleFileNameA(g_hInst, path, MAX_PATH); + + skinDesc.pszDefaultFile = path; + skinDesc.pszDescription = LPGEN("Button Smiley"); + HANDLE hSkinIcon = Skin_AddIcon(&skinDesc); + + INT_PTR temp = CallService(MS_MC_GETPROTOCOLNAME, 0, 0); + metaProtoName = mir_strdup(temp == CALLSERVICE_NOTFOUND ? NULL : (char*)temp); + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB; + mi.popupPosition = 2000070050; + mi.position = 2000070050; + mi.icolibItem = hSkinIcon; + mi.pszPopupName = (char*)-1; + mi.pszName = "Assign Smiley Category"; + hContactMenuItem = Menu_AddContactMenuItem(&mi); + + DownloadInit(); + + //install hooks if enabled + InstallDialogBoxHook(); + + g_SmileyCategories.AddAllProtocolsAsCategory(); + g_SmileyCategories.ClearAndLoadAll(); + + return 0; +} + +static int MirandaShutdown(WPARAM, LPARAM) +{ + CloseSmileys(); + return 0; +} + +extern "C" __declspec(dllexport) int Load(void) +{ + + mir_getLP(&pluginInfoEx); + + if (ServiceExists(MS_SMILEYADD_REPLACESMILEYS)) + { + static const TCHAR errmsg[] = _T("Only one instance of SmileyAdd could be executed.\n") + _T("Remove duplicate instances from 'Plugins' directory"); + ReportError(TranslateTS(errmsg)); + + return 1; + } + + char temp[80]; + CallService(MS_SYSTEM_GETVERSIONTEXT, (WPARAM)SIZEOF(temp), (LPARAM)temp); + + + if (strstr(temp, "Unicode") == NULL) + { + ReportError(TranslateT("Please update SmileyAdd to ANSI Version")); + return 1; + } + + + InitImageCache(); + + g_SmileyCategories.SetSmileyPackStore(&g_SmileyPacks); + + opt.Load(); + + // create smiley events + hEvent1 = CreateHookableEvent(ME_SMILEYADD_OPTIONSCHANGED); + + hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, MirandaShutdown); + hHooks[2] = HookEvent(ME_OPT_INITIALISE, SmileysOptionsInitialize); + hHooks[3] = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, RebuildContactMenu); + hHooks[4] = HookEvent(ME_SMILEYADD_OPTIONSCHANGED, UpdateSrmmDlg); + hHooks[5] = HookEvent(ME_PROTO_ACCLISTCHANGED, AccountListChanged); + hHooks[6] = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DbSettingChanged); + + //create the smiley services + hService[0] = CreateServiceFunction(MS_SMILEYADD_REPLACESMILEYS, ReplaceSmileysCommand); + hService[1] = CreateServiceFunction(MS_SMILEYADD_GETSMILEYICON, GetSmileyIconCommand); + hService[2] = CreateServiceFunction(MS_SMILEYADD_SHOWSELECTION, ShowSmileySelectionCommand); + hService[3] = CreateServiceFunction(MS_SMILEYADD_GETINFO, GetInfoCommand); + hService[4] = CreateServiceFunction(MS_SMILEYADD_GETINFO2, GetInfoCommand2); + hService[5] = CreateServiceFunction(MS_SMILEYADD_PARSE, ParseText); + hService[6] = CreateServiceFunction(MS_SMILEYADD_REGISTERCATEGORY, RegisterPack); + hService[7] = CreateServiceFunction(MS_SMILEYADD_BATCHPARSE, ParseTextBatch); + hService[8] = CreateServiceFunction(MS_SMILEYADD_BATCHFREE, FreeTextBatch); + hService[9] = CreateServiceFunction(MS_SMILEYADD_CUSTOMCATMENU, CustomCatMenu); + hService[10] = CreateServiceFunction(MS_SMILEYADD_RELOAD, ReloadPack); + hService[11] = CreateServiceFunction(MS_SMILEYADD_LOADCONTACTSMILEYS, LoadContactSmileys); + + + hService[12] = CreateServiceFunction(MS_SMILEYADD_PARSEW, ParseTextW); + + + return 0; +} + + +extern "C" __declspec(dllexport) int Unload(void) +{ + int i; + + RemoveDialogBoxHook(); + + for (i=0; i. +*/ + +#include "general.h" +#include "options.h" +#include "smileys.h" +#include "customsmiley.h" +#include "services.h" + +//globals, defined int main.cpp +extern HINSTANCE g_hInst; +extern HANDLE hEvent1; +extern SmileyCategoryListType g_SmileyCategories; +extern SmileyPackListType g_SmileyPacks; + +OptionsType opt; + +#define UM_CHECKSTATECHANGE (WM_USER + 100) + +class OptionsDialogType +{ +private: + HWND m_hwndDialog; + SmileyCategoryListType tmpsmcat; + SmileyPackType smPack; + + void InitDialog(void); + void DestroyDialog(void); + void AddCategory(void); + void ApplyChanges(void); + void UpdateControls(bool force = false); + void SetChanged(void); + bool BrowseForSmileyPacks(int item); + void FilenameChanged(void); + void ShowSmileyPreview(void); + void PopulateSmPackList(void); + void UserAction(HTREEITEM hItem); + long GetSelProto(HTREEITEM hItem = NULL); + +public: + OptionsDialogType(HWND hWnd) { m_hwndDialog = hWnd; } + BOOL DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam); +}; + +static INT_PTR CALLBACK DlgProcSmileysOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//Init and de-init functions, called from main +int SmileysOptionsInitialize(WPARAM addInfo, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof(odp); + odp.position = 910000000; + odp.hInstance = g_hInst; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SMILEYS); + odp.pszTitle = LPGEN("Smileys"); + odp.pszGroup = LPGEN("Customize"); + odp.pfnDlgProc = DlgProcSmileysOptions; + odp.flags = ODPF_BOLDGROUPS; + Options_AddPage(addInfo, &odp); + + return 0; +} + + +// +// dialog procedure for the options dialog. creates or +// retrieves the options class and calls it +// +static INT_PTR CALLBACK DlgProcSmileysOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + OptionsDialogType* pOD; + INT_PTR Result; + + pOD = (OptionsDialogType*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (pOD == NULL) + { + pOD = new OptionsDialogType(hwndDlg); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pOD); + } + + Result = pOD->DialogProcedure(msg, wParam, lParam); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result); + + if (msg == WM_NCDESTROY) + { + delete pOD; + } + + return Result; +} + + +//OptionsDialog class functions +BOOL OptionsDialogType::DialogProcedure(UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL Result = FALSE; + + switch(msg) + { + case WM_INITDIALOG: + InitDialog(); + Result = TRUE; + break; + + case WM_DESTROY: + DestroyDialog(); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_FILENAME: + switch(HIWORD(wParam)) + { + case EN_KILLFOCUS: + FilenameChanged(); + break; + + case EN_CHANGE: + if (GetFocus() == (HWND)lParam) SetChanged(); + break; + } + break; + + case IDC_BROWSE: + if (HIWORD(wParam) == BN_CLICKED) + { + if (BrowseForSmileyPacks(GetSelProto())) + { + UpdateControls(true); + SetChanged(); + } + } + break; + + case IDC_SMLOPTBUTTON: + if (HIWORD(wParam) == BN_CLICKED) ShowSmileyPreview(); + break; + + case IDC_USESTDPACK: + if (HIWORD(wParam) == BN_CLICKED) + { + PopulateSmPackList(); + SetChanged(); + } + break; + + case IDC_PLUGENABLED: + if (HIWORD(wParam) == BN_CLICKED) + { + BOOL en = IsDlgButtonChecked(m_hwndDialog, IDC_PLUGENABLED) == BST_UNCHECKED; + EnableWindow(GetDlgItem(m_hwndDialog, IDC_SMLBUT), en); + SetChanged(); + } + break; + + case IDC_ADDCATEGORY: + if (HIWORD(wParam) == BN_CLICKED) + { + AddCategory(); + } + break; + + case IDC_DELETECATEGORY: + if (HIWORD(wParam) == BN_CLICKED) + { + if (tmpsmcat.DeleteCustomCategory(GetSelProto())) + { + PopulateSmPackList(); + SetChanged(); + } + } + break; + + case IDC_SPACES: + case IDC_SCALETOTEXTHEIGHT: + case IDC_APPENDSPACES: + case IDC_SMLBUT: + case IDC_SCALEALLSMILEYS: + case IDC_IEVIEWSTYLE: + case IDC_ANIMATESEL: + case IDC_ANIMATEDLG: + case IDC_INPUTSMILEYS: + case IDC_DCURSORSMILEY: + case IDC_DISABLECUSTOM: + case IDC_HQSCALING: + if (HIWORD(wParam) == BN_CLICKED) SetChanged(); + break; + + case IDC_SELCLR: + if (HIWORD(wParam) == CPN_COLOURCHANGED) SetChanged(); + break; + + case IDC_MAXCUSTSMSZ: + case IDC_MINSMSZ: + if (HIWORD(wParam) == EN_CHANGE && GetFocus() == (HWND)lParam) SetChanged(); + break; + } + break; + + case UM_CHECKSTATECHANGE: + UserAction((HTREEITEM)lParam); + break; + + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) + { + case 0: + switch (((LPNMHDR)lParam)->code) + { + case PSN_APPLY: + ApplyChanges(); + break; + } + break; + + case IDC_CATEGORYLIST: + switch (((LPNMHDR)lParam)->code) + { + case NM_CLICK: + { + TVHITTESTINFO ht = {0}; + + DWORD dwpos = GetMessagePos(); + POINTSTOPOINT(ht.pt, MAKEPOINTS(dwpos)); + MapWindowPoints(HWND_DESKTOP, ((LPNMHDR)lParam)->hwndFrom, &ht.pt, 1); + + TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &ht); + if (TVHT_ONITEM & ht.flags) + FilenameChanged(); + if (TVHT_ONITEMSTATEICON & ht.flags) + PostMessage(m_hwndDialog, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.hItem); + } + + case TVN_KEYDOWN: + if (((LPNMTVKEYDOWN) lParam)->wVKey == VK_SPACE) + PostMessage(m_hwndDialog, UM_CHECKSTATECHANGE, 0, + (LPARAM)TreeView_GetSelection(((LPNMHDR)lParam)->hwndFrom)); + break; + + case TVN_SELCHANGEDA: + case TVN_SELCHANGEDW: + { + LPNMTREEVIEW pnmtv = (LPNMTREEVIEW) lParam; + if (pnmtv->itemNew.state & TVIS_SELECTED) + UpdateControls(); + } + break; + } + break; + } + break; + } + + return Result; +} + + +void OptionsDialogType::AddCategory(void) +{ + TCHAR cat[30]; + + GetDlgItemText(m_hwndDialog, IDC_NEWCATEGORY, cat, SIZEOF(cat)); + bkstring catd = cat; + + if (!catd.empty()) + { + tmpsmcat.AddCategory(cat, catd, smcCustom); + + PopulateSmPackList(); + SetChanged(); + } +} + + +void OptionsDialogType::UserAction(HTREEITEM hItem) +{ + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + + if (TreeView_GetCheckState(hLstView, hItem)) + { + if (!BrowseForSmileyPacks(GetSelProto(hItem))) + { + TreeView_SetCheckState(hLstView, hItem, TRUE) + } + } + else + { + tmpsmcat.GetSmileyCategory(GetSelProto(hItem))->ClearFilename(); + } + + if (hItem == TreeView_GetSelection(hLstView)) + UpdateControls(); + else + TreeView_SelectItem(hLstView, hItem); + + SetChanged(); +} + + +void OptionsDialogType::SetChanged(void) +{ + SendMessage(GetParent(m_hwndDialog), PSM_CHANGED, 0, 0); +} + + +void OptionsDialogType::UpdateControls(bool force) +{ + const SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(GetSelProto()); + if (smc == NULL) return; + + const bkstring& smf = smc->GetFilename(); + + SetDlgItemText(m_hwndDialog, IDC_FILENAME, smf.c_str()); + + if (smPack.GetFilename() != smf || force) + { + smPack.LoadSmileyFile(smf, false, true); + } + + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + TreeView_SetCheckState(hLstView, TreeView_GetSelection(hLstView), smPack.SmileyCount() != 0); + + SetDlgItemText(m_hwndDialog, IDC_LIBAUTHOR, smPack.GetAuthor().c_str()); + SetDlgItemText(m_hwndDialog, IDC_LIBVERSION, smPack.GetVersion().c_str()); + SetDlgItemText(m_hwndDialog, IDC_LIBNAME, TranslateTS(smPack.GetName().c_str())); +} + + +long OptionsDialogType::GetSelProto(HTREEITEM hItem) +{ + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + TVITEM tvi = {0}; + + tvi.mask = TVIF_PARAM; + tvi.hItem = hItem == NULL ? TreeView_GetSelection(hLstView) : hItem; + + TreeView_GetItem(hLstView, &tvi); + + return (long)tvi.lParam; +} + + +void OptionsDialogType::PopulateSmPackList(void) +{ + bool useOne = IsDlgButtonChecked(m_hwndDialog, IDC_USESTDPACK) == BST_UNCHECKED; + + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + + TreeView_SelectItem(hLstView, NULL); + TreeView_DeleteAllItems(hLstView); + + TVINSERTSTRUCT tvi = {0}; + tvi.hParent = TVI_ROOT; + tvi.hInsertAfter = TVI_LAST; + tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_PARAM; + tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_SELECTED; + + SmileyCategoryListType::SmileyCategoryVectorType& smc = *tmpsmcat.GetSmileyCategoryList(); + for (int i = 0; i < smc.getCount(); i++) + { + if (!useOne || !smc[i].IsProto()) + { + tvi.item.pszText = (TCHAR*)smc[i].GetDisplayName().c_str(); + if (!smc[i].IsProto()) + { + tvi.item.iImage = 0; + tvi.item.iSelectedImage = 0; + } + else + { + tvi.item.iImage = i; + tvi.item.iSelectedImage = i; + } + tvi.item.lParam = i; + tvi.item.state = + INDEXTOSTATEIMAGEMASK(smPack.LoadSmileyFile(smc[i].GetFilename(), true, true) ? 2 : 1); + + TreeView_InsertItem(hLstView, &tvi); + + smPack.Clear(); + } + } + TreeView_SelectItem(hLstView, TreeView_GetRoot(hLstView)); +} + +void OptionsDialogType::InitDialog(void) +{ + TranslateDialogDefault(m_hwndDialog); + + CheckDlgButton(m_hwndDialog, IDC_PLUGENABLED, opt.PluginSupportEnabled ? BST_UNCHECKED : BST_CHECKED); + CheckDlgButton(m_hwndDialog, IDC_SPACES, opt.EnforceSpaces ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_SCALETOTEXTHEIGHT, opt.ScaleToTextheight ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_USESTDPACK, opt.UseOneForAll ? BST_UNCHECKED : BST_CHECKED); + CheckDlgButton(m_hwndDialog, IDC_APPENDSPACES, opt.SurroundSmileyWithSpaces ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_SCALEALLSMILEYS, opt.ScaleAllSmileys ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_IEVIEWSTYLE, opt.IEViewStyle ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_ANIMATESEL, opt.AnimateSel ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_ANIMATEDLG, opt.AnimateDlg ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_INPUTSMILEYS, opt.InputSmileys ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_DCURSORSMILEY, opt.DCursorSmiley ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_DISABLECUSTOM, opt.DisableCustom ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwndDialog, IDC_HQSCALING, opt.HQScaling ? BST_CHECKED : BST_UNCHECKED); + + SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Off")); + SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Top")); + + if (IsOldSrmm()) + SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_ADDSTRING, 0, (LPARAM) TranslateT("Bottom")); + + SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_SETCURSEL, opt.ButtonStatus, 0); + EnableWindow(GetDlgItem(m_hwndDialog, IDC_SMLBUT), opt.PluginSupportEnabled); + + SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSPIN, UDM_SETRANGE32, 0, 99); + SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSPIN, UDM_SETPOS, 0, opt.MaxCustomSmileySize); + SendDlgItemMessage(m_hwndDialog, IDC_MAXCUSTSMSZ, EM_LIMITTEXT, 2, 0); + + SendDlgItemMessage(m_hwndDialog, IDC_MINSPIN, UDM_SETRANGE32, 0, 99); + SendDlgItemMessage(m_hwndDialog, IDC_MINSPIN, UDM_SETPOS, 0, opt.MinSmileySize); + SendDlgItemMessage(m_hwndDialog, IDC_MINSMSZ, EM_LIMITTEXT, 2, 0); + + SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_SETCOLOUR, 0, opt.SelWndBkgClr); + SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_SETDEFAULTCOLOUR, 0, GetSysColor(COLOR_WINDOW)); + + // Create and populate image list + HIMAGELIST hImList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + ILC_MASK | ILC_COLOR32, g_SmileyCategories.NumberOfSmileyCategories(), 0); + + tmpsmcat = g_SmileyCategories; + + SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList(); + for (int i = 0; i < smc.getCount(); i++) + { + HICON hIcon = NULL; + if (smc[i].IsProto()) + { + hIcon=(HICON)CallProtoService(T2A_SM(smc[i].GetName().c_str()), PS_LOADICON, + PLI_PROTOCOL | PLIF_SMALL, 0); + if (hIcon == NULL || (INT_PTR)hIcon == CALLSERVICE_NOTFOUND) + { + hIcon=(HICON)CallProtoService(T2A_SM(smc[i].GetName().c_str()), PS_LOADICON, PLI_PROTOCOL, 0); + } + } + if (hIcon == NULL || hIcon == (HICON)CALLSERVICE_NOTFOUND) + hIcon = GetDefaultIcon(); + + ImageList_AddIcon(hImList, hIcon); + DestroyIcon(hIcon); + } + + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + TreeView_SetImageList(hLstView, hImList, TVSIL_NORMAL); + + PopulateSmPackList(); +} + +void OptionsDialogType::DestroyDialog(void) +{ + HWND hLstView = GetDlgItem(m_hwndDialog, IDC_CATEGORYLIST); + HIMAGELIST hImList = TreeView_SetImageList(hLstView, NULL, TVSIL_NORMAL); + ImageList_Destroy(hImList); +} + + +void OptionsDialogType::ApplyChanges(void) +{ + ProcessAllInputAreas(true); + CloseSmileys(); + + opt.PluginSupportEnabled = IsDlgButtonChecked(m_hwndDialog, IDC_PLUGENABLED) == BST_UNCHECKED; + opt.EnforceSpaces = IsDlgButtonChecked(m_hwndDialog, IDC_SPACES) == BST_CHECKED; + opt.ScaleToTextheight = IsDlgButtonChecked(m_hwndDialog, IDC_SCALETOTEXTHEIGHT) == BST_CHECKED; + opt.UseOneForAll = IsDlgButtonChecked(m_hwndDialog, IDC_USESTDPACK) == BST_UNCHECKED; + opt.SurroundSmileyWithSpaces = IsDlgButtonChecked(m_hwndDialog, IDC_APPENDSPACES) == BST_CHECKED; + opt.ScaleAllSmileys = IsDlgButtonChecked(m_hwndDialog, IDC_SCALEALLSMILEYS) == BST_CHECKED; + opt.IEViewStyle = IsDlgButtonChecked(m_hwndDialog, IDC_IEVIEWSTYLE) == BST_CHECKED; + opt.AnimateSel = IsDlgButtonChecked(m_hwndDialog, IDC_ANIMATESEL) == BST_CHECKED; + opt.AnimateDlg = IsDlgButtonChecked(m_hwndDialog, IDC_ANIMATEDLG) == BST_CHECKED; + opt.InputSmileys = IsDlgButtonChecked(m_hwndDialog, IDC_INPUTSMILEYS) == BST_CHECKED; + opt.DCursorSmiley = IsDlgButtonChecked(m_hwndDialog, IDC_DCURSORSMILEY) == BST_CHECKED; + opt.DisableCustom = IsDlgButtonChecked(m_hwndDialog, IDC_DISABLECUSTOM) == BST_CHECKED; + opt.HQScaling = IsDlgButtonChecked(m_hwndDialog, IDC_HQSCALING) == BST_CHECKED; + + opt.ButtonStatus = (unsigned)SendDlgItemMessage(m_hwndDialog, IDC_SMLBUT, CB_GETCURSEL, 0, 0); + opt.SelWndBkgClr = (unsigned)SendDlgItemMessage(m_hwndDialog, IDC_SELCLR, CPM_GETCOLOUR, 0, 0); + opt.MaxCustomSmileySize = GetDlgItemInt(m_hwndDialog, IDC_MAXCUSTSMSZ, NULL, FALSE); + opt.MinSmileySize = GetDlgItemInt(m_hwndDialog, IDC_MINSMSZ, NULL, FALSE); + + opt.Save(); + + // Cleanup database + SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList(); + for (int i = 0; i < smc.getCount(); i++) + { + if (tmpsmcat.GetSmileyCategory(smc[i].GetName()) == NULL) + { + bkstring empty; + opt.WritePackFileName(empty, smc[i].GetName()); + } + } + + g_SmileyCategories = tmpsmcat; + g_SmileyCategories.SaveSettings(); + g_SmileyCategories.ClearAndLoadAll(); + + smPack.LoadSmileyFile(tmpsmcat.GetSmileyCategory(GetSelProto())->GetFilename(), false); + + NotifyEventHooks(hEvent1, 0, 0); + ProcessAllInputAreas(false); +} + +bool OptionsDialogType::BrowseForSmileyPacks(int item) +{ + OPENFILENAME ofn = {0}; + + TCHAR filename[MAX_PATH] = _T(""); + ofn.lpstrFile = filename; + ofn.nMaxFile = SIZEOF(filename); + + bkstring inidir; + SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(item); + if (smc->GetFilename().empty()) + { + pathToAbsolute(_T("Smileys"), inidir); + } + else + { + pathToAbsolute(smc->GetFilename(), inidir); + inidir.erase(inidir.find_last_of('\\')); + } + + ofn.lpstrInitialDir = inidir.c_str(); + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwndDialog; + + TCHAR filter[512], *pfilter; + _tcscpy(filter, TranslateT("Smiley Packs")); + lstrcat(filter, _T(" (*.msl;*.asl;*.xep)")); + pfilter = filter + _tcslen(filter) + 1; + _tcscpy(pfilter, _T("*.msl;*.asl;*.xep")); + pfilter = pfilter + _tcslen(pfilter) + 1; + _tcscpy(pfilter, TranslateT("All Files")); + lstrcat(pfilter, _T(" (*.*)")); + pfilter = pfilter + _tcslen(pfilter) + 1; + _tcscpy(pfilter, _T("*.*")); + pfilter = pfilter + _tcslen(pfilter) + 1; + *pfilter = '\0'; + ofn.lpstrFilter = filter; + + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_READONLY | + OFN_EXPLORER | OFN_LONGNAMES | OFN_NOCHANGEDIR; + ofn.lpstrDefExt = _T("msl"); + + if (GetOpenFileName(&ofn)) + { + bkstring relpath; + pathToRelative(filename, relpath); + smc->SetFilename(relpath); + + return true; + } + + return false; +} + +void OptionsDialogType::FilenameChanged(void) +{ + TCHAR str[MAX_PATH]; + GetDlgItemText(m_hwndDialog, IDC_FILENAME, str, SIZEOF(str)); + + SmileyCategoryType* smc = tmpsmcat.GetSmileyCategory(GetSelProto()); + if (smc->GetFilename() != str) + { + bkstring temp(str); + smc->SetFilename(temp); + UpdateControls(); + } +} + +void OptionsDialogType::ShowSmileyPreview(void) +{ + RECT rect; + GetWindowRect(GetDlgItem(m_hwndDialog, IDC_SMLOPTBUTTON), &rect); + + SmileyToolWindowParam *stwp = new SmileyToolWindowParam; + stwp->pSmileyPack = &smPack; + stwp->hWndParent = m_hwndDialog; + stwp->hWndTarget = NULL; + stwp->targetMessage = 0; + stwp->targetWParam = 0; + stwp->xPosition = rect.left; + stwp->yPosition = rect.bottom + 4; + stwp->direction = 1; + stwp->hContact = NULL; + + mir_forkthread(SmileyToolThread, stwp); +} + + +void OptionsType::Save(void) +{ + DBWriteContactSettingByte(NULL, "SmileyAdd", "PluginSupportEnabled", PluginSupportEnabled); + DBWriteContactSettingByte(NULL, "SmileyAdd", "EnforceSpaces", EnforceSpaces); + DBWriteContactSettingByte(NULL, "SmileyAdd", "ScaleToTextheight", ScaleToTextheight); + DBWriteContactSettingByte(NULL, "SmileyAdd", "UseOneForAll", UseOneForAll); + DBWriteContactSettingByte(NULL, "SmileyAdd", "SurroundSmileyWithSpaces", SurroundSmileyWithSpaces); + DBWriteContactSettingByte(NULL, "SmileyAdd", "ScaleAllSmileys", ScaleAllSmileys); + DBWriteContactSettingByte(NULL, "SmileyAdd", "IEViewStyle", IEViewStyle); + DBWriteContactSettingByte(NULL, "SmileyAdd", "AnimateSel", AnimateSel); + DBWriteContactSettingByte(NULL, "SmileyAdd", "AnimateDlg", AnimateDlg); + DBWriteContactSettingByte(NULL, "SmileyAdd", "InputSmileys", InputSmileys); + DBWriteContactSettingByte(NULL, "SmileyAdd", "DCursorSmiley", DCursorSmiley); + DBWriteContactSettingByte(NULL, "SmileyAdd", "DisableCustom", DisableCustom); + DBWriteContactSettingByte(NULL, "SmileyAdd", "HQScaling", HQScaling); + DBWriteContactSettingByte(NULL, "SmileyAdd", "ButtonStatus", (BYTE)ButtonStatus); + DBWriteContactSettingDword(NULL, "SmileyAdd", "SelWndBkgClr", SelWndBkgClr); + DBWriteContactSettingDword(NULL, "SmileyAdd", "MaxCustomSmileySize", MaxCustomSmileySize); + DBWriteContactSettingDword(NULL, "SmileyAdd", "MinSmileySize", MinSmileySize); +} + + +void OptionsType::Load(void) +{ + PluginSupportEnabled = DBGetContactSettingByte(NULL, "SmileyAdd", "PluginSupportEnabled", TRUE) != 0; + EnforceSpaces = DBGetContactSettingByte(NULL, "SmileyAdd", "EnforceSpaces", FALSE) != 0; + ScaleToTextheight = DBGetContactSettingByte(NULL, "SmileyAdd", "ScaleToTextheight", FALSE) != 0; + UseOneForAll = DBGetContactSettingByte(NULL, "SmileyAdd", "UseOneForAll", TRUE) != 0; + SurroundSmileyWithSpaces = + DBGetContactSettingByte(NULL, "SmileyAdd", "SurroundSmileyWithSpaces", FALSE) != 0; + ScaleAllSmileys = DBGetContactSettingByte(NULL, "SmileyAdd", "ScaleAllSmileys", FALSE) != 0; + IEViewStyle = DBGetContactSettingByte(NULL, "SmileyAdd", "IEViewStyle", FALSE) != 0; + AnimateSel = DBGetContactSettingByte(NULL, "SmileyAdd", "AnimateSel", TRUE) != 0; + AnimateDlg = DBGetContactSettingByte(NULL, "SmileyAdd", "AnimateDlg", TRUE) != 0; + InputSmileys = DBGetContactSettingByte(NULL, "SmileyAdd", "InputSmileys", TRUE) != 0; + DCursorSmiley = DBGetContactSettingByte(NULL, "SmileyAdd", "DCursorSmiley", FALSE) != 0; + DisableCustom = DBGetContactSettingByte(NULL, "SmileyAdd", "DisableCustom", FALSE) != 0; + HQScaling = DBGetContactSettingByte(NULL, "SmileyAdd", "HQScaling", FALSE) != 0; + + ButtonStatus = DBGetContactSettingByte(NULL, "SmileyAdd", "ButtonStatus", 1); + SelWndBkgClr = DBGetContactSettingDword(NULL, "SmileyAdd", "SelWndBkgClr", GetSysColor(COLOR_WINDOW)); + MaxCustomSmileySize = DBGetContactSettingDword(NULL, "SmileyAdd", "MaxCustomSmileySize", 0); + MinSmileySize = DBGetContactSettingDword(NULL, "SmileyAdd", "MinSmileySize", 0); +} + + +void OptionsType::ReadPackFileName(bkstring& filename, const bkstring& name, + const bkstring& defaultFilename) +{ + DBVARIANT dbv; + bkstring settingKey = name + _T("-filename"); + + INT_PTR res = DBGetContactSettingTString(NULL, "SmileyAdd", T2A_SM(settingKey.c_str()), &dbv); + if (res == 0) + { + filename = dbv.ptszVal; + DBFreeVariant(&dbv); + } + else + filename = defaultFilename; +} + + +void OptionsType::WritePackFileName(const bkstring& filename, const bkstring& name) +{ + bkstring settingKey = name + _T("-filename"); + DBWriteContactSettingTString(NULL, "SmileyAdd", T2A_SM(settingKey.c_str()), + filename.c_str()); +} + + +void OptionsType::ReadCustomCategories(bkstring& cats) +{ + DBVARIANT dbv; + + INT_PTR res = DBGetContactSettingTString(NULL, "SmileyAdd", "CustomCategories", &dbv); + if (res == 0) + { + cats = dbv.ptszVal; + DBFreeVariant(&dbv); + } +} + + +void OptionsType::WriteCustomCategories(const bkstring& cats) +{ + if (cats.empty()) + DBDeleteContactSetting(NULL, "SmileyAdd", "CustomCategories"); + else + DBWriteContactSettingTString(NULL, "SmileyAdd", "CustomCategories", cats.c_str()); +} + + +void OptionsType::ReadContactCategory(HANDLE hContact, bkstring& cats) +{ + DBVARIANT dbv; + + INT_PTR res = DBGetContactSettingTString(hContact, "SmileyAdd", "CustomCategory", &dbv); + if (res == 0) + { + cats = dbv.ptszVal; + DBFreeVariant(&dbv); + } +} + + +void OptionsType::WriteContactCategory(HANDLE hContact, const bkstring& cats) +{ + if (cats.empty()) + DBDeleteContactSetting(hContact, "SmileyAdd", "CustomCategory"); + else + DBWriteContactSettingTString(hContact, "SmileyAdd", "CustomCategory", cats.c_str()); +} diff --git a/plugins/SmileyAdd/src/options.h b/plugins/SmileyAdd/src/options.h new file mode 100644 index 0000000000..3b83b0dc8f --- /dev/null +++ b/plugins/SmileyAdd/src/options.h @@ -0,0 +1,60 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef SMILEYADD_OPTIONS_H_ +#define SMILEYADD_OPTIONS_H_ + +int SmileysOptionsInitialize(WPARAM wParam, LPARAM lParam); + +class OptionsType +{ +public: + bool PluginSupportEnabled; + bool EnforceSpaces; + bool ScaleToTextheight; + bool UseOneForAll; + bool SurroundSmileyWithSpaces; + bool ScaleAllSmileys; + bool IEViewStyle; + bool AnimateSel; + bool AnimateDlg; + bool InputSmileys; + bool DCursorSmiley; + bool DisableCustom; + bool HQScaling; + unsigned ButtonStatus; + unsigned SelWndBkgClr; + unsigned MaxCustomSmileySize; + unsigned MinSmileySize; + + void Load(void); + void Save(void); + static void ReadPackFileName(bkstring& filename, const bkstring& name, + const bkstring& defaultFilename); + static void ReadCustomCategories(bkstring& cats); + static void ReadContactCategory(HANDLE hContact, bkstring& cats); + static void WritePackFileName(const bkstring& filename, const bkstring& name); + static void WriteCustomCategories(const bkstring& cats); + static void WriteContactCategory(HANDLE hContact, const bkstring& cats); +}; + +extern OptionsType opt; + +#endif // SMILEYADD_OPTIONS_H_ + diff --git a/plugins/SmileyAdd/src/regexp/Matcher.cpp b/plugins/SmileyAdd/src/regexp/Matcher.cpp new file mode 100644 index 0000000000..ebd1f57850 --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/Matcher.cpp @@ -0,0 +1,178 @@ +#include +#include + +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 Matcher::findAll() +{ + std::vector 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 Matcher::getGroups(const bool includeGroupZero) const +{ + int i, start = (includeGroupZero ? 0 : 1); + std::vector ret; + + for (i = start; i < gc; ++i) ret.push_back(getGroup(i)); + return ret; +} + diff --git a/plugins/SmileyAdd/src/regexp/Matcher.h b/plugins/SmileyAdd/src/regexp/Matcher.h new file mode 100644 index 0000000000..621184d8c2 --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/Matcher.h @@ -0,0 +1,248 @@ +#ifndef __MATCHER_H__ +#define __MATCHER_H__ + +#include "bkstring.h" +#include + +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 Matcher 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. +

+ The most common methods needed by the matcher are matches, + findNextMatch, and getGroup. matches + and findNextMatch both return success or failure, and further + details can be gathered from their documentation. +

+ Unlike Java's Matcher, 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. +

+ This class also provides an extremely handy method for replacing text with + captured data via the replaceWithGroups method. A typical + invocation looks like: +

+  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})$",
+                                 Pattern::MULTILINE_MATCHING | Pattern::UNIX_LINE_MODE);
+  Matcher * m = p->createMatcher("");
+  while (fgets(buf, 9999, fp))
+  {
+    m->setString(buf);
+    if (m->matches())
+    {
+      printf("%s\n", m->replaceWithGroups(str).c_str());
+    }
+  }
+  fclose(fp);
+
+  
+ Calling any of the following functions before first calling + matches, findFirstMatch, or + findNextMatch results in undefined behavior and may cause your + program to crash. + +
    +
  • replaceWithGroups +
  • getStartingIndex
  • +
  • getEndingIndex
  • +
  • getGroup
  • +
  • getGroups
  • +
+ +

+ The function findFirstMatch will attempt to find the first match + in the input string. The same results can be obtained by first calling + reset followed by findNextMatch. +

+ To eliminate the necessity of looping through a string to find all the + matching substrings, findAll was created. The function will find + all matching substrings and return them in a vector. 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 text using + pattern. + + @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 str with the appropriate captured + text. str 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 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 getGroups(const bool includeGroupZero = 0) const; +}; + +#endif diff --git a/plugins/SmileyAdd/src/regexp/Pattern.cpp b/plugins/SmileyAdd/src/regexp/Pattern.cpp new file mode 100644 index 0000000000..4487dddd4b --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/Pattern.cpp @@ -0,0 +1,1709 @@ +/** + From the author (Jeff Stuart) + " + Let me start by saying this file is pretty big. If you feel up to it, you can + try making changes yourself, but you would be better off to just email me at + stuart@cs.ucdavis.edu if you think there is a bug, or have something useful you + would like added. This project is very "near and dear" to me, so I am fairly + quick to make bug fixes. The header files for Pattern and Matcher are fairly + well documented and the function names are pretty self-explanatory, but if you + are having any trouble, feel free to email me at stuart@cs.ucdavis.edu. + + If you email me, make sure you put something like C++RE in the subject because + I tend to delete email if I don't recognize the name and the subject is + something like "I Need Your Help" or "Got A Second" or "I Found It". + " + */ + +/* + Detailed documentation is provided in this class' header file + + @author Jeffery Stuart + @since November 2004 + @version 1.07.00 +*/ + +#define to_lower(a) (char)(unsigned)CharLowerA((LPSTR)static_cast(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 +#include +#include +#include + +std::map Pattern::compiledPatterns; +std::map > 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 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; 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::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 Pattern::split(const bkstring & pattern, const bkstring & str, const bool keepEmptys, + const unsigned long limit, const unsigned long mode) +{ + std::vector ret; + Pattern * p = Pattern::compile(pattern, mode); + if (p) + { + ret = p->split(str, keepEmptys, limit); + delete p; + } + return ret; +} + +std::vector Pattern::findAll(const bkstring & pattern, const bkstring & str, const unsigned long mode) +{ + std::vector 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::iterator it; + for (it = compiledPatterns.begin(); it != compiledPatterns.end(); ++it) + { + delete it->second; + } + compiledPatterns.clear(); +} + +std::pair Pattern::findNthMatch(const bkstring & pattern, const bkstring & str, + const int matchNum, const unsigned long mode) +{ + std::pair 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::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 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 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 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 & 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 & 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 & 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 & 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 & soFar) +{ + if (inner) inner->findAllNodes(soFar); + NFANode::findAllNodes(soFar); +} +int NFAGroupLoopNode::match(const bkstring & str, Matcher * matcher, const int curInd) const +{ + bool b = (curInd > matcher->groupIndeces[gi]); + + if (b && matcher->groups[gi] < min) + { + ++matcher->groups[gi]; + int o = matcher->groupIndeces[gi]; + matcher->groupIndeces[gi] = curInd; + int ret = inner->match(str, matcher, curInd); + if (ret < 0) + { + matcher->groupIndeces[gi] = o; + --matcher->groups[gi]; + } + return ret; + } + else if (!b || matcher->groups[gi] >= max) + { + return next->match(str, matcher, curInd); + } + else + { + switch (type) + { + case 0: return matchGreedy(str, matcher, curInd); + case 1: return matchLazy(str, matcher, curInd); + case 2: return matchPossessive(str, matcher, curInd); + } + } + return -1; +} +int NFAGroupLoopNode::matchGreedy(const bkstring & str, Matcher * matcher, const int curInd) const +{ + int o = matcher->groupIndeces[gi]; // save our info for backtracking + matcher->groupIndeces[gi] = curInd; // move along + ++matcher->groups[gi]; + int ret = inner->match(str, matcher, curInd); // match internally + if (ret < 0) + { // if we failed, then restore info and match next + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + ret = next->match(str, matcher, curInd); + } + return ret; +} +int NFAGroupLoopNode::matchLazy(const bkstring & str, Matcher * matcher, const int curInd) const +{ + int ret = next->match(str, matcher, curInd); // be lazy, just go on + if (ret < 0) + { + int o = matcher->groupIndeces[gi]; // save info for backtracking + matcher->groupIndeces[gi] = curInd; // advance our position + ++matcher->groups[gi]; + ret = inner->match(str, matcher, curInd); // match our internal stuff + if (ret < 0) // if we failed, then restore the info + { + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + } + } + return ret; +} +int NFAGroupLoopNode::matchPossessive(const bkstring & str, Matcher * matcher, const int curInd) const +{ + int o = matcher->groupIndeces[gi]; // save info for backtracking + matcher->groupPos[gi] = matcher->groups[gi]; // set a flag stating we have matcher at least this much + matcher->groupIndeces[gi] = curInd; // move along + ++matcher->groups[gi]; + int ret = inner->match(str, matcher, curInd); // try and match again + if (ret < 0) + { // if we fail, back off, but to an extent + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + if (matcher->groups[gi] == matcher->groupPos[gi]) ret = next->match(str, matcher, curInd); + } + return ret; +} + +#ifdef _WIN32 + #pragma warning(pop) +#endif diff --git a/plugins/SmileyAdd/src/regexp/Pattern.h b/plugins/SmileyAdd/src/regexp/Pattern.h new file mode 100644 index 0000000000..bb16ad90fa --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/Pattern.h @@ -0,0 +1,1663 @@ +#ifndef __PATTERN_H__ +#define __PATTERN_H__ + +#ifdef _WIN32 + #pragma warning(disable:4786) +#endif + +#include +#include + +#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: + +

+  Pattern * p = Pattern::compile("a*b");
+  Matcher * m = p->createMatcher("aaaaaab");
+  if (m->matches()) ...
+  
+ + 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: + +
+  if (Pattern::matches("a*b", "aaaab")) { ... }
+  
+ + 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 Pattern. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Construct + + Matches + +
+   +
+ Characters +
+ x + + The character x +
+ \\ + + The character \ +
+ \0nn + + The character with octal ASCII value nn +
+ \0nnn + + The character with octal ASCII value nnn +
+ \xhh + + The character with hexadecimal ASCII value hh +
+ \t + + A tab character +
+ \r + + A carriage return character +
+ \n + + A new-line character +
+   +
+ Character Classes +
+ [abc] + + Either a, b, or c +
+ [^abc] + + Any character but a, b, or c +
+ [a-zA-Z] + + Any character ranging from a thru z, or + A thru Z +
+ [^a-zA-Z] + + Any character except those ranging from a thru + z, or A thru Z +
+ [a\-z] + + Either a, -, or z +
+ [a-z[A-Z]] + + Same as [a-zA-Z] +
+ [a-z&&[g-i]] + + Any character in the intersection of a-z and + g-i +
+ [a-z&&[^g-i]] + + Any character in a-z and not in g-i +
+   +
+ Prefefined character classes +
+ . + + Any character. Multiline matching must be compiled into the pattern for + . to match a \r or a \n. + Even if multiline matching is enabled, . will not + match a \r\n, only a \r or a \n. +
+ \d + + [0-9] +
+ \D + + [^\d] +
+ \s + + [ \t\r\n\x0B] +
+ \S + + [^\s] +
+ \w + + [a-zA-Z0-9_] +
+ \W + + [^\w] +
+   +
+ POSIX character classes +
+ \p{Lower} + + [a-z] +
+ \p{Upper} + + [A-Z] +
+ \p{ASCII} + + [\x00-\x7F] +
+ \p{Alpha} + + [a-zA-Z] +
+ \p{Digit} + + [0-9] +
+ \p{Alnum} + + [\w&&[^_]] +
+ \p{Punct} + + [!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~] +
+ \p{XDigit} + + [a-fA-F0-9] +
+   +
+ Boundary Matches +
+ ^ + + The beginning of a line. Also matches the beginning of input. +
+ $ + + The end of a line. Also matches the end of input. +
+ \b + + A word boundary +
+ \B + + A non word boundary +
+ \A + + The beginning of input +
+ \G + + 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. +
+ \Z + + The end of input. Will also match if there is a single trailing + \r\n, a single trailing \r, or a single + trailing \n. +
+ \z + + The end of input +
+   +
+ Greedy Quantifiers +
+ x? + + x, either zero times or one time +
+ x* + + x, zero or more times +
+ x+ + + x, one or more times +
+ x{n} + + x, exactly n times +
+ x{n,} + + x, at least n times +
+ x{,m} + + x, at most m times +
+ x{n,m} + + x, at least n times and at most + m times +
+   +
+ Possessive Quantifiers +
+ x?+ + + x, either zero times or one time +
+ x*+ + + x, zero or more times +
+ x++ + + x, one or more times +
+ x{n}+ + + x, exactly n times +
+ x{n,}+ + + x, at least n times +
+ x{,m}+ + + x, at most m times +
+ x{n,m}+ + + x, at least n times and at most + m times +
+   +
+ Reluctant Quantifiers +
+ x?? + + x, either zero times or one time +
+ x*? + + x, zero or more times +
+ x+? + + x, one or more times +
+ x{n}? + + x, exactly n times +
+ x{n,}? + + x, at least n times +
+ x{,m}? + + x, at most m times +
+ x{n,m}? + + x, at least n times and at most + m times +
+   +
+ Operators +
+ xy + + x then y +
+ x|y + + x or y +
+ (x) + + x as a capturing group +
+   +
+ Quoting +
+ \Q + + Nothing, but treat every character (including \s) literally until a + matching \E +
+ \E + + Nothing, but ends its matching \Q +
+   +
+ Special Constructs +
+ (?:x) + + x, but not as a capturing group +
+ (?=x) + + x, via positive lookahead. This means that the + expression will match only if it is trailed by x. + It will not "eat" any of the characters matched by + x. +
+ (?!x) + + x, via negative lookahead. This means that the + expression will match only if it is not trailed by + x. It will not "eat" any of the characters + matched by x. +
+ (?<=x) + + x, via positive lookbehind. x + cannot contain any quantifiers. +
+ (?x) + + x, via negative lookbehind. x + cannot contain any quantifiers. +
+ (?>x) + + x{1}+ +
+   +
+ Registered Expression Matching +
+ {x} + + The registered pattern x +
+ +
+ + Begin Text Extracted And Modified From java.util.regex.Pattern documentation + +

Backslashes, escapes, and quoting

+ +

The backslash character ('\') 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 \\ matches a single backslash and \{ matches a + left brace. + +

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. + +

It is necessary to double backslashes in string literals that represent + regular expressions to protect them from interpretation by a compiler. The + string literal "\b", for example, matches a single backspace + character when interpreted as a regular expression, while + "\\b" matches a word boundary. The string litera + "\(hello\)" is illegal and leads to a compile-time error; + in order to match the string (hello) the string literal + "\\(hello\\)" must be used. + +

Character Classes

+ +

Character classes may appear within other character classes, and + may be composed by the union operator (implicit) and the intersection + operator (&&). + 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. + +

The precedence of character-class operators is as follows, from + highest to lowest: + +

+ + + + + + + + + + + + + + + + +
1    Literal escape    \x
2    Rangea-z
3    Grouping[...]
4    Intersection[a-z&&[aeiou]]
5    Union[a-e][i-u]
+ +

Note that a different set of metacharacters are in effect inside + a character class than outside a character class. For instance, the + regular expression . loses its special meaning inside a + character class, while the expression - becomes a range + forming metacharacter. + + + + +

Groups and capturing

+ +

Capturing groups are numbered by counting their opening parentheses from + left to right. In the expression ((A)(B(C))), for example, there + are four such groups:

+ +
+ + + + + + + + + + +
1    ((A)(B(C)))
2    (A)
3    (B(C))
4    (C)
+ +

Group zero always stands for the entire expression. + +

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. + +

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 + "aba" against the expression (a(b)?)+, for example, leaves + group two set to "b". All captured input is discarded at the + beginning of each match. + +

Groups beginning with (? are pure, non-capturing groups + that do not capture text and do not count towards the group total. + + +

Unicode support

+ +

Coming Soon. + +

Comparison to Perl 5

+ +

The Pattern engine performs traditional NFA-based matching + with ordered alternation as occurs in Perl 5. + +

Perl constructs not supported by this class:

+ +
    + +
  • The conditional constructs (?{X}) and + (?(condition)X|Y), +

  • + +
  • The embedded code constructs (?{code}) + and (??{code}),

  • + +
  • The embedded comment syntax (?#comment), and

  • + +
  • The preprocessing operations \l \u, + \L, and \U.

  • + +
  • Embedded flags

  • + +
+ +

Constructs supported by this class but not by Perl:

+ +
    + +
  • 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.

  • + +
  • Character-class union and intersection as described + above.

  • + +
+ +

Notable differences from Perl:

+ +
    + +
  • In Perl, \1 through \9 are always interpreted + as back references; a backslash-escaped number greater than 9 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, + \1 through \9 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. +

  • + +
  • Perl uses the g flag to request a match that resumes + where the last match left off. This functionality is provided implicitly + by the Matcher class: Repeated invocations of the + find method will resume where the last match left off, + unless the matcher is reset.

  • + +
  • Perl is forgiving about malformed matching constructs, as in the + expression *a, as well as dangling brackets, as in the + expression abc], and treats them as literals. This + class also strict and will not compile a pattern when dangling characters + are encountered.

  • + +
+ + +

For a more precise description of the behavior of regular expression + constructs, please see + Mastering Regular Expressions, 2nd Edition, Jeffrey E. F. Friedl, + O'Reilly and Associates, 2002. +

+

+ + End Text Extracted And Modified From java.util.regex.Pattern documentation + +


+ + @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 rhs. + */ + 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 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 > 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 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, + error is no longer used. + */ + bool error; + /** + Used during compilation to keep track of the current index into + {@link pattern pattern}. Once the pattern is successfully + compiled, error 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 NULL. + */ + void raiseError(); + /** + Convenience function for registering a node in nodes. + @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 s1 and + s2. + */ + 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 s1 and s2. + */ + bkstring classIntersect (bkstring s1, bkstring s2) const; + /** + Calculates the negation of a string. The negation is the set of all + characters between \x00 and \xFF not + contained in s1. + @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 s1 and s2. + */ + bkstring classNegate (bkstring s1) const; + /** + Creates a new "class" representing the range from low thru + hi. This function will wrap if low > + hi. 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 + {@link pattern pattern} starting at start and + ending at end. + @param start The starting index in {@link pattern pattern} + @param end The last index in {@link pattern pattern} + @return The decimal number in {@link pattern pattern} + */ + int getInt(int start, int end); + /** + Parses a {n,m} string out of the member-variable + {@link pattern pattern} stores the result in sNum + and eNum. + @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 + {@link pattern pattern}, 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, return value != newNode + */ + NFANode * quantify(NFANode * newNode); + /** + Parses the current class being examined in + {@link pattern pattern}. + @return A string of unique characters contained in the current class being + parsed + */ + bkstring parseClass(); + /** + Parses the current POSIX class being examined in + {@link pattern pattern}. + @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 *end. 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 + *end. + @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 {@link pattern pattern}. This function is called + recursively when an or (|) 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 (|) + @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 . as [\x00-\x7F] + const static unsigned long DOT_MATCHES_ALL; + /** ^ and $ should anchor to the beginning and + ending of lines, not all input + */ + const static unsigned long MULTILINE_MATCHING; + /** When enabled, only instances of \n 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 + Pattern object. Special values can be assigned to + mode when certain non-standard behaviors are expected from + the Pattern object. + @param pattern The regular expression to compile + @param mode A bitwise or of flags signalling what special behaviors are + wanted from this Pattern object + @return If successful, compile returns a Pattern + pointer. Upon failure, compile returns + NULL + */ + 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 Pattern object + @return If successful, compileAndKeep returns a + Pattern pointer. Upon failure, compile + returns NULL. + */ + static Pattern * compileAndKeep (const bkstring & pattern, + const unsigned long mode = 0); + + /** + Searches through replace and replaces all substrings matched + by pattern with str. str may + contain backreferences (e.g. \1) to capture groups. A typical + invocation looks like: +

+ + Pattern::replace("(a+)b(c+)", "abcccbbabcbabc", "\\2b\\1"); + +

+ which would replace abcccbbabcbabc with + cccbabbcbabcba. + @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 Pattern + 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: +

+ + bkstring str(strSize, '\0');
+ FILE * fp = fopen(fileName, "r");
+ fread((char*)str.data(), strSize, 1, fp);
+ fclose(fp);
+
+ std::vector<bkstring> lines = Pattern::split("[\r\n]+", str, true);
+
+
+ + @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 Pattern + during the split process + @return All substrings of str split across pattern. + */ + static std::vector 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 a* can be matched by an empty string, so + instead you should pass a+ since at least one character must + be matched. A typical invocation of findAll looks like: +

+ + std::vector<td::string> numbers = Pattern::findAll("\\d+", string); + +

+ + @param pattern The pattern for which to search + @param str The string to search + @param mode The special mode requested of the Pattern + during the find process + @return All instances of pattern in str + */ + static std::vector 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 Pattern + during the replacement process + @return True if str is recognized by pattern + */ + 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: +

+ + Pattern::registerPattern("ip", "(?:\\d{1,3}\\.){3}\\d{1,3}");
+ Pattern * p1 = Pattern::compile("{ip}:\\d+");
+ Pattern * p2 = Pattern::compile("Connection from ({ip}) on port \\d+");
+
+

+ Multiple calls to registerPattern with the same + name 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 pattern 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 nth match of the + given pattern in the string. Match indeces start at zero, not one. + A typical invocation looks like this: +

+ + std::pair<bkstring, int> match = Pattern::findNthMatch("\\d{1,3}", "192.168.1.101:22", 1);
+ printf("%s %i\n", match.first.c_str(), match.second);
+
+ Output: 168 4
+
+ + @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 + str. You can check for success/failure by making sure + that the integer returned is greater than or equal to zero. + */ + static std::pair 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 split (const bkstring & str, const bool keepEmptys = 0, + const unsigned long limit = 0); + std::vector 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 & 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 & 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 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 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 & 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 & 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 & soFar); + virtual int match(const bkstring & str, Matcher * matcher, const int curInd = 0) const; + int matchGreedy(const bkstring & str, Matcher * matcher, const int curInd = 0) const; + int matchLazy(const bkstring & str, Matcher * matcher, const int curInd = 0) const; + int matchPossessive(const bkstring & str, Matcher * matcher, const int curInd = 0) const; +}; + +#endif + diff --git a/plugins/SmileyAdd/src/regexp/WCMatcher.cpp b/plugins/SmileyAdd/src/regexp/WCMatcher.cpp new file mode 100644 index 0000000000..bed5a1944b --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/WCMatcher.cpp @@ -0,0 +1,181 @@ +#include "WCMatcher.h" +#include "WCPattern.h" + +const int WCMatcher::MATCH_ENTIRE_STRING = 0x01; + +/* + Detailed documentation is provided in this class' header file + + @author Jeffery Stuart + @since November 2004 + @version 1.07.00 +*/ + +WCMatcher::WCMatcher(WCPattern * pattern, const bkstring & text) +{ + pat = pattern; + str = &text; + gc = pattern->groupCount; + ncgc = -pattern->nonCapGroupCount; + flags = 0; + matchedSomething = false; + starts = new int[gc + ncgc]; + ends = new int[gc + ncgc]; + groups = new int[gc + ncgc]; + groupPos = new int[gc + ncgc]; + groupIndeces = new int[gc + ncgc]; + starts = starts + ncgc; + ends = ends + ncgc; + groups = groups + ncgc; + groupPos = groupPos + ncgc; + groupIndeces = groupIndeces + ncgc; + for (int i = 0; i < gc; ++i) starts[i] = ends[i] = 0; +} +WCMatcher::~WCMatcher() +{ + delete [] (starts - ncgc); + delete [] (ends - ncgc); + delete [] (groups - ncgc); + delete [] (groupIndeces - ncgc); + delete [] (groupPos - ncgc); +} +void WCMatcher::clearGroups() +{ + int i; + lm = 0; + for (i = 0; i < gc; ++i) groups[i] = starts[i] = ends[i] = -1; + for (i = 1; i <= ncgc; ++i) groups[0 - i] = starts[0 - i] = ends[0 - i] = -1; +} +bkstring WCMatcher::replaceWithGroups(const bkstring & str) +{ + bkstring ret = L""; + + bkstring t = str; + while (t.size() > 0) + { + if (t[0] == (wchar_t)'\\') + { + t.erase(0, 1); + if (t.size() == 0) + { + ret += L"\\"; + } + else if (t[0] < (wchar_t)'0' || t[0] > (wchar_t)'9') + { + ret += t[0]; + t.erase(0, 1); + } + else + { + int gn = 0; + while (t.size() > 0 && t[0] >= (wchar_t)'0' && t[0] <= (wchar_t)'9') + { + gn = gn * 10 + (t[0] - (wchar_t)'0'); + t.erase(0, 1); + } + ret += getGroup(gn); + } + } + else + { + ret += t[0]; + t.erase(0, 1); + } + } + + return ret; +} +unsigned long WCMatcher::getFlags() const +{ + return flags; +} +const bkstring& WCMatcher::getText() const +{ + return *str; +} + +bool WCMatcher::matches() +{ + flags = MATCH_ENTIRE_STRING; + matchedSomething = false; + clearGroups(); + lm = 0; + return pat->head->match(*str, this, 0) == (int)str->size(); +} +bool WCMatcher::findFirstMatch() +{ + starts[0] = 0; + flags = 0; + clearGroups(); + start = 0; + lm = 0; + ends[0] = pat->head->match(*str, this, 0); + if (ends[0] >= 0) + { + matchedSomething = true; + return 1; + } + return 0; +} +bool WCMatcher::findNextMatch() +{ + int s = starts[0], e = ends[0]; + + if (!matchedSomething) return findFirstMatch(); + if (s == e) ++e; + flags = 0; + clearGroups(); + + starts[0] = e; + if (e >= (int)str->size()) return 0; + start = e; + lm = e; + ends[0] = pat->head->match(*str, this, e); + return ends[0] >= 0; +} +std::vector WCMatcher::findAll() +{ + std::vector 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 WCMatcher::getGroups(const bool includeGroupZero) const +{ + int i, start = (includeGroupZero ? 0 : 1); + std::vector ret; + + for (i = start; i < gc; ++i) + { + ret.push_back(getGroup(i)); + } + return ret; +} + diff --git a/plugins/SmileyAdd/src/regexp/WCMatcher.h b/plugins/SmileyAdd/src/regexp/WCMatcher.h new file mode 100644 index 0000000000..23cc49e41f --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/WCMatcher.h @@ -0,0 +1,234 @@ +#ifndef __WCMATCHER_H__ +#define __WCMATCHER_H__ + +#include "bkstring.h" +#include +#include + +/** + A matcher is a non thread-safe object used to scan strings using a given + {@link WCPattern WCPattern} object. Using a WCMatcher 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. +

+ The most common methods needed by the matcher are matches, + findNextMatch, and getGroup. matches + and findNextMatch both return success or failure, and further + details can be gathered from their documentation. +

+ Unlike Java's WCMatcher, 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. +

+ This class also provides an extremely handy method for replacing text with + captured data via the replaceWithGroups method. A typical + invocation looks like: +

+  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})$",
+                                 WCPattern::MULTILINE_MATCHING | WCPattern::UNIX_LINE_MODE);
+  WCMatcher * m = p->createWCMatcher("");
+  while (fgets(buf, 9999, fp))
+  {
+    m->setString(buf);
+    if (m->matches())
+    {
+      printf("%s\n", m->replaceWithGroups(str).c_str());
+    }
+  }
+  fclose(fp);
+
+  
+ Calling any of the following functions before first calling + matches, findFirstMatch, or + findNextMatch results in undefined behavior and may cause your + program to crash. + +
    +
  • replaceWithGroups +
  • getStartingIndex
  • +
  • getEndingIndex
  • +
  • getGroup
  • +
  • getGroups
  • +
+
+

+ The function findFirstMatch will attempt to find the first match + in the input string. The same results can be obtained by first calling + reset followed by findNextMatch. +

+ To eliminate the necessity of looping through a string to find all the + matching substrings, findAll was created. The function will find + all matching substrings and return them in a vector. 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 text using + pattern. + + @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 str with the appropriate captured + text. str 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 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 getGroups(const bool includeGroupZero = 0) const; +}; + +#endif diff --git a/plugins/SmileyAdd/src/regexp/WCPattern.cpp b/plugins/SmileyAdd/src/regexp/WCPattern.cpp new file mode 100644 index 0000000000..25f379f5e4 --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/WCPattern.cpp @@ -0,0 +1,1747 @@ +/** + From the author (Jeff Stuart) + " + Let me start by saying this file is pretty big. If you feel up to it, you can + try making changes yourself, but you would be better off to just email me at + stuart@cs.ucdavis.edu if you think there is a bug, or have something useful you + would like added. This project is very "near and dear" to me, so I am fairly quick + to make bug fixes. The header files for WCPattern and WCMatcher are fairly well + documented and the function names are pretty self-explanatory, but if you are having + any trouble, feel free to email me at stuart@cs.ucdavis.edu. + + If you email me, make sure you put something like C++RE in the subject because + I tend to delete email if I don't recognize the name and the subject is + something like "I Need Your Help" or "Got A Second" or "I Found It". + " + */ + +/* + Detailed documentation is provided in this class' header file + + @author Jeffery Stuart + @since November 2004 + @version 1.07.00 +*/ + +#ifdef _WIN32 + #pragma warning(push) + #pragma warning(disable:4996) +#endif + +#include +#include +#include +#include +#ifndef _WIN32 + #include +#endif + +std::map WCPattern::compiledWCPatterns; +std::map > 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 + static inline int str_icmp(const wchar_t * a, const wchar_t * b) + { + while (*a && *b) + { + const int t = (int)towlower(*a) - (int)tolower(*b); + if (t) return t; + ++a; ++b; + } + if (*a) + { + if (*b) return (int)towlower(*a) - (int)tolower(*b); + return 1; + } + else if (*b) return 1; + return 0; + } +#else + #define str_icmp wcscasecmp +#endif + +WCPattern::WCPattern(const bkstring & rhs) +{ + matcher = NULL; + pattern = rhs; + curInd = 0; + groupCount = 0; + nonCapGroupCount = 0; + error = 0; + head = NULL; +} +// convenience function in case we want to add any extra debugging output +void WCPattern::raiseError() +{ +/* switch (pattern[curInd - 1]) + { + case '*': + case ')': + case '+': + case '?': + case ']': + case '}': + fwprintf(stderr, L"%s\n%*c^\n", pattern.c_str(), curInd - 1, ' '); + fwprintf(stderr, L"Syntax Error near here. Possible unescaped meta character.\n"); + break; + default: + fwprintf(stderr, L"%s\n%*c^\n", pattern.c_str(), curInd - 1, ' '); + fwprintf(stderr, L"Syntax Error near here. \n"); + break; + }*/ + error = 1; +} +NFAUNode * WCPattern::registerNode(NFAUNode * node) +{ + nodes[node] = 1; + return node; +} + +bkstring WCPattern::classUnion (bkstring s1, bkstring s2) const +{ + wchar_t * out = new wchar_t[66000]; + std::sort(s1.begin(), s1.end()); + std::sort(s2.begin(), s2.end()); + wchar_t* p = std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out); *p = 0; + bkstring ret = out; + delete [] out; + return ret; +} +bkstring WCPattern::classIntersect (bkstring s1, bkstring s2) const +{ + wchar_t * out = new wchar_t[66000]; + std::sort(s1.begin(), s1.end()); + std::sort(s2.begin(), s2.end()); + *std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), out) = 0; + bkstring ret = out; + delete [] out; + return ret; +} +bkstring WCPattern::classNegate (bkstring s1) const +{ + wchar_t * out = new wchar_t[66000]; + int i, ind = 0; + std::map 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; 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::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 WCPattern::split(const bkstring & pattern, const bkstring & str, const bool keepEmptys, + const unsigned long limit, const unsigned long mode) +{ + std::vector ret; + WCPattern * p = WCPattern::compile(pattern, mode); + if (p) + { + ret = p->split(str, keepEmptys, limit); + delete p; + } + return ret; +} + +std::vector WCPattern::findAll(const bkstring & pattern, const bkstring & str, const unsigned long mode) +{ + std::vector 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::iterator it; + for (it = compiledWCPatterns.begin(); it != compiledWCPatterns.end(); ++it) + { + delete it->second; + } + compiledWCPatterns.clear(); +} + +std::pair WCPattern::findNthMatch(const bkstring & pattern, const bkstring & str, + const int matchNum, const unsigned long mode) +{ + std::pair 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::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 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 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 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 & 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 & 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 & 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 & 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 & soFar) +{ + if (inner) inner->findAllNodes(soFar); + NFAUNode::findAllNodes(soFar); +} +int NFAGroupLoopUNode::match(const bkstring & str, WCMatcher * matcher, const int curInd) const +{ + bool b = (curInd > matcher->groupIndeces[gi]); + + if (b && matcher->groups[gi] < min) + { + ++matcher->groups[gi]; + int o = matcher->groupIndeces[gi]; + matcher->groupIndeces[gi] = curInd; + int ret = inner->match(str, matcher, curInd); + if (ret < 0) + { + matcher->groupIndeces[gi] = o; + --matcher->groups[gi]; + } + return ret; + } + else if (!b || matcher->groups[gi] >= max) + { + return next->match(str, matcher, curInd); + } + else + { + switch (type) + { + case 0: return matchGreedy(str, matcher, curInd); + case 1: return matchLazy(str, matcher, curInd); + case 2: return matchPossessive(str, matcher, curInd); + } + } + return -1; +} +int NFAGroupLoopUNode::matchGreedy(const bkstring & str, WCMatcher * matcher, const int curInd) const +{ + int o = matcher->groupIndeces[gi]; // save our info for backtracking + matcher->groupIndeces[gi] = curInd; // move along + ++matcher->groups[gi]; + int ret = inner->match(str, matcher, curInd); // match internally + if (ret < 0) + { // if we failed, then restore info and match next + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + ret = next->match(str, matcher, curInd); + } + return ret; +} +int NFAGroupLoopUNode::matchLazy(const bkstring & str, WCMatcher * matcher, const int curInd) const +{ + int ret = next->match(str, matcher, curInd); // be lazy, just go on + if (ret < 0) + { + int o = matcher->groupIndeces[gi]; // save info for backtracking + matcher->groupIndeces[gi] = curInd; // advance our position + ++matcher->groups[gi]; + ret = inner->match(str, matcher, curInd); // match our internal stuff + if (ret < 0) // if we failed, then restore the info + { + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + } + } + return ret; +} +int NFAGroupLoopUNode::matchPossessive(const bkstring & str, WCMatcher * matcher, const int curInd) const +{ + int o = matcher->groupIndeces[gi]; // save info for backtracking + matcher->groupPos[gi] = matcher->groups[gi]; // set a flag stating we have matcher at least this much + matcher->groupIndeces[gi] = curInd; // move along + ++matcher->groups[gi]; + int ret = inner->match(str, matcher, curInd); // try and match again + if (ret < 0) + { // if we fail, back off, but to an extent + --matcher->groups[gi]; + matcher->groupIndeces[gi] = o; + if (matcher->groups[gi] == matcher->groupPos[gi]) ret = next->match(str, matcher, curInd); + } + return ret; +} + +#ifdef _WIN32 + #pragma warning(pop) +#endif diff --git a/plugins/SmileyAdd/src/regexp/WCPattern.h b/plugins/SmileyAdd/src/regexp/WCPattern.h new file mode 100644 index 0000000000..3d52a7fd2e --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/WCPattern.h @@ -0,0 +1,1663 @@ +#ifndef __WCPATTERN_H__ +#define __WCPATTERN_H__ + +#ifdef _WIN32 + #pragma warning(disable:4786) +#endif + +#include "bkstring.h" + +#include +#include + +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: + +

+  WCPattern * p = WCPattern::compile(L"a*b");
+  WCMatcher * m = p->createWCMatcher(L"aaaaaab");
+  if (m->matches()) ...
+  
+ + 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: + +
+  if (WCPattern::matches(L"a*b", L"aaaab")) { ... }
+  
+ + 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 WCPattern. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Construct + + Matches + +
+   +
+ Characters +
+ x + + The character x +
+ \\ + + The character \ +
+ \0nn + + The character with octal ASCII value nn +
+ \0nnn + + The character with octal ASCII value nnn +
+ \xhh + + The character with hexadecimal ASCII value hh +
+ \t + + A tab character +
+ \r + + A carriage return character +
+ \n + + A new-line character +
+   +
+ Character Classes +
+ [abc] + + Either a, b, or c +
+ [^abc] + + Any character but a, b, or c +
+ [a-zA-Z] + + Any character ranging from a thru z, or + A thru Z +
+ [^a-zA-Z] + + Any character except those ranging from a thru + z, or A thru Z +
+ [a\-z] + + Either a, -, or z +
+ [a-z[A-Z]] + + Same as [a-zA-Z] +
+ [a-z&&[g-i]] + + Any character in the intersection of a-z and + g-i +
+ [a-z&&[^g-i]] + + Any character in a-z and not in g-i +
+   +
+ Prefefined character classes +
+ . + + Any character. Multiline matching must be compiled into the pattern for + . to match a \r or a \n. + Even if multiline matching is enabled, . will not + match a \r\n, only a \r or a \n. +
+ \d + + [0-9] +
+ \D + + [^\d] +
+ \s + + [ \t\r\n\x0B] +
+ \S + + [^\s] +
+ \w + + [a-zA-Z0-9_] +
+ \W + + [^\w] +
+   +
+ POSIX character classes +
+ \p{Lower} + + [a-z] +
+ \p{Upper} + + [A-Z] +
+ \p{ASCII} + + [\x00-\x7F] +
+ \p{Alpha} + + [a-zA-Z] +
+ \p{Digit} + + [0-9] +
+ \p{Alnum} + + [\w&&[^_]] +
+ \p{Punct} + + [!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~] +
+ \p{XDigit} + + [a-fA-F0-9] +
+   +
+ Boundary Matches +
+ ^ + + The beginning of a line. Also matches the beginning of input. +
+ $ + + The end of a line. Also matches the end of input. +
+ \b + + A word boundary +
+ \B + + A non word boundary +
+ \A + + The beginning of input +
+ \G + + 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. +
+ \Z + + The end of input. Will also match if there is a single trailing + \r\n, a single trailing \r, or a single + trailing \n. +
+ \z + + The end of input +
+   +
+ Greedy Quantifiers +
+ x? + + x, either zero times or one time +
+ x* + + x, zero or more times +
+ x+ + + x, one or more times +
+ x{n} + + x, exactly n times +
+ x{n,} + + x, at least n times +
+ x{,m} + + x, at most m times +
+ x{n,m} + + x, at least n times and at most + m times +
+   +
+ Possessive Quantifiers +
+ x?+ + + x, either zero times or one time +
+ x*+ + + x, zero or more times +
+ x++ + + x, one or more times +
+ x{n}+ + + x, exactly n times +
+ x{n,}+ + + x, at least n times +
+ x{,m}+ + + x, at most m times +
+ x{n,m}+ + + x, at least n times and at most + m times +
+   +
+ Reluctant Quantifiers +
+ x?? + + x, either zero times or one time +
+ x*? + + x, zero or more times +
+ x+? + + x, one or more times +
+ x{n}? + + x, exactly n times +
+ x{n,}? + + x, at least n times +
+ x{,m}? + + x, at most m times +
+ x{n,m}? + + x, at least n times and at most + m times +
+   +
+ Operators +
+ xy + + x then y +
+ x|y + + x or y +
+ (x) + + x as a capturing group +
+   +
+ Quoting +
+ \Q + + Nothing, but treat every character (including \s) literally until a + matching \E +
+ \E + + Nothing, but ends its matching \Q +
+   +
+ Special Constructs +
+ (?:x) + + x, but not as a capturing group +
+ (?=x) + + x, via positive lookahead. This means that the + expression will match only if it is trailed by x. + It will not "eat" any of the characters matched by + x. +
+ (?!x) + + x, via negative lookahead. This means that the + expression will match only if it is not trailed by + x. It will not "eat" any of the characters + matched by x. +
+ (?<=x) + + x, via positive lookbehind. x + cannot contain any quantifiers. +
+ (?x) + + x, via negative lookbehind. x + cannot contain any quantifiers. +
+ (?>x) + + x{1}+ +
+   +
+ Registered Expression Matching +
+ {x} + + The registered pattern x +
+ +
+ + Begin Text Extracted And Modified From java.util.regex.WCPattern documentation + +

Backslashes, escapes, and quoting

+ +

The backslash character ((wchar_t)'\') 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 \\ matches a single backslash and \{ matches a + left brace. + +

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. + +

It is necessary to double backslashes in string literals that represent + regular expressions to protect them from interpretation by a compiler. The + string literal "\b", for example, matches a single backspace + character when interpreted as a regular expression, while + "\\b" matches a word boundary. The string litera + "\(hello\)" is illegal and leads to a compile-time error; + in order to match the string (hello) the string literal + "\\(hello\\)" must be used. + +

Character Classes

+ +

Character classes may appear within other character classes, and + may be composed by the union operator (implicit) and the intersection + operator (&&). + 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. + +

The precedence of character-class operators is as follows, from + highest to lowest: + +

+ + + + + + + + + + + + + + + + +
1    Literal escape    \x
2    Rangea-z
3    Grouping[...]
4    Intersection[a-z&&[aeiou]]
5    Union[a-e][i-u]
+ +

Note that a different set of metacharacters are in effect inside + a character class than outside a character class. For instance, the + regular expression . loses its special meaning inside a + character class, while the expression - becomes a range + forming metacharacter. + + + + +

Groups and capturing

+ +

Capturing groups are numbered by counting their opening parentheses from + left to right. In the expression ((A)(B(C))), for example, there + are four such groups:

+ +
+ + + + + + + + + + +
1    ((A)(B(C)))
2    (A)
3    (B(C))
4    (C)
+ +

Group zero always stands for the entire expression. + +

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. + +

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 + L"aba" against the expression (a(b)?)+, for example, leaves + group two set to L"b". All captured input is discarded at the + beginning of each match. + +

Groups beginning with (? are pure, non-capturing groups + that do not capture text and do not count towards the group total. + + +

WC support

+ +

Coming Soon. + +

Comparison to Perl 5

+ +

The WCPattern engine performs traditional NFA-based matching + with ordered alternation as occurs in Perl 5. + +

Perl constructs not supported by this class:

+ +
    + +
  • The conditional constructs (?{X}) and + (?(condition)X|Y), +

  • + +
  • The embedded code constructs (?{code}) + and (??{code}),

  • + +
  • The embedded comment syntax (?#comment), and

  • + +
  • The preprocessing operations \l \u, + \L, and \U.

  • + +
  • Embedded flags

  • + +
+ +

Constructs supported by this class but not by Perl:

+ +
    + +
  • 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.

  • + +
  • Character-class union and intersection as described + above.

  • + +
+ +

Notable differences from Perl:

+ +
    + +
  • In Perl, \1 through \9 are always interpreted + as back references; a backslash-escaped number greater than 9 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, + \1 through \9 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. +

  • + +
  • Perl uses the g flag to request a match that resumes + where the last match left off. This functionality is provided implicitly + by the WCMatcher class: Repeated invocations of the + find method will resume where the last match left off, + unless the matcher is reset.

  • + +
  • Perl is forgiving about malformed matching constructs, as in the + expression *a, as well as dangling brackets, as in the + expression abc], and treats them as literals. This + class also strict and will not compile a pattern when dangling characters + are encountered.

  • + +
+ + +

For a more precise description of the behavior of regular expression + constructs, please see + Mastering Regular Expressions, 2nd Edition, Jeffrey E. F. Friedl, + O'Reilly and Associates, 2002. +

+

+ + End Text Extracted And Modified From java.util.regex.WCPattern documentation + +


+ + @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 rhs. + */ + 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 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 > 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 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, + error is no longer used. + */ + bool error; + /** + Used during compilation to keep track of the current index into + {@link pattern pattern}. Once the pattern is successfully + compiled, error 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 NULL. + */ + void raiseError(); + /** + Convenience function for registering a node in nodes. + @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 s1 and + s2. + */ + 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 s1 and s2. + */ + bkstring classIntersect (bkstring s1, bkstring s2) const; + /** + Calculates the negation of a string. The negation is the set of all + characters between \x00 and \xFF not + contained in s1. + @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 s1 and s2. + */ + bkstring classNegate (bkstring s1) const; + /** + Creates a new "class" representing the range from low thru + hi. This function will wrap if low > + hi. 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 + {@link pattern pattern} starting at start and + ending at end. + @param start The starting index in {@link pattern pattern} + @param end The last index in {@link pattern pattern} + @return The decimal number in {@link pattern pattern} + */ + int getInt(int start, int end); + /** + Parses a {n,m} string out of the member-variable + {@link pattern pattern} stores the result in sNum + and eNum. + @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 + {@link pattern pattern}, 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, return value != newNode + */ + NFAUNode * quantify(NFAUNode * newNode); + /** + Parses the current class being examined in + {@link pattern pattern}. + @return A string of unique characters contained in the current class being + parsed + */ + bkstring parseClass(); + /** + Parses the current POSIX class being examined in + {@link pattern pattern}. + @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 *end. 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 + *end. + @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 {@link pattern pattern}. This function is called + recursively when an or (|) 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 (|) + @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 . as [\x00-\x7F] + const static unsigned long DOT_MATCHES_ALL; + /** ^ and $ should anchor to the beginning and + ending of lines, not all input + */ + const static unsigned long MULTILINE_MATCHING; + /** When enabled, only instances of \n 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 + WCPattern object. Special values can be assigned to + mode when certain non-standard behaviors are expected from + the WCPattern object. + @param pattern The regular expression to compile + @param mode A bitwise or of flags signalling what special behaviors are + wanted from this WCPattern object + @return If successful, compile returns a WCPattern + pointer. Upon failure, compile returns + NULL + */ + 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 WCPattern object + @return If successful, compileAndKeep returns a + WCPattern pointer. Upon failure, compile + returns NULL. + */ + static WCPattern * compileAndKeep (const bkstring & pattern, + const unsigned long mode = 0); + + /** + Searches through replace and replaces all substrings matched + by pattern with str. str may + contain backreferences (e.g. \1) to capture groups. A typical + invocation looks like: +

+ + WCPattern::replace(L"(a+)b(c+)", L"abcccbbabcbabc", L"\\2b\\1"); + +

+ which would replace abcccbbabcbabc with + cccbabbcbabcba. + @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 WCPattern + 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: +

+ + bkstring str(strSize, 0);
+ FILE * fp = fopen(fileName, "r");
+ fread((char*)str.data(), strSize * 2, 1, fp);
+ fclose(fp);
+
+ std::vector<bkstring> lines = WCPattern::split(L"[\r\n]+", str, true);
+
+
+ + @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 WCPattern + during the split process + @return All substrings of str split across pattern. + */ + static std::vector 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 a* can be matched by an empty string, so + instead you should pass a+ since at least one character must + be matched. A typical invocation of findAll looks like: +

+ + std::vector<td::string> numbers = WCPattern::findAll(L"\\d+", string); + +

+ + @param pattern The pattern for which to search + @param str The string to search + @param mode The special mode requested of the WCPattern + during the find process + @return All instances of pattern in str + */ + static std::vector 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 WCPattern + during the replacement process + @return True if str is recognized by pattern + */ + 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: +

+ + WCPattern::registerWCPattern(L"ip", L"(?:\\d{1,3}\\.){3}\\d{1,3}");
+ WCPattern * p1 = WCPattern::compile(L"{ip}:\\d+");
+ WCPattern * p2 = WCPattern::compile(L"Connection from ({ip}) on port \\d+");
+
+

+ Multiple calls to registerWCPattern with the same + name 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 pattern 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 nth match of the + given pattern in the string. Match indeces start at zero, not one. + A typical invocation looks like this: +

+ + std::pair<bkstring, int> match = WCPattern::findNthMatch(L"\\d{1,3}", L"192.168.1.101:22", 1);
+ wprintf(L"%s %i\n", match.first.c_str(), match.second);
+
+ Output: 168 4
+
+ + @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 + str. You can check for success/failure by making sure + that the integer returned is greater than or equal to zero. + */ + static std::pair 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 split (const bkstring & str, const bool keepEmptys = 0, + const unsigned long limit = 0); + std::vector 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 & 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 & 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 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 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 & 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 & 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 & soFar); + virtual int match(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const; + int matchGreedy(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const; + int matchLazy(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const; + int matchPossessive(const bkstring & str, WCMatcher * matcher, const int curInd = 0) const; +}; + +#endif + diff --git a/plugins/SmileyAdd/src/regexp/test.cpp b/plugins/SmileyAdd/src/regexp/test.cpp new file mode 100644 index 0000000000..bb41566f96 --- /dev/null +++ b/plugins/SmileyAdd/src/regexp/test.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +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 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 groups = m0->getGroups(true); + for (int i = 0; i < (int)groups.size(); ++i) + { + wprintf(L"m->group(%d): %s\n", i, groups[i].c_str()); + } + } + } + + return 0; +} diff --git a/plugins/SmileyAdd/src/resource.h b/plugins/SmileyAdd/src/resource.h new file mode 100644 index 0000000000..bdf799fe2c --- /dev/null +++ b/plugins/SmileyAdd/src/resource.h @@ -0,0 +1,49 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDI_SMILINGICON 124 +#define IDD_OPT_SMILEYS 245 +#define IDC_CATEGORYLIST 1017 +#define IDC_PLUGENABLED 1019 +#define IDC_INPUTSMILEYS 1020 +#define IDC_DCURSORSMILEY 1021 +#define IDC_LIBNAME 1023 +#define IDC_SPACES 1024 +#define IDC_LIBAUTHOR 1026 +#define IDC_LIBVERSION 1027 +#define IDC_SCALETOTEXTHEIGHT 1037 +#define IDC_APPENDSPACES 1040 +#define IDC_SMLOPTBUTTON 1045 +#define IDC_SCALEALLSMILEYS 1046 +#define IDC_IEVIEWSTYLE 1047 +#define IDC_ANIMATESEL 1048 +#define IDC_USESTDPACK 1049 +#define IDC_ANIMATEDLG 1051 +#define IDC_DISABLECUSTOM 1052 +#define IDC_DISABLECUSTOM2 1053 +#define IDC_HQSCALING 1053 +#define IDC_SELCLR 1055 +#define IDC_NEWCATEGORY 1058 +#define IDC_ADDCATEGORY 1059 +#define IDC_DELETECATEGORY 1060 +#define IDC_SMLBUT 1061 +#define IDC_MAXCUSTSMSZ 1062 +#define IDC_MAXCUSTSPIN 1063 +#define IDC_MINSMSZ 1064 +#define IDC_MINSPIN 1065 +#define IDC_BROWSE 1184 +#define IDC_FILENAME 1271 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 127 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1064 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/SmileyAdd/src/richcall.cpp b/plugins/SmileyAdd/src/richcall.cpp new file mode 100644 index 0000000000..d9b9123c48 --- /dev/null +++ b/plugins/SmileyAdd/src/richcall.cpp @@ -0,0 +1,554 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "smileys.h" +#include "smileyroutines.h" +#include "services.h" +#include "options.h" +#include "SmileyBase.h" + +#include +#include + +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 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 g_RichEditOwnerList(5, CompareRichEditData); + + +static void SetPosition(HWND hwnd) +{ + IRichEditOle* RichEditOle; + if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) + return; + + ITextDocument* TextDocument; + if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) + { + RichEditOle->Release(); + return; + } + + // retrieve text range + ITextRange* TextRange; + if (TextDocument->Range(0, 0, &TextRange) != S_OK) + { + TextDocument->Release(); + RichEditOle->Release(); + return; + } + TextDocument->Release(); + + int objectCount = RichEditOle->GetObjectCount(); + for (int i = objectCount - 1; i >= 0; i--) + { + REOBJECT reObj = {0}; + reObj.cbStruct = sizeof(REOBJECT); + + HRESULT hr = RichEditOle->GetObject(i, &reObj, REO_GETOBJ_POLEOBJ); + if (FAILED(hr)) continue; + + ISmileyBase *igsc = NULL; + if (reObj.clsid == CLSID_NULL) + reObj.poleobj->QueryInterface(IID_ISmileyAddSmiley, (void**) &igsc); + + reObj.poleobj->Release(); + if (igsc == NULL) continue; + + TextRange->SetRange(reObj.cp, reObj.cp); + + BOOL res; + POINT pt; + RECT rect; + hr = TextRange->GetPoint(tomStart | TA_BOTTOM | TA_LEFT, &pt.x, &pt.y); + if (hr == S_OK) + { + res = ScreenToClient(hwnd, &pt); + rect.bottom = pt.y; + rect.left = pt.x; + } + else + rect.bottom = -1; + + hr = TextRange->GetPoint(tomStart | TA_TOP | TA_LEFT, &pt.x, &pt.y); + if (hr == S_OK) + { + res = ScreenToClient(hwnd, &pt); + rect.top = pt.y; + rect.left = pt.x; + } + else + rect.top = -1; + + igsc->SetPosition(hwnd, &rect); + igsc->Release(); + } + TextRange->Release(); + RichEditOle->Release(); +} + +static void SetTooltip(long x, long y, HWND hwnd, RichEditData* rdt) +{ + TCHAR* smltxt; + int needtip = CheckForTip(x, y, hwnd, &smltxt); + if (needtip != rdt->tipActive) + { + TOOLINFO ti = {0}; + ti.cbSize = sizeof(ti); + ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + ti.hwnd = hwnd; + ti.uId = (UINT_PTR)ti.hwnd; + + if (needtip != -1) + { + if (rdt->tipActive == -1) + { + rdt->hToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, _T(""), + TTS_NOPREFIX | WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + hwnd, NULL, g_hInst, NULL); + + SendMessage(rdt->hToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti); + } + + ti.lpszText = smltxt; + SendMessage(rdt->hToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); + SendMessage(rdt->hToolTip, TTM_ACTIVATE, TRUE, 0); + } + else + { + if (rdt->tipActive != -1) + { + SendMessage(rdt->hToolTip, TTM_ACTIVATE, FALSE, 0); + DestroyWindow(rdt->hToolTip); + rdt->hToolTip = NULL; + } + } + rdt->tipActive = needtip; + } +} + +static const CHARRANGE allsel = { 0, LONG_MAX }; + + +static void ReplaceContactSmileys(RichEditData *rdt, const CHARRANGE &sel, bool ignoreLast, bool unFreeze) +{ + if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return; + SmileyPackCType *smcp = NULL; + SmileyPackType* SmileyPack = GetSmileyPack(NULL, rdt->hContact, rdt->inputarea ? NULL : &smcp); + ReplaceSmileys(rdt->hwnd, SmileyPack, smcp, sel, false, ignoreLast, unFreeze); +} + +static void ReplaceContactSmileysWithText(RichEditData *rdt, CHARRANGE &sel, bool freeze) +{ + if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return; + ReplaceSmileysWithText(rdt->hwnd, sel, freeze); +} + +static void SmileyToTextCutPrep(RichEditData* rdt) +{ + if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return; + + SendMessage(rdt->hwnd, WM_SETREDRAW, FALSE, 0); + CHARRANGE sel; + SendMessage(rdt->hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + ReplaceContactSmileysWithText(rdt, sel, true); +} + +static void SmileyToTextCutRest(RichEditData* rdt) +{ + if ((rdt->inputarea && !opt.InputSmileys) || rdt->dontReplace) return; + + CHARRANGE sel; + SendMessage(rdt->hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + ReplaceContactSmileys(rdt, sel, false, true); + SendMessage(rdt->hwnd, WM_SETREDRAW, TRUE, 0); + RedrawWindow(rdt->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); +} + + +static LRESULT CALLBACK RichEditSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RichEditData* rdt = g_RichEditList.find((RichEditData*)&hwnd); + if (rdt == NULL) return 0; + + CHARRANGE sel; + + WNDPROC wpOrigWndProc = rdt->wpOrigWndProc; + + switch(uMsg) + { + case WM_DESTROY: + CloseRichCallback(hwnd, false); + break; + + case WM_COPY: + case WM_CUT: + SmileyToTextCutPrep(rdt); + break; + + case WM_PAINT: + SetPosition(hwnd); + break; + + case EM_STREAMOUT: + if (wParam & SFF_SELECTION) + SmileyToTextCutPrep(rdt); + else + { + sel = allsel; + ReplaceContactSmileysWithText(rdt, sel, true); + } + break; + + case WM_KEYDOWN: + if ((wParam == 'C' || wParam == VK_INSERT) && (GetKeyState(VK_CONTROL) & 0x8000)) + { + SmileyToTextCutPrep(rdt); + } + else if ((wParam == 'X' && (GetKeyState(VK_CONTROL) & 0x8000)) || + (wParam == VK_DELETE && (GetKeyState(VK_SHIFT) & 0x8000))) + { + SmileyToTextCutPrep(rdt); + } + else if (wParam == VK_TAB && ((GetKeyState(VK_CONTROL) | GetKeyState(VK_SHIFT)) & 0x8000) == 0) + { + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + sel.cpMin = max(sel.cpMin - 20, 0); + + ReplaceContactSmileysWithText(rdt, sel, true); + } + break; + } + + LRESULT result = CallWindowProc(wpOrigWndProc, hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_DESTROY: + CloseRichCallback(hwnd, true); + break; + + case WM_MOUSEMOVE: + SetTooltip(LOWORD(lParam), HIWORD(lParam), hwnd, rdt); + break; + + case WM_PAINT: + case WM_HSCROLL: + case WM_VSCROLL: + SetPosition(hwnd); + break; + + case WM_COPY: + case WM_CUT: + SmileyToTextCutRest(rdt); + break; + + case EM_STREAMOUT: + if (wParam & SFF_SELECTION) + SmileyToTextCutRest(rdt); + else + ReplaceContactSmileys(rdt, allsel, false, true); + break; + + case WM_KEYDOWN: + if ((wParam == 'C' || wParam == VK_INSERT) && (GetKeyState(VK_CONTROL) & 0x8000)) + { + SmileyToTextCutRest(rdt); + } + else if ((wParam == 'X' && (GetKeyState(VK_CONTROL) & 0x8000)) || + (wParam == VK_DELETE && (GetKeyState(VK_SHIFT) & 0x8000))) + { + SmileyToTextCutRest(rdt); + } + else if (wParam == VK_TAB && ((GetKeyState(VK_CONTROL) | GetKeyState(VK_SHIFT)) & 0x8000) == 0) + { + sel.cpMax = LONG_MAX; + bool hascont = rdt->hContact != NULL; + ReplaceContactSmileys(rdt, sel, false, hascont); + } + break; + + case WM_CHAR: + if (!rdt->inputarea || (rdt->inputarea && !opt.InputSmileys)) + break; + + if (lParam & (1 << 28)) // ALT key + break; + + if ((lParam & 0xFF) > 2) // Repeat rate + break; + + if (wParam > ' ' && opt.EnforceSpaces) + break; + + if (wParam == 0x16) + { + ReplaceContactSmileys(rdt, allsel, false, false); + break; + } + + if (opt.DCursorSmiley) + { + ReplaceContactSmileys(rdt, allsel, true, true); + } + else + { + if (wParam >= ' ' || wParam == '\n' || wParam == '\r') + { + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + sel.cpMin = max(sel.cpMin - 20, 0); + sel.cpMax += 20; + + ReplaceContactSmileysWithText(rdt, sel, true); + ReplaceContactSmileys(rdt, sel, false, true); + } + } + break; + + case EM_PASTESPECIAL: + case WM_PASTE: + case EM_REPLACESEL: + case WM_SETTEXT: + case EM_SETTEXTEX: + if (rdt->inputarea) + ReplaceContactSmileys(rdt, allsel, false, false); + break; + + case WM_REMAKERICH: + ReplaceContactSmileys(rdt, allsel, false, false); + break; + } + + return result; +} + +void CloseRichCallback(HWND hwnd, bool force) +{ + int ind = g_RichEditList.getIndex((RichEditData*)&hwnd); + if ( ind != -1 ) + { + RichEditData* rdt = g_RichEditList[ind]; + bool richsub = GetWindowLongPtr(hwnd, GWLP_WNDPROC) == (LONG_PTR)RichEditSubclass; + if (richsub) SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)rdt->wpOrigWndProc); + if (richsub || force) + { + if (rdt->hToolTip) DestroyWindow(rdt->hToolTip); + delete rdt; + g_RichEditList.remove(ind); + } + } +} + +bool SetRichCallback(HWND hwnd, HANDLE hContact, bool subany, bool subnew) +{ + RichEditData* rdt = g_RichEditList.find((RichEditData*)&hwnd); + if (rdt == NULL) + { + IRichEditOle* RichEditOle; + if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) + return false; + RichEditOle->Release(); + + rdt = new RichEditData; + + rdt->hwnd = hwnd; + rdt->hContact = hContact; + rdt->inputarea = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) == 0; + rdt->dontReplace = false; + rdt->tipActive = -1; + rdt->wpOrigWndProc = NULL; + rdt->hToolTip = NULL; + g_RichEditList.insert(rdt); + + if (subnew) + rdt->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditSubclass); + } + else + { + if (hContact && !rdt->hContact) rdt->hContact = hContact; + if (subany && rdt->wpOrigWndProc == NULL) + rdt->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditSubclass); + } + return true; +} + +static LRESULT CALLBACK RichEditOwnerSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RichEditOwnerData* rdto = g_RichEditOwnerList.find((RichEditOwnerData*)&hwnd); + if (rdto == NULL) return 0; + + WNDPROC wpOrigWndProc = rdto->wpOrigWndProc; + + switch(uMsg) + { + case WM_DESTROY: + { + RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput); + if (rdt && (!rdt->inputarea || opt.InputSmileys)) + { + CHARRANGE sel = allsel; + rdt->dontReplace = true; + ReplaceSmileysWithText(rdt->hwnd, sel, false); + } + } + CloseRichOwnerCallback(hwnd, false); + break; + + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 1624) + { + RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput); + if (rdt && (!rdt->inputarea || opt.InputSmileys)) + { + rdt->dontReplace = true; + CHARRANGE sel = allsel; + ReplaceSmileysWithText(rdt->hwnd, sel, false); + } + } + break; + } + + LRESULT result = CallWindowProc(wpOrigWndProc, hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_DESTROY: + CloseRichOwnerCallback(hwnd, true); + break; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 1624) // && lParam == 0) + { + RichEditData* rdt = g_RichEditList.find((RichEditData*)&rdto->hwndInput); + if (rdt) + { + CHARRANGE sel = allsel; + if (!result) ReplaceContactSmileys(rdt, sel, false, false); + rdt->dontReplace = false; + } + } + break; + } + return result; +} + +void CloseRichOwnerCallback(HWND hwnd, bool force) +{ + int ind = g_RichEditOwnerList.getIndex((RichEditOwnerData*)&hwnd); + if ( ind != -1 ) + { + RichEditOwnerData* rdto = g_RichEditOwnerList[ind]; + bool richsub = GetWindowLongPtr(hwnd, GWLP_WNDPROC) == (LONG_PTR)RichEditOwnerSubclass; + if (richsub) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)rdto->wpOrigWndProc); + rdto->wpOrigWndProc = NULL; + } + if (force) + { + CloseRichCallback(rdto->hwndInput, true); + CloseRichCallback(rdto->hwndLog, true); + + delete rdto; + g_RichEditOwnerList.remove(ind); + } + } +} + +void SetRichOwnerCallback(HWND hwnd, HWND hwndInput, HWND hwndLog) +{ + RichEditOwnerData* rdto = g_RichEditOwnerList.find((RichEditOwnerData*)&hwnd); + if (rdto == NULL) + { + rdto = new RichEditOwnerData; + + rdto->hwnd = hwnd; + rdto->hwndInput = hwndInput; + rdto->hwndLog = hwndLog; + rdto->wpOrigWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RichEditOwnerSubclass); + g_RichEditOwnerList.insert(rdto); + } + else + { + if (rdto->hwndInput == NULL) rdto->hwndInput = hwndInput; + if (rdto->hwndLog == NULL) rdto->hwndLog = hwndLog; + } +} + +void ProcessAllInputAreas(bool restoreText) +{ + for (int i=g_RichEditList.getCount(); i--; ) + { + RichEditData* rdt = g_RichEditList[i]; + if (rdt->inputarea) + { + if (restoreText) + { + CHARRANGE sel = allsel; + ReplaceContactSmileysWithText(rdt, sel, false); + } + else + { + ReplaceContactSmileys(rdt, allsel, false, false); + } + } + } +} + + +void RichEditData_Destroy(void) +{ + int i; + for (i=g_RichEditList.getCount(); i--; ) + CloseRichCallback(g_RichEditList[i]->hwnd, true); + g_RichEditList.destroy(); + + for (i=g_RichEditOwnerList.getCount(); i--; ) + CloseRichOwnerCallback(g_RichEditOwnerList[i]->hwnd, true); + g_RichEditOwnerList.destroy(); +} diff --git a/plugins/SmileyAdd/src/services.cpp b/plugins/SmileyAdd/src/services.cpp new file mode 100644 index 0000000000..fed1b4c7ad --- /dev/null +++ b/plugins/SmileyAdd/src/services.cpp @@ -0,0 +1,590 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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 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("")) return NULL; + if (!categoryName.empty() && + g_SmileyCategories.GetSmileyCategory(categoryName) == NULL) + { + categoryName.clear(); + opt.WriteContactCategory(hContact, categoryName); + } + + if (categoryName.empty() && !opt.UseOneForAll) + { + char *protonam = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (protonam != NULL) + { + DBVARIANT dbv; + if (DBGetContactSettingTString(hContact, protonam, "Transport", &dbv) == 0) + { + categoryName = dbv.ptszVal; + DBFreeVariant(&dbv); + } + else + categoryName = A2T_SM(protonam); + } + } + } + + if (categoryName.empty()) + { + if (proto == NULL || proto[0] == 0) + { + categoryName = _T("Standard"); + } + else + { + categoryName = A2T_SM(proto); + if (opt.UseOneForAll) + { + SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(categoryName); + if (smc == NULL || smc->IsProto()) categoryName = _T("Standard"); + } + } + } + + return g_SmileyCategories.GetSmileyPack(categoryName); +} + + +INT_PTR ReplaceSmileysCommand(WPARAM, LPARAM lParam) +{ + SMADD_RICHEDIT3* smre = (SMADD_RICHEDIT3*) lParam; + if (smre == NULL || smre->cbSize < SMADD_RICHEDIT_SIZE_V1) return FALSE; + + SMADD_RICHEDIT3 smrec = {0}; + memcpy(&smrec, smre, min(smre->cbSize, sizeof(smrec))); + + static const CHARRANGE selection = { 0, LONG_MAX }; + if (smre->rangeToReplace == NULL) smrec.rangeToReplace = (CHARRANGE*)&selection; + else if (smrec.rangeToReplace->cpMax < 0) smrec.rangeToReplace->cpMax = LONG_MAX; + + SmileyPackCType* smcp = NULL; + SmileyPackType* SmileyPack = GetSmileyPack(smrec.Protocolname, smrec.hContact, + (smrec.flags & (SAFLRE_OUTGOING | SAFLRE_NOCUSTOM)) ? NULL : &smcp); + + ReplaceSmileys(smre->hwndRichEditControl, SmileyPack, smcp, *smrec.rangeToReplace, + smrec.hContact == NULL, false, false); + + return TRUE; +} + + +INT_PTR ShowSmileySelectionCommand(WPARAM, LPARAM lParam) +{ + SMADD_SHOWSEL3* smaddInfo = (SMADD_SHOWSEL3*) lParam; + + if (smaddInfo == NULL || smaddInfo->cbSize < SMADD_SHOWSEL_SIZE_V1) return FALSE; + HWND parent = smaddInfo->cbSize > SMADD_SHOWSEL_SIZE_V1 ? smaddInfo->hwndParent : NULL; + HANDLE hContact = smaddInfo->cbSize > SMADD_SHOWSEL_SIZE_V2 ? smaddInfo->hContact : NULL; + + SmileyToolWindowParam *stwp = new SmileyToolWindowParam; + stwp->pSmileyPack = GetSmileyPack(smaddInfo->Protocolname, hContact); + stwp->hContact = hContact; + + stwp->hWndParent = parent; + stwp->hWndTarget = smaddInfo->hwndTarget; + stwp->targetMessage = smaddInfo->targetMessage; + stwp->targetWParam = smaddInfo->targetWParam; + stwp->xPosition = smaddInfo->xPosition; + stwp->yPosition = smaddInfo->yPosition; + stwp->direction = smaddInfo->Direction; + + mir_forkthread(SmileyToolThread, stwp); + + return TRUE; +} + +INT_PTR GetSmileyIconCommand(WPARAM, LPARAM lParam) +{ + SMADD_GETICON* smre = (SMADD_GETICON*) lParam; + + if (smre == NULL || smre->cbSize < sizeof(SMADD_GETICON)) return FALSE; + + SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname); + + if (SmileyPack == NULL || IsBadStringPtrA(smre->SmileySequence, MAX_SMILEY_LENGTH)) + { + smre->SmileyIcon = NULL; + smre->Smileylength = 0; + return FALSE; + } + + unsigned start, size; + SmileyType* sml; + FindSmileyInText(SmileyPack, A2T_SM(smre->SmileySequence), start, size, &sml); + + if (size == 0 || start != 0) + { + smre->SmileyIcon = NULL; + smre->Smileylength = 0; + } + else + { + smre->SmileyIcon = sml->GetIcon(); + smre->Smileylength = size; + } + + return TRUE; +} + + +static int GetInfoCommandE(SMADD_INFO2* smre, bool retDup) +{ + if (smre == NULL || smre->cbSize < SMADD_INFO_SIZE_V1) return FALSE; + HANDLE hContact = smre->cbSize > SMADD_INFO_SIZE_V1 ? smre->hContact : NULL; + + SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname, hContact); + + if (SmileyPack == NULL || SmileyPack->SmileyCount() == 0) + { + smre->ButtonIcon = NULL; + smre->NumberOfSmileys = 0; + smre->NumberOfVisibleSmileys = 0; + return FALSE; + } + + SmileyType* sml = FindButtonSmiley(SmileyPack); + + if (sml != NULL) + smre->ButtonIcon = retDup ? sml->GetIconDup() : sml->GetIcon(); + else + smre->ButtonIcon = GetDefaultIcon(retDup); + + smre->NumberOfSmileys = SmileyPack->SmileyCount(); + smre->NumberOfVisibleSmileys = SmileyPack->VisibleSmileyCount(); + + return TRUE; +} + + +INT_PTR GetInfoCommand(WPARAM, LPARAM lParam) +{ + return GetInfoCommandE((SMADD_INFO2*) lParam, false); +} + + +INT_PTR GetInfoCommand2(WPARAM, LPARAM lParam) +{ + return GetInfoCommandE((SMADD_INFO2*) lParam, true); +} + + +INT_PTR ParseText(WPARAM, LPARAM lParam) +{ + SMADD_PARSE* smre = (SMADD_PARSE*) lParam; + + if (smre == NULL || smre->cbSize < sizeof(SMADD_PARSE)) return FALSE; + + SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname); + + if (SmileyPack == NULL) + { + smre->SmileyIcon = NULL; + smre->size = 0; + return FALSE; + } + + unsigned strtChrOff = smre->startChar + smre->size; + char* workstr = smre->str + strtChrOff; + + if (strtChrOff > 1024 || IsBadStringPtrA(workstr, 10)) + { + smre->SmileyIcon = NULL; + smre->size = 0; + return FALSE; + } + + SmileyType* sml; + FindSmileyInText(SmileyPack, A2T_SM(workstr), smre->startChar, smre->size, &sml); + + if (smre->size == 0) + { + smre->SmileyIcon = NULL; + } + else + { + smre->SmileyIcon = sml->GetIconDup(); + smre->startChar += strtChrOff; + } + + return TRUE; +} + + +INT_PTR ParseTextW(WPARAM, LPARAM lParam) +{ + SMADD_PARSEW* smre = (SMADD_PARSEW*) lParam; + + if (smre == NULL || smre->cbSize < sizeof(SMADD_PARSEW)) return FALSE; + + SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname); + + if (SmileyPack == NULL) + { + smre->SmileyIcon = NULL; + smre->size = 0; + return FALSE; + } + + unsigned strtChrOff = smre->startChar + smre->size; + wchar_t* workstr = smre->str + strtChrOff; + + if (strtChrOff > 1024 || IsBadStringPtrW(workstr, 10)) + { + smre->SmileyIcon = NULL; + smre->size = 0; + return FALSE; + } + + SmileyType* sml; + FindSmileyInText(SmileyPack, workstr, smre->startChar, smre->size, &sml); + + if (smre->size == 0) + { + smre->SmileyIcon = NULL; + } + else + { + smre->SmileyIcon = sml->GetIconDup(); + smre->startChar += strtChrOff; + } + + return TRUE; +} + + +INT_PTR ParseTextBatch(WPARAM, LPARAM lParam) +{ + SMADD_BATCHPARSE2* smre = (SMADD_BATCHPARSE2*) lParam; + + if (smre == NULL || smre->cbSize < SMADD_BATCHPARSE_SIZE_V1) return FALSE; + HANDLE hContact = smre->cbSize > SMADD_BATCHPARSE_SIZE_V1 ? smre->hContact : NULL; + + SmileyPackCType* smcp = NULL; + SmileyPackType* SmileyPack = GetSmileyPack(smre->Protocolname, hContact, + (smre->flag & (SAFL_OUTGOING | SAFL_NOCUSTOM)) ? NULL : &smcp); + + SmileysQueueType smllist; + + if (smre->flag & SAFL_UNICODE) + LookupAllSmileys(SmileyPack, smcp, W2T_SM(smre->wstr), smllist, false); + else + LookupAllSmileys(SmileyPack, smcp, A2T_SM(smre->astr), smllist, false); + + if (smllist.getCount() == 0) return 0; + + SMADD_BATCHPARSERES *res = new SMADD_BATCHPARSERES[smllist.getCount()]; + SMADD_BATCHPARSERES* cres = res; + for (int j = 0; j < smllist.getCount(); j++) + { + cres->startChar = smllist[j].loc.cpMin; + cres->size = smllist[j].loc.cpMax - smllist[j].loc.cpMin; + if (smllist[j].sml) + { + if (smre->flag & SAFL_PATH) + cres->filepath = smllist[j].sml->GetFilePath().c_str(); + else + cres->hIcon = smllist[j].sml->GetIconDup(); + } + else + { + if (smre->flag & SAFL_PATH) + cres->filepath = smllist[j].smlc->GetFilePath().c_str(); + else + cres->hIcon = smllist[j].smlc->GetIcon(); + } + + cres++; + } + + smre->numSmileys = smllist.getCount(); + + smre->oflag = smre->flag | SAFL_UNICODE; + + return (INT_PTR)res; +} + +INT_PTR FreeTextBatch(WPARAM, LPARAM lParam) +{ + delete[] (SMADD_BATCHPARSERES*)lParam; + return TRUE; +} + +INT_PTR RegisterPack(WPARAM, LPARAM lParam) +{ + SMADD_REGCAT* smre = (SMADD_REGCAT*) lParam; + + if (smre == NULL || smre->cbSize < sizeof(SMADD_REGCAT)) return FALSE; + if (IsBadStringPtrA(smre->name, 50) || IsBadStringPtrA(smre->dispname, 50)) return FALSE; + + + unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP; + + + + bkstring nmd(A2W_SM(smre->dispname, lpcp)); + + + bkstring nm(A2T_SM(smre->name)); + g_SmileyCategories.AddAndLoad(nm, nmd); + + return TRUE; +} + +INT_PTR CustomCatMenu(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + if (lParam != 0) + { + SmileyCategoryType* smct = g_SmileyCategories.GetSmileyCategory((unsigned)lParam - 3); + if (smct != NULL) + opt.WriteContactCategory(hContact, smct->GetName()); + else + { + bkstring empty; + if (lParam == 1) empty = _T(""); + opt.WriteContactCategory(hContact, empty); + } + NotifyEventHooks(hEvent1, (WPARAM)hContact, 0); + } + + for (int i = 0; i < menuHandleArray.getCount(); i++) + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)menuHandleArray[i], 0); + menuHandleArray.destroy(); + + return TRUE; +} + + +int RebuildContactMenu(WPARAM wParam, LPARAM) +{ + int i; + CLISTMENUITEM mi = {0}; + + mi.cbSize = sizeof(mi); + mi.flags = CMIM_FLAGS | CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB; + + SmileyCategoryListType::SmileyCategoryVectorType& smc = *g_SmileyCategories.GetSmileyCategoryList(); + + char* protnam = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0); + bool haveMenu = IsSmileyProto(protnam); + if (haveMenu && opt.UseOneForAll) + { + unsigned cnt = 0; + for (i = 0; i < smc.getCount(); ++i) + cnt += smc[i].IsCustom(); + haveMenu = cnt != 0; + } + + if (!haveMenu) mi.flags |= CMIF_HIDDEN; + + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenuItem, (LPARAM)&mi); + + for (i = 0; i < menuHandleArray.getCount(); ++i) + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)menuHandleArray[i], 0); + menuHandleArray.destroy(); + + if (haveMenu) + { + bkstring cat; + opt.ReadContactCategory((HANDLE)wParam, cat); + + mi.pszPopupName = (char*)hContactMenuItem; + mi.flags = CMIF_CHILDPOPUP | CMIM_FLAGS | CMIF_TCHAR; + mi.pszService = MS_SMILEYADD_CUSTOMCATMENU; + + bool nonecheck = true; + HGENMENU hMenu; + + for (i = 0; i < smc.getCount(); i++) + { + if (smc[i].IsExt() || (smc[i].IsProto() && opt.UseOneForAll)) continue; + + const int ind = i + 3; + + mi.position = ind; + mi.popupPosition = ind; + mi.ptszName = (TCHAR*)smc[i].GetDisplayName().c_str(); + + if (cat == smc[i].GetName()) + { + mi.flags |= CMIF_CHECKED; + nonecheck = false; + } + + hMenu = Menu_AddContactMenuItem(&mi); + menuHandleArray.insert(hMenu); + mi.flags &= ~CMIF_CHECKED; + } + + mi.position = 1; + mi.popupPosition = 1; + mi.ptszName = _T(""); + if (cat == _T("")) { + mi.flags |= CMIF_CHECKED; + nonecheck = false; + } + + hMenu = Menu_AddContactMenuItem(&mi); + menuHandleArray.insert(hMenu); + + mi.position = 2; + mi.popupPosition = 2; + mi.ptszName = _T("Protocol specific"); + if (nonecheck) mi.flags |= CMIF_CHECKED; else mi.flags &= ~CMIF_CHECKED; + + hMenu = Menu_AddContactMenuItem(&mi); + menuHandleArray.insert(hMenu); + } + + return 0; +} + +INT_PTR ReloadPack(WPARAM, LPARAM lParam) +{ + if (lParam) + { + bkstring categoryName = A2T_SM((char*)lParam); + SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(categoryName); + if (smc != NULL) smc->Load(); + } + else + g_SmileyCategories.ClearAndLoadAll(); + + NotifyEventHooks(hEvent1, 0, 0); + return 0; +} + +INT_PTR LoadContactSmileys(WPARAM, LPARAM lParam) +{ + if (opt.DisableCustom) return 0; + + SMADD_CONT* cont = (SMADD_CONT*)lParam; + + switch (cont->type) + { + case 0: + g_SmileyPackCStore.AddSmileyPack(cont->hContact, cont->path); + NotifyEventHooks(hEvent1, (WPARAM)cont->hContact, 0); + break; + + case 1: + g_SmileyPackCStore.AddSmiley(cont->hContact, cont->path); + NotifyEventHooks(hEvent1, (WPARAM)cont->hContact, 0); + break; + } + return 0; +} + +int AccountListChanged(WPARAM wParam, LPARAM lParam) +{ + PROTOACCOUNT* acc = (PROTOACCOUNT*)lParam; + + switch (wParam) + { + case PRAC_ADDED: + if (acc != NULL) + { + bkstring catname(_T("Standard")); + const bkstring& defaultFile = g_SmileyCategories.GetSmileyCategory(catname)->GetFilename(); + g_SmileyCategories.AddAccountAsCategory(acc, defaultFile); + } + break; + + case PRAC_CHANGED: + if (acc != NULL && acc->szModuleName != NULL) + { + bkstring name(A2T_SM(acc->szModuleName)); + SmileyCategoryType* smc = g_SmileyCategories.GetSmileyCategory(name); + if (smc != NULL) + { + if (acc->tszAccountName) name = acc->tszAccountName; + smc->SetDisplayName(name); + } + } + break; + + case PRAC_REMOVED: + g_SmileyCategories.DeleteAccountAsCategory(acc); + break; + + case PRAC_CHECKED: + if (acc != NULL) + { + if (acc->bIsEnabled) + { + bkstring catname(_T("Standard")); + const bkstring& defaultFile = g_SmileyCategories.GetSmileyCategory(catname)->GetFilename(); + g_SmileyCategories.AddAccountAsCategory(acc, defaultFile); + } + else + { + g_SmileyCategories.DeleteAccountAsCategory(acc); + } + } + break; + } + return 0; +} + +int DbSettingChanged(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + DBCONTACTWRITESETTING* cws = (DBCONTACTWRITESETTING*)lParam; + + if (hContact == NULL) return 0; + if (cws->value.type == DBVT_DELETED) return 0; + + if (strcmp(cws->szSetting, "Transport") == 0) + { + bkstring catname(_T("Standard")); + SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(catname); + if (smc != NULL) + g_SmileyCategories.AddContactTransportAsCategory(hContact, smc->GetFilename()); + } + return 0; +} diff --git a/plugins/SmileyAdd/src/services.h b/plugins/SmileyAdd/src/services.h new file mode 100644 index 0000000000..9311e3637d --- /dev/null +++ b/plugins/SmileyAdd/src/services.h @@ -0,0 +1,48 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef SMILEYADD_SERVICES_H_ +#define SMILEYADD_SERVICES_H_ + +#include "m_smileyadd.h" +#include "m_smileyadd_deprecated.h" + +// service commands +INT_PTR ReplaceSmileysCommand(WPARAM wParam, LPARAM lParam); +INT_PTR GetSmileyIconCommand(WPARAM wParam, LPARAM lParam); +INT_PTR ShowSmileySelectionCommand(WPARAM wParam, LPARAM lParam); +INT_PTR GetInfoCommand(WPARAM wParam, LPARAM); +INT_PTR GetInfoCommand2(WPARAM wParam, LPARAM); +INT_PTR ParseText(WPARAM wParam, LPARAM lParam); +INT_PTR ParseTextW(WPARAM wParam, LPARAM lParam); +INT_PTR RegisterPack(WPARAM wParam, LPARAM lParam); +INT_PTR ParseTextBatch(WPARAM wParam, LPARAM lParam); +INT_PTR FreeTextBatch(WPARAM wParam, LPARAM lParam); +INT_PTR CustomCatMenu(WPARAM, LPARAM lParam); +int RebuildContactMenu(WPARAM wParam, LPARAM); +INT_PTR ReloadPack(WPARAM, LPARAM lParam); +INT_PTR LoadContactSmileys(WPARAM, LPARAM lParam); +int AccountListChanged(WPARAM wParam, LPARAM lParam); +int DbSettingChanged(WPARAM wParam, LPARAM lParam); + +SmileyPackType* GetSmileyPack(const char* proto, HANDLE hContact = NULL, SmileyPackCType** smlc = NULL); + + +#endif // SMILEYADD_SERVICES_H_ + diff --git a/plugins/SmileyAdd/src/smileyroutines.cpp b/plugins/SmileyAdd/src/smileyroutines.cpp new file mode 100644 index 0000000000..ba136d7ed5 --- /dev/null +++ b/plugins/SmileyAdd/src/smileyroutines.cpp @@ -0,0 +1,610 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "general.h" +#include "smileyroutines.h" +#include "SmileyBase.h" +#include "options.h" + +#include +#include + +ISmileyBase* CreateSmileyObject(SmileyType* sml); +ISmileyBase* CreateAniSmileyObject(SmileyType* sml, COLORREF clr, bool ishpp); + + + +bool g_HiddenTextSupported = true; + + +// {8CC497C0-A1DF-11CE-8098-00AA0047BE5D} +const GUID IID_ITextDocument = +{ 0x8CC497C0, 0xA1DF, 0x11CE, { 0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D } }; + +void LookupAllSmileys(SmileyPackType* smileyPack, SmileyPackCType* smileyCPack, const TCHAR* lpstrText, + SmileysQueueType& smllist, const bool firstOnly) +{ + if (lpstrText == NULL || *lpstrText == 0) return; + + SmileyPackType::SmileyLookupType* sml = smileyPack ? smileyPack->GetSmileyLookup() : NULL; + SmileyPackCType::SmileyLookupType* smlc = smileyCPack ? &smileyCPack->GetSmileyLookup() : NULL; + + // Precompute number of smileys + int smlszo = sml ? sml->getCount() : 0; + int smlszc = smlc ? smlc->getCount() : 0; + int smlsz = smlszo + smlszc; + + if (smlsz == 0) return; + + // All possible smileys + SmileyLookup::SmileyLocVecType* smileys = new SmileyLookup::SmileyLocVecType [smlsz]; + + // Find all possible smileys + bkstring tmpstr(lpstrText); + int i = 0; + + if (sml) + { + for (int j=0; jgetCount(); j++) + { + (*sml)[j].find(tmpstr, smileys[i], false); + i++; + } + } + + if (smlc) + { + for (int j=0; jgetCount(); 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= smloff) + { + if (firstSmlRef == -1 || smlv[tsml].pos < (*smlf)[firstSmlRef].pos || + (smlv[tsml].pos == (*smlf)[firstSmlRef].pos && smlv[tsml].len > (*smlf)[firstSmlRef].len)) + { + firstSmlRef = tsml; + firstSml = csml; + smlf = &smileys[csml]; + } + break; + } + } + csmlit[csml] = tsml; + } + + // Check if smiley found + if (firstSml != -1) + { + ReplaceSmileyType *dat = new ReplaceSmileyType; + + const TCHAR* textToSearch = lpstrText + smloff; + const TCHAR* textSmlStart = lpstrText + (*smlf)[firstSmlRef].pos; + const TCHAR* textSmlEnd = textSmlStart + (*smlf)[firstSmlRef].len; + + // check if leading space exist + const TCHAR* prech = _tcsdec(textToSearch, textSmlStart); + dat->ldspace = prech != NULL ? _istspace(*prech) != 0 : smloff == 0; + + // check if trailing space exist + dat->trspace = *textSmlEnd == 0 || _istspace(*textSmlEnd); + + // compute text location in RichEdit + dat->loc.cpMin = (long)_tcsnccnt(textToSearch, (*smlf)[firstSmlRef].pos - smloff) + numCharsSoFar; + dat->loc.cpMax = numCharsSoFar = (long)_tcsnccnt(textSmlStart, (*smlf)[firstSmlRef].len) + dat->loc.cpMin; + + if (!opt.EnforceSpaces || (dat->ldspace && dat->trspace)) + { + dat->ldspace |= !opt.SurroundSmileyWithSpaces; + dat->trspace |= !opt.SurroundSmileyWithSpaces; + + if (firstSml < smlszo) + { + dat->sml = smileyPack->GetSmiley((*sml)[firstSml].GetIndex()); + dat->smlc = NULL; + } + else + { + dat->smlc = smileyCPack->GetSmiley((*smlc)[firstSml-smlszo].GetIndex()); + dat->sml = NULL; + } + + if (dat->sml != NULL || dat->smlc != NULL) + { + // First smiley found record it + smllist.insert(dat); + if (firstOnly) break; + } + else + delete dat; + } + else + delete dat; + + // Advance string pointer to search for the next smiley + smloff = (*smlf)[firstSmlRef].pos + (*smlf)[firstSmlRef].len; + csmlit[firstSml]++; + } + else + // Nothing to parse exit + break; + } + delete[] smileys; +} + + +void FindSmileyInText(SmileyPackType* smp, const TCHAR* str, + unsigned& first, unsigned& size, SmileyType** sml) +{ + SmileysQueueType smllist; + LookupAllSmileys(smp, NULL, str, smllist, true); + if (smllist.getCount() == 0) + { + size = 0; + *sml = NULL; + } + else + { + first = smllist[0].loc.cpMin; + size = smllist[0].loc.cpMax - smllist[0].loc.cpMin; + *sml = smllist[0].sml; + } +} + + +SmileyType* FindButtonSmiley(SmileyPackType* smp) +{ + unsigned start, size; + SmileyType* sml; + + FindSmileyInText(smp, smp->GetButtonSmiley(), start, size, &sml); + + return sml; +} + +void UpdateSelection(CHARRANGE& sel, int pos, int dif) +{ + if (sel.cpMax == sel.cpMin) + { + if (sel.cpMax < LONG_MAX && sel.cpMax > pos) + { + sel.cpMax += dif; + sel.cpMin += dif; + } + } + else + { + if (sel.cpMax >= pos && sel.cpMax < LONG_MAX) sel.cpMax += dif; + if (sel.cpMin > pos) sel.cpMin += dif; + } +} + +void ReplaceSmileys(HWND hwnd, SmileyPackType* smp, SmileyPackCType* smcp, const CHARRANGE& sel, + bool useHidden, bool ignoreLast, bool unFreeze) +{ +/* + LARGE_INTEGER freq, strt, end; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&strt); +*/ + IRichEditOle* RichEditOle; + if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) + return; + + ITextDocument* TextDocument; + if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) + { + RichEditOle->Release(); + return; + } + + long cnt; + if (smp == NULL && smcp == NULL) + { + if (unFreeze) TextDocument->Unfreeze(&cnt); + TextDocument->Release(); + RichEditOle->Release(); + return; + } + + // retrieve text range + ITextRange* TextRange; + if (TextDocument->Range(sel.cpMin, sel.cpMax, &TextRange) != S_OK) + { + TextDocument->Release(); + RichEditOle->Release(); + return; + } + + // retrieve text to parse for smileys + BSTR btxt = 0; + if (TextRange->GetText(&btxt) != S_OK) + { + TextRange->Release(); + TextDocument->Release(); + RichEditOle->Release(); + return; + } + + TextRange->Release(); + + SmileysQueueType smllist; + + + LookupAllSmileys(smp, smcp, W2T_SM(btxt), smllist, false); + + SysFreeString(btxt); + + if (smllist.getCount() != 0) + { + // disable screen updates + TextDocument->Freeze(&cnt); + + TCHAR classname[20]; + GetClassName(hwnd, classname, SIZEOF(classname)); + bool ishpp = (_tcsncmp(classname, _T("THppRichEdit"), 12) == 0); + + SetRichCallback(hwnd, NULL, false, true); + + bool rdo = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) != 0; + if (rdo) SendMessage(hwnd, EM_SETREADONLY, FALSE, 0); + + ITextSelection* TextSelection; + TextDocument->GetSelection(&TextSelection); + + ITextFont *TextFont; + TextSelection->GetFont(&TextFont); + + //save selection + CHARRANGE oldSel; + TextSelection->GetStart(&oldSel.cpMin); + TextSelection->GetEnd(&oldSel.cpMax); + + CHARFORMAT2 chf; + + chf.cbSize = sizeof(chf); + chf.dwMask = CFM_ALL2; + + // Determine background color + // This logic trying to minimize number of background color changes + static COLORREF bkgColor = GetSysColor(COLOR_WINDOW); +// if (!insemf) +// { + COLORREF bkgColorPv = (COLORREF)SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, bkgColor); + if (bkgColorPv != bkgColor) + { + bkgColor = bkgColorPv; + SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, bkgColor); + } +// } + + HDC hdc = GetDC(hwnd); + int sclX = GetDeviceCaps(hdc, LOGPIXELSX); + int sclY = GetDeviceCaps(hdc, LOGPIXELSY); + + unsigned numBTBSm = 0; + + BSTR spaceb = SysAllocString(L" "); + + // Replace smileys specified in the list in RichEdit + for (int j = smllist.getCount(); j--; ) + { + CHARRANGE& smlpos = smllist[j].loc; + if (ignoreLast && oldSel.cpMax == smlpos.cpMax) continue; + + smlpos.cpMin += sel.cpMin; + smlpos.cpMax += sel.cpMin; + + // Find all back to back smileys and for propper hidden text detection + if ( numBTBSm == 0 ) + { + CHARRANGE lastPos = smlpos; + for (int jn = j; jn--; ) + { + if (jn != j && smllist[jn].loc.cpMax != lastPos.cpMin) + break; + + ++numBTBSm; + lastPos.cpMin = smllist[jn].loc.cpMin; + } + TextSelection->SetRange(lastPos.cpMin, lastPos.cpMax); + long hid; + TextFont->GetHidden(&hid); + if (hid == tomFalse) numBTBSm = 0; + } + if ( numBTBSm != 0 ) + { + --numBTBSm; + continue; + } + + SmileyType* sml = smllist[j].sml; + SmileyCType* smlc = smllist[j].smlc; + if (sml == NULL && smlc == NULL) continue; + + // Select text analyze + TextSelection->SetRange(smlpos.cpMin, smlpos.cpMax); + + BSTR btxt = NULL; + + if (smlc == NULL && sml->IsText()) + { + btxt = SysAllocString(T2W_SM(sml->GetToolText().c_str())); + TextSelection->SetText(btxt); + } + else + { + TextSelection->GetText(&btxt); + + // Get font properties + SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf); + + //do not look for smileys in hyperlinks + if ((chf.dwEffects & (CFE_LINK | CFE_HIDDEN)) != 0) continue; + + SIZE osize; + if (sml) + sml->GetSize(osize); + else + smlc->GetSize(osize); + + if (osize.cx == 0 || osize.cy == 0) continue; + + int sizeX, sizeY; + if (opt.ScaleToTextheight) + { + sizeY = CalculateTextHeight(hdc, &chf); + sizeX = osize.cx * sizeY / osize.cy; + + int dx = osize.cx - sizeX; + sizeX += dx & 1; + + int dy = osize.cy - sizeY; + sizeY += dy & 1; + } + else + { + sizeX = osize.cx; + sizeY = osize.cy; + } + + if (smlc != NULL && opt.MaxCustomSmileySize && (unsigned)sizeY > opt.MaxCustomSmileySize) + { + sizeY = opt.MaxCustomSmileySize; + sizeX = osize.cx * sizeY / osize.cy; + + int dx = osize.cx - sizeX; + sizeX += dx & 1; + + int dy = osize.cy - opt.MaxCustomSmileySize; + sizeY += dy & 1; + } + + if (opt.MinSmileySize && (unsigned)sizeY < opt.MinSmileySize) + { + sizeY = opt.MinSmileySize; + sizeX = osize.cx * sizeY / osize.cy; + + int dx = osize.cx - sizeX; + sizeX += dx & 1; + + int dy = osize.cy - opt.MinSmileySize; + sizeY += dy & 1; + } + + // Convert pixel to HIMETRIC + SIZEL sizehm; + sizehm.cx = (2540 * (sizeX+1) + (sclX >> 1)) / sclX; + sizehm.cy = (2540 * (sizeY+1) + (sclY >> 1)) / sclY; + + // If font does not have designated background use control background + if (chf.dwEffects & CFE_AUTOBACKCOLOR) chf.crBackColor = bkgColor; + + // insert space after + if (!smllist[j].trspace && useHidden) + { + TextSelection->SetStart(smlpos.cpMax); + TextSelection->TypeText(spaceb); + UpdateSelection(oldSel, smlpos.cpMax , 1); + + // Restore selection + TextSelection->SetRange(smlpos.cpMin, smlpos.cpMax); + } + + if (g_HiddenTextSupported && useHidden) + { + TextFont->SetHidden(tomTrue); + TextSelection->SetEnd(smlpos.cpMin); + UpdateSelection(oldSel, smlpos.cpMin , 1); + } + else + { + UpdateSelection(oldSel, smlpos.cpMin, -(int)SysStringLen(btxt)+1); + } + + ISmileyBase* smileyBase = CreateAniSmileyObject(smlc ? smlc : sml, chf.crBackColor, ishpp); + if (smileyBase == NULL) continue; + + smileyBase->SetExtent(DVASPECT_CONTENT, &sizehm); + smileyBase->SetHint(W2T_SM(btxt)); + + smileyBase->SetPosition(hwnd, NULL); + + // Get the RichEdit container site + IOleClientSite *pOleClientSite; + RichEditOle->GetClientSite(&pOleClientSite); + + // Now Add the object to the RichEdit + REOBJECT reobject = { 0 }; + + reobject.cbStruct = sizeof(REOBJECT); + reobject.cp = REO_CP_SELECTION; + reobject.dvaspect = DVASPECT_CONTENT; + reobject.poleobj = smileyBase; + reobject.polesite = pOleClientSite; + reobject.dwFlags = REO_BELOWBASELINE | REO_BLANK; + reobject.dwUser = (DWORD)smileyBase; + + // Insert the bitmap at the current location in the richedit control + RichEditOle->InsertObject(&reobject); + + smileyBase->Release(); + + // insert space before + if (!smllist[j].ldspace && useHidden) + { + TextSelection->SetRange(smlpos.cpMin, smlpos.cpMin); + TextSelection->TypeText(spaceb); + UpdateSelection(oldSel, smlpos.cpMin , 1); + } + } + SysFreeString(btxt); + } + SysFreeString(spaceb); + + TextSelection->SetRange(oldSel.cpMin, oldSel.cpMax); + if (rdo) SendMessage(hwnd, EM_SETREADONLY, TRUE, 0); + + TextFont->Release(); + TextSelection->Release(); + + ReleaseDC(hwnd, hdc); + + TextDocument->Unfreeze(&cnt); + if (cnt == 0) UpdateWindow(hwnd); + } + + if (unFreeze) + { + TextDocument->Unfreeze(&cnt); + if (cnt == 0) UpdateWindow(hwnd); + } + + TextDocument->Release(); + RichEditOle->Release(); + +/* + QueryPerformanceCounter(&end); + unsigned dif = (end.QuadPart - strt.QuadPart)/(freq.QuadPart/1000); + TCHAR mess[300]; + wsprintf(mess, _T("Time elapsed: %u"), dif); + MessageBox(NULL, mess, _T(""), MB_OK); +*/ +} + + +void ReplaceSmileysWithText(HWND hwnd, CHARRANGE& sel, bool keepFrozen) +{ + IRichEditOle* RichEditOle; + if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) + return; + + ITextDocument* TextDocument; + if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) + { + RichEditOle->Release(); + return; + } + + // retrieve text range + ITextRange* TextRange; + if (TextDocument->Range(0, 0, &TextRange) != S_OK) + { + TextDocument->Release(); + RichEditOle->Release(); + return; + } + + long cnt; + TextDocument->Freeze(&cnt); + + bool rdo = (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) != 0; + if (rdo) SendMessage(hwnd, EM_SETREADONLY, FALSE, 0); + + CHARRANGE oldSel; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&oldSel); + + int objectCount = RichEditOle->GetObjectCount(); + for (int i = objectCount - 1; i >= 0; i--) + { + REOBJECT reObj = {0}; + reObj.cbStruct = sizeof(REOBJECT); + + HRESULT hr = RichEditOle->GetObject(i, &reObj, REO_GETOBJ_POLEOBJ); + if (FAILED(hr)) continue; + + if (reObj.cp < sel.cpMin) + { + reObj.poleobj->Release(); + break; + } + + ISmileyBase *igsc = NULL; + if (reObj.cp < sel.cpMax && reObj.clsid == CLSID_NULL) + reObj.poleobj->QueryInterface(IID_ISmileyAddSmiley, (void**) &igsc); + + reObj.poleobj->Release(); + if (igsc == NULL) continue; + + TextRange->SetRange(reObj.cp, reObj.cp + 1); + + BSTR bstr = NULL; + igsc->GetTooltip(&bstr); + TextRange->SetText(bstr); + + unsigned int len = SysStringLen(bstr); + UpdateSelection(oldSel, reObj.cp, len-1); + UpdateSelection(sel, reObj.cp, len-1); + + SysFreeString(bstr); + + igsc->Release(); + } + + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&oldSel); + if (rdo) SendMessage(hwnd, EM_SETREADONLY, TRUE, 0); + if (!keepFrozen) TextDocument->Unfreeze(&cnt); + + TextRange->Release(); + TextDocument->Release(); + RichEditOle->Release(); +} diff --git a/plugins/SmileyAdd/src/smileyroutines.h b/plugins/SmileyAdd/src/smileyroutines.h new file mode 100644 index 0000000000..6b12ee6ead --- /dev/null +++ b/plugins/SmileyAdd/src/smileyroutines.h @@ -0,0 +1,49 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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 SmileysQueueType; + + + +void LookupAllSmileys(SmileyPackType* smileyPack, SmileyPackCType* smileyCPack, const TCHAR* lpstrText, + SmileysQueueType& smllist, const bool firstOnly); +void ReplaceSmileys(HWND hwnd, SmileyPackType* smp, SmileyPackCType* smcp, const CHARRANGE& sel, + bool useHidden, bool ignoreLast, bool unFreeze); +void ReplaceSmileysWithText(HWND hwnd, CHARRANGE& sel, bool keepFrozen); +void FindSmileyInText(SmileyPackType* smp, const TCHAR* str, + unsigned& first, unsigned& size, SmileyType** index); +SmileyType* FindButtonSmiley(SmileyPackType* smp); + +#endif diff --git a/plugins/SmileyAdd/src/smileys.cpp b/plugins/SmileyAdd/src/smileys.cpp new file mode 100644 index 0000000000..2f16cee51e --- /dev/null +++ b/plugins/SmileyAdd/src/smileys.cpp @@ -0,0 +1,1146 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + + +#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("<")), str, bkstring(_T("<"))); + str = _TPattern::replace(bkstring(_T(">")), 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("\\s*\"(.*?)\"\\s*"), + _TPattern::MULTILINE_MATCHING); + _TPattern * author_re = _TPattern::compile(_T("\\s*\"(.*?)\"\\s*"), + _TPattern::MULTILINE_MATCHING); + _TPattern * settings_re = _TPattern::compile(_T("(.*?)"), + _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("(?:\\s*\"(.*?)\")?(.*?)"), + _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL); + _TPattern * expression_re = _TPattern::compile(_T("\\s*\"(.*?)\"\\s*"), + _TPattern::MULTILINE_MATCHING); + _TPattern * pastetext_re = _TPattern::compile(_T("\\s*\"(.*?)\"\\s*"), + _TPattern::MULTILINE_MATCHING); + _TPattern * images_re = _TPattern::compile(_T("(.*?)"), + _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL); + _TPattern * image_re = _TPattern::compile(_T("(.*?)"), + _TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL); + _TPattern * imagedt_re = _TPattern::compile(_T(""), + _TPattern::MULTILINE_MATCHING ); + + m0 = images_re->createTMatcher(tbuf); + if (m0->findFirstMatch()) + { + bkstring images = m0->getGroup(1); + + m1 = imagedt_re->createTMatcher(images); + if (m1->findFirstMatch()) + { + IStream* pStream = DecodeBase64Data(T2A_SM(m1->getGroup(1).c_str())); + if (pStream != NULL) + { + if (m_hSmList != NULL) ImageList_Destroy(m_hSmList); + m_hSmList = ImageList_Read(pStream); + pStream->Release(); + } + } + delete m1; + } + delete m0; + + m0 = record_re->createTMatcher(tbuf); + while (m0->findNextMatch()) + { + SmileyType *dat = new SmileyType; + + dat->SetRegEx(true); + dat->SetImList(m_hSmList, _ttol(m0->getGroup(1).c_str())); + dat->m_ToolText = m0->getGroup(2); + DecodeHTML(dat->m_ToolText); + + bkstring rec = m0->getGroup(3); + + m1 = expression_re->createTMatcher(rec); + if (m1->findFirstMatch()) + { + dat->m_TriggerText = m1->getGroup(1); + DecodeHTML(dat->m_TriggerText); + } + delete m1; + + m1 = pastetext_re->createTMatcher(rec); + if (m1->findFirstMatch()) + { + dat->m_InsertText = m1->getGroup(1); + DecodeHTML(dat->m_InsertText); + } + delete m1; + dat->SetHidden(dat->m_InsertText.empty()); + + m1 = image_re->createTMatcher(rec); + if (m1->findFirstMatch()) + { + bkstring images = m1->getGroup(1); + + m2 = imagedt_re->createTMatcher(images); + if (m2->findFirstMatch()) + { + IStream* pStream = DecodeBase64Data(T2A_SM(m2->getGroup(1).c_str())); + if (pStream != NULL) + { + dat->LoadFromImage(pStream); + pStream->Release(); + } + } + delete m2; + } + delete m1; + + m_SmileyList.insert(dat); + } + delete m0; + + delete record_re; + delete expression_re; + delete pastetext_re; + delete images_re; + delete image_re; + delete imagedt_re; + } + + m_VisibleCount = m_SmileyList.getCount(); + + AddTriggersToSmileyLookup(); + + selec.x = 0; + selec.y = 0; + win.x = 0; + win.y = 0; + + return true; +} + + +// +// SmileyPackListType +// + + +bool SmileyPackListType::AddSmileyPack(bkstring& filename) +{ + bool res = true; + if (GetSmileyPack(filename) == NULL) + { //not exist yet, so add + SmileyPackType *smileyPack = new SmileyPackType; + + res = smileyPack->LoadSmileyFile(filename, FALSE); + if (res) + m_SmileyPacks.insert(smileyPack); + else + delete smileyPack; + } + return res; +} + + +SmileyPackType* SmileyPackListType::GetSmileyPack(bkstring& filename) +{ + bkstring modpath; + pathToAbsolute(filename, modpath); + + for (int i = 0; i < m_SmileyPacks.getCount(); i++) + { + bkstring modpath1; + pathToAbsolute(m_SmileyPacks[i].GetFilename(), modpath1); + if (lstrcmpi(modpath.c_str(), modpath1.c_str()) == 0) return &m_SmileyPacks[i]; + } + return NULL; +} + +void SmileyPackListType::ClearAndFreeAll() +{ + m_SmileyPacks.destroy(); +} + + +// +// SmileyCategoryType +// + + +SmileyCategoryType::SmileyCategoryType(SmileyPackListType* pSPS, const bkstring& name, + const bkstring& displayName, + const bkstring& defaultFilename, SmcType typ) +{ + m_pSmileyPackStore = pSPS; + type = typ; + m_Name = name; + m_DisplayName = displayName; + + opt.ReadPackFileName(m_Filename, m_Name, defaultFilename); +} + + +void SmileyCategoryType::Load(void) +{ + if (!opt.UseOneForAll || type != smcProto) + m_pSmileyPackStore->AddSmileyPack(m_Filename); +} + + +SmileyPackType* SmileyCategoryType::GetSmileyPack(void) +{ + return m_pSmileyPackStore->GetSmileyPack(m_Filename); +} + + +void SmileyCategoryType::SaveSettings(void) +{ + opt.WritePackFileName(m_Filename, m_Name); +} + +// +// SmileyCategoryListType +// + +void SmileyCategoryListType::ClearAndLoadAll(void) +{ + m_pSmileyPackStore->ClearAndFreeAll(); + + for (int i = 0; i < m_SmileyCategories.getCount(); i++) + m_SmileyCategories[i].Load(); +} + + +SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const bkstring& name) +{ + for (int i = 0; i < m_SmileyCategories.getCount(); i++) + { + if (name.comparei(m_SmileyCategories[i].GetName()) == 0) return &m_SmileyCategories[i]; + } + return NULL; +} + + +SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index) +{ + return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : NULL; +} + + +SmileyPackType* SmileyCategoryListType::GetSmileyPack(bkstring& categoryname) +{ + SmileyCategoryType* smc = GetSmileyCategory(categoryname); + return smc != NULL ? smc->GetSmileyPack() : NULL; +} + + +void SmileyCategoryListType::SaveSettings(void) +{ + bkstring catstr; + for (int i = 0; i < m_SmileyCategories.getCount(); i++) + { + m_SmileyCategories[i].SaveSettings(); + if (m_SmileyCategories[i].IsCustom()) + { + if (!catstr.empty()) catstr += '#'; + catstr += m_SmileyCategories[i].GetName(); + } + } + opt.WriteCustomCategories(catstr); +} + + +void SmileyCategoryListType::AddAndLoad(const bkstring& name, const bkstring& displayName) +{ + if (GetSmileyCategory(name) != NULL) return; + + AddCategory(name, displayName, smcExt); + // Load only if other smileys have been loaded already + if (m_SmileyCategories.getCount() > 1) + m_SmileyCategories[m_SmileyCategories.getCount()-1].Load(); +} + + +void SmileyCategoryListType::AddCategory(const bkstring& name, const bkstring& displayName, + SmcType typ, const bkstring& defaultFilename) +{ + if (GetSmileyCategory(name) == NULL) + m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, + displayName, defaultFilename, typ)); +} + + +bool SmileyCategoryListType::DeleteCustomCategory(int index) +{ + if (index < m_SmileyCategories.getCount()) + { + if (m_SmileyCategories[index].IsCustom()) + { + m_SmileyCategories.remove(index); + return true; + } + + } + return false; +} + +void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const bkstring& defaultFile) +{ + if (IsAccountEnabled(acc) && acc->szProtoName && IsSmileyProto(acc->szModuleName)) + { + bkstring displayName(acc->tszAccountName ? acc->tszAccountName : A2T_SM(acc->szModuleName)); + + const char* packnam = acc->szProtoName; + if (strcmp(packnam, "JABBER") == 0) + packnam = "JGMail"; + else if (strstr(packnam, "SIP") != NULL) + packnam = "MSN"; + + char path[MAX_PATH]; + mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packnam); + + bkstring paths = A2T_SM(path), patha; + pathToAbsolute(paths, patha); + + if (_taccess(patha.c_str(), 0) != 0) + paths = defaultFile; + + bkstring tname(A2T_SM(acc->szModuleName)); + AddCategory(tname, displayName, smcProto, paths); + } +} + +void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc) +{ + bkstring tname(A2T_SM(acc->szModuleName)); + + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact != NULL) + { + char* proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (proto) + { + DBVARIANT dbv; + if (DBGetContactSettingTString(hContact, proto, "Transport", &dbv) == 0) + { + bool found = (tname.comparei(dbv.ptszVal) == 0); + DBFreeVariant(&dbv); + if (found) return; + } + } + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + + for (int i = 0; i < m_SmileyCategories.getCount(); i++) + { + if (tname.comparei(m_SmileyCategories[i].GetName()) == 0) + { + m_SmileyCategories.remove(i); + break; + } + } +} + +void SmileyCategoryListType::AddContactTransportAsCategory(HANDLE hContact, const bkstring& defaultFile) +{ + char* proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (proto == NULL) return; + + DBVARIANT dbv; + if (DBGetContactSettingTString(hContact, proto, "Transport", &dbv) == 0) + { + if (dbv.ptszVal[0] == '\0') + { + DBFreeVariant(&dbv); + return; + } + char* trsp = mir_strdup(T2A_SM(dbv.ptszVal)); + _strlwr(trsp); + + const char *packname = NULL; + if (strstr(trsp, "msn") != NULL) + packname = "msn"; + else if (strstr(trsp, "icq") != NULL) + packname = "icq"; + else if (strstr(trsp, "yahoo") != NULL) + packname = "yahoo"; + else if (strstr(trsp, "aim") != NULL) + packname = "aim"; + else if (strstr(trsp, "lcs") != NULL) + packname = "msn"; + + mir_free(trsp); + + bkstring displayName = dbv.ptszVal; + if (packname != NULL) + { + char path[MAX_PATH]; + mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packname); + + bkstring paths = A2T_SM(path), patha; + pathToAbsolute(paths, patha); + + if (_taccess(patha.c_str(), 0) != 0) + paths = defaultFile; + + AddCategory(displayName, displayName, smcProto, paths); + } + else + AddCategory(displayName, displayName, smcProto, defaultFile); + + DBFreeVariant(&dbv); + } +} + + +void SmileyCategoryListType::AddAllProtocolsAsCategory(void) +{ + bkstring displayName = TranslateT("Standard"); + bkstring tname = _T("Standard"); + AddCategory(tname, displayName, smcStd); + + const bkstring& defaultFile = GetSmileyCategory(tname)->GetFilename(); + + + unsigned lpcp = (unsigned)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + if (lpcp == CALLSERVICE_NOTFOUND) lpcp = CP_ACP; + + + PROTOCOLDESCRIPTOR **protoList; + PROTOACCOUNT **accList; + int protoCount; + + if (ProtoEnumAccounts(&protoCount, &accList) == CALLSERVICE_NOTFOUND || (protoCount > 0 && accList[0]->cbSize == 0)) + { + CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&protoCount, (LPARAM)&protoList); + for (int i = 0; i < protoCount; i++) + { + if (protoList[i]->type != PROTOTYPE_PROTOCOL) continue; + + if (IsSmileyProto(protoList[i]->szName)) + { + const char* packnam = protoList[i]->szName; + if (strcmp(packnam, "JABBER") == 0) + packnam = "JGMail"; + else if (strstr(packnam, "SIP") != NULL) + packnam = "MSN"; + + char path[MAX_PATH]; + mir_snprintf(path, sizeof(path), "Smileys\\nova\\%s.msl", packnam); + + bkstring paths = A2T_SM(path), patha; + pathToAbsolute(paths, patha); + + if (_taccess(patha.c_str(), 0) != 0) + paths = defaultFile; + + char protoName[128]; + CallProtoService(protoList[i]->szName, PS_GETNAME, sizeof(protoName), (LPARAM)protoName); + + + displayName = A2W_SM(protoName, lpcp); + + tname = A2T_SM(protoList[i]->szName); + AddCategory(tname, displayName, smcProto, paths); + } + } + } + else + { + for (int i = 0; i < protoCount; i++) + AddAccountAsCategory(accList[i], defaultFile); + } + + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact != NULL) + { + AddContactTransportAsCategory(hContact, defaultFile); + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + + bkstring cats; + opt.ReadCustomCategories(cats); + + bkstring::size_type cppv = 0; + for (;;) + { + bkstring::size_type cp = cats.find('#', cppv); + if (cp != cats.npos) + { + displayName = cats.substr(cppv, cp - cppv); + AddCategory(displayName, displayName, smcCustom, defaultFile); + cppv = cp + 1; + } + else break; + } + if (cppv != cats.size()) + { + displayName = cats.substr(cppv, cats.size() - cppv); + AddCategory(displayName, displayName, smcCustom, defaultFile); + } +} + + +SmileyLookup::SmileyLookup(const bkstring& str, const bool regexs, const int ind, const bkstring& smpt) +{ + TCHAR msgtxt[1024]; + + m_ind = ind; + if (regexs) + { + static const bkstring testString(_T("Test String")); + m_pattern = _TPattern::compile(str); + m_valid = m_pattern != NULL; + if (m_valid) + { + _TMatcher* matcher = m_pattern->createTMatcher(testString); + m_valid &= (!matcher->findFirstMatch() || + matcher->getStartingIndex() != matcher->getEndingIndex()); + if (!m_valid) + { + static const TCHAR errmsg[] = _T("Regular Expression \"%s\" in smiley pack \"%s\" could produce \"empty matches\"."); + mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), str.c_str(), smpt.c_str()); + } + delete matcher; + } + else + { + static const TCHAR errmsg[] = _T("Regular Expression \"%s\" in smiley pack \"%s\" malformed.") ; + mir_sntprintf(msgtxt, SIZEOF(msgtxt), TranslateTS(errmsg), str.c_str(), smpt.c_str()); + } + + if (!m_valid) CallService(MS_NETLIB_LOG, (WPARAM) hNetlibUser, (LPARAM)(char*)T2A_SM(msgtxt)); + } + else + { + m_text = str; + m_pattern = NULL; + m_valid = !str.empty(); + } +} + + +SmileyLookup::~SmileyLookup() +{ + if (m_pattern) delete m_pattern; +} + + +void SmileyLookup::find(const bkstring& str, SmileyLocVecType& smlcur, bool firstOnly) const +{ + if (!m_valid) return; + + if (m_text.empty()) + { + _TMatcher* matcher = m_pattern->createTMatcher(str); + while( matcher->findNextMatch()) + { + bkstring::size_type st = matcher->getStartingIndex(); + bkstring::size_type sz = matcher->getEndingIndex() - st; + if (sz != 0) + { + smlcur.insert(new SmileyLocType(st, sz)); + if (firstOnly && m_ind != -1) return; + } + } + delete matcher; + } + else + { + const TCHAR* pos = str.c_str(); + while( (pos = _tcsstr(pos, m_text.c_str())) != NULL ) + { + smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.size())); + pos += m_text.size(); + if (firstOnly && m_ind != -1) return; + } + } +} diff --git a/plugins/SmileyAdd/src/smileys.h b/plugins/SmileyAdd/src/smileys.h new file mode 100644 index 0000000000..2b127eb225 --- /dev/null +++ b/plugins/SmileyAdd/src/smileys.h @@ -0,0 +1,287 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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 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 SmileyVectorType; + typedef SMOBJLIST 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 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 SmileyCategoryVectorType; + +private: + SmileyCategoryVectorType m_SmileyCategories; + SmileyPackListType* m_pSmileyPackStore; + +public: + void SetSmileyPackStore(SmileyPackListType* pSPS) { m_pSmileyPackStore = pSPS; } + + SmileyCategoryType* GetSmileyCategory(const bkstring& name); + SmileyCategoryType* GetSmileyCategory(unsigned index); + SmileyPackType* GetSmileyPack(bkstring& name); + SmileyCategoryVectorType* GetSmileyCategoryList(void) { return &m_SmileyCategories; }; + + int NumberOfSmileyCategories(void) { return m_SmileyCategories.getCount(); } + + void AddCategory(const bkstring& name, const bkstring& displayName, SmcType typ, + const bkstring& defaultFilename = bkstring(_T("Smileys\\nova\\default.msl"))); + void AddAndLoad(const bkstring& name, const bkstring& displayName); + void AddAllProtocolsAsCategory(void); + void AddAccountAsCategory(PROTOACCOUNT *acc, const bkstring& defaultFile); + void AddContactTransportAsCategory(HANDLE hContact, const bkstring& defaultFile); + + void ClearAndLoadAll(void); + void ClearAll(void) + { m_pSmileyPackStore->ClearAndFreeAll(); m_SmileyCategories.destroy(); } + + bool DeleteCustomCategory(int index); + void DeleteAccountAsCategory(PROTOACCOUNT *acc); + + void SaveSettings(void); +}; + +extern SmileyPackListType g_SmileyPacks; +extern SmileyCategoryListType g_SmileyCategories; + +#endif //SMILEYADD_SMILEYS_H_ diff --git a/plugins/SmileyAdd/src/smltool.cpp b/plugins/SmileyAdd/src/smltool.cpp new file mode 100644 index 0000000000..31695a516a --- /dev/null +++ b/plugins/SmileyAdd/src/smltool.cpp @@ -0,0 +1,785 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#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(toolText.c_str()); + SendMessage(m_hToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); + SendMessage(m_hToolTip, TTM_ACTIVATE, TRUE, 0); + + RECT rect = CalculateButtonToCoordinates(m_CurrentHotTrack, si.nPos); + DrawFocusRect(hdc, &rect); + if (m_AniPack) m_AniPack->SetSel(rect); + } + ReleaseDC(m_hwndDialog, hdc); + } +} + + +void SmileyToolWindowType::ScrollV(int action, int dist) +{ + SCROLLINFO si; + si.cbSize = sizeof (si); + si.fMask = SIF_ALL; + GetScrollInfo (m_hwndDialog, SB_VERT, &si); + + // Save the position for comparison later on + int yPos = si.nPos; + switch (action) + { + // user clicked the HOME keyboard key + case SB_TOP: + si.nPos = si.nMin; + break; + + // user clicked the END keyboard key + case SB_BOTTOM: + si.nPos = si.nMax; + break; + + // user clicked the top arrow + case SB_LINEUP: + si.nPos -= 1; + break; + + // user clicked the bottom arrow + case SB_LINEDOWN: + si.nPos += 1; + break; + + // user clicked the scroll bar shaft above the scroll box + case SB_PAGEUP: + si.nPos -= si.nPage; + break; + + // user clicked the scroll bar shaft below the scroll box + case SB_PAGEDOWN: + si.nPos += si.nPage; + break; + + // user dragged the scroll box + case SB_THUMBTRACK: + si.nPos = si.nTrackPos; + break; + + // user dragged the scroll box + case SB_MYMOVE: + si.nPos += dist; + break; + } + // Set the position and then retrieve it. Due to adjustments + // by Windows it may not be the same as the value set. + si.fMask = SIF_POS; + SetScrollInfo (m_hwndDialog, SB_VERT, &si, TRUE); + GetScrollInfo (m_hwndDialog, SB_VERT, &si); + // If the position has changed, scroll window and update it + if (si.nPos != yPos) + { + if (m_AniPack) m_AniPack->SetOffset(si.nPos*GetRowSize()); + + ScrollWindowEx(m_hwndDialog, 0, (yPos - si.nPos) * GetRowSize(), + NULL, NULL, NULL, NULL, SW_INVALIDATE); + + UpdateWindow (m_hwndDialog); + } +} + + +void SmileyToolWindowType::MouseMove(int xposition, int yposition) +{ + if (m_CurrentHotTrack == -2) return; //prevent focussing when not drawn yet! + // SetFocus(m_hwndDialog); + + + SCROLLINFO si; + si.cbSize = sizeof (si); + si.fMask = SIF_POS; + si.nPos = 0; + GetScrollInfo (m_hwndDialog, SB_VERT, &si); + + POINT pt = { xposition, yposition }; + int but = CalculateCoordinatesToButton(pt, si.nPos); + SmileySel(but); +} + + + +void SmileyToolWindowType::KeyUp(WPARAM wParam, LPARAM lParam) +{ + int colSel = -1, numKey = -1; + int but = m_CurrentHotTrack; + + switch(wParam) + { + case VK_END: + but = m_NumberOfButtons-1; + break; + + case VK_HOME: + but = 0; + break; + + case VK_LEFT: + but -= (opt.IEViewStyle ? 1 : m_NumberOfVerticalButtons) * LOWORD(lParam); + break; + + case VK_UP: + but -= (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * LOWORD(lParam); + break; + + case VK_RIGHT: + but += (opt.IEViewStyle ? 1 : m_NumberOfVerticalButtons) * LOWORD(lParam); + break; + + case VK_DOWN: + but += (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * LOWORD(lParam); + break; + + case VK_SPACE: + case VK_RETURN: + if (but != -1) InsertSmiley(); + return; + + case VK_ESCAPE: + DestroyWindow(m_hwndDialog); + return; + + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + if ((GetKeyState(VK_NUMLOCK) & 1) != 0) + numKey = (int)wParam - VK_NUMPAD1; + else + { + rowSel = -1; + return; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + numKey = (int)wParam - '1'; + break; + + default: + rowSel = -1; + return; + } + + if (numKey != -1) + { + if (rowSel == -1) + { + rowSel = numKey; + but = (opt.IEViewStyle ? m_NumberOfHorizontalButtons : 1) * rowSel; + } + else + { + colSel = numKey; + if (opt.IEViewStyle) + but = colSel + m_NumberOfHorizontalButtons * rowSel; + else + but = rowSel + m_NumberOfVerticalButtons * colSel; + } + } + + if (but < 0) but = 0; + if (but >= (int)m_NumberOfButtons) but = m_NumberOfButtons-1; + + SmileySel(but); + if (colSel != -1) InsertSmiley(); +} + + +void SmileyToolWindowType::InitDialog(LPARAM lParam) +{ + LPCREATESTRUCT createStruct = (LPCREATESTRUCT)lParam; + SmileyToolWindowParam* stwp = (SmileyToolWindowParam*) createStruct->lpCreateParams; + + m_pSmileyPack = stwp->pSmileyPack; + m_XPosition = stwp->xPosition; + m_YPosition = stwp->yPosition; + m_hWndTarget = stwp->hWndTarget; + m_TargetMessage = stwp->targetMessage; + m_TargetWParam = stwp->targetWParam; + m_Direction = stwp->direction; + m_hContact = stwp->hContact; + + m_CurrentHotTrack = -2; + m_Choosing = false; + + CreateSmileyWinDim(); + + int width = m_BitmapWidth.cx; + int height = m_BitmapWidth.cy; + + const int colsz = GetRowSize(); + const int heightn = m_WindowSizeY; + + SCROLLINFO si; + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = height / colsz - 1; + si.nPage = heightn / colsz; + si.nPos = 0; + SetScrollInfo(m_hwndDialog, SB_VERT, &si, TRUE); + + if (GetWindowLongPtr(m_hwndDialog, GWL_STYLE) & WS_VSCROLL) + width += GetSystemMetrics(SM_CXVSCROLL); + + RECT rc = { 0, 0, width, heightn }; + AdjustWindowRectEx(&rc, GetWindowLongPtr(m_hwndDialog, GWL_STYLE), + FALSE, GetWindowLongPtr(m_hwndDialog, GWL_EXSTYLE)); + + width = rc.right - rc.left; + height = rc.bottom - rc.top; + + switch (m_Direction) + { + case 1: + m_XPosition-=width; + break; + case 2: + m_XPosition-=width; + m_YPosition-=height; + break; + case 3: + m_YPosition-=height; + break; + } + + // Get screen dimentions + int xoScreen = GetSystemMetrics(SM_XVIRTUALSCREEN); + int yoScreen = GetSystemMetrics(SM_YVIRTUALSCREEN); + + int xScreen = GetSystemMetrics(SM_CXVIRTUALSCREEN); + int yScreen = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + if (xScreen == 0) xScreen = GetSystemMetrics(SM_CXSCREEN); + if (yScreen == 0) yScreen = GetSystemMetrics(SM_CYSCREEN); + + xScreen += xoScreen; + yScreen += yoScreen; + + // Prevent window from opening off-screen + if (m_YPosition + height > yScreen) m_YPosition = yScreen - height; + if (m_XPosition + width > xScreen) m_XPosition = xScreen - width; + if (m_YPosition < yoScreen) m_YPosition = yoScreen; + if (m_XPosition < xoScreen) m_XPosition = xoScreen; + + // Move window to desired location + SetWindowPos(m_hwndDialog, NULL, m_XPosition, m_YPosition, + width, height, SWP_NOZORDER); + + m_AniPack = new AnimatedPack(m_hwndDialog, height, m_ButtonSize, opt.SelWndBkgClr); + + SmileyPackType::SmileyVectorType &sml = m_pSmileyPack->GetSmileyList(); + for (unsigned i=0; iAdd(&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; iselec; + } + + if (m_pSmileyPack->win.x == 0 || m_pSmileyPack->win.y == 0) + { + if (opt.IEViewStyle) + { + // All integer square root + unsigned i; + for (i=1; i*iwin.x; + m_NumberOfVerticalButtons = m_NumberOfButtons / m_NumberOfHorizontalButtons + + (m_NumberOfButtons % m_NumberOfHorizontalButtons != 0); + } + + m_BitmapWidth.cx = m_NumberOfHorizontalButtons * (m_ButtonSize.cx + m_ButtonSpace) + m_ButtonSpace; + m_BitmapWidth.cy = m_NumberOfVerticalButtons * (m_ButtonSize.cy + m_ButtonSpace) + m_ButtonSpace; + + const int colsz = m_ButtonSize.cy + m_ButtonSpace; + int wndsz = min((int)m_BitmapWidth.cy, GetSystemMetrics(SM_CYSCREEN) / 2); + if (opt.IEViewStyle) wndsz = min(wndsz, 250); + + if (m_pSmileyPack->win.x != 0 && m_pSmileyPack->win.y != 0) + wndsz = min(wndsz, m_pSmileyPack->win.y * (m_ButtonSize.cy + (int)m_ButtonSpace) + (int)m_ButtonSpace); + + m_WindowSizeY = wndsz - (wndsz % colsz) + m_ButtonSpace; +} + + +void SmileyToolWindowType::CreateSmileyBitmap(HDC hdc) +{ + const RECT rc = { 0, 0, m_BitmapWidth.cx, m_WindowSizeY }; + + SetBkColor(hdc, opt.SelWndBkgClr); + const HBRUSH hBkgBrush = CreateSolidBrush(opt.SelWndBkgClr); + FillRect(hdc, &rc, hBkgBrush); + DeleteObject(hBkgBrush); + + if (opt.IEViewStyle) + { + HPEN hpen = CreatePen(PS_DOT, 1, 0); + HGDIOBJ hOldPen = SelectObject(hdc, hpen); + + POINT pts[2] = { {0, 0}, {m_BitmapWidth.cx, 0} }; + + for (unsigned i=0; i<=m_NumberOfVerticalButtons; i++) + { + pts[0].y = pts[1].y = i * (m_ButtonSize.cy + m_ButtonSpace); + if (pts[0].y > m_WindowSizeY) break; + Polyline(hdc, pts, 2); + } + + pts[0].y = 0; pts[1].y = m_BitmapWidth.cy; + for (unsigned j=0; j<=m_NumberOfHorizontalButtons; j++) + { + pts[0].x = pts[1].x = j * (m_ButtonSize.cx + m_ButtonSpace); + Polyline(hdc, pts, 2); + } + + SelectObject(hdc, hOldPen); + DeleteObject(hpen); + } +} + + +RECT SmileyToolWindowType::CalculateButtonToCoordinates(int buttonPosition, int scroll) +{ + int row, rowpos; + + if (opt.IEViewStyle) + { + row = buttonPosition / m_NumberOfHorizontalButtons; + rowpos = buttonPosition % m_NumberOfHorizontalButtons; + } + else + { + row = buttonPosition % m_NumberOfVerticalButtons; + rowpos = buttonPosition / m_NumberOfVerticalButtons; + } + + RECT pt; + pt.left = rowpos * (m_ButtonSize.cx + m_ButtonSpace) + m_ButtonSpace; + pt.top = (row - scroll) * (m_ButtonSize.cy + m_ButtonSpace) + m_ButtonSpace; + pt.right = pt.left + m_ButtonSize.cx; + pt.bottom = pt.top + m_ButtonSize.cy; + + return pt; +} + + +int SmileyToolWindowType::CalculateCoordinatesToButton(POINT pt, int scroll) +{ + const int rowpos = (pt.x - m_ButtonSpace) / (m_ButtonSize.cx + m_ButtonSpace); + const int row = (pt.y - m_ButtonSpace) / (m_ButtonSize.cy + m_ButtonSpace) + scroll; + + int pos; + if (opt.IEViewStyle) + pos = m_NumberOfHorizontalButtons * row + rowpos; + else + pos = m_NumberOfVerticalButtons * rowpos + row; + + if (pos >= (int)m_NumberOfButtons) pos = -1; + + return pos; +} + +void __cdecl SmileyToolThread(void *arg) +{ + SmileyToolWindowParam* stwp = (SmileyToolWindowParam*)arg; + if (stwp->pSmileyPack && stwp->pSmileyPack->VisibleSmileyCount()) + { + WNDCLASSEX wndclass; + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_SAVEBITS; + wndclass.lpfnWndProc = DlgProcSmileyToolWindow; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 4; + wndclass.hInstance = g_hInst; + wndclass.hIcon = NULL; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = CreateSolidBrush(opt.SelWndBkgClr); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = _T("SmileyTool"); + wndclass.hIconSm = NULL; + RegisterClassEx(&wndclass); + + CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOPARENTNOTIFY, _T("SmileyTool"), NULL, + WS_BORDER | WS_POPUP | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + ((SmileyToolWindowParam*)arg)->hWndParent, NULL, g_hInst, arg); + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + UnregisterClass(_T("SmileyTool"), g_hInst); + } + delete stwp; +} diff --git a/plugins/SmileyAdd/src/smltool.h b/plugins/SmileyAdd/src/smltool.h new file mode 100644 index 0000000000..89fb786862 --- /dev/null +++ b/plugins/SmileyAdd/src/smltool.h @@ -0,0 +1,45 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2005 - 2011 Boris Krasnovskiy +Copyright (C) 2003 - 2004 Rein-Peter de Boer + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef SMILEYADD_SMLTOOL_H_ +#define SMILEYADD_SMLTOOL_H_ + +class SmileyPackType; + +struct SmileyToolWindowParam +{ + SmileyPackType* pSmileyPack; + int xPosition; + int yPosition; + int direction; + HWND hWndTarget; + HWND hWndParent; + UINT targetMessage; + WPARAM targetWParam; + HANDLE hContact; +}; + + +void __cdecl SmileyToolThread(void *arg); + +#ifndef min +#define min(A, B) ((A) < (B) ? (A) : (B)) +#endif + +#endif // SMILEYADD_SMLTOOL_H_ diff --git a/plugins/SmileyAdd/src/version.h b/plugins/SmileyAdd/src/version.h new file mode 100644 index 0000000000..104839a3a2 --- /dev/null +++ b/plugins/SmileyAdd/src/version.h @@ -0,0 +1,3 @@ +#define __FILEVERSION_STRING 0,2,3,17 +#define __VERSION_STRING "0.2.3.17" +#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 2, 3, 17) -- cgit v1.2.3