Listing 2: simphtml.cpp Implementation of SimpleHTMLDisplay
#include <windows.h> #include "simphtml.hpp" #define PROP_SIMPLEHTML TEXT("prop_simplehtml") // Constructor - adds simple HTML capabilities to an edit control SimpleHTMLDisplay::SimpleHTMLDisplay(HWND hWnd) { TCHAR tchClass[40]; // Initialise data and use SetProp to store data pointer shd_hwndEdit=hWnd; shd_lpFirstURL=0; shd_iRichEditVersion=0; SetProp(hWnd,PROP_SIMPLEHTML,(HANDLE)this); // Check the control class name to establish our abilities if (GetClassName(shd_hwndEdit,tchClass,40)) { if (lstrcmpi(tchClass,TEXT("EDIT"))!=0) { if (lstrcmpi(tchClass,TEXT("RICHEDIT"))==0) shd_iRichEditVersion=1; else { shd_iRichEditVersion=2; SendMessage(shd_hwndEdit,EM_SETEVENTMASK, 0,ENM_LINK); } } } // Subclass the edit control shd_lpfnOrigProc=SetWindowLong(shd_hwndEdit, GWL_WNDPROC,(LONG)SubProc); } // Sub-class window procedure LRESULT CALLBACK SimpleHTMLDisplay::SubProc(HWND hWnd,UINT msg,WPARAM w,LPARAM l) { SimpleHTMLDisplay *lpSHD; LONG lpfnOrigProc; // Get our data from window property lpSHD=(SimpleHTMLDisplay *)GetProp(hWnd,PROP_SIMPLEHTML); lpfnOrigProc=lpSHD->shd_lpfnOrigProc; switch (msg) { case WM_SETTEXT: lpSHD->PrintText((LPTSTR)l); return TRUE; case SHDMSG_GETURL: return (LRESULT)lpSHD->RetrieveURL((ENLINK *)l); case SHDMSG_HIGHLIGHTURL: lpSHD->HighlightURL((ENLINK *)l,w); return 0; case WM_DESTROY: delete lpSHD; break; } return CallWindowProc((WNDPROC)lpfnOrigProc,hWnd,msg,w,l); } // Set HTML text in the control void SimpleHTMLDisplay::PrintText(LPTSTR lpszStr) { int iPos,iStart,iInTag=0; CHARFORMAT2 cf; // Clear existing text CallWindowProc((WNDPROC)shd_lpfnOrigProc, shd_hwndEdit,WM_SETTEXT,0,0); // Check for null text if (!lpszStr || !*lpszStr) return; // Initialise edit control format structure cf.cbSize=(shd_iRichEditVersion>1)?sizeof(CHARFORMAT2): sizeof(CHARFORMAT); // Parse the supplied data for (iPos=0,iStart=0;;iPos++) { // Check for end of string if (!lpszStr[iPos]) { if (iStart<iPos && !iInTag) SendMessage(shd_hwndEdit,EM_REPLACESEL, FALSE,(LPARAM)(lpszStr+iStart)); break; } // HTML Tag? if (lpszStr[iPos]==TEXT('<')) { if (iStart<iPos && !iInTag) { LPTSTR lpszTemp; if (lpszTemp=(LPTSTR)malloc((iPos-iStart+1)* sizeof(TCHAR))) { lstrcpyn(lpszTemp,lpszStr+iStart, (iPos-iStart)+1); SendMessage(shd_hwndEdit,EM_REPLACESEL, FALSE,(LPARAM)lpszTemp); free((LPVOID)lpszTemp); } } ++iInTag; // End of line? if (strnicmp(lpszStr+iPos,TEXT("<br>"),4)==0 || strnicmp(lpszStr+iPos,TEXT("<p>"),3)==0) { SendMessage(shd_hwndEdit,EM_REPLACESEL, FALSE,(LPARAM)TEXT("\r\n")); } // Tags are only interpreted with a richedit control if (shd_iRichEditVersion>0) { cf.dwMask=0; cf.dwEffects=0; // Bold? if (strnicmp(lpszStr+iPos,TEXT("<b>"),3)==0) { cf.dwMask=CFM_BOLD; cf.dwEffects=CFE_BOLD; } else if (strnicmp(lpszStr+iPos,TEXT("</b>"),4)==0) cf.dwMask=CFM_BOLD; // Underline? else if (strnicmp(lpszStr+iPos,TEXT("<u>"),3)==0) { cf.dwMask=CFM_UNDERLINE; cf.dwEffects=CFE_UNDERLINE; } else if (strnicmp(lpszStr+iPos,TEXT("</u>"),4)==0) cf.dwMask=CFM_UNDERLINE; // Italic? else if (strnicmp(lpszStr+iPos,TEXT("<i>"),3)==0) { cf.dwMask=CFM_ITALIC; cf.dwEffects=CFE_ITALIC; } else if (strnicmp(lpszStr+iPos,TEXT("</i>"),4)==0) cf.dwMask=CFM_ITALIC; // Change font settings? else if (strnicmp(lpszStr+iPos,TEXT("<font"),5)==0) HandleFont(lpszStr+iPos,&cf); else if (strnicmp(lpszStr+iPos,TEXT("</font>"),7)==0) { cf.dwMask=CFM_COLOR; cf.dwEffects=CFE_AUTOCOLOR; } // Links only supported with richedit 2.0 or better else if (shd_iRichEditVersion>1) { if (strnicmp(lpszStr+iPos, TEXT("<a href=\""),9)==0) { DWORD dwLink; // Add link to list dwLink=AddLink(lpszStr+iPos+8); // Turn link state on, and store the URL // node address in 'lcid' cf.dwMask=CFM_LINK|CFM_COLOR| CFM_UNDERLINE|CFM_LCID; cf.dwEffects=CFE_LINK|CFE_UNDERLINE; cf.crTextColor=URL_NORMALCOLOR; cf.lcid=(LCID)dwLink; } else if (strnicmp(lpszStr+iPos,TEXT("</a>"),4)==0) { // Turn link state on, clear 'lcid' field cf.dwMask=CFM_LINK|CFM_COLOR| CFM_UNDERLINE|CFM_LCID; cf.dwEffects=CFE_AUTOCOLOR; cf.lcid=0; } } // Send formatting changes to control if (cf.dwMask) SendMessage(shd_hwndEdit,EM_SETCHARFORMAT, SCF_SELECTION,(LPARAM)&cf); } } // End of tag? else if (lpszStr[iPos]==TEXT('>') && iInTag) { if (--iInTag==0) iStart=iPos+1; } } } // Add a link to the list DWORD SimpleHTMLDisplay::AddLink(LPTSTR lpszLink) { TCHAR tchBuf[300]; SimpleHTML_UrlNode *lpURL; int iLen; // Copy into buffer if (*lpszLink==TEXT('\"')) ++lpszLink; for (iLen=0;*lpszLink && *lpszLink!=TEXT('\"') && iLen<300; lpszLink++,iLen++) tchBuf[iLen]=*lpszLink; tchBuf[iLen]=0; // Allocate URL node and link into list if (lpURL=(SimpleHTML_UrlNode *) malloc(sizeof(SimpleHTML_UrlNode)+(iLen*sizeof(TCHAR)))) { lstrcpy(lpURL->shu_tchURL,tchBuf); lpURL->shu_lpNext=shd_lpFirstURL; shd_lpFirstURL=lpURL; } return (DWORD)lpURL; } // Handle font changes in richedit control (only supports colors) BOOL SimpleHTMLDisplay::HandleFont(LPTSTR lpszTag,CHARFORMAT2 *lpCF) { while (*lpszTag && *lpszTag!=TEXT('>')) { if (strnicmp(lpszTag,TEXT("color="),6)==0) { // Get color string lpszTag+=6; if (*lpszTag==TEXT('\"')) ++lpszTag; // Only handle # which we assume means hex if (*lpszTag==TEXT('#')) { BYTE r,g,b; // Parse color value ++lpszTag; r=hexcharval(*(lpszTag++))<<4; r|=hexcharval(*(lpszTag++)); g=hexcharval(*(lpszTag++))<<4; g|=hexcharval(*(lpszTag++)); b=hexcharval(*(lpszTag++))<<4; b|=hexcharval(*(lpszTag++)); // Turn color on in charformat lpCF->dwMask=CFM_COLOR; lpCF->dwEffects=0; lpCF->crTextColor=RGB(r,g,b); return 1; } } ++lpszTag; } return 0; } // Retrieve URL string from control LPTSTR SimpleHTMLDisplay::RetrieveURL(ENLINK *lpLink) { CHARFORMAT2 cf2; SimpleHTML_UrlNode *lpURL; // Set the selection to that of the link SendMessage(shd_hwndEdit,EM_HIDESELECTION,TRUE,0); SendMessage(shd_hwndEdit,EM_EXSETSEL,0,(LPARAM)&lpLink->chrg); // Get the URL information cf2.cbSize=sizeof(cf2); cf2.lcid=0; SendMessage(shd_hwndEdit,EM_GETCHARFORMAT,TRUE,(LPARAM)&cf2); lpURL=(SimpleHTML_UrlNode *)cf2.lcid; // Move cursor to position following the URL SendMessage(shd_hwndEdit,EM_SETSEL, lpLink->chrg.cpMax,lpLink->chrg.cpMax); SendMessage(shd_hwndEdit,EM_HIDESELECTION,FALSE,0); return (lpURL)?lpURL->shu_tchURL:0; } // Highlight URL text in control void SimpleHTMLDisplay::HighlightURL(ENLINK *lpLink,BOOL fState) { CHARFORMAT2 cf2; CHARRANGE cr; // Get current selection point SendMessage(shd_hwndEdit,EM_EXGETSEL,0,(LPARAM)&cr); // Set the selection to that of the link SendMessage(shd_hwndEdit,EM_HIDESELECTION,TRUE,0); SendMessage(shd_hwndEdit,EM_EXSETSEL,0,(LPARAM)&lpLink->chrg); // Change text format and redraw window immediately cf2.cbSize=sizeof(cf2); cf2.dwMask=CFM_COLOR|CFM_LINK; cf2.dwEffects=(fState)?0:CFE_LINK; cf2.crTextColor=(fState)?URL_CLICKCOLOR:URL_NORMALCOLOR; SendMessage(shd_hwndEdit,EM_SETCHARFORMAT, SCF_SELECTION,(LPARAM)&cf2); UpdateWindow(shd_hwndEdit); // Restore original cursor position cr.cpMin=cr.cpMin; SendMessage(shd_hwndEdit,EM_EXSETSEL,0,(LPARAM)&cr); SendMessage(shd_hwndEdit,EM_HIDESELECTION,FALSE,0); } // Destructor SimpleHTMLDisplay::~SimpleHTMLDisplay() { SimpleHTML_UrlNode *lpURL,*lpNext; // Free URL linked list for (lpURL=shd_lpFirstURL;lpURL;lpURL=lpNext) { lpNext=lpURL->shu_lpNext; free((LPVOID)lpURL); } SetWindowLong(shd_hwndEdit,GWL_WNDPROC,shd_lpfnOrigProc); RemoveProp(shd_hwndEdit,PROP_SIMPLEHTML); } //End of File