From 528949c71a12f54dc7b71133a32d9ee91cb53298 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 5 Jun 2012 19:31:43 +0000 Subject: BasicHistory - the alternative for history++ git-svn-id: http://svn.miranda-ng.org/main/trunk@317 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/BasicHistory/RichHtmlExport.cpp | 545 ++++++++++++++++++++++++++++++++ 1 file changed, 545 insertions(+) create mode 100644 plugins/BasicHistory/RichHtmlExport.cpp (limited to 'plugins/BasicHistory/RichHtmlExport.cpp') diff --git a/plugins/BasicHistory/RichHtmlExport.cpp b/plugins/BasicHistory/RichHtmlExport.cpp new file mode 100644 index 0000000000..5a772a4914 --- /dev/null +++ b/plugins/BasicHistory/RichHtmlExport.cpp @@ -0,0 +1,545 @@ +/* +Basic History plugin +Copyright (C) 2011-2012 Krzysztof Kral + +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 "StdAfx.h" +#include "RichHtmlExport.h" +#include "Options.h" +#include "resource.h" +#define EXP_FILE (*stream) + +RichHtmlExport::~RichHtmlExport() +{ +} + +extern HINSTANCE hInst; +extern HANDLE *hEventIcons; +extern HANDLE hPlusExIcon, hMinusExIcon; +extern bool g_SmileyAddAvail; + +std::wstring MakeTextHtmled(const std::wstring& message, std::queue >* positionMap = NULL) +{ + std::wstring ret; + std::wstring search = _T("&<>\t\r\n"); + size_t start = 0; + size_t find; + size_t currentAdd = 0; + while((find = message.find_first_of(search, start)) < message.length()) + { + ret += message.substr(start, find - start); + switch(message[find]) + { + case _T('&'): + ret += _T("&"); + break; + case _T('<'): + ret += _T("<"); + break; + case _T('>'): + ret += _T(">"); + break; + case _T('\t'): + ret += _T(" "); + break; + case _T('\n'): + ret += _T("
"); + break; + } + + start = find + 1; + if(positionMap != NULL) + { + size_t len = ret.length() - start - currentAdd; + if(len != 0) + { + positionMap->push(std::pair(start + currentAdd, len)); + currentAdd += len; + } + } + } + + ret += message.substr(start, message.length() - start); + return ret; +} + +std::wstring UrlHighlightHtml(const std::wstring& message, bool& isUrl) +{ + std::wstring ret; + std::wstring htmlStop = _T("\'\" []<>\r\n"); + std::wstring search = _T("://"); + size_t start = 0; + size_t find; + while((find = message.find(search, start)) < message.length()) + { + size_t urlStart = message.find_last_of(htmlStop, find); + size_t urlEnd = message.find_first_of(htmlStop, find + 3); + if(urlStart >= message.length()) + urlStart = -1; + if(urlEnd >= message.length()) + urlEnd = message.length(); + if(((int)urlEnd -3 - (int)find > 0) && ((int)find - (int)urlStart -1 > 0)) + { + ret += message.substr(start, (urlStart + 1) - start); + std::wstring url = message.substr(urlStart + 1, urlEnd - urlStart - 1); + start = urlEnd; + ret += _T("") + url + _T(""); + isUrl = true; + } + else + { + ret += message.substr(start, (find + 3) - start); + start = find + 3; + } + } + + ret += message.substr(start, message.length() - start); + return ret; +} + +std::wstring RemoveExt(const std::wstring &fileName) +{ + size_t find = fileName.find_last_of(L'.'); + if(find < fileName.length()) + { + return fileName.substr(0, find); + } + + return fileName; +} + +std::wstring GetName(const std::wstring &path) +{ + size_t find = path.find_last_of(L"\\/"); + if(find < path.length()) + { + return path.substr(find + 1); + } + + return path; +} + +void ExtractFile(short int res, const std::wstring &fileName) +{ + HRSRC rSrc = FindResource(hInst, MAKEINTRESOURCE(res), MAKEINTRESOURCE(CUSTOMRES)); + if(rSrc != NULL) + { + HGLOBAL res = LoadResource(hInst, rSrc); + int size = SizeofResource(hInst, rSrc); + if(res != NULL) + { + char* resData = (char*)LockResource(res); + if(resData != NULL) + { + std::ofstream stream (fileName.c_str(), std::ios_base::binary); + if(stream.is_open()) + { + stream.write(resData, size); + stream.close(); + } + } + + FreeResource(res); + } + } +} + +#pragma pack(push, 2) +typedef struct +{ + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image (0 if >=8bpp) + BYTE bReserved; // Reserved ( must be 0) + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // How many bytes in this resource? + DWORD dwImageOffset; // Where in the file is this image? +} ICONDIRENTRY, *LPICONDIRENTRY; + +typedef struct +{ + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource Type (1 for icons) + WORD idCount; // How many images? + //ICONDIRENTRY idEntries; // An entry for each image (idCount of 'em) +} ICONDIR, *LPICONDIR; + +#pragma pack(pop) + +typedef struct tagMyBITMAPINFO { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; +} MYBITMAPINFO; + +void IcoSave(const std::wstring &fileName, HICON hicon) +{ + std::ofstream store (fileName.c_str(), std::ios_base::binary); + if(!store.is_open()) + return; + ICONINFO ii; + if(!GetIconInfo(hicon,&ii)) + { + store.close(); + return; + } + + HBITMAP hbmMask = ii.hbmMask; + HBITMAP hbmColor = ii.hbmColor; + BITMAP bmiMask; + BITMAP bmiColor; + if( + GetObject(hbmColor,sizeof(bmiColor),&bmiColor) && + GetObject(hbmMask,sizeof(bmiMask),&bmiMask) && + (bmiColor.bmWidth==bmiMask.bmWidth) && + (bmiColor.bmHeight==bmiMask.bmHeight) && + (bmiMask.bmHeight) > 0 && + (bmiMask.bmWidth) > 0 + ) + { + BITMAPINFOHEADER icobmi = {0}; + MYBITMAPINFO info1 = {0}; + MYBITMAPINFO info2 = {0}; + + HDC hDC = CreateCompatibleDC(NULL); + info1.bmiHeader.biSize = sizeof(info1.bmiHeader); + info1.bmiHeader.biWidth = bmiColor.bmWidth; + info1.bmiHeader.biHeight = bmiColor.bmHeight; + info1.bmiHeader.biPlanes = 1; + info1.bmiHeader.biBitCount = bmiColor.bmBitsPixel; + unsigned int size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info1,DIB_RGB_COLORS); + char* bits1 = new char[info1.bmiHeader.biSizeImage]; + size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,bits1,(BITMAPINFO*)&info1,DIB_RGB_COLORS); + info2.bmiHeader.biSize = sizeof(info2.bmiHeader); + info2.bmiHeader.biWidth = bmiMask.bmWidth; + info2.bmiHeader.biHeight = bmiMask.bmHeight; + info2.bmiHeader.biPlanes = 1; + info2.bmiHeader.biBitCount = bmiMask.bmBitsPixel; + size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info2,DIB_RGB_COLORS); + char* bits2 = new char[info2.bmiHeader.biSizeImage]; + size = GetDIBits(hDC,hbmMask,0,info2.bmiHeader.biHeight,bits2,(BITMAPINFO*)&info2,DIB_RGB_COLORS); + + ICONDIR icodir; + ICONDIRENTRY icoent; + icodir.idReserved = 0; + icodir.idType = 1; + icodir.idCount = 1; + + icoent.bWidth = (unsigned char)bmiColor.bmWidth; + icoent.bHeight = (unsigned char)bmiColor.bmHeight; + icoent.bColorCount = 8<=bmiColor.bmBitsPixel?0:1<extCssHtml2.empty()) + { + cssCopied = CopyFile(Options::instance->extCssHtml2.c_str(), css.c_str(), FALSE); + } + + if(!cssCopied) + ExtractFile(IDR_CSS, css); + ExtractFile(IDR_JS, folder + _T("\\history.js")); + + HICON ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hPlusExIcon); + IcoSave(folder + _T("\\pnode.ico"), ico); + CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); + ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hMinusExIcon); + IcoSave(folder + _T("\\mnode.ico"), ico); + CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); + ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[0]); + IcoSave(folder + _T("\\event0.ico"), ico); + CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); + ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[1]); + IcoSave(folder + _T("\\event1.ico"), ico); + CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0); + + EXP_FILE << _T("\n"); + EXP_FILE << _T("\n\n"); + EXP_FILE << _T("") << TranslateT("History Log") << _T(" [") << MakeTextHtmled(myName) << _T("] - [") << MakeTextHtmled(name1) << _T("]\n"); + EXP_FILE << _T("\n"); + EXP_FILE << _T("\n"); + EXP_FILE << _T("\n"); + + EXP_FILE << _T("\n"); + EXP_FILE << _T("") << TranslateT("Menu") << _T("\n"); + EXP_FILE << _T("\n"); + EXP_FILE << _T("\n"); + EXP_FILE << _T("\n"); + EXP_FILE << _T("
\n"); + EXP_FILE << _T("\n"); + + EXP_FILE << _T("

") << TranslateT("History Log") << _T("

\n

"); + EXP_FILE << MakeTextHtmled(myName); + if(proto1.length() || myId.length()) + { + EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(myId) << _T(") - "); + } + else + { + EXP_FILE << _T(" - "); + } + + EXP_FILE << MakeTextHtmled(name1); + if(proto1.length() || id1.length()) + { + EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(id1) << _T(")

\n"); + } + else + { + EXP_FILE << _T("\n"); + } + + EXP_FILE << _T("
") << TranslateT("Filter:") << _T(" ") << MakeTextHtmled(filterName) << _T("
\n"); + groupId = 0; +} + +void RichHtmlExport::WriteFooter() +{ + if(groupId > 0) + { + EXP_FILE << _T("\n"); + } + + EXP_FILE << _T("
\n\n"); +} + +void RichHtmlExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText) +{ + TCHAR *id = isMe ? _T("out") : _T("inc"); + TCHAR* ev = (isMe ? _T("1") : _T("0")); + if(groupId > 0) + { + EXP_FILE << _T("\n"); + } + + bool isUrl = false; + std::wstring& mes = ReplaceSmileys(isMe, eventText, isUrl); + EXP_FILE << _T("
\n"); + EXP_FILE << _T(""); + EXP_FILE << _T("\n"); + EXP_FILE << _T("") << time << _T("\n\n") << mes; + EXP_FILE << _T("\n
\n"); + EXP_FILE << _T("
\n"); + ++groupId; +} + +void RichHtmlExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei) +{ + TCHAR *id = isMe ? _T("out") : _T("inc"); + TCHAR* ev = (isMe ? _T("1") : _T("0")); + TCHAR* ev1 = ev; + bool isUrl = false; + std::wstring& mes = ReplaceSmileys(isMe, message, isUrl); + if(isUrl) + ev = _T("2"); + EXP_FILE << _T("
\n"); + EXP_FILE << _T("
") << _T("
\n"); + EXP_FILE << _T("
") << (Options::instance->exportHtml2ShowDate ? longDate : shortDate) << _T("
\n"); + EXP_FILE << _T("
") << MakeTextHtmled(user) << _T("
\n"); + EXP_FILE << _T("
\n"); + EXP_FILE << mes; + EXP_FILE << _T("\n
\n"); + EXP_FILE << _T("
\n"); +} + +std::wstring RichHtmlExport::ReplaceSmileys(bool isMe, const std::wstring &msg, bool &isUrl) +{ + if(Options::instance->exportHtml2UseSmileys && g_SmileyAddAvail) + { + TCHAR* msgbuf = new TCHAR[msg.length() + 1]; + memcpy_s(msgbuf, (msg.length() + 1) * sizeof(TCHAR), msg.c_str(), (msg.length() + 1) * sizeof(TCHAR)); + SMADD_BATCHPARSE2 sp = {0}; + SMADD_BATCHPARSERES *spr; + sp.cbSize = sizeof(sp); + sp.Protocolname = baseProto.length() == 0 ? NULL : baseProto.c_str(); + sp.str = msgbuf; + sp.flag = SAFL_TCHAR | SAFL_PATH | (isMe ? SAFL_OUTGOING : 0); + spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp); + delete[] msgbuf; + + if (spr == NULL || (INT_PTR)spr == CALLSERVICE_NOTFOUND) + { + // Did not find a simley + return UrlHighlightHtml(MakeTextHtmled(msg), isUrl); + } + + std::queue > positionMap; + std::wstring newMsg = MakeTextHtmled(msg, &positionMap); + std::wstring smileyMsg; + + size_t last_pos=0; + std::pair pos(0, 0); + size_t currentAdd = 0; + if(!positionMap.empty()) + { + pos = positionMap.front(); + positionMap.pop(); + } + + for (unsigned i = 0; i < sp.numSmileys; ++i) + { + size_t startChar = spr[i].startChar + currentAdd; + while(startChar >= pos.first && pos.second) + { + startChar += pos.second; + currentAdd += pos.second; + if(!positionMap.empty()) + { + pos = positionMap.front(); + positionMap.pop(); + } + else + { + pos = std::pair(0, 0); + } + } + + size_t endChar = spr[i].startChar + spr[i].size + currentAdd; + while(endChar >= pos.first && pos.second) + { + endChar += pos.second; + currentAdd += pos.second; + if(!positionMap.empty()) + { + pos = positionMap.front(); + positionMap.pop(); + } + else + { + pos = std::pair(0, 0); + } + } + + size_t size = endChar - startChar; + + if (spr[i].filepath != NULL) // For deffective smileypacks + { + // Add text + if (startChar - last_pos > 0) + { + smileyMsg += newMsg.substr(last_pos, startChar - last_pos); + } + + std::wstring smileyName = GetName(spr[i].filepath); + if(smileys.find(smileyName) == smileys.end()) + { + smileys.insert(smileyName); + CopyFile(spr[i].filepath, (folder + _T("\\") + smileyName).c_str(), FALSE); + } + + std::wstring smileyText = newMsg.substr(startChar, size); + smileyMsg += _T("\"");"); + } + + // Get next + last_pos = endChar; + } + + // Add rest of text + if (last_pos < newMsg.length()) + { + smileyMsg += newMsg.substr(last_pos); + } + + CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr); + return UrlHighlightHtml(smileyMsg, isUrl); + } + else + { + return UrlHighlightHtml(MakeTextHtmled(msg), isUrl); + } +} -- cgit v1.2.3