// // TXTED.CPP // Editor for Text Objects // // Copyright Microsoft 1998- // // PRECOMP #include "precomp.h" ///////////////////////////////////////////////////////////////////////////// // WbTextBox // // This is a subclassed edit field // WbTextBox::WbTextBox(WbTextEditor * pEditor) { OSVERSIONINFO OsData; m_hwnd = NULL; m_pfnEditPrev = NULL; m_MaxRect.left = 0; m_MaxRect.top = 0; m_MaxRect.right = INT_MAX; m_MaxRect.bottom = INT_MAX; ::SetRectEmpty(&m_rectErase); m_bInIME = FALSE; m_bDontEscapeThisTime = FALSE; // see if we need to make adjustments for NT. m_ptNTBooger.x = 0; m_ptNTBooger.y = 0; OsData.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if( GetVersionEx( &OsData ) ) { if( OsData.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // NT editboxes are offset from Win95 editboxes. We // have to de-booger them m_ptNTBooger.x = 3; } } m_pEditor = pEditor; } // // ~WbTextBox() // WbTextBox::~WbTextBox() { if (m_hwnd != NULL) { ::DestroyWindow(m_hwnd); m_hwnd = NULL; } } // // Create() // Creates an edit field then subclasses it with our window procedure // BOOL WbTextBox::Create(HWND hwndParent) { ASSERT(!m_hwnd); m_hwnd = ::CreateWindowEx(0, _T("EDIT"), NULL, WS_CHILD | WS_BORDER | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hwndParent, NULL, g_hInstance, NULL); if (!m_hwnd) { ERROR_OUT(("WbTextBox::Create failed to create edit window")); return(FALSE); } // Init the data ::SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LPARAM)this); // Subclass the window m_pfnEditPrev = (WNDPROC)::SetWindowLongPtr(m_hwnd, GWLP_WNDPROC, (LONG_PTR)TextWndProc); return(TRUE); } // // TextWndProc() // Message subclass handler for edit field // LRESULT CALLBACK TextWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult; WbTextBox * ptb; ptb = (WbTextBox *)::GetWindowLongPtr(hwnd, GWLP_USERDATA); ASSERT(ptb != NULL); ASSERT(ptb->m_pfnEditPrev != NULL); lResult = 0; switch( message ) { case WM_CLEAR: case WM_CUT: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnClearCut(); break; case WM_UNDO: case WM_PASTE: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnUndoPaste(); break; case WM_IME_STARTCOMPOSITION: { ptb->m_bInIME = TRUE; ptb->m_bDontEscapeThisTime = TRUE; // let editbox take it from here goto DefEditProc; break; } case WM_IME_CHAR: { ptb->m_bDontEscapeThisTime = FALSE; goto DefEditProc; break; } case WM_IME_ENDCOMPOSITION: { ptb->m_bInIME = FALSE; goto DefEditProc; break; } case WM_KILLFOCUS: { if (ptb->m_bInIME && g_fnImmGetContext) { HIMC hImc = g_fnImmGetContext(hwnd); if ((hImc != NULL) && g_fnImmNotifyIME) { // we're loosing control, tell IME to wrap it up (bug 130) g_fnImmNotifyIME( hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0 ); } } // goto DefEditProc; break; } case WM_CHAR: ptb->OnChar((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_KEYUP: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnKeyUp((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_SYSKEYDOWN: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnSysKeyDown((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_TIMER: ptb->OnTimer(wParam); break; case WM_MOUSEMOVE: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnMouseMove((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_LBUTTONUP: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnLButtonUp((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_MOVE: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); ptb->OnMove(LOWORD(lParam), HIWORD(lParam)); break; default: DefEditProc: lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam); break; } return(lResult); } // // OnClearCut() // void WbTextBox::OnClearCut() { POINT ptCaret; POINT ptPos; ::GetCaretPos(&ptCaret); m_pEditor->GetPosition(&ptPos); m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x; m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y; m_pEditor->m_bChanged = TRUE; } // // OnUndoPaste() // void WbTextBox::OnUndoPaste(void) { FitBox(); AutoCaretScroll(); m_pEditor->m_bChanged = TRUE; } // // OnChar() // void WbTextBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { int nFirst; int nLast; int nPrevNumLines; int nPrevNumChars; LRESULT dwPosChar; POINT ptTop; // clear ignore next escape (NM4db:456) m_bDontEscapeThisTime = FALSE; ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast); dwPosChar = ::SendMessage(m_hwnd, EM_POSFROMCHAR, nFirst, 0); ptTop.x = (short)LOWORD(dwPosChar); ptTop.y = (short)HIWORD(dwPosChar); nPrevNumLines = (int)::SendMessage(m_hwnd, EM_GETLINECOUNT, 0, 0); nPrevNumChars = (int)::SendMessage(m_hwnd, EM_LINELENGTH, (WPARAM)-1, 0); ::CallWindowProc(m_pfnEditPrev, m_hwnd, WM_CHAR, nChar, MAKELONG(nRepCnt, nFlags)); SetupBackgroundRepaint( ptTop, (nPrevNumLines != ::SendMessage(m_hwnd, EM_GETLINECOUNT, 0, 0))|| (nPrevNumChars > ::SendMessage(m_hwnd, EM_LINELENGTH, (WPARAM)-1, 0))); FitBox(); m_pEditor->m_bChanged = TRUE; } void WbTextBox::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { POINT ptCaret; POINT ptPos; AutoCaretScroll(); switch( nChar ) { case VK_MENU: case VK_SHIFT: case VK_CONTROL: break; case VK_DELETE: SelectAtLeastOne(); ::SendMessage(m_hwnd, WM_CLEAR, 0, 0); break; case VK_ESCAPE: if( !m_bInIME ) { if( m_bDontEscapeThisTime ) m_bDontEscapeThisTime = FALSE; else { // End the text entry abandoning the changes g_pDraw->EndTextEntry(FALSE); return; // we don't exist anymore, bail out } } break; default: break; } ::GetCaretPos(&ptCaret); m_pEditor->GetPosition(&ptPos); m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x; m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y; } void WbTextBox::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch( nChar ) { case VK_MENU: // ignore next escape (NM4db:456) m_bDontEscapeThisTime = TRUE; break; default: break; } } BOOL WbTextBox::FitBox( void ) { RECT rectErase; RECT crEditRect; RECT crEditBox; RECT crEditWnd; RECT crDrawWnd; int nDeltaWidth, nDeltaHeight; HDC hDC = NULL; HDC hDrawDC = NULL; TCHAR *szBoxText = NULL; BOOL bNoChanges; POINT ptDrawLoc; HFONT hSaveFont = NULL; POINT ptBitmapTopLeft; LPTSTR pszText; int nTextLen; int textSize; bNoChanges = TRUE; hDC = ::GetDC(m_hwnd); if (hDC == NULL) { bNoChanges = TRUE; goto bail_out; } hDrawDC = g_pDraw->GetCachedDC(); if (hDrawDC == NULL) { bNoChanges = TRUE; goto bail_out; } hSaveFont = SelectFont(hDC, m_pEditor->GetFont()); textSize = ::GetWindowTextLength(m_hwnd); if (!textSize) { // Use a " " for a string, which is two characters. szBoxText = new TCHAR[2]; if (!szBoxText) { ERROR_OUT(("Failed to allocate TCHAR array for empty text")); goto bail_out; } else { lstrcpy(szBoxText, _T(" ")); } } else { szBoxText = new TCHAR[textSize+1]; if (!szBoxText) { ERROR_OUT(("Failed to allocate TCHAR array for object text")); goto bail_out; } else { ::GetWindowText(m_hwnd, (LPTSTR)szBoxText, textSize+1); } } // Have to check string length for NT. crEditRect comes back from // DrawText giant-positive (i.e., still equal to m_MaxRect) if szBoxText // is empty. This causes crEditRect to overflow to giant negative later // resulting in very bizare painting problems. Win95 doesn't care because // it returns 16bit results that look like -1 instead of giant positive. ::GetClientRect(g_pDraw->m_hwnd, &crDrawWnd ); // BUG 464 -DrawText doesn't calc widths involving tabs the same way a // standard edit control does so replace it with DrawTextEx // using the DT_EDITCONTROL format crEditRect = m_MaxRect; pszText = szBoxText; nTextLen = lstrlen(szBoxText); ::DrawTextEx( hDC, pszText, nTextLen, &crEditRect, DT_CALCRECT | DT_EXPANDTABS | DT_NOPREFIX | DT_EDITCONTROL, NULL ); // BUG 464 (continued) -DrawTextEx doesn't include blank-last-lines in its height // calc like DrawText does so have to add an extra line // height for blank lines to have same behavior as DrawText if ((nTextLen >= 2) && !IsDBCSLeadByte(pszText[nTextLen-2]) && (pszText[nTextLen-1] == _T('\n')) ) crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight; // NT sanity check if this still fails if ( ((crEditRect.right - crEditRect.left) == (m_MaxRect.right - m_MaxRect.left)) || ((crEditRect.right - crEditRect.left) <= 0) ) { crEditRect.right = crEditRect.left + crDrawWnd.right - crDrawWnd.left; } if ( ((crEditRect.bottom - crEditRect.top) == (m_MaxRect.bottom - m_MaxRect.top)) || ((crEditRect.bottom - crEditRect.top) <= 0) ) { crEditRect.bottom = crEditRect.top + crDrawWnd.bottom - crDrawWnd.top; } ::GetClientRect(m_hwnd, &crEditBox); ::GetWindowRect(m_hwnd, &crEditWnd); ::MapWindowPoints(NULL, g_pDraw->m_hwnd, (LPPOINT)&crEditWnd, 2); // do min size check for IME's. int nMinWidth; nMinWidth = MIN_FITBOX_CHARS*m_pEditor->m_textMetrics.tmMaxCharWidth; if ((crEditRect.right - crEditRect.left) < nMinWidth ) crEditRect.right = crEditRect.left + nMinWidth; // must add some slop to prevent autoscroll from kicking in crEditRect.right += 2*m_pEditor->m_textMetrics.tmMaxCharWidth; crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight; //pretend we had a right or bottom scroll ::OffsetRect(&crEditRect, -1, -1); nDeltaWidth = (crEditRect.right - crEditRect.left) - (crEditBox.right - crEditBox.left); if (nDeltaWidth > 0) { bNoChanges = FALSE; if ( crEditRect.left < 0 ) { // right scroll, adjust right edge crEditWnd.right += nDeltaWidth; } else { // left scroll, adjust left edge crEditWnd.left -= nDeltaWidth; } } nDeltaHeight = (crEditRect.bottom - crEditRect.top) - (crEditBox.bottom - crEditBox.top); if (nDeltaHeight > 0) { bNoChanges = FALSE; if( crEditRect.left < 0 ) { // bottom scroll, adjust bottom edge crEditWnd.bottom += nDeltaHeight; } else { // top scroll, adjust top edge crEditWnd.top -= nDeltaHeight; } } if( bNoChanges ) goto bail_out; // resize ::MoveWindow(m_hwnd, crEditWnd.left, crEditWnd.top, crEditWnd.right - crEditWnd.left, crEditWnd.bottom - crEditWnd.top, TRUE ); // update bounding box ::GetClientRect(m_hwnd, &crEditBox); ::MapWindowPoints(m_hwnd, g_pDraw->m_hwnd, (LPPOINT)&crEditBox, 2); ::OffsetRect(&crEditBox, g_pDraw->m_originOffset.cx+1 + m_ptNTBooger.x, g_pDraw->m_originOffset.cy + m_ptNTBooger.y );//+1 ); m_pEditor->m_boundsRect = crEditBox; bail_out: if (hDC != NULL ) { if(hSaveFont) { SelectFont(hDC, hSaveFont); } ::ReleaseDC(m_hwnd, hDC ); } if(szBoxText) { delete [] szBoxText; } return( !bNoChanges ); } void WbTextBox::OnTimer(UINT_PTR nIDEvent) { TRACE_TIMER(("WbTextBox::OnTimer")); // If the text object has not been added to the page before... if (m_pEditor->Handle() == NULL) { // Add it now m_pEditor->AddToPageLast(g_pDraw->Page()); } else { // Otherwise, replace the external copy m_pEditor->Replace(); } } // // WbTextBox::OnLButtonUp() // void WbTextBox::OnLButtonUp(UINT nFlags, int x, int y) { POINT ptCaret; POINT ptPos; ::GetCaretPos(&ptCaret); m_pEditor->GetPosition(&ptPos); m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x; m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y; } // // WbTextBox::OnMouseMove() // void WbTextBox::OnMouseMove(UINT nFlags, int x, int y) { if (nFlags & MK_LBUTTON ) { // we're dragging ::HideCaret(m_hwnd); AutoCaretScroll(); ::ShowCaret(m_hwnd); } } void WbTextBox::AutoCaretScroll( void ) { POINT ptCaret; POINT ptPos; ::GetCaretPos(&ptCaret); m_pEditor->GetPosition(&ptPos); ptCaret.x += ptPos.x; ptCaret.y += ptPos.y; g_pDraw->AutoScroll(ptCaret.x, ptCaret.y, FALSE, m_pEditor->m_cursorXYPos.x, m_pEditor->m_cursorXYPos.y); m_pEditor->m_cursorXYPos = ptCaret; } void WbTextBox::SetupBackgroundRepaint ( POINT ptTopPaint, BOOL bNumLinesChanged ) { RECT rectBox; ::GetClientRect(m_hwnd, &rectBox); if (ptTopPaint.y == -1) { ::GetCaretPos(&ptTopPaint); } rectBox.top = ptTopPaint.y; if( !bNumLinesChanged ) rectBox.bottom = rectBox.top + m_pEditor->m_textMetrics.tmHeight; ::InvalidateRect(m_hwnd, &rectBox, TRUE ); } // // Selects at least one (DBCS) char if there is not a selection already. // void WbTextBox::SelectAtLeastOne( void ) { int nFirst, nLast; TCHAR * szBoxText; ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast); if( nFirst == nLast ) { int textSize = ::GetWindowTextLength(m_hwnd); szBoxText = new TCHAR[textSize + 1]; if (!szBoxText) { ERROR_OUT(("Failed to allocate TCHAR array for object text")); } else { ::GetWindowText( m_hwnd, szBoxText, textSize+1); if (nFirst < textSize) { nLast++; if( IsDBCSLeadByte( (BYTE) szBoxText[ nFirst ] )|| (szBoxText[ nFirst ] == _T('\r')) ) nLast++; ::SendMessage(m_hwnd, EM_SETSEL, nFirst, nLast); } delete [] szBoxText; } } } void WbTextBox::OnMove(int x, int y) { if (m_pEditor->m_nLastShow == SW_SHOW) { FitBox(); ::ShowCaret(m_hwnd); } } int WbTextBox::GetMaxCharHeight( void ) { return( m_pEditor->m_textMetrics.tmHeight ); } int WbTextBox::GetMaxCharWidth( void ) { return( m_pEditor->m_textMetrics.tmMaxCharWidth ); } // // Aborts and shuts down text editor without hitting the cores. Call this // to shutdown editing during a lock condition (that we don't own) // void WbTextBox::AbortEditGently( void ) { RECT boundsRect; // have to be carefull how we bail out of this so we // don't fall into an infinte exception loop (bugs 3550, 3556) if (g_pDraw->m_pActiveText != NULL) { delete g_pDraw->m_pActiveText; // zap in memory original g_pDraw->m_pActiveText = NULL; // text object, loose any current // edits } // // NOTE: // Can't reread the text object from cores to update // boundsrect because the code that does that // (DCWbGraphic::ReadExternal()) tries to lock // the object and off we go into another exception // loop. SO, the boundsrect we use below might // not be the right size and the text might // appear to be clipped when we redraw it. This // will correct itself as soon as another graphic // update notice comes in from DCL (the lock owner // types another char). I think we have to live // with that. // m_pEditor->GetBoundsRect(&boundsRect); // let go of core text object (see above) // don't unlock if handle is NULL or we will blow up the cores (bug 4621) if (g_pDraw->m_textEditor.Handle() != NULL ) { if (g_pDraw->m_textEditor.GotLock() ) { g_pDraw->m_textEditor.Unlock(); g_pDraw->m_textEditor.ForceUpdate(); } } g_pDraw->m_textEditor.ZapHandle(); // shut down rejecting all edits g_pDraw->EndTextEntry(FALSE); // Redraw any altered parts of the screen g_pDraw->InvalidateSurfaceRect(&boundsRect); } // // // Function: WbTextEditor // // Purpose: Constructor // // WbTextEditor::WbTextEditor(void) { // Initialize the cursor position m_cursorCharPos.x = 0; m_cursorCharPos.y = 0; // set parent for editbox m_pEditBox = NULL; m_cursorXYPos.x = 0; m_cursorXYPos.y = 0; m_bFirstSetFontCall = TRUE; m_nLastShow = -1; } WbTextEditor::~WbTextEditor(void) { if (m_pEditBox != NULL) { delete m_pEditBox; m_pEditBox = NULL; } } // // // Function: SetCursorPosFromPoint // // Purpose: Return the character position most closely matching a // given co-ordinate position in the text object. // // void WbTextEditor::SetCursorPosFromPoint(POINT pointXY) { int nCharPos; if (::PtInRect(&m_boundsRect, pointXY)) { // make point relative to editbox pointXY.x -= g_pDraw->m_originOffset.cx; pointXY.y -= g_pDraw->m_originOffset.cy; ::MapWindowPoints(g_pDraw->m_hwnd, m_pEditBox->m_hwnd, &pointXY, 1); ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONDOWN, 0, MAKELONG( pointXY.x, pointXY.y ) ); ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONUP, 0, MAKELONG( pointXY.x, pointXY.y ) ); // get char index in editbox nCharPos = (int)::SendMessage(m_pEditBox->m_hwnd, EM_CHARFROMPOS, 0, MAKELPARAM(pointXY.x, pointXY.y)); if( nCharPos < 0 ) return; // Set the new cursor char co-ordinates m_cursorCharPos.x = (short)LOWORD(nCharPos); m_cursorCharPos.y = (short)HIWORD(nCharPos); // Move the cursor to the new position GetXYPosition(m_cursorCharPos, &m_cursorXYPos); } } // // // Function: GetCursorSize // // Purpose: Return the cursor size for the current font // // void WbTextEditor::GetCursorSize(LPSIZE lpsize) { lpsize->cx = ::GetSystemMetrics(SM_CXBORDER); lpsize->cy = m_textMetrics.tmHeight; } // // // Function: XYPosition // // Purpose: Calculate the X,Y co-ordinates of a character position // // void WbTextEditor::GetXYPosition(POINT pointChar, LPPOINT lpptXY) { int nCharIndex; LRESULT dwCharPos; nCharIndex = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, pointChar.y, 0) + pointChar.x; GetPosition(lpptXY); dwCharPos = ::SendMessage(m_pEditBox->m_hwnd, EM_POSFROMCHAR, nCharIndex, 0); lpptXY->x += (short)LOWORD(dwCharPos); lpptXY->y += (short)HIWORD(dwCharPos); } // // // Function: Clear // // Purpose: Clear the text editor // // void WbTextEditor::Clear(void) { RECT cEWndRect; // Remove all the current stored text strTextArray.RemoveAll(); strTextArray.Add(_TEXT("")); // Reset the cursor position m_cursorCharPos.x = 0; m_cursorCharPos.y = 0; // clear editbox ::SetWindowText(m_pEditBox->m_hwnd, _TEXT("")); // Show that the text has not been changed m_bChanged = FALSE; // init editbox size m_boundsRect.right = m_boundsRect.left + 2*m_pEditBox->GetMaxCharWidth(); m_boundsRect.bottom = m_boundsRect.top + 2*m_pEditBox->GetMaxCharHeight(); cEWndRect = m_boundsRect; ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x), -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) ); ::BringWindowToTop(m_pEditBox->m_hwnd); ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top, cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top, TRUE); } // // // Function: New // // Purpose: Clear the text editor and reset the graphic handle // // BOOL WbTextEditor::New(void) { // create editbox if (!Create()) { ERROR_OUT(("Error creating drawing area window")); return(FALSE); } // Clear the object Clear(); // Show that the text has not been changed m_bChanged = FALSE; // Reset the graphic and page handles m_hGraphic = NULL; m_hPage = WB_PAGE_HANDLE_NULL; return(TRUE); } // // // Function: SetTextObject // // Purpose: Attach a text object to the editor // // BOOL WbTextEditor::SetTextObject(DCWbGraphicText* ptext) { // create editbox if (!Create()) { ERROR_OUT(("Error creating drawing area window")); return(FALSE); } // Clear and place the object Clear(); // setup font DCWbGraphicText::SetFont( ptext->m_hFont ); // Copy the text object's attributes to the editor SetText(ptext->strTextArray ); ptext->GetBoundsRect(&m_boundsRect); SetColor(ptext->m_clrPenColor ); MoveTo(m_boundsRect.left, m_boundsRect.top); // avoid kerning offset correction in Position()(bug 469) // Copy the page and handle of the graphic text object m_hPage = ptext->m_hPage; m_hGraphic = ptext->m_hGraphic; // copy the text to the editbox GetText(); // Say the text has changed so that all nodes will notice this thing // is locked - bug 2185 m_bChanged = TRUE; return(TRUE); } // // // Function: IsEmpty // // Purpose: Return TRUE if there is no text in the object // // BOOL WbTextEditor::IsEmpty(void) { return(::GetWindowTextLength(m_pEditBox->m_hwnd) <= 0 ); } void WbTextEditor::PutText(void) { int nNumLines; int i; int nMaxLineLen, nLineLen; int nLine; TCHAR *cbuf; WbTextEditor *pThis; pThis = (WbTextEditor *)this; // overide const declaration if(m_pEditBox == NULL) { return; } nNumLines = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINECOUNT, 0, 0); // figure out buf size nMaxLineLen = 0; for (i = 0; i < nNumLines; i++) { nLine = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, i, 0); nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINELENGTH, nLine, 0); if (nMaxLineLen < nLineLen) nMaxLineLen = nLineLen; } // make buf nMaxLineLen += sizeof(WORD); cbuf = new TCHAR[nMaxLineLen]; if( cbuf == NULL ) { ERROR_OUT(("PutText failing; couldn't allocate TCHAR array")); return; } // // copy editbox innards to textbox string // Again, we know in advance how many lines; use SetSize/SetAt() // strTextArray.RemoveAll(); strTextArray.SetSize(nNumLines); for(i = 0; i < nNumLines; i++) { *(LPWORD)cbuf = (WORD)nMaxLineLen; nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINE, i, (LPARAM)cbuf); cbuf[nLineLen] = _T('\0'); strTextArray.SetAt(i, cbuf ); } // clean up delete [] cbuf; } void WbTextEditor::GetText(void) { int nNumLines; int textSize = 0; int i; TCHAR * pText = NULL; TCHAR * pStartText; nNumLines = strTextArray.GetSize(); // // Calculate the buffer size we need // for (i = 0; i < nNumLines; i++ ) { textSize += lstrlen(strTextArray[i]); if ((i + 1) < nNumLines) textSize += lstrlen(_T("\r\n")); } // // Get the lines, with \r\n separating them // pText = new TCHAR[textSize + 1]; if (!pText) { ERROR_OUT(("GetText failing; couldn't allocate TCHAR array")); } else { // Null this out in casae textSize is 0. pStartText = pText; pStartText[0] = 0; for (i = 0; i < nNumLines; i++) { lstrcpy(pStartText, strTextArray[i]); pStartText += lstrlen(strTextArray[i]); if ((i + 1) < nNumLines) { lstrcpy(pStartText, _T("\r\n")); pStartText += lstrlen(_T("\r\n")); } } ::SetWindowText(m_pEditBox->m_hwnd, pText); delete []pText; } } DWORD WbTextEditor::CalculateExternalLength( void ) { // stick text in underlying text object if( m_bChanged ) PutText(); // fix bounds to get rid off editbox slop ((WbTextEditor *)this)->DCWbGraphicText::CalculateBoundsRect(); // tell textbox to use it in length calculation return( DCWbGraphicText::CalculateExternalLength() ); } void WbTextEditor::CalculateBoundsRect( void ) { RECT cEWndRect; ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect); ::MapWindowPoints(m_pEditBox->m_hwnd, g_pDraw->m_hwnd, (LPPOINT)&cEWndRect, 2); m_boundsRect = cEWndRect; ::OffsetRect(&m_boundsRect, g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x, g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y);//+1 ); ::BringWindowToTop(m_pEditBox->m_hwnd); } // set editbox visibility void WbTextEditor::ShowBox( int nShow ) { if (m_nLastShow == nShow) { if( nShow == SW_SHOW) ::SetFocus(m_pEditBox->m_hwnd); return; } m_nLastShow = nShow; if (nShow == SW_SHOW) { // show it if (m_pEditBox != NULL) { ::ShowWindow(m_pEditBox->m_hwnd, SW_SHOW); ::BringWindowToTop(m_pEditBox->m_hwnd); ::SetFocus(m_pEditBox->m_hwnd); ::HideCaret(m_pEditBox->m_hwnd); m_pEditBox->FitBox(); ::ShowCaret(m_pEditBox->m_hwnd); } } else { if (m_pEditBox != NULL) { ::ShowWindow(m_pEditBox->m_hwnd, SW_HIDE); m_bFirstSetFontCall = TRUE; delete m_pEditBox; m_pEditBox = NULL; } } } // // Create() // // Creates the edit box window // BOOL WbTextEditor::Create( void ) { if (m_pEditBox == NULL) { m_pEditBox = new WbTextBox(this); if (m_pEditBox == NULL) { ERROR_OUT(("Couldn't create edit text box")); return(FALSE); } if (!m_pEditBox->Create(g_pDraw->m_hwnd)) { ERROR_OUT(("WbTextEditor::Create failed; can't create edit field")); return(FALSE); } } return(TRUE); } void WbTextEditor::MoveBy(int cx, int cy) { RECT cEWndRect; DCWbGraphicText::MoveBy(cx, cy); ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect); if ((m_boundsRect.right - m_boundsRect.left) < (cEWndRect.right - cEWndRect.left)) m_boundsRect.right = m_boundsRect.left + (cEWndRect.right - cEWndRect.left); if ((m_boundsRect.bottom - m_boundsRect.top) < (cEWndRect.bottom - cEWndRect.top)) m_boundsRect.bottom = m_boundsRect.top + (cEWndRect.bottom - cEWndRect.top); cEWndRect = m_boundsRect; ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x), -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );//+1) ); ::BringWindowToTop(m_pEditBox->m_hwnd); ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top, cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top, TRUE); } void WbTextEditor::SetFont( LOGFONT *pLogFont, BOOL bDummy ) { // Ignore bDummy. Had to add that so polymorph will work correctly // for DCWbGraphicText::SetFont() // Have to delay recalc of bounding rect because editbox will have a // bogus (bad) font until SetFont is called. Can't call SetFont // before this because new font hasn't been made yet. DCWbGraphicText::SetFont( pLogFont, FALSE ); ::SendMessage(m_pEditBox->m_hwnd, WM_SETFONT, (WPARAM)m_hFont, TRUE); // now do bounding rect CalculateBoundsRect(); if( m_bFirstSetFontCall ) m_bFirstSetFontCall = FALSE; else { ::HideCaret(m_pEditBox->m_hwnd); m_pEditBox->FitBox(); m_pEditBox->AutoCaretScroll(); ::ShowCaret(m_pEditBox->m_hwnd); } } void WbTextEditor::WriteExtra( PWB_GRAPHIC pHeader ) { PutText(); DCWbGraphicText::WriteExtra( pHeader ); } void WbTextEditor::SetTimer( UINT nElapse ) { ::SetTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE, nElapse, NULL); } void WbTextEditor::KillTimer( void ) { ::KillTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE); } // Resets editbox painting for a resized drawing window void WbTextEditor::ParentResize( void ) { ::HideCaret(m_pEditBox->m_hwnd); m_pEditBox->FitBox(); ::ShowCaret(m_pEditBox->m_hwnd); } void WbTextEditor::RedrawEditbox(void) { ::InvalidateRect(m_pEditBox->m_hwnd, NULL, TRUE); m_pEditBox->FitBox(); }