/****************************************************************************** * SRTextView.cpp - HTML Renderer for rendering biblical texts on Pocket PC * devices. * Author: David C Trotz Jr. * e-mail: dtrotzjr@crosswire.org * * $Id$ * * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org) * CrossWire Bible Society * P. O. Box 2528 * Tempe, AZ 85280-2528 * * 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. * * 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. * */ #include "SRTextView.h" #include "SRFramework/SRApp.h" #include using namespace SRFramework; BOOL SRTextView::s_fRegistered = false; VOID LOGTAG(const char *tag) { #if 0 FILE *fp = fopen("\\Storage Card\\tags.txt", "a"); fprintf(fp, "%s\n", tag); fclose(fp); #endif } SRTextView::SRTextView(BOOL fSubWindow) : m_fDragging(FALSE) , m_nTop(0) , m_nDragStart(0) , m_dwBuffSize(BTEXT_BUFF_INC) , m_dwBuffEnd(0) , m_fPreRendered(FALSE) , m_nRollVelocity(FALSE) , m_dwTrueStartPos(0) , m_dwTrueStartTime(0) , m_hCurFont(NULL) , m_dwWordIndex(0) , m_fAppended(FALSE) , m_nLineX(0) , m_nLineY(0) , m_dwLineNum(0) , m_wStatusBarHeight(0) { m_wcsClassName = "SRTextView"; m_wcsWindowName = "SwordReader"; m_hInstance = SRFramework::SRApp::GetInstanceHandle(); memset(&m_rsState, 0, sizeof(m_rsState)); m_ptLastClick.x = 0; m_ptLastClick.y = 0; // Init the current font HFONT hSystemVariableFont = (HFONT ) GetStockObject(SYSTEM_FONT); GetObject(hSystemVariableFont, sizeof(LOGFONT), &m_lfCurFont); m_lfCurFont.lfHeight = -11; // TODO Detect compiler and determine if we can use CLEARTYPE_QUALITY m_lfCurFont.lfQuality = ANTIALIASED_QUALITY/*CLEARTYPE_QUALITY*/; lstrcpy(m_lfCurFont.lfFaceName, _T("Veranda")); try{ m_lpszBuff = new TCHAR[m_dwBuffSize]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < BTEXT_FONT_CACHE_MAX; i++) m_hFontCache[i] = 0; //InitializeCriticalSection(&m_csLoadText); } SRTextView::~SRTextView() { if(m_lpszBuff) delete [] m_lpszBuff; //DeleteCriticalSection(&m_csLoadText); DestroyWindow(m_hWnd); } BOOL SRTextView::Create(SRWnd *pParentWnd, RECT bounds) { if(!Register()) return FALSE; if(!SRWnd::Create(m_wcsClassName,m_wcsWindowName,WS_CHILD, bounds, pParentWnd, NULL, m_hInstance)) return FALSE; return TRUE; } BOOL SRTextView::Register() { // Register window class... WNDCLASS wc; if(s_fRegistered) return TRUE; wc.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC; wc.lpfnWndProc = (WNDPROC) MessageRoute; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = m_hInstance; wc.hIcon = NULL; wc.hCursor = 0; wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = m_wcsClassName.w_str(); if(RegisterClass(&wc) == 0) return FALSE; s_fRegistered = TRUE; return TRUE; } VOID SRTextView::Show() { ShowWindow(m_hWnd, SW_SHOW); // FOR COMPARATIVE PERFORMANCE TESTING... // REMOVE ONCE INTEGRATED //m_fPreRendered = FALSE; UpdateWindow(); } VOID SRTextView::Hide() { ShowWindow(m_hWnd, SW_HIDE); } BOOL SRTextView::OnLButtonDown(WORD fwKeys, INT xPos, INT yPos) { this->SetTapState(TRUE, xPos, yPos); return TRUE; } BOOL SRTextView::OnLButtonUp(WORD fwKeys, INT xPos, INT yPos) { this->SetTapState(FALSE, xPos, yPos); return TRUE; } VOID SRTextView::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint) { m_fPreRendered = false; SRWnd::MoveWindow(x, y, nWidth, nHeight, TRUE); } VOID SRTextView::MoveWindow(LPCRECT lpRect,BOOL bRepaint) { MoveWindow(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, bRepaint); } BOOL SRTextView::OnMouseMove(WORD fwKeys, INT xPos, INT yPos) { this->DragScreenToPoint(yPos); return TRUE; } INT SRTextView::GetHTMLTags(RenderState& rsState, DWORD &dwWordIndex) { BOOL fDone = FALSE; DWORD dwBegin; DWORD dwEnd; DWORD dwIdentifyResult; INT nLineBreaks = 0; while(!fDone){ GetSpaces(rsState, dwWordIndex); dwBegin = dwWordIndex + 1; if(m_lpszBuff[dwWordIndex] != 0 && m_lpszBuff[dwWordIndex] == '<'){ while(m_lpszBuff[dwWordIndex] != 0 && m_lpszBuff[dwWordIndex] != '>') dwWordIndex++; dwEnd = dwWordIndex - dwBegin; dwIdentifyResult = IndentifyTag(rsState, dwBegin, dwEnd); dwWordIndex++; switch(dwIdentifyResult){ case BTEXT_HTML_B_BEG: rsState.m_wBoldState++; break; case BTEXT_HTML_B_END: if(rsState.m_wBoldState) rsState.m_wBoldState--; break; case BTEXT_HTML_BR: nLineBreaks++; break; case BTEXT_HTML_I_BEG: rsState.m_wItalicState++; break; case BTEXT_HTML_I_END: if(rsState.m_wItalicState) rsState.m_wItalicState--; break; case BTEXT_HTML_A_BEG: rsState.m_wAState++; rsState.m_dwlWordNum++; rsState.m_dwSubWordNum = 0; break; case BTEXT_HTML_A_END: if(rsState.m_wAState) rsState.m_wAState--; rsState.m_dwlWordNum++; rsState.m_dwSubWordNum = 0; break; case BTEXT_HTML_SUB_BEG: rsState.m_wSubState++; break; case BTEXT_HTML_SUB_END: if(rsState.m_wSubState) rsState.m_wSubState--; break; case BTEXT_HTML_SUP_BEG: rsState.m_wSuperState++; break; case BTEXT_HTML_SUP_END: if(rsState.m_wSuperState) rsState.m_wSuperState--; break; case BTEXT_HTML_P_BEG: rsState.m_wParagraphState++; nLineBreaks++; break; case BTEXT_HTML_P_END: if(rsState.m_wParagraphState) rsState.m_wParagraphState--; nLineBreaks++; break; } }else fDone = TRUE; } return nLineBreaks; } DWORD SRTextView::IndentifyTag(RenderState& rsState,DWORD dwWordIndex,DWORD dwWordEnd) { DWORD dwMask = 0x00000000; DWORD dwResult = 0x00000000; DWORD dwLen = 0; DWORD dwOrigIndex = dwWordIndex; DWORD dwSubIndex = 0; if(!dwWordEnd) return BTEXT_HTML_ILLEGAL_TAG_FORMAT; // There is nothing to do here. if((dwWordIndex - dwOrigIndex) >= dwWordEnd) return BTEXT_HTML_ILLEGAL_TAG_FORMAT; if(m_lpszBuff[dwWordIndex] == '/'){ dwMask = 0xFFFFFFFF; dwWordIndex++; } if((dwWordIndex - dwOrigIndex) >= dwWordEnd) return BTEXT_HTML_ILLEGAL_TAG_FORMAT; // Get the token while((dwWordIndex - dwOrigIndex) + dwLen < dwWordEnd && m_lpszBuff[dwWordIndex + dwLen] != ' '){ dwLen++; } // Determine the token switch(dwLen){ case 0: dwResult = BTEXT_HTML_ILLEGAL_TAG_FORMAT; break; case 1: if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"a", 1) == 0){ dwSubIndex = dwWordIndex + 1; while(m_lpszBuff[dwSubIndex] == ' ') dwSubIndex++; if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"name=\"", 6) == 0){ dwSubIndex += 6; rsState.m_wVerseNum = (short)_wtoi(&m_lpszBuff[dwSubIndex]); }else if(dwMask == 0){ // This is not an '/a' while(m_lpszBuff[dwSubIndex] == ' ') dwSubIndex++; if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"href=\"", 6) == 0){ dwSubIndex += 6; rsState.m_lpszHref = &m_lpszBuff[dwSubIndex]; rsState.m_dwHrefLen = 0; while(m_lpszBuff[dwSubIndex++] != '\"') rsState.m_dwHrefLen++; } dwResult = dwMask ^ BTEXT_HTML_A_BEG; }else dwResult = dwMask ^ BTEXT_HTML_A_BEG; }else if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"b", 1) == 0){ dwResult = dwMask ^ BTEXT_HTML_B_BEG; }else if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"i", 1) == 0){ dwResult = dwMask ^ BTEXT_HTML_I_BEG; }else if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"p", 1) == 0){ dwResult = dwMask ^ BTEXT_HTML_P_BEG; } break; case 2: if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"br", 2) == 0){ dwResult = BTEXT_HTML_BR; } break; case 3: if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"sup", 3) == 0){ dwResult = dwMask ^ BTEXT_HTML_SUP_BEG; }else if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"sub", 3) == 0){ dwResult = dwMask ^ BTEXT_HTML_SUB_BEG; }else if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"br/", 3) == 0){ dwResult = BTEXT_HTML_BR; } break; case 4: if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"font", 4) == 0){ dwResult = dwMask ^ BTEXT_HTML_FONT_BEG; if(!dwMask){ // Found a m_lpftNext = new SRFontTagItem(); }catch(char * str){ printf(str); } rsState.m_lpftFontTagTail = rsState.m_lpftFontTagTail->m_lpftNext; } dwSubIndex += dwWordIndex + 4; while(m_lpszBuff[dwSubIndex] != '>'){ while(m_lpszBuff[dwSubIndex] == ' ') dwSubIndex++; if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"size=\"", 6) == 0){ dwSubIndex += 6; rsState.m_lpftFontTagTail->m_siRelFontSize = (short)_wtoi(&m_lpszBuff[dwSubIndex]); while(m_lpszBuff[dwSubIndex] != '\"') dwSubIndex++; dwSubIndex++; }else if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"color=\"", 7) == 0){ dwSubIndex += 7; if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"red\"", 4) == 0){ dwSubIndex += 4; rsState.m_lpftFontTagTail->m_crFontColor = 0x000000FF; }else if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"green\"", 6) == 0){ dwSubIndex += 6; rsState.m_lpftFontTagTail->m_crFontColor = 0x0000FF00; }else if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"blue\"", 5) == 0){ dwSubIndex += 5; rsState.m_lpftFontTagTail->m_crFontColor = 0x00FF0000; }else if(wcsnicmp(&m_lpszBuff[dwSubIndex], L"black\"", 6) == 0){ dwSubIndex += 6; rsState.m_lpftFontTagTail->m_crFontColor = 0x00000000; } }else dwSubIndex++; } }else{ // Its a m_lpftNext; } delete next; rsState.m_lpftFontTagTail = prev; } } } } break; case 5: if(wcsnicmp(&m_lpszBuff[dwWordIndex], L"small", 5) == 0){ dwResult = dwMask ^ BTEXT_HTML_SMALL_BEG; } break; default: dwResult = 0; } return dwResult; } INT SRTextView::NextWord(RenderState& rsState,DWORD dwWordIndex, DWORD &dwWordEnd) { dwWordEnd = 0; // Special Entities are: // > < & // and others (later) rsState.m_wTotalSpclEnt = 0; if(dwWordIndex > m_dwBuffEnd) return BTEXT_BUFFER_END; while(m_lpszBuff[dwWordIndex + dwWordEnd] != 0 && m_lpszBuff[dwWordIndex + dwWordEnd] != ' ' && m_lpszBuff[dwWordIndex + dwWordEnd] != '\n' && m_lpszBuff[dwWordIndex + dwWordEnd] != '<'){ if(m_lpszBuff[dwWordIndex + dwWordEnd] == '&'){ dwWordEnd++; while(m_lpszBuff[dwWordIndex + dwWordEnd] != ';'){ dwWordEnd++; rsState.m_wTotalSpclEnt++; } } dwWordEnd++; if(dwWordIndex + dwWordEnd > m_dwBuffEnd) break; } if(m_lpszBuff[dwWordIndex + dwWordEnd] == '\n') return BTEXT_NEWLINE_ENCOUNTERED; else if(m_lpszBuff[dwWordIndex + dwWordEnd] == '<') return BTEXT_HTML_OPEN_BRACKET; return BTEXT_SPACE_ENCOUNTERED; } VOID SRTextView::Clear() { m_BTLines.ClearLines(); m_BTLines.InitLines(0); if(m_lpszBuff) delete [] m_lpszBuff; m_lpszBuff = NULL; m_dwBuffEnd = 0; m_dwBuffSize = 0; m_nTop = 0; m_fPreRendered = FALSE; m_fAppended = FALSE; m_dwWordIndex = 0; } VOID SRTextView::PreRenderBuff() { HDC hdc = GetDC(m_hWnd); PreRenderBuff(hdc); ReleaseDC(m_hWnd, hdc); } void SRTextView::RefreshWindow() { PreRenderBuff(); SRWnd::RefreshWindow(); } VOID SRTextView::PreRenderBuff(HDC hdc) { SRTextWord thisWord; RECT rectDims; RECT clientRect = GetBounds(); rectDims.top = 0; rectDims.left = 0; rectDims.right = 0; rectDims.bottom = 0; INT nLineH = (INT)(BTEXT_LINE_SPACING*(FLOAT)DrawText(hdc, L" ", 1, &rectDims, DT_CALCRECT | DT_LEFT | DT_BOTTOM | DT_SINGLELINE)); DWORD dwWordEnd = 0; INT nSpaceWidth = rectDims.right - rectDims.left; INT nScreenWidth= (clientRect.right - clientRect.left) - BTEXT_MARGIN; DWORD dwResult = 0; INT nAddLines = 0; DWORDLONG dwlFontState= 0; DWORDLONG dwlFontColor; DWORDLONG dwlFontHeight; if(m_dwBuffEnd == 0) return; //EnterCriticalSection(EnterCriticalSection(&m_csLoadText);m_csLoadText); if(!m_fAppended){ memset(&m_rsState, 0, sizeof(m_rsState)); m_nLineX = BTEXT_MARGIN; m_nLineY = BTEXT_MARGIN; m_dwLineNum = 0; m_dwWordIndex = 0; m_BTLines.ClearLines(); m_BTLines.InitLines(nLineH); } int tmpI = 0; while(TRUE){ thisWord.Clear(); m_rsState.m_space_encountered = FALSE; GetSpaces(m_rsState, m_dwWordIndex); nAddLines = GetHTMLTags(m_rsState, m_dwWordIndex); if(nAddLines){ m_dwLineNum += nAddLines; m_nLineX = BTEXT_MARGIN; m_nLineY += nAddLines * nLineH; m_rsState.m_space_encountered = FALSE; } GetSpaces(m_rsState, m_dwWordIndex); if(m_rsState.m_space_encountered){ m_nLineX += nSpaceWidth; m_rsState.m_space_encountered = FALSE; m_rsState.m_dwlWordNum++; m_rsState.m_dwSubWordNum = 0; }else m_rsState.m_dwSubWordNum++; dwResult = NextWord(m_rsState, m_dwWordIndex, dwWordEnd); if(dwResult == BTEXT_BUFFER_END || dwWordEnd == 0){ break; } rectDims.left = m_nLineX; rectDims.right = m_nLineX; // Font... SRFontTagItem *tag = m_rsState.m_lpftFontTagHead; dwlFontColor = BTEXT_DEFAULT_FONT_COLOR; dwlFontHeight = abs(BTEXT_DEFAULT_FONT_HEIGHT); while(tag){ if(tag->m_crFontColor != BTEXT_FONT_NOT_A_COLOR) dwlFontColor = tag->m_crFontColor; dwlFontHeight += tag->m_siRelFontSize; tag = tag->m_lpftNext; } if(m_rsState.m_wSubState || m_rsState.m_wSuperState) dwlFontHeight -= 3; dwlFontState = ((DWORDLONG)(dwlFontColor << 40) & 0xFFFFFF0000000000) | ((DWORDLONG)(dwlFontHeight << 32) & 0x000000FF00000000) | (m_rsState.m_wAState > 0 ? BTEXT_HTML_A_BEG : 0) | (m_rsState.m_wBoldState > 0 ? BTEXT_HTML_B_BEG : 0) | (m_rsState.m_wItalicState > 0 ? BTEXT_HTML_I_BEG : 0); SetFont(hdc, dwlFontState); rectDims.top = m_nLineY; rectDims.bottom = m_nLineY; // Set the properties for this word. thisWord.m_rect = rectDims; thisWord.m_dwlfFontState = dwlFontState; thisWord.m_lpszHref = m_rsState.m_lpszHref; thisWord.m_dwHrefLen = m_rsState.m_dwHrefLen; thisWord.m_dwlWordNum = m_rsState.m_dwlWordNum; thisWord.m_dwSubWordNum = m_rsState.m_dwSubWordNum; thisWord.m_wVerseNum = m_rsState.m_wVerseNum; // Determine if we encountered any special entities... if(m_rsState.m_wTotalSpclEnt > 0){ InterpretSpecialEntities(m_dwWordIndex, dwWordEnd, &thisWord); }else{ thisWord.m_fOwner = FALSE; thisWord.m_lpszWord = (TCHAR*)((DWORD)&m_lpszBuff[m_dwWordIndex] - (DWORD)&m_lpszBuff[0]); thisWord.m_dwWordLen = dwWordEnd; } // Calc the text rect DrawText(hdc, (TCHAR*)((DWORD)thisWord.m_lpszWord + (thisWord.m_fOwner ? 0 : (DWORD)&m_lpszBuff[0])), thisWord.m_dwWordLen, &thisWord.m_rect, DT_CALCRECT | DT_LEFT | DT_BOTTOM | DT_SINGLELINE); if(m_rsState.m_wSuperState){ thisWord.m_rect.top -= 1; // TODO: Determine the '1' value based upon the font size thisWord.m_rect.bottom = thisWord.m_rect.top + nLineH + 1; }else if(m_rsState.m_wSubState){ thisWord.m_rect.top = 2*thisWord.m_rect.top + nLineH - thisWord.m_rect.bottom + 1; // TODO: Same as above... }else{ thisWord.m_rect.top = 2*thisWord.m_rect.top + nLineH - thisWord.m_rect.bottom; // Baseline justify } if(thisWord.m_rect.right > (nScreenWidth - BTEXT_MARGIN)){ // Place on next line. But... // We may need to move the prefix of this word down, thus we need to adjust the left // edge of this word to make room. int adjustedMargin = BTEXT_MARGIN; if(thisWord.m_dwSubWordNum > 0) adjustedMargin = m_BTLines.ValidateNewLineWord(thisWord.m_dwlWordNum,nLineH, BTEXT_MARGIN); m_dwLineNum++; thisWord.m_rect.right = thisWord.m_rect.right - thisWord.m_rect.left + adjustedMargin; thisWord.m_rect.left = adjustedMargin; thisWord.m_rect.top += nLineH; thisWord.m_rect.bottom += nLineH; m_nLineY += nLineH; } // Store the line to be rendered later. m_BTLines.AddWordToLine(m_dwLineNum, thisWord); // Prep for next iteration if(m_rsState.m_space_encountered) m_nLineX = thisWord.m_rect.right + nSpaceWidth; else m_nLineX = thisWord.m_rect.right; //curWord++; m_dwWordIndex += dwWordEnd; } if(m_rsState.m_lpftFontTagHead) // Properly formatted text would never need this, but that cannot be assumed. delete m_rsState.m_lpftFontTagHead; // Do not delete Tail it is a convenience pointer... m_dwWordIndex = m_dwWordIndex; m_fAppended = FALSE; m_fPreRendered = TRUE; //DeleteCriticalSection(&m_csLoadText); } // I wanted to use the same font setter for both prerendering and rendering // so I wrote this method. The first if statement will try to determine if // we can safely skip this call since it is a rather expensive call // after several thousand words are pre-rendered and rendered. But, // If the text is already pre-rendered and we are calling this during // the actual rendering stage we are sure we want to make this call already // and thus the !m_bnRendering statement ensures we will change the font - otherwise // certain lines get the wrong fonts applied to them. VOID SRTextView::SetFont(HDC hdc, DWORDLONG dwlFontState) { INT lfWeight = dwlFontState & BTEXT_HTML_B_BEG ? FW_BOLD : FW_NORMAL; UCHAR lfItalic = (char)((dwlFontState & BTEXT_HTML_I_BEG) > 0 ? 1 : 0); UCHAR lfUnderline = (char)((dwlFontState & BTEXT_HTML_A_BEG) > 0 ? 1 : 0); INT lfHeight = -1*((int)(0x000000FF & ((dwlFontState & 0x000000FF00000000) >> 32))); HGDIOBJ oldFont = 0; // Compute the font cache index... DWORD dwFontCacheIndex = (DWORD) ((dwlFontState & (BTEXT_HTML_B_BEG | BTEXT_HTML_I_BEG | BTEXT_HTML_A_BEG)) | ((dwlFontState & 0x000000FF00000000) >> 24)); if(!m_fPreRendered && m_lfCurFont.lfWeight == lfWeight && m_lfCurFont.lfItalic == lfItalic && m_lfCurFont.lfHeight == lfHeight && m_lfCurFont.lfUnderline == lfUnderline) return; m_lfCurFont.lfWeight = lfWeight; m_lfCurFont.lfItalic = lfItalic; m_lfCurFont.lfHeight = lfHeight; m_lfCurFont.lfUnderline = lfUnderline; if(dwFontCacheIndex < 0 || dwFontCacheIndex >= BTEXT_FONT_CACHE_MAX){ ::MessageBox(m_hWnd, L"Error dwFontCacheIndex is out of bounds. This is never supposed to happen. Font change ignored.", L"Error index out of range", MB_OK | MB_ICONERROR); return; } if(!m_hFontCache[dwFontCacheIndex]){ m_hFontCache[dwFontCacheIndex] = CreateFontIndirect(&m_lfCurFont); } SelectObject(hdc, m_hFontCache[dwFontCacheIndex]); /* m_hCurFont = CreateFontIndirect(&m_lfCurFont); oldFont = SelectObject(hdc, m_hCurFont); if(oldFont) DeleteObject(oldFont); */ } BOOL SRTextView::GetSpaces(RenderState& rsState, DWORD &dwWordIndex){ DWORD i = 0; while(m_lpszBuff[dwWordIndex] != '\0' && (m_lpszBuff[dwWordIndex] == ' ' || (m_lpszBuff[dwWordIndex] == '&' && wcsnicmp(&m_lpszBuff[dwWordIndex], L" ", 6) == 0)) ){ dwWordIndex += m_lpszBuff[dwWordIndex] == '&' ? 6 : 1; // Did we find a   then we need to increment that much. i++; } if(i) rsState.m_space_encountered = TRUE; return i > 0; } VOID SRTextView::InterpretSpecialEntities(DWORD dwWordIndex, DWORD dwWordLen, SRTextWord *pbtWord) { DWORD dwSubIndex = 0; DWORD dwBufIndex = 0; TCHAR szBuf[256] = {0}; while(dwSubIndex < dwWordLen){ if(m_lpszBuff[dwWordIndex + dwSubIndex] == '&'){ dwSubIndex++; if(wcsncmp(&m_lpszBuff[dwWordIndex + dwSubIndex], L"lt;",3) == 0){ szBuf[dwBufIndex++] = '<'; dwSubIndex += 3; }else if(wcsncmp(&m_lpszBuff[dwWordIndex + dwSubIndex], L"gt;",3) == 0){ szBuf[dwBufIndex++] = '>'; dwSubIndex += 3; }else if(wcsncmp(&m_lpszBuff[dwWordIndex + dwSubIndex], L"amp;",4) == 0){ szBuf[dwBufIndex++] = '&'; dwSubIndex += 4; }else if(wcsncmp(&m_lpszBuff[dwWordIndex + dwSubIndex], L"nbsp;",5) == 0){ szBuf[dwBufIndex++] = ' '; dwSubIndex += 5; }else{ // I have no idea what this is, simply display it... szBuf[dwBufIndex++] = '&'; dwSubIndex++; } } else szBuf[dwBufIndex++] = m_lpszBuff[dwWordIndex + dwSubIndex++]; } pbtWord->OwnWord(szBuf, dwBufIndex); } BOOL SRTextView::OnPaint() { PAINTSTRUCT ps; HDC hdc = BeginPaint(m_hWnd, &ps); COLORREF crColor = 0x00000000; COLORREF crOldColor = SetTextColor(hdc, crColor); INT nSavedDC = SaveDC(hdc); RECT rectDims, clientRect; rectDims.top = 0; rectDims.left = 0; rectDims.right = 0; rectDims.bottom = 0; GetClientRect(m_hWnd, &clientRect); INT nOldBkMode = SetBkMode(hdc, TRANSPARENT); // *TODO* The next line should not be needed, but for some strange reason its not // erasing the background. I need to look into this more. FillRect(hdc, &ps.rcPaint,(HBRUSH)GetStockObject(WHITE_BRUSH)); if(!m_fPreRendered){ PreRenderBuff(hdc); } for(unsigned int line = 0; line <= m_BTLines.m_dwLastLine; line++){ SRTextWord *pTWord = &m_BTLines.m_lpLines[line]; if((pTWord->m_rect.top + m_BTLines.m_nLineH + m_nTop) <= ps.rcPaint.top) continue; // this line is above the region we are interested in painting else if(pTWord->m_rect.top + m_nTop > ps.rcPaint.bottom) break; // We are done the rest of the lines are below the region we care about. while(pTWord){ if(!pTWord->m_lpPrevWord || pTWord->m_dwlfFontState != pTWord->m_lpPrevWord->m_dwlfFontState){ if(pTWord->m_dwlfFontState & BTEXT_HTML_A_BEG){ // links get priority coloring crColor = COLORREF(0x00FF0000); }else{ crColor = COLORREF(((pTWord->m_dwlfFontState >> 40) & 0x00FFFFFF)); } SetTextColor(hdc, crColor); SetFont(hdc, pTWord->m_dwlfFontState); } ExtTextOut(hdc,pTWord->m_rect.left, pTWord->m_rect.top + m_nTop, NULL, NULL, (TCHAR*)((DWORD)pTWord->m_lpszWord + (pTWord->m_fOwner ? 0 : (DWORD)&m_lpszBuff[0])), pTWord->m_dwWordLen, NULL); pTWord = pTWord->m_lpNextWord; } } SetTextColor(hdc, crOldColor); ClearFontCache(hdc); RestoreDC(hdc, nSavedDC); SetBkMode(hdc,nOldBkMode); EndPaint(m_hWnd, &ps); return 0; } VOID SRTextView::ClearFontCache(HDC hdc) { // Clear the font from device context so it can be deleted with the rest... SelectObject(hdc, GetStockObject(SYSTEM_FONT)); for(DWORD i = 0; i < BTEXT_FONT_CACHE_MAX; i++){ if(m_hFontCache[i]){ DeleteObject(m_hFontCache[i]); m_hFontCache[i] = 0; } } } VOID SRTextView::SetTapState(BOOL fMouseDown, INT xPos, INT yPos) { POINT pt = {xPos, yPos}; DWORD dwDiffTime = 0; ScreenToClient(m_hWnd, &pt); // Test if the mouse moved more than some threshold value. BOOL mouseMoved = (abs(m_ptLastClick.y - pt.y) > 5) || (abs(m_ptLastClick.x - pt.x) > 5); if(!m_fDragging){ m_nDragStart = pt.y; } // If we are about to go into a dragging mode, // or we are about to leave a dragging mode, // we should calculate a rolling velocity. if(fMouseDown && !m_fDragging){ m_dwTrueStartPos = pt.y; m_dwTrueStartTime = GetTickCount(); m_nRollVelocity = 0; }else if(!fMouseDown && m_fDragging){ dwDiffTime = GetTickCount() - m_dwTrueStartTime; if(dwDiffTime > 200) m_nRollVelocity = 0; else m_nRollVelocity = (80*(int)(pt.y - m_dwTrueStartPos))/((int)dwDiffTime); } if(fMouseDown){ m_dwClickTime = GetTickCount(); m_fDragging = TRUE; SetCapture(m_hWnd); }else{ m_fDragging = FALSE; ReleaseCapture(); } INT test, assigned; if(!fMouseDown){ test = (GetTickCount() - m_dwClickTime) < 120; } // If we are absolutely positive the user wants to click... if(!fMouseDown && !mouseMoved && ((GetTickCount() - m_dwClickTime) < 120)){ GetWordAtPoint(xPos, yPos); assigned = test; } if(!fMouseDown) assigned = test; m_ptLastClick = pt; // If there is a velocity set this will use it up, otherwise it drops out nicely. RollTillStopped(); } VOID SRTextView::GetWordAtPoint(INT xPos, INT yPos) { POINT pt = {xPos, yPos}; SRTextWord *pbtWord; DWORD dwLineNum = 0; static TCHAR szWordFound[SR_MAX_MISC_STRING_LEN] = {0}; while(dwLineNum <= m_BTLines.m_dwLastLine && (m_BTLines.m_lpLines[dwLineNum].m_rect.bottom + m_nTop) < pt.y) dwLineNum++; if(dwLineNum > m_BTLines.m_dwLastLine) return; pbtWord = &m_BTLines.m_lpLines[dwLineNum]; while(pbtWord->m_lpszWord && pbtWord->m_rect.right < pt.x) pbtWord = pbtWord->m_lpNextWord; if(pbtWord->m_lpszWord){ if((pbtWord->m_dwlfFontState & BTEXT_HTML_A_BEG) && pbtWord->m_lpszHref){ wcsncpy(szWordFound, pbtWord->m_lpszHref, pbtWord->m_dwHrefLen); szWordFound[pbtWord->m_dwHrefLen] = 0; ::SendMessage(m_pParentWnd->GetWindowHandle(), WM_COMMAND, (SR_MSRC_TEXT << 16) | SR_HYPERLINK_CLICKED, (LPARAM)szWordFound); }/*else{ wcsncpy(szWordFound, (TCHAR*)((DWORD)pbtWord->m_lpszWord + (DWORD)(pbtWord->m_fOwner ? 0 : &m_lpszBuff[0])), pbtWord->m_dwWordLen); } MessageBox(m_hWnd, szWordFound, L"Found...", MB_OK); */ } } VOID SRTextView::DragScreenToPoint(INT yPos) { POINT pt; RECT rectUpdateRect; INT nDragDist = 0; RECT rectScroll; GetClientRect(m_hWnd, &rectScroll); rectScroll.bottom -= m_wStatusBarHeight; if(m_fDragging){ pt.x = 0; pt.y = yPos; ScreenToClient(m_hWnd, &pt); nDragDist = pt.y - m_nDragStart; m_nTop += nDragDist; // Prevent rolling past top or bottom if(m_nTop > 0){ nDragDist -= m_nTop; m_nTop = 0; }else if(m_nTop < -m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom){ // nDragDist does not matter here because we are Scrolling a white window, // Keeping track of m_nTop is important as it is our reference point for // scrolling later m_nTop = -(m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom); } ScrollWindowEx(m_hWnd, 0, nDragDist, &rectScroll, NULL, NULL, &rectUpdateRect, NULL); // Invalidate the status bar area as well, otherwise it leaves artifacts. rectUpdateRect.top -= m_wStatusBarHeight; InvalidateRect(m_hWnd, &rectUpdateRect, TRUE); ::UpdateWindow(m_hWnd); m_nDragStart = pt.y; } SendVerseChangedMessage(); } VOID SRTextView::RollTillStopped() { RECT rectUpdateRect; BOOL fDone = FALSE; INT nDirection = m_nRollVelocity > 0 ? 1 : -1; RECT rectScroll; GetClientRect(m_hWnd, &rectScroll); rectScroll.bottom -= m_wStatusBarHeight; while(!fDone && nDirection*m_nRollVelocity > 0){ m_nTop += m_nRollVelocity; // Prevent rolling beyond the top of the page. if(m_nTop > 0){ m_nRollVelocity -= m_nTop; m_nTop = 0; fDone = TRUE; }else if(m_nTop < -m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom){ // dist does not matter here because we are Scrolling a white window, // Keeping track of m_nTop is important as it is our reference point for // scrolling later m_nTop = -(m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom); fDone = TRUE; } ScrollWindowEx(m_hWnd, 0, m_nRollVelocity, &rectScroll, NULL, NULL, &rectUpdateRect, NULL); // Invalidate the status bar area as well, otherwise it leaves artifacts. rectUpdateRect.top -= m_wStatusBarHeight; InvalidateRect(m_hWnd, &rectUpdateRect, TRUE); ::UpdateWindow(m_hWnd); m_nRollVelocity = (m_nRollVelocity - m_nRollVelocity/6) - ((nDirection*m_nRollVelocity < 10) ? nDirection : 0); // the nDirection part ensures continual degrading in the velocity } SendVerseChangedMessage(); } VOID SRTextView::SendVerseChangedMessage() { ::SendMessage(m_pParentWnd->GetParentWindow()->GetWindowHandle(),WM_COMMAND, (SR_MSRC_TEXT << 16) | SR_SETVERSE, GetVerseNum()); } VOID SRTextView::ScrollFullPage(INT nDirection) { RECT rectUpdateRect; RECT rectClientRect; INT nDist = 0; BOOL fDone = FALSE; GetClientRect(m_hWnd,&rectClientRect); INT nRollBy = rectClientRect.bottom - rectClientRect.top; INT nCalcDist = 0; CONST INT UPPER_SB = 10; // Upper bound on the scrolling per iteration. RECT rectScroll = rectClientRect; rectScroll.bottom -= m_wStatusBarHeight; nDirection = (nDirection > 0) ? 1 : -1; // normalize nDirection // Adjust bottom to not scroll on half a line. for(unsigned int line = 0; line <= m_BTLines.m_dwLastLine; line++){ SRTextWord *pTWord = &m_BTLines.m_lpLines[line]; if(nDirection == -1){ if((pTWord->m_rect.top + m_BTLines.m_nLineH + m_nTop) > rectClientRect.bottom){ nRollBy = (pTWord->m_rect.top + m_nTop) - rectClientRect.top; break; } }else if(nDirection == 1){ if((pTWord->m_rect.top + m_nTop) > (rectClientRect.top - nRollBy)){ nRollBy = rectClientRect.bottom - ((pTWord->m_rect.top + m_nTop) + nRollBy); break; } } } for(INT i = 0;!fDone && i < nRollBy; i+=nCalcDist){ nCalcDist = UPPER_SB - (int)(UPPER_SB*((float)i/(float)nRollBy)) + 1; nDist = nDirection*nCalcDist; m_nTop += nDist; // Prevent rolling beyond the top of the page. if(m_nTop > 0){ nDist -= m_nTop; m_nTop = 0; fDone = TRUE; }else if(m_nTop < -m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom){ // nDist does not matter here because we are Scrolling a white window, // Keeping track of m_nTop is important as it is our reference point for // scrolling later m_nTop = -(m_BTLines.m_lpLines[m_BTLines.m_dwLastLine].m_rect.bottom); fDone = TRUE; } ScrollWindowEx(m_hWnd, 0, nDist, &rectScroll, NULL, NULL, &rectUpdateRect, NULL); // Invalidate the status bar area as well, otherwise it leaves artifacts. rectUpdateRect.top -= m_wStatusBarHeight; InvalidateRect(m_hWnd, &rectUpdateRect, TRUE); ::UpdateWindow(m_hWnd); } SendVerseChangedMessage(); } VOID SRTextView::ScrollToPosition(INT yPos, BOOL fSendMessage) { POINT pt; RECT rectUpdateRect; INT nDragDist = 0; RECT rectScroll; GetClientRect(m_hWnd, &rectScroll); rectScroll.bottom -= m_wStatusBarHeight; if( yPos >= 0){ pt.x = 0; pt.y = yPos; nDragDist = m_nTop - pt.y; m_nTop = -pt.y; ScrollWindowEx(m_hWnd, 0, nDragDist, &rectScroll, NULL, NULL, &rectUpdateRect, NULL); // Invalidate the status bar area as well, otherwise it leaves artifacts. rectUpdateRect.top -= m_wStatusBarHeight; InvalidateRect(m_hWnd, &rectUpdateRect, TRUE); ::UpdateWindow(m_hWnd); } if(fSendMessage) SendVerseChangedMessage(); } VOID SRTextView::ScrollToVerse(WORD wVerseNum) { if(wVerseNum == 1) ScrollToPosition(0, TRUE); // Addresses a problem when a heading exists prior to verse 1. Temp fix (dctrotz) else ScrollToPosition(m_BTLines.GetVersePosition(wVerseNum), TRUE); } INT SRTextView::GetVerseNum() { return m_BTLines.GetVerseAt(m_nTop); } BOOL SRTextView::AddText(const TCHAR *szText, DWORD dwSize) { DWORD i = 0; INT tag_pairs = 0; DWORD dwOldEnd = m_dwBuffEnd; TCHAR *lpszTmp = NULL; if(m_dwBuffEnd != 0 && m_fPreRendered) // not a new buffer... m_fAppended = TRUE; if(!m_dwBuffSize){ m_dwBuffSize = BTEXT_BUFF_INC > dwSize ? BTEXT_BUFF_INC : dwSize; try{ m_lpszBuff = new TCHAR[m_dwBuffSize]; }catch(char * str){ printf(str); } } // Do we need to make room for the new szText if((m_dwBuffEnd + dwSize) > m_dwBuffSize){ DWORD dwNewSize = m_dwBuffSize + (BTEXT_BUFF_INC > dwSize ? BTEXT_BUFF_INC : dwSize); try{ lpszTmp = new TCHAR[dwNewSize]; }catch(char * str){ printf(str); } for(i = 0; i < m_dwBuffSize; i++){ lpszTmp[i] = m_lpszBuff[i]; } delete [] m_lpszBuff; m_lpszBuff = lpszTmp; m_dwBuffSize = dwNewSize; } // If this is a new buffer we need to // discard HTML header stuff by pointing to the beginning of the i = 0; if(m_dwBuffEnd == 0){ do{ while(szText[i] != '<' && i < dwSize) i++; if(wcsnicmp(&szText[i], L"", 6) == 0){ i += 6; break; } while(szText[i++] != '>' && i < dwSize); if(i > dwSize){ i = 0; break;// This buffer does not appear to have a tag, let's just pretend it did. } }while(TRUE); } // Copy it in, and ignore the closing tags, if any for(; i < dwSize; i++){ /* // We arbitrarily choose to start looking at the last 100 characters for the tail end // of this HTML stream. If we fail to chop it off its not the end of the world, but // it would be nice to get out of the way now. if(dwSize > 100 && i > (dwSize - 100) && wcsnicmp(&szText[i], L"", 7) == 0) break; */ if(szText[i] == 0x09) // Horizontal tab, replace with a single space. (for now at least.) m_lpszBuff[m_dwBuffEnd++] = ' '; else if(szText[i] != '\n'){ // Ignore new-lines if(szText[i] == '<') tag_pairs++; else if(szText[i] == '>') tag_pairs--; m_lpszBuff[m_dwBuffEnd++] = szText[i]; } } if(tag_pairs != 0) m_dwBuffEnd = dwOldEnd; m_lpszBuff[m_dwBuffEnd] = 0; return tag_pairs == 0; } BOOL SRTextView::UpdateWindow() { InvalidateRect(m_hWnd, NULL, TRUE); return ::UpdateWindow(m_hWnd); } BOOL SRTextView::CurrentPageFilled() { BOOL fPageFull = FALSE; RECT clientRect = GetBounds(); HDC hdc = GetDC(m_hWnd); PreRenderBuff(hdc); ReleaseDC(m_hWnd, hdc); if(m_BTLines.m_lppLinesLastWord && m_BTLines.m_lppLinesLastWord[m_BTLines.m_dwLastLine]->m_lpPrevWord) fPageFull = (m_BTLines.m_lppLinesLastWord[m_BTLines.m_dwLastLine]->m_lpPrevWord->m_rect.bottom - m_nTop) > (clientRect.bottom - clientRect.top); return fPageFull; } /***************************************************************************** * SRTextWord * *****************************************************************************/ SRTextView::SRTextWord::SRTextWord() : m_lpNextWord(0) , m_lpPrevWord(0) , m_lpszWord(NULL) , m_dwWordLen(0) , m_lpszHref(NULL) , m_dwHrefLen(0) , m_fOwner(FALSE) , m_dwlfFontState(0) , m_dwlWordNum(0) , m_dwSubWordNum(0) { m_rect.top = 0; m_rect.left = 0; m_rect.bottom = 0; m_rect.right = 0; } VOID SRTextView::SRTextWord::Clear() { if(m_fOwner && m_lpszWord) delete [] m_lpszWord; m_fOwner = FALSE; m_dwlfFontState = 0; m_dwHrefLen = 0; m_dwWordLen = 0; m_lpszWord = NULL; m_lpszHref = NULL; m_rect.top = 0; m_rect.bottom = 0; m_rect.left = 0; m_rect.right = 0; m_dwlWordNum = 0; m_dwSubWordNum = 0; m_lpNextWord = NULL; m_lpPrevWord = NULL; } VOID SRTextView::SRTextWord::OwnWord(TCHAR *lpszWord, DWORD dwWordLen) { if(m_fOwner && m_lpszWord){ delete [] m_lpszWord; } try{ m_lpszWord = new TCHAR[dwWordLen + 1]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < dwWordLen; i++) m_lpszWord[i] = lpszWord[i]; m_lpszWord[dwWordLen] = 0; m_dwWordLen = dwWordLen; m_fOwner = TRUE; } SRTextView::SRTextWord::SRTextWord(const SRTextView::SRTextWord &rhs) { m_dwlfFontState = rhs.m_dwlfFontState; m_dwHrefLen = rhs.m_dwHrefLen; m_dwWordLen = rhs.m_dwWordLen; m_lpszHref = rhs.m_lpszHref; m_lpNextWord = rhs.m_lpNextWord; m_lpPrevWord = rhs.m_lpPrevWord; m_fOwner = rhs.m_fOwner; m_rect = rhs.m_rect; m_dwlWordNum = rhs.m_dwlWordNum; m_dwSubWordNum = rhs.m_dwSubWordNum; m_wVerseNum = rhs.m_wVerseNum; if(m_fOwner && rhs.m_lpszWord){ try{ m_lpszWord = new TCHAR[m_dwWordLen + 1]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < m_dwWordLen; i++) m_lpszWord[i] = rhs.m_lpszWord[i]; m_lpszWord[m_dwWordLen] = 0; }else{ m_lpszWord = rhs.m_lpszWord; } } SRTextView::SRTextWord & SRTextView::SRTextWord::operator=(const SRTextView::SRTextWord &rhs) { if(this == &rhs) return *this; // We detect self assignment m_dwlfFontState = rhs.m_dwlfFontState; m_dwHrefLen = rhs.m_dwHrefLen; m_dwWordLen = rhs.m_dwWordLen; m_lpszHref = rhs.m_lpszHref; m_lpNextWord = rhs.m_lpNextWord; m_lpPrevWord = rhs.m_lpPrevWord; m_rect = rhs.m_rect; m_dwlWordNum = rhs.m_dwlWordNum; m_dwSubWordNum = rhs.m_dwSubWordNum; m_wVerseNum = rhs.m_wVerseNum; if(m_fOwner && m_lpszWord) delete [] m_lpszWord; m_fOwner = rhs.m_fOwner; if(m_fOwner && rhs.m_lpszWord){ try{ m_lpszWord = new TCHAR[m_dwWordLen + 1]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < m_dwWordLen; i++) m_lpszWord[i] = rhs.m_lpszWord[i]; m_lpszWord[m_dwWordLen] = 0; }else{ m_lpszWord = rhs.m_lpszWord; } return *this; } SRTextView::SRTextWord::~SRTextWord() { if(m_fOwner && m_lpszWord){ delete [] m_lpszWord; } } /***************************************************************************** * SRTextLines * *****************************************************************************/ SRTextView::SRTextLines::SRTextLines() : m_nLineH(0) , m_dwLastLine(0) { InitLines(0); } VOID SRTextView::SRTextLines::InitLines(CONST INT nLineH) { m_dwLines = BTEXT_LINE_INC; try{ m_lpLines = new SRTextWord[m_dwLines]; m_lppLinesLastWord = new SRTextWord*[m_dwLines]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < m_dwLines; i++) m_lppLinesLastWord[i] = &m_lpLines[i]; m_nLineH = nLineH; } SRTextView::SRTextLines::SRTextLines(CONST SRTextView::SRTextLines &rhs) { m_dwLastLine = rhs.m_dwLastLine; m_dwLines = rhs.m_dwLines; try{ m_lpLines = new SRTextWord[m_dwLines]; m_lppLinesLastWord = new SRTextWord*[m_dwLines]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < m_dwLines; i++){ m_lpLines[i] = rhs.m_lpLines[i]; m_lppLinesLastWord[i] = rhs.m_lppLinesLastWord[i]; } } INT SRTextView::SRTextLines::GetVersePosition(WORD wVerseNum) { for(DWORD i = 0; i < m_dwLastLine; i++){ if(!m_lppLinesLastWord[i]->m_lpPrevWord) continue; // We hit a blank line (not neccesarrily the end). if(m_lppLinesLastWord[i]->m_lpPrevWord->m_wVerseNum == wVerseNum) return m_lppLinesLastWord[i]->m_lpPrevWord->m_rect.top; } return -1; } INT SRTextView::SRTextLines::GetVerseAt(INT yPos) { for(DWORD i = 0; i < m_dwLastLine; i++){ if(!m_lppLinesLastWord[i]->m_lpPrevWord) continue; // We hit a blank line (not neccesarrily the end). if(m_lppLinesLastWord[i]->m_lpPrevWord->m_rect.top >= -yPos) return m_lppLinesLastWord[i]->m_lpPrevWord->m_wVerseNum; } return -1; } SRTextView::SRTextLines & SRTextView::SRTextLines::operator=(CONST SRTextView::SRTextLines &rhs) { if(this == &rhs) return *this; // We detect self assignment ClearLines(); m_dwLastLine = rhs.m_dwLastLine; m_dwLines = rhs.m_dwLines; try{ m_lpLines = new SRTextWord[m_dwLines]; m_lppLinesLastWord = new SRTextWord*[m_dwLines]; }catch(char * str){ printf(str); } for(DWORD i = 0; i < m_dwLines; i++){ m_lpLines[i] = rhs.m_lpLines[i]; m_lppLinesLastWord[i] = rhs.m_lppLinesLastWord[i]; } return *this; } SRTextView::SRTextLines::~SRTextLines() { ClearLines(); } VOID SRTextView::SRTextLines::ClearLines() { DWORD dwCurLine = 0; SRTextWord *pbtNextWord = NULL; SRTextWord *pbtTempWord = NULL; for(dwCurLine = 0; dwCurLine < m_dwLines; dwCurLine++){ pbtNextWord = m_lpLines[dwCurLine].m_lpNextWord; // Delete all the words in this line while(pbtNextWord){ pbtTempWord = pbtNextWord->m_lpNextWord; delete pbtNextWord; pbtNextWord = pbtTempWord; } } m_dwLastLine = 0; m_dwLines = 0; delete [] m_lpLines; m_lpLines = NULL; delete [] m_lppLinesLastWord; m_lppLinesLastWord = NULL; } INT SRTextView::SRTextLines::ValidateNewLineWord(CONST DWORDLONG dwlWordNum,CONST INT nLineH, CONST INT nMargin) { if(!m_lppLinesLastWord[m_dwLastLine]->m_lpPrevWord) return nMargin; // There is no word on the last line, nothing to do... SRTextView::SRTextWord *pbtWord = m_lppLinesLastWord[m_dwLastLine]->m_lpPrevWord; if(pbtWord->m_dwlWordNum != dwlWordNum) return nMargin; // Different word, nothing to do... pbtWord->m_rect.top += nLineH; pbtWord->m_rect.bottom += nLineH; pbtWord->m_rect.right = nMargin + (pbtWord->m_rect.right - pbtWord->m_rect.left); pbtWord->m_rect.left = nMargin; // break this word off the old line's chain SRTextView::SRTextWord *pbtChainPrev = pbtWord->m_lpPrevWord; SRTextView::SRTextWord *pbtChainNext = pbtWord->m_lpNextWord; if(pbtWord->m_lpPrevWord) pbtWord->m_lpPrevWord->m_lpNextWord = pbtChainNext; if(pbtWord->m_lpNextWord) pbtWord->m_lpNextWord->m_lpPrevWord = pbtChainPrev; m_lppLinesLastWord[m_dwLastLine] = pbtChainNext; pbtWord->m_lpPrevWord = NULL; pbtWord->m_lpNextWord = NULL; AddWordToLine(m_dwLastLine + 1, *pbtWord); delete pbtWord; return m_lppLinesLastWord[m_dwLastLine]->m_lpPrevWord->m_rect.right; } VOID SRTextView::SRTextLines::AddWordToLine(DWORD dwLine, CONST SRTextView::SRTextWord &btWord) { DWORD dwLines = 0; DWORD i = 0; SRTextWord *pbtTemp1 = NULL; SRTextWord **ppbtTemp2 = NULL; // Check if we are about to overflow the allocated lines // If so resize... if(dwLine >= m_dwLines){ dwLines = m_dwLines + BTEXT_LINE_INC; //unsigned int nLastLine = m_dwLastLine; i = 0; try{ pbtTemp1 = new SRTextWord[dwLines]; ppbtTemp2 = new SRTextWord*[dwLines]; }catch(char * str){ printf(str); } for(i = 0; i <= m_dwLastLine; i++){ pbtTemp1[i] = m_lpLines[i]; // Now the next btWord doesn't know it has a new prev btWord // Fixing... if(pbtTemp1[i].m_lpNextWord) pbtTemp1[i].m_lpNextWord->m_lpPrevWord = &pbtTemp1[i]; ppbtTemp2[i] = m_lppLinesLastWord[i]; } for(;i < dwLines; i++){ ppbtTemp2[i] = &pbtTemp1[i]; } m_dwLastLine = dwLine; m_dwLines = dwLines; delete [] m_lpLines; delete [] m_lppLinesLastWord; m_lpLines = pbtTemp1; m_lppLinesLastWord = ppbtTemp2; }else if(dwLine > m_dwLastLine){ m_dwLastLine = dwLine; } SRTextView::SRTextWord *pbtPrevWord = m_lppLinesLastWord[dwLine]->m_lpPrevWord; *m_lppLinesLastWord[dwLine] = btWord; try{ m_lppLinesLastWord[dwLine]->m_lpNextWord = new SRTextWord(); }catch(char * str){ printf(str); } m_lppLinesLastWord[dwLine]->m_lpPrevWord = pbtPrevWord; // Create the relationship between this btWord and the next // potential btWord... m_lppLinesLastWord[dwLine]->m_lpNextWord->m_lpPrevWord = m_lppLinesLastWord[dwLine]; m_lppLinesLastWord[dwLine] = m_lppLinesLastWord[dwLine]->m_lpNextWord; }