//This file is part of Msg_Export a Miranda IM plugin //Copyright (C)2002 Kennet Nielsen ( http://sourceforge.net/projects/msg-export/ ) // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either //version 2 of the License, or (at your option) any later version. // //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "Glob.h" static UINT UM_FIND_CMD = RegisterWindowMessage( FINDMSGSTRING ); #define ID_FV_FONT 0x0010 #define ID_FV_COLOR 0x0020 #define ID_FV_SYNTAX_HL 0x0030 #define ID_FV_SAVE_AS_RTF 0x0040 // ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); // ASSERT(IDM_ABOUTBOX < 0xF000); // Specifies if history is opened internaly or externaly bool bUseIntViewer = true; // External program used to view files tstring sFileViewerPrg; // handle to the RichEditDll. We need to load this dll to use a RichEdit. HMODULE hRichEditDll = NULL; #define CONT(i) ((in[i]&0xc0) == 0x80) #define VAL(i, s) ((in[i]&0x3f) << s) void swap(char &c1, char &c2) { char ch; ch=c1; c1=c2; c2=ch; } int DecodeUTF8(const char *pcBuff,int /*iBufSize*/,char *pcOutBuf) { int iBytesInOut=0; int /*cp,*/i; char ch,*p; //Parse UTF-8 sequence //Support only chars up to three bytes (UCS-4 - go away!) //Warning: Partial decoding is possible! i=0; ch=pcBuff[i]; if(!(ch&0x80)) { pcOutBuf[iBytesInOut++]=ch; pcOutBuf[iBytesInOut++]='\0'; } else if(!(ch&0x20)) { pcOutBuf[iBytesInOut++]=(ch>>2)&0x07; i++; pcOutBuf[iBytesInOut++]=(pcBuff[i]&0x3F)|(ch<<6); swap(pcOutBuf[iBytesInOut-1],pcOutBuf[iBytesInOut-2]); } else if(!(ch&0x10)) { i++; pcOutBuf[iBytesInOut++]=(ch<<4)|((pcBuff[i]>>2)&0x0F); ch=pcBuff[i]; i++; pcOutBuf[iBytesInOut++]=(pcBuff[i]&0x3F)|(ch<<6); swap(pcOutBuf[iBytesInOut-1],pcOutBuf[iBytesInOut-2]); } else { p=(char*)&pcBuff[i]; pcOutBuf[iBytesInOut++]='\x3F'; pcOutBuf[iBytesInOut++]='\0'; if(!(ch&0x08)) i+=3; else if(!(ch&0x04)) i+=4; else if(!(ch&0x02)) i+=5; } i++; return i; } int __utf8_get_char(const char *in, int *chr) { /* 2-byte, 0x80-0x7ff */ return DecodeUTF8(in,256,(char *)chr); } // there is one instance of CLHistoryDlg for every history dialog on screeen. class CLHistoryDlg { public: HWND hWnd; HANDLE hContact; tstring sPath; HWND hFindDlg; FINDREPLACE fr; TCHAR acFindStr[100]; bool bFirstLoad; bool bUtf8File; CLHistoryDlg( HANDLE hContact) : hContact( hContact ) { hFindDlg = NULL; acFindStr[0] = 0; ZeroMemory( &fr, sizeof( fr )); fr.lStructSize = sizeof( fr ); fr.hInstance = hInstance; fr.Flags = FR_NOUPDOWN|FR_HIDEUPDOWN;//|FR_MATCHCASE|FR_WHOLEWORD; // FR_DOWN|FR_FINDNEXT|FR_NOMATCHCASE; fr.lpstrFindWhat = acFindStr; fr.wFindWhatLen = sizeof(acFindStr); bFirstLoad = true; bUtf8File = false; } }; // List of all open history windows list< CLHistoryDlg* > clHistoryDlgList; // CRITICAL_SECTION used to access the window list // this is nesery because UpdateFileViews is called from callback thread. CRITICAL_SECTION csHistoryList; // CLStreamRTFInfo is used when reading RTF into rich edit from a stream. // RTF is used when Syntax highlighting is used. class CLStreamRTFInfo { private: HANDLE hFile; bool bHeaderWriten; bool bTailWriten; bool bCheckFirstForNick; bool bLastColorMyNick; // buffer size supplyed on win XP 4092 byte when streamin in // optimal size it to fully use this buffer but we can guess // how may bytes need converting in the file we are reading. BYTE abBuf[3300]; char szMyNick[100]; int nNickLen; static int nOptimalReadLen; int nWriteHeader( char *pszTarget, int nLen ); public: bool bUtf8File; CLStreamRTFInfo( HANDLE hFile ) { this->hFile = hFile; bHeaderWriten = false; bTailWriten = false; bCheckFirstForNick = false; bLastColorMyNick = false; bUtf8File = false; nNickLen = 0; } int nLoadFileStream( LPBYTE pbBuff, LONG cb ); }; int CLStreamRTFInfo::nOptimalReadLen = 3300; ///////////////////////////////////////////////////////////////////// // Member Function : nWriteHeader // Type : Private / Public / Protected // Parameters : pszTarget - ? // nLen - ? // Returns : int // Description : // // References : - // Remarks : - // Created : 030204, 04 February 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// int CLStreamRTFInfo::nWriteHeader( char *pszTarget, int nLen ) { COLORREF cMyText = db_get_dw(NULL,"SRMsg","Font3Col",RGB(64,0,128)); COLORREF cYourText = db_get_dw(NULL,"SRMsg","Font0Col",RGB(240,0,0)); /* original header !! "{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0\\fnil\\fcharset0 Courier New;}}\r\n" "{\\colortbl ;\\red%d\\green%d\\blue%d;\\red%d\\green%d\\blue%d;}\r\n" "\\viewkind4\\uc1\\pard\\cf2\\lang1033\\f0\\fs16 ", */ char szRtfHeader[400]; int nSrcLen = _snprintf( szRtfHeader, sizeof( szRtfHeader ), "{\\rtf1\\ansi\r\n" "{\\colortbl ;\\red%d\\green%d\\blue%d;\\red%d\\green%d\\blue%d;}\r\n" "\\viewkind4\\uc1\\pard\\cf2 ", GetRValue( cMyText) ,GetGValue( cMyText) ,GetBValue( cMyText ), GetRValue( cYourText ),GetGValue( cYourText ),GetBValue( cYourText)); if (nSrcLen > nLen ) { MessageBox( NULL, TranslateT("Failed to write to the RichEdit the buffer was to small."), MSG_BOX_TITEL, MB_OK ); return 0; // target buffer to small } memcpy( pszTarget, szRtfHeader, nSrcLen ); bHeaderWriten = true; return nSrcLen; } const char szNewLine[] = "\n\\par "; const char szRtfEnd[] = "\r\n\\par }\r\n\0"; ///////////////////////////////////////////////////////////////////// // Member Function : nLoadFileStream // Type : Private / Public / Protected // Parameters : pbBuff - ? // cb - ? // Returns : int // Description : // // References : - // Remarks : - // Created : 030204, 04 February 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// int CLStreamRTFInfo::nLoadFileStream( LPBYTE pbBuff, LONG cb ) { if (bTailWriten ) return 0; if (nOptimalReadLen < 500 ) { MessageBox( NULL, TranslateT("Error: Optimal buffer size decrecied to a to low size !!"), MSG_BOX_TITEL, MB_OK ); return 0; } DWORD dwRead; DWORD dwToRead = nOptimalReadLen; if ( !ReadFile(hFile, abBuf, dwToRead, &dwRead, (LPOVERLAPPED)NULL)) return 0; DWORD dwCurrent = 0; DWORD n = 0; if ( !bHeaderWriten ) { if (dwRead >= 3 ) { bUtf8File = bIsUtf8Header( abBuf ); if (bUtf8File ) n = 3; } dwCurrent += nWriteHeader( (char*)pbBuff, cb ); tstring sMyNick = NickFromHandle(0); nNickLen = WideCharToMultiByte(bUtf8File ? CP_UTF8 : CP_ACP, 0, sMyNick.c_str(), (int)sMyNick.length(), szMyNick, sizeof( szMyNick ), NULL, NULL ); } else { if (bCheckFirstForNick ) { // Test against "<<" also if (( (memcmp( abBuf, szMyNick, nNickLen) == 0) || (abBuf[0] == '<' && abBuf[1] == '<') ) != bLastColorMyNick ) { // we shut only get here if we need to change color !! bLastColorMyNick = !bLastColorMyNick; // change color memcpy( &pbBuff[dwCurrent], bLastColorMyNick ? "\\cf1 " : "\\cf2 ", 5 ); } bCheckFirstForNick = false; } } bool bIsFileEnd = dwRead < dwToRead; for( ; n < dwRead ; n++ ) { // worst case is a file ending with \n or a unicode letter. resulting in a big unicode string // here we need szNewLine and szRtfEnd. the 10 is a small safty margin. if (dwCurrent + (sizeof( szNewLine) + sizeof( szRtfEnd) + 10) > (DWORD)cb ) { // oh no !!! we have almost reached the end of the windows supplyed buffer // we are writing to. we need to abort mision *S*!! // and rewinde file // we will adjust the optima buffer size nOptimalReadLen -= 50; SetFilePointer( hFile, n - dwRead, NULL, FILE_CURRENT ); return dwCurrent; } if (abBuf[n] == '\n' ) { memcpy( &pbBuff[dwCurrent], szNewLine, sizeof( szNewLine )-1 ); dwCurrent += sizeof( szNewLine )-1; if (n + 1 >= dwRead ) { // this is an anoing case because here we have read \n as the last char in the file // this means that the if the next data read from file begins with it has // to be highlighted if (!bIsFileEnd ) bCheckFirstForNick = true; break; } if (abBuf[n+1] == ' ' || abBuf[n+1] == '\t' || abBuf[n+1] == '\r' ) continue; if (n + nNickLen >= dwRead ) { if (!bIsFileEnd ) { // here we have a problem we haven't read this data yet // the data we need to compare to is still in the file. // we can't read more data from the file because the we // might just move the problem. if file contains \n\n\n\n\n ... LONG lExtraRead = (n+1) - dwRead; if (lExtraRead >= 0 ) MessageBox( NULL, TranslateT("Internal error !! (lExtraRead >= 0)"), MSG_BOX_TITEL, MB_OK ); SetFilePointer( hFile, lExtraRead, NULL, FILE_CURRENT ); bCheckFirstForNick = true; return dwCurrent; } if ( !bLastColorMyNick ) continue; // else the last color user was my nick // we needd to change color to the other user color. /* old code !! DWORD dwAddedToBuf; if ( !ReadFile(hFile, &abBuf[dwRead], dwNeeded, &dwAddedToBuf, (LPOVERLAPPED)NULL)) return 0; dwToRead += dwNeeded; dwRead += dwAddedToBuf;*/ } else { // the data we need is here just compare if (( ( memcmp( &abBuf[n+1], szMyNick, nNickLen) == 0) || ( abBuf[n+1] == '<' && abBuf[n+2] == '<') ) == bLastColorMyNick ) continue; } // we shut only get here if we need to change color !! bLastColorMyNick = !bLastColorMyNick; // change color memcpy( &pbBuff[dwCurrent], bLastColorMyNick ? "\\cf1 " : "\\cf2 ", 5 ); dwCurrent += 5; continue; } else if (abBuf[n] == '\\' || abBuf[n] == '}' || abBuf[n] == '{') { pbBuff[dwCurrent++]='\\'; } else if (bUtf8File && (abBuf[n] & 0x80)) { int nValue; int nLen = __utf8_get_char( (const char *)&abBuf[n], &nValue ); if(nLen+n>dwRead) { SetFilePointer(hFile,n-dwRead,NULL,FILE_CURRENT); break; } dwCurrent += sprintf( (char*)&pbBuff[dwCurrent], "\\u%d?", nValue ); //continue; /* // Then we have an extended char in the UTF8 file. // we need to convert this to UCS-2 and then to \uN in the RTF int nUtf8Len = 1; while( ( (n + nUtf8Len) < dwRead) && ((abBuf[ n + nUtf8Len ] & 0xC0) == 0x80)) nUtf8Len++; wchar_t szWstr[2]; if (MultiByteToWideChar( CP_UTF8, 0, (char*)&abBuf[n], nUtf8Len, szWstr, 2) == 1 ) { if ((int)(szWstr[0]) != nValue ) __utf8_get_char( (const char *)&abBuf[n], &nValue ); // dwCurrent += sprintf( (char*)&pbBuff[dwCurrent], "\\u%d?", (int)(szWstr[0])); // n += nUtf8Len - 1; // continue; }*/ n += nLen-1; continue; } pbBuff[dwCurrent++] = abBuf[n]; } if (bIsFileEnd ) {// write end memcpy( &pbBuff[dwCurrent], szRtfEnd, sizeof( szRtfEnd )-1 ); dwCurrent += sizeof( szRtfEnd )-1; bTailWriten = true; } //memcpy( pbBuff, abBuf, dwRead ); return dwCurrent; } ///////////////////////////////////////////////////////////////////// // Member Function : Initialize // Type : Global // Parameters : None // Returns : void // Description : // // References : - // Remarks : - // Created : 021213, 13 December 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// void Initialize() { InitializeCriticalSection( &csHistoryList ); } ///////////////////////////////////////////////////////////////////// // Member Function : Uninitilize // Type : Global // Parameters : None // Returns : void // Description : // // References : - // Remarks : - // Created : 021213, 13 December 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// void Uninitilize() { DeleteCriticalSection( &csHistoryList ); } ///////////////////////////////////////////////////////////////////// // Member Function : UpdateFileViews // Type : Global // Parameters : pszFile - File which has been updated // Returns : void // Description : Send a message to alle to windows that need updating // // References : - // Remarks : - // Created : 021213, 13 December 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// void UpdateFileViews( const TCHAR *pszFile ) { EnterCriticalSection( &csHistoryList ); list< CLHistoryDlg* >::const_iterator iterator; for( iterator = clHistoryDlgList.begin() ; iterator != clHistoryDlgList.end() ; ++iterator ) { CLHistoryDlg* pcl = (*iterator); if (pcl->sPath == pszFile ) { PostMessage( pcl->hWnd, WM_RELOAD_FILE, 0, 0 ); } } LeaveCriticalSection( &csHistoryList ); } ///////////////////////////////////////////////////////////////////// // Member Function : bOpenExternaly // Type : Global // Parameters : hContact - ? // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 021010, 10 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bOpenExternaly( HANDLE hContact ) { tstring sPath = GetFilePathFromUser( hContact ); if (sFileViewerPrg.empty()) { SHELLEXECUTEINFO st = {0}; st.cbSize = sizeof(st); st.fMask = SEE_MASK_INVOKEIDLIST; st.hwnd = NULL; st.lpFile = sPath.c_str(); st.nShow = SW_SHOWDEFAULT; ShellExecuteEx(&st); return true; } tstring sTmp = sFileViewerPrg; sTmp += _T(" \""); sTmp += sPath; sTmp += '\"'; STARTUPINFO sStartupInfo = { 0 }; GetStartupInfo(&sStartupInfo); // we parse oure owne info on sStartupInfo.lpTitle = (TCHAR*)sFileViewerPrg.c_str(); PROCESS_INFORMATION stProcesses = {0}; if ( !CreateProcess( NULL, (TCHAR*)sTmp.c_str(), NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &sStartupInfo, &stProcesses)) { DisplayLastError( LPGENT("Faile to execute external file view")); } return true; } ///////////////////////////////////////////////////////////////////// // Member Function : bGetInternalViewer // Type : Global // Parameters : None // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 021016, 16 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bUseInternalViewer() { return bUseIntViewer; } ///////////////////////////////////////////////////////////////////// // Member Function : bUseInternalViewer // Type : Global // Parameters : bNew - ? // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 021016, 16 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bUseInternalViewer( bool bNew ) { bUseIntViewer = bNew; if (bUseIntViewer && !hRichEditDll ) { hRichEditDll = LoadLibraryA("RICHED32.DLL"); if (!hRichEditDll ) { DisplayLastError( LPGENT("Failed to load Rich Edit ( RICHED32.DLL )" )); return false; } } return true; } ///////////////////////////////////////////////////////////////////// // Member Function : RichEditStreamLoadFile // Type : Global // Parameters : dwCookie - ? // pbBuff - ? // cb - ? // pcb - ? // Returns : DWORD CALLBACK // Description : // // References : - // Remarks : - // Created : 021010, 10 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// DWORD CALLBACK RichEditStreamLoadFile(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { ReadFile((HANDLE)dwCookie, pbBuff, (DWORD)cb, (DWORD *)pcb, (LPOVERLAPPED)NULL); return (DWORD) ( *pcb >= 0 ? NOERROR : ( *pcb = 0, E_FAIL)); } DWORD CALLBACK RichEditRTFStreamLoadFile(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { *pcb = ((CLStreamRTFInfo *)dwCookie)->nLoadFileStream( pbBuff, cb ); if (*pcb ) return NOERROR; return (DWORD)E_FAIL; } DWORD CALLBACK RichEditStreamSaveFile(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { WriteFile((HANDLE)dwCookie, pbBuff, cb, (DWORD*)pcb, (LPOVERLAPPED)NULL); return *pcb != cb; } /* DWORD dwCurPos = 0; DWORD dwDataRead = 0; BYTE * pabFileData = NULL; DWORD CALLBACK RichEditStreamLoadFile(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { *pcb = 0; while( dwCurPos < dwDataRead && *pcb < cb ) { pbBuff[ *pcb ] = pabFileData[ dwCurPos ]; dwCurPos++; (*pcb)++; } return (DWORD) ( *pcb >= 0 ? NOERROR : ( *pcb = 0, E_FAIL)); } */ ///////////////////////////////////////////////////////////////////// // Member Function : bLoadFile // Type : Global // Parameters : hwndDlg - ? // hContact - ? // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 021010, 10 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bLoadFile( HWND hwndDlg, CLHistoryDlg * pclDlg ) { DWORD dwStart = GetTickCount(); HWND hRichEdit = GetDlgItem( hwndDlg, IDC_RICHEDIT); if (!hRichEdit) { MessageBox( hwndDlg, TranslateT("Failed to get handle to RichEdit!"), MSG_BOX_TITEL, MB_OK ); return false; } HANDLE hFile = CreateFile( pclDlg->sPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { int nDBCount = db_event_count(pclDlg->hContact); TCHAR szTmp[1500]; if (nDBCount == -1 ) mir_sntprintf(szTmp, 1499, LPGENT("Failed to open file\r\n%s\r\n\r\nContact handle is invalid"), pclDlg->sPath.c_str()); else mir_sntprintf( szTmp, 1499, LPGENT("Failed to open file\r\n%s\r\n\r\nMiranda database contains %d events"), pclDlg->sPath.c_str(), nDBCount ); SETTEXTEX stText = {0}; stText.codepage = CP_ACP; SendMessage( hRichEdit, EM_SETTEXTEX, (WPARAM) &stText, (LPARAM) szTmp ); return false; } POINT ptOldPos; SendMessage( hRichEdit, EM_GETSCROLLPOS, 0, (LPARAM) &ptOldPos ); bool bScrollToBottom = true; if (pclDlg->bFirstLoad ) pclDlg->bFirstLoad = false; else { SCROLLINFO sScrollInfo = { 0 }; sScrollInfo.cbSize = sizeof( SCROLLINFO ); sScrollInfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; if (GetScrollInfo( hRichEdit,SB_VERT,&sScrollInfo)) bScrollToBottom = sScrollInfo.nPos + (int)sScrollInfo.nPage + 50 > sScrollInfo.nMax; } HMENU hSysMenu = GetSystemMenu( hwndDlg, FALSE ); bool bUseSyntaxHL = (GetMenuState( hSysMenu , ID_FV_SYNTAX_HL, MF_BYCOMMAND) & MF_CHECKED)!=0; /* DWORD dwSize = GetFileSize( hFile, NULL ); dwCurPos = 0; pabFileData = new BYTE[ dwSize ]; ReadFile( hFile, pabFileData, dwSize , &dwDataRead, (LPOVERLAPPED)NULL); */ // SendMessage( hRichEdit, EM_SETBKGNDCOLOR, 0, RGB( 0, 0, 128)); // SendMessage( hRichEdit, EM_SETTEXTMODE, TM_RICHTEXT,0); // DWORD dw = SendMessage( hRichEdit, EM_GETLIMITTEXT, NULL, NULL); EDITSTREAM eds; eds.dwError = 0; if (bUseSyntaxHL ) { SendMessage( hRichEdit, // handle to destination window EM_EXLIMITTEXT, // message to send 0, // not used; must be zero 0x7FFFFFFF ); CLStreamRTFInfo clInfo( hFile ); eds.dwCookie = (DWORD)&clInfo; eds.pfnCallback = RichEditRTFStreamLoadFile; SendMessage(hRichEdit, EM_STREAMIN, (WPARAM)SF_RTF, (LPARAM)&eds); pclDlg->bUtf8File = clInfo.bUtf8File; } else { eds.dwCookie = (DWORD )hFile; eds.pfnCallback = RichEditStreamLoadFile; SendMessage(hRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&eds); } CloseHandle( hFile ); //delete [] pabFileData; TCHAR szTmp[100]; mir_sntprintf( szTmp, 99, _T("File open time %d\n"), GetTickCount() - dwStart ); OutputDebugString( szTmp ); GETTEXTLENGTHEX sData = { 0 }; sData.flags = GTL_NUMCHARS; sData.flags = GTL_DEFAULT; DWORD dwDataRead = (DWORD)SendMessage( hRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&sData, 0 ); SendMessage( hRichEdit, EM_SETSEL, dwDataRead - 1, dwDataRead - 1 ); if ( !bScrollToBottom ) SendMessage( hRichEdit, EM_SETSCROLLPOS, 0, (LPARAM) &ptOldPos ); mir_sntprintf( szTmp, 99, LPGENT("With scroll to bottom %d\n"), GetTickCount() - dwStart ); OutputDebugString( szTmp ); return true; } ///////////////////////////////////////////////////////////////////// // Member Function : bAdvancedCopy // Type : Global // Parameters : hwnd - handle to RichEdit control // Returns : Returns true if text was copied to the clipboard // Description : // // References : - // Remarks : - // Created : 030730, 30 juli 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bAdvancedCopy(HWND hwnd) { CHARRANGE sSelectRange; SendMessage( hwnd, EM_EXGETSEL, 0, (LPARAM)&sSelectRange ); int nSelLenght = sSelectRange.cpMax - sSelectRange.cpMin + 1; // +1 for null termination if (nSelLenght > 1 ) { OpenClipboard(NULL); EmptyClipboard(); TCHAR *pszSrcBuf = new TCHAR[ nSelLenght]; pszSrcBuf[0] = 0; SendMessage( hwnd, EM_GETSELTEXT, 0, (LPARAM)pszSrcBuf ); HANDLE hDecMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, nSelLenght ); TCHAR *pszCurDec = (TCHAR*)GlobalLock(hDecMem); bool bInSpaces = false; for( TCHAR *pszCur = pszSrcBuf ; pszCur[0] ; pszCur++ ) { if (bInSpaces ) { if (pszCur[0] == ' ' ) continue; bInSpaces = false; } if (pszCur[0] == '\n' ) { bInSpaces = true; } pszCurDec[0] = pszCur[0]; pszCurDec++; } pszCurDec[0] = 0; GlobalUnlock(hDecMem); SetClipboardData(CF_TEXT,hDecMem); delete [] pszSrcBuf; CloseClipboard(); return true; } return false; } ///////////////////////////////////////////////////////////////////// // Member Function : EditSubclassProc // Type : Global // Parameters : hwnd - ? // uMsg - ? // wParam - ? // lParam - ? // Returns : LRESULT CALLBACK // Description : // // References : - // Remarks : - // Created : 021013, 13 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// LRESULT CALLBACK EditSubclassProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { CLHistoryDlg * pclDlg = (CLHistoryDlg *)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch( msg ) { case WM_CONTEXTMENU: { HMENU nMenu = LoadMenu( hInstance, MAKEINTRESOURCE( IDR_FV_EDIT)); HMENU nSubMenu = GetSubMenu( nMenu, 0 ); POINT pt; pt.x=(short)LOWORD(lParam); pt.y=(short)HIWORD(lParam); if(pt.x==-1 && pt.y==-1) { DWORD dwStart,dwEnd; SendMessage( hwnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd ); SendMessage( hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)dwEnd); ClientToScreen( hwnd, &pt ); } TrackPopupMenu( nSubMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, 0 ); DestroyMenu( nSubMenu ); DestroyMenu( nMenu ); return TRUE; } case WM_GETDLGCODE: { return DLGC_WANTARROWS; } case WM_COPY: { // not working for "CTRL + C" if (bAdvancedCopy( hwnd)) return TRUE; break; } case WM_KEYDOWN: { if ((wParam == VK_INSERT || wParam == 'C') && (GetKeyState(VK_CONTROL) & 0x80)) { if (bAdvancedCopy( hwnd)) return TRUE; } break; } case WM_COMMAND: { switch (LOWORD(wParam)) { case ID_EDIT_COPY: { SendMessage( hwnd, WM_COPY, 0, 0 ); return TRUE; } } } } if (msg == UM_FIND_CMD ) { FINDREPLACE *fr = (FINDREPLACE *)lParam; if (fr->Flags & FR_DIALOGTERM ) { pclDlg->hFindDlg = NULL; return 0; } if (fr->Flags & FR_FINDNEXT) { FINDTEXT ft = { 0 }; ft.lpstrText = fr->lpstrFindWhat; SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&ft.chrg); ft.chrg.cpMin = ft.chrg.cpMax+1; ft.chrg.cpMax = -1; LRESULT res = SendMessage(hwnd, EM_FINDTEXTW, (WPARAM)fr->Flags,(LPARAM)&ft); if(res == -1) { ft.chrg.cpMin = 0; res = (int)SendMessage(hwnd, EM_FINDTEXTW, (WPARAM)fr->Flags,(LPARAM)&ft); if(res == -1) { MessageBox( hwnd, TranslateT("Search string was not found !"), MSG_BOX_TITEL, MB_OK ); return 0; } } ft.chrg.cpMin = LONG(res); ft.chrg.cpMax = LONG(res + _tcslen(fr->lpstrFindWhat)); SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&ft.chrg); return 0; } } return mir_callNextSubclass(hwnd, EditSubclassProc, msg, wParam, lParam); } ///////////////////////////////////////////////////////////////////// // Member Function : SetWindowsCtrls // Type : Global // Parameters : hwndDlg - ? // Returns : void // Description : // // References : - // Remarks : - // Created : 021001, 01 October 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// void SetWindowsCtrls( HWND hwndDlg ) { RECT rNewSize; GetClientRect( hwndDlg, &rNewSize ); RECT rCurSize; const int nSpacing = 12; HWND hButton = GetDlgItem( hwndDlg, IDOK ); GetWindowRect( hButton, &rCurSize ); int nButtonHeight = rCurSize.bottom - rCurSize.top; SetWindowPos( GetDlgItem( hwndDlg, IDC_RICHEDIT ), 0, nSpacing, nSpacing, rNewSize.right - (nSpacing * 2), rNewSize.bottom - ( nSpacing * 3 + nButtonHeight ), SWP_NOZORDER ); int nButtonWidth = rCurSize.right - rCurSize.left; int nButtonSpace = (rNewSize.right - ( 3 * nButtonWidth )) / 4; int nButtonTop = rNewSize.bottom - ( nSpacing + nButtonHeight ); int nCurLeft = nButtonSpace; SetWindowPos( GetDlgItem( hwndDlg, IDC_FV_FIND ), 0, nCurLeft, nButtonTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE ); nCurLeft += nButtonSpace + nButtonWidth; SetWindowPos( GetDlgItem( hwndDlg, IDC_FV_EXTERNAL ), 0, nCurLeft, nButtonTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE ); nCurLeft += nButtonSpace + nButtonWidth; SetWindowPos( hButton, 0, nCurLeft, nButtonTop, 0, 0, SWP_NOZORDER | SWP_NOSIZE ); } ///////////////////////////////////////////////////////////////////// // Member Function : SetRichEditFont // Type : Global // Parameters : hRichEdit - RichEdit to set the font in // bUseSyntaxHL - Is Syntax hilighting is used the color // will not be set // Returns : void // Description : // // References : - // Remarks : - // Created : 030205, 05 February 2003 // Developer : KN ///////////////////////////////////////////////////////////////////// void SetRichEditFont(HWND hRichEdit, bool bUseSyntaxHL ) { CHARFORMAT ncf = { 0 }; ncf.cbSize = sizeof( CHARFORMAT ); ncf.dwMask = CFM_BOLD | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_UNDERLINE; ncf.dwEffects = db_get_dw( NULL, MODULE, szFileViewDB "TEffects", 0 ); ncf.yHeight = db_get_dw( NULL, MODULE, szFileViewDB "THeight", 165 ); _tcscpy( ncf.szFaceName , _DBGetString( NULL, MODULE, szFileViewDB "TFace", _T("Courier New")).c_str()); if ( !bUseSyntaxHL ) { ncf.dwMask |= CFM_COLOR; ncf.crTextColor = db_get_dw( NULL, MODULE, szFileViewDB "TColor", 0 ); } SendMessage(hRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&ncf); } ///////////////////////////////////////////////////////////////////// // Member Function : DlgProcFileViewer // Type : Global // Parameters : hwndDlg - ? // msg - ? // wParam - ? // lParam - ? // Returns : static BOOL CALLBACK // Description : // // References : - // Remarks : - // Created : 020929, 29 September 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// static INT_PTR CALLBACK DlgProcFileViewer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { CLHistoryDlg * pclDlg = (CLHistoryDlg *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); switch (msg) { case WM_INITDIALOG: { SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam); CLHistoryDlg * pclDlg = (CLHistoryDlg *)lParam; SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon( hInstance, MAKEINTRESOURCE(IDI_EXPORT_MESSAGE))); HWND hRichEdit = GetDlgItem( hwndDlg, IDC_RICHEDIT ); mir_subclassWindow(hRichEdit, EditSubclassProc); SetWindowLongPtr( hRichEdit, GWLP_USERDATA, (LONG) pclDlg ); SendMessage( hRichEdit, EM_SETEVENTMASK , 0 , ENM_LINK); SendMessage( hRichEdit, EM_AUTOURLDETECT, TRUE, 0 ); HMENU hSysMenu = GetSystemMenu( hwndDlg, FALSE ); InsertMenu( hSysMenu, 0, MF_SEPARATOR | MF_BYPOSITION, 0, 0 ); InsertMenu( hSysMenu, 0, MF_STRING | MF_BYPOSITION, ID_FV_SAVE_AS_RTF, LPGENT("Save as RTF")); InsertMenu( hSysMenu, 0, MF_SEPARATOR | MF_BYPOSITION, 0, 0 ); BYTE bUseCC = (BYTE)db_get_b( NULL, MODULE, szFileViewDB "UseCC", 0 ); InsertMenu( hSysMenu, 0, MF_STRING | MF_BYPOSITION | ( bUseCC ? MF_CHECKED : 0 ), ID_FV_COLOR, LPGENT("Color...")); if (bUseCC ) SendMessage( hRichEdit, EM_SETBKGNDCOLOR, 0, db_get_dw( NULL, MODULE, szFileViewDB "CustomC", RGB(255,255,255))); InsertMenu( hSysMenu, 0, MF_STRING | MF_BYPOSITION, ID_FV_FONT, LPGENT("Font...")); bool bUseSyntaxHL = db_get_b( NULL, MODULE, szFileViewDB "UseSyntaxHL", 1 )!=0; InsertMenu( hSysMenu, 0, MF_STRING | MF_BYPOSITION | ( bUseSyntaxHL ? MF_CHECKED : 0 ), ID_FV_SYNTAX_HL, LPGENT("Syntax highlight")); SetRichEditFont( hRichEdit, bUseSyntaxHL ); TranslateDialogDefault(hwndDlg); Utils_RestoreWindowPosition(hwndDlg,pclDlg->hContact,MODULE,szFileViewDB); pclDlg->sPath = GetFilePathFromUser( pclDlg->hContact ); SetWindowsCtrls( hwndDlg ); bLoadFile(hwndDlg, pclDlg ); { // set Title TCHAR szFormat[200]; TCHAR szTitle[200]; if (GetWindowText( hwndDlg, szFormat, sizeof( szFormat)) ) { const TCHAR *pszNick = NickFromHandle( pclDlg->hContact ); tstring sPath = pclDlg->sPath; string::size_type n = sPath.find_last_of( '\\' ); if (n != sPath.npos ) sPath.erase( 0, n + 1 ); if (_sntprintf( szTitle, sizeof( szTitle ), szFormat, pszNick, sPath.c_str(), (pclDlg->bUtf8File ? _T("UTF8"):_T("ANSI"))) > 0 ) SetWindowText( hwndDlg, szTitle); } } WindowList_Add(hInternalWindowList,hwndDlg,pclDlg->hContact); } return TRUE; case WM_RELOAD_FILE: bLoadFile(hwndDlg, pclDlg ); return TRUE; case WM_SIZE: case WM_SIZING: SetWindowsCtrls( hwndDlg ); return TRUE; case WM_NCDESTROY: EnterCriticalSection( &csHistoryList ); clHistoryDlgList.remove( pclDlg ); LeaveCriticalSection( &csHistoryList ); delete pclDlg; SetWindowLongPtr(hwndDlg,GWLP_USERDATA,NULL); return 0; case WM_DESTROY: Utils_SaveWindowPosition(hwndDlg,pclDlg->hContact,MODULE,szFileViewDB); WindowList_Remove(hInternalWindowList,hwndDlg); return 0; case WM_SYSCOMMAND: { HMENU hSysMenu = GetSystemMenu( hwndDlg, FALSE ); bool bUseSyntaxHL = (GetMenuState( hSysMenu , ID_FV_SYNTAX_HL, MF_BYCOMMAND) & MF_CHECKED)!=0; HWND hRichEdit = GetDlgItem( hwndDlg, IDC_RICHEDIT ); if ((wParam & 0xFFF0) == ID_FV_FONT) { LOGFONT lf = { 0 }; lf.lfHeight = 14L; { DWORD dwEffects = db_get_dw( NULL, MODULE, szFileViewDB "TEffects", 0 ); lf.lfWeight = (dwEffects & CFE_BOLD) ? FW_BOLD : 0; lf.lfUnderline = (dwEffects & CFE_UNDERLINE) != 0; lf.lfStrikeOut = (dwEffects & CFE_STRIKEOUT) != 0; lf.lfItalic = (dwEffects & CFE_ITALIC) != 0; } _tcscpy(lf.lfFaceName, _DBGetString( NULL, MODULE, szFileViewDB "TFace", _T("Courier New")).c_str()); CHOOSEFONT cf = { 0 }; cf.lStructSize = sizeof( cf ); cf.hwndOwner = hwndDlg; cf.lpLogFont = &lf; cf.rgbColors = db_get_dw( NULL, MODULE, szFileViewDB "TColor", 0 ); cf.Flags = CF_EFFECTS | CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT; if (ChooseFont( &cf)) { DWORD dwEffects = (lf.lfWeight == FW_BOLD ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0); db_set_dw( NULL, MODULE, szFileViewDB "TEffects", dwEffects ); db_set_dw( NULL, MODULE, szFileViewDB "THeight", cf.iPointSize * 2 ); db_set_dw( NULL, MODULE, szFileViewDB "TColor", cf.rgbColors ); db_set_ts( NULL, MODULE, szFileViewDB "TFace", lf.lfFaceName ); SetRichEditFont( hRichEdit, bUseSyntaxHL ); } return TRUE; } else if ((wParam & 0xFFF0) == ID_FV_COLOR) { BYTE bUseCC = ! db_get_b( NULL, MODULE, szFileViewDB "UseCC", 0 ); if (bUseCC ) { CHOOSECOLOR cc = {0}; cc.lStructSize = sizeof( cc ); cc.hwndOwner = hwndDlg; cc.rgbResult = db_get_dw( NULL, MODULE, szFileViewDB "CustomC", RGB(255,255,255)); cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; static COLORREF MyCustColors[16] = { 0xFFFFFFFF }; cc.lpCustColors = MyCustColors; if (ChooseColor( &cc)) { SendMessage( hRichEdit, EM_SETBKGNDCOLOR, 0, cc.rgbResult ); db_set_dw( NULL, MODULE, szFileViewDB "CustomC", cc.rgbResult ); } else { /*DWORD dwError =*/ CommDlgExtendedError(); return TRUE; } } else { SendMessage( hRichEdit, EM_SETBKGNDCOLOR, TRUE, 0 ); } CheckMenuItem( hSysMenu, ID_FV_COLOR, MF_BYCOMMAND | (bUseCC ? MF_CHECKED : 0)); db_set_b( NULL, MODULE, szFileViewDB "UseCC", bUseCC ); return TRUE; } else if ((wParam & 0xFFF0) == ID_FV_SYNTAX_HL) { // we use the current state from the menu not the DB value // because we want to toggel the option for this window // still the new option selected will be stored. // so user may open 2 windows, now he can set SyntaxHL in both. bUseSyntaxHL = !bUseSyntaxHL; CheckMenuItem( hSysMenu, ID_FV_SYNTAX_HL, MF_BYCOMMAND | (bUseSyntaxHL ? MF_CHECKED : 0)); db_set_b( NULL, MODULE, szFileViewDB "UseSyntaxHL", bUseSyntaxHL ); if (bUseSyntaxHL ) bLoadFile(hwndDlg, pclDlg ); else SetRichEditFont( hRichEdit, bUseSyntaxHL ); return TRUE; } else if ((wParam & 0xFFF0) == ID_FV_SAVE_AS_RTF) { tstring sFile = pclDlg->sPath; sFile += _T(".rtf"); HANDLE hFile = CreateFile( sFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE ) { DisplayLastError( LPGENT("Failed to create file")); return TRUE; } EDITSTREAM eds; eds.dwCookie = (DWORD )hFile; eds.dwError = 0; eds.pfnCallback = RichEditStreamSaveFile; LRESULT nWriteOk = SendMessage(hRichEdit, EM_STREAMOUT, (WPARAM)SF_RTF, (LPARAM)&eds); if (nWriteOk <= 0 || eds.dwError != 0 ) { DisplayLastError( TranslateT("Failed to save file")); CloseHandle( hFile ); return TRUE; } CloseHandle( hFile ); tstring sReport = TranslateT("History was saved successfully in file\r\n"); sReport += sFile; MessageBox( NULL, sReport.c_str(),MSG_BOX_TITEL,MB_OK ); return TRUE; } return FALSE; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: case IDOK: DestroyWindow(hwndDlg); return TRUE; case IDC_FV_EXTERNAL: bOpenExternaly( pclDlg->hContact ); return TRUE; case IDC_FV_FIND: { if (pclDlg->hFindDlg ) { BringWindowToTop( pclDlg->hFindDlg ); return TRUE; } pclDlg->fr.hwndOwner = GetDlgItem( hwndDlg, IDC_RICHEDIT ); pclDlg->hFindDlg = FindText( &pclDlg->fr ); return TRUE; } } break; case WM_NOTIFY: if (((NMHDR*)lParam)->idFrom == IDC_RICHEDIT ) { if (((NMHDR*)lParam)->code == EN_LINK ) { ENLINK* pstLink = (ENLINK*)lParam; if (pstLink->msg == WM_LBUTTONUP ) { TCHAR szUrl[ 500 ]; if ((pstLink->chrg.cpMax - pstLink->chrg.cpMin) > (sizeof( szUrl) - 2)) return FALSE; TEXTRANGE stToGet; stToGet.chrg = pstLink->chrg; stToGet.lpstrText = szUrl; if (SendMessage( pstLink->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&stToGet) > 0 ) { CallService(MS_UTILS_OPENURL,1,(LPARAM)szUrl); } return TRUE; } } } break; case WM_CLOSE: DestroyWindow(hwndDlg); return TRUE; } return FALSE; } ///////////////////////////////////////////////////////////////////// // Member Function : bShowFileViewer // Type : Global // Parameters : hContact - ? // Returns : Returns true if // Description : // // References : - // Remarks : - // Created : 020929, 29 September 2002 // Developer : KN ///////////////////////////////////////////////////////////////////// bool bShowFileViewer( HANDLE hContact ) { HWND hInternalWindow = WindowList_Find(hInternalWindowList,hContact); if(hInternalWindow) { SetForegroundWindow(hInternalWindow); SetFocus(hInternalWindow); return true; } CLHistoryDlg * pcl = new CLHistoryDlg( hContact ); pcl->hWnd = CreateDialogParam( hInstance,MAKEINTRESOURCE(IDD_FILE_VIEWER),NULL,DlgProcFileViewer,(LPARAM)pcl); if (pcl->hWnd ) { EnterCriticalSection( &csHistoryList ); clHistoryDlgList.push_front( pcl ); LeaveCriticalSection( &csHistoryList ); ShowWindow( pcl->hWnd, SW_SHOWNORMAL ); return true; } DisplayLastError( LPGENT("Failed to create history dialog")); delete pcl; return false; }