|
|
//
// TEXTOBJ.CPP
// Drawing objects: point, openpolyline, closepolyline, ellipse
//
// Copyright Microsoft 1998-
//
#include "precomp.h"
#include "nmwbobj.h"
TextObj::TextObj(void) { #ifdef _DEBUG
FillMemory(&m_textMetrics, sizeof(m_textMetrics), DBG_UNINIT); #endif // _DEBUG
//
// ALWAYS ZERO OUT m_textMetrics. Calculations depend on the height
// and width of chars being zero before the font is set.
//
ZeroMemory(&m_textMetrics, sizeof(m_textMetrics));
SetMyWorkspace(NULL); SetOwnerID(g_MyMemberID);
m_ToolType = TOOLTYPE_TEXT;
//
// Created locally, not selected, not editing or deleting.
//
CreatedLocally(); ClearSelectionFlags(); ClearEditionFlags(); ClearDeletionFlags(); SetType(siNonStandardPDU_chosen);
SetFillColor(RGB(-1,-1,-1),TRUE); SetZOrder(front);
//
// No attributes changed, they will be set as we change them
//
SetWorkspaceHandle(g_pCurrentWorkspace == NULL ? 0 : g_pCurrentWorkspace->GetWorkspaceHandle()); SetType(drawingCreatePDU_chosen); SetROP(R2_NOTXORPEN); SetPlaneID(1); SetMyPosition(NULL); SetMyWorkspace(NULL); // 1 Pixels for pen thickness
SetPenThickness(2); SetAnchorPoint(0,0); RECT rect; ::SetRectEmpty(&rect); SetRect(&rect); SetBoundsRect(&rect);
m_hFontThumb = ::CreateFont(0,0,0,0,FW_NORMAL,0,0,0,0,OUT_TT_PRECIS, CLIP_DFA_OVERRIDE, DRAFT_QUALITY,FF_SWISS,NULL);
m_hFont = ::CreateFont(0,0,0,0,FW_NORMAL,0,0,0,0,OUT_TT_PRECIS, CLIP_DFA_OVERRIDE, DRAFT_QUALITY, FF_SWISS,NULL);
m_nKerningOffset = 0; ResetAttrib();
}
TextObj::~TextObj( void ) { RemoveObjectFromResendList(this); RemoveObjectFromRequestHandleList(this);
TRACE_DEBUG(("drawingHandle = %d", GetThisObjectHandle() ));
//
// Tell other nodes that we are gone
//
if(GetMyWorkspace() != NULL && WasDeletedLocally()) { OnObjectDelete(); }
if(m_hFont) { ::DeleteFont(m_hFont); m_hFont = NULL; }
if (m_hFontThumb) { ::DeleteFont(m_hFontThumb); m_hFontThumb = NULL; }
strTextArray.ClearOut(); strTextArray.RemoveAll();
}
void TextObj::TextEditObj (TEXTPDU_ATTRIB* pEditAttrib ) {
RECT rect; POSITION pos; POINT anchorPoint; LONG deltaX = 0; LONG deltaY = 0;
TRACE_DEBUG(("TextEditObj drawingHandle = %d", GetThisObjectHandle() ));
//
// Was edited remotely
//
ClearEditionFlags();
//
// Get the previous anchor point
//
GetAnchorPoint(&anchorPoint);
//
// Read attributes
//
m_dwChangedAttrib = pEditAttrib->attributesFlag; GetTextAttrib(pEditAttrib);
//
// Change the anchor point
//
if(HasAnchorPointChanged()) { { //
// Get the delta from previous anchor point
//
deltaX -= anchorPoint.x; deltaY -= anchorPoint.y;
//
// Get the new anchor point
//
GetAnchorPoint(&anchorPoint); deltaX += anchorPoint.x; deltaY += anchorPoint.y; TRACE_DEBUG(("Delta (%d,%d)", deltaX , deltaY));
//
// Was edited remotely
//
ClearEditionFlags(); } UnDraw();
GetRect(&rect); ::OffsetRect(&rect, deltaX, deltaY); SetRect(&rect); SetBoundsRect(&rect);
}
if(HasAnchorPointChanged() || HasFillColorChanged() || HasPenColorChanged() || HasFontChanged() || HasTextChanged()) { Draw(NULL); } else if(HasZOrderChanged()) { if(GetZOrder() == front) { g_pDraw->BringToTopSelection(FALSE, this); } else { g_pDraw->SendToBackSelection(FALSE, this); } } //
// If it just select/unselected it
//
else if(HasViewStateChanged()) { ; // do nothing
} //
// If we have a valid font.
//
else if(GetFont()) { Draw(); }
//
// Reset all the attributes
//
ResetAttrib(); }
void TextObj::GetTextAttrib(TEXTPDU_ATTRIB * pattributes) { if(HasPenColorChanged()) { SetPenColor(pattributes->textPenColor, TRUE); } if(HasFillColorChanged()) { SetFillColor(pattributes->textFillColor, TRUE); } if(HasViewStateChanged()) {
//
// If the other node is selecting the drawing or unselecting
//
if(pattributes->textViewState == selected_chosen) { SelectedRemotely(); } else if(pattributes->textViewState == unselected_chosen) { ClearSelectionFlags(); }
SetViewState(pattributes->textViewState); } if(HasZOrderChanged()) { SetZOrder((ZOrder)pattributes->textZOrder); } if(HasAnchorPointChanged()) { SetAnchorPoint(pattributes->textAnchorPoint.x, pattributes->textAnchorPoint.y ); } if(HasFontChanged()) { UnDraw();
if(m_hFont) { ::DeleteFont(m_hFont); m_hFont = NULL; } m_hFont = ::CreateFontIndirect(&pattributes->textFont); if (!m_hFont) { // Could not create the font
ERROR_OUT(("Failed to create font")); }
if (m_hFontThumb) { ::DeleteFont(m_hFontThumb); m_hFontThumb = NULL; } m_hFontThumb = ::CreateFontIndirect(&pattributes->textFont); if (!m_hFontThumb) { // Could not create the font
ERROR_OUT(("Failed to create thumbnail font")); } } int lines = 0; UINT maxString = 0; if(HasTextChanged()) {
BYTE * pBuff = (BYTE *)&pattributes->textString; VARIABLE_STRING * pVarString = NULL;
lines = pattributes->numberOfLines; int i; CHAR * cBuff = NULL; LPWSTR lpWideCharStr;
for (i = 0; i < lines ; i++) { pVarString = (VARIABLE_STRING *) pBuff;
lpWideCharStr = (LPWSTR)&pVarString->string; UINT strSize = 0; strSize= WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, -1, NULL, 0, NULL, NULL );
//
// Get the longest string
//
if(strSize > maxString) { maxString = strSize; } DBG_SAVE_FILE_LINE cBuff = new TCHAR[strSize]; WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, -1, cBuff, strSize, NULL, NULL ); strTextArray.SetSize(i); strTextArray.SetAtGrow(i, cBuff ); delete cBuff;
ASSERT(pVarString->header.start.y == i); pBuff += pVarString->header.len;
}
//
// Calculate the rect
//
if(m_hFont) {
//
// Remove the old text before we paly with the text size
//
UnDraw();
g_pDraw->PrimeFont(g_pDraw->m_hDCCached, m_hFont, &m_textMetrics); g_pDraw->UnPrimeFont(g_pDraw->m_hDCCached); } }
}
void TextObj::SetTextAttrib(TEXTPDU_ATTRIB * pattributes) {
if(HasPenColorChanged()) { GetPenColor(&pattributes->textPenColor); } if(HasFillColorChanged()) { GetFillColor(&pattributes->textFillColor); } if(HasViewStateChanged()) { pattributes->textViewState = GetViewState(); } if(HasZOrderChanged()) { pattributes->textZOrder = GetZOrder(); } if(HasAnchorPointChanged()) { GetAnchorPoint(&pattributes->textAnchorPoint); } if(HasFontChanged()) { ::GetObject(m_hFont, sizeof(LOGFONT), &pattributes->textFont); }
if(HasTextChanged()) { BYTE * pBuff = (BYTE *)&pattributes->textString; VARIABLE_STRING * pVarString= NULL; LPWSTR lpWideCharStr;
int size = strTextArray.GetSize(); int i;
for (i = 0; i < size ; i++) { pVarString = (VARIABLE_STRING *)pBuff; lpWideCharStr = (LPWSTR)&pVarString->string; int strSize = 0; strSize= MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, lpWideCharStr, 0)*sizeof(WCHAR); MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, lpWideCharStr, strSize); pVarString->header.len = strSize + sizeof(VARIABLE_STRING_HEADER); pVarString->header.start.x = 0; // JOSEF change that
pVarString->header.start.y = i; pBuff += pVarString->header.len; }
pattributes->numberOfLines = size;
//
// Since we are sending text, need to send some font
//
::GetObject(m_hFont, sizeof(LOGFONT), &pattributes->textFont); }
}
void TextObj::CreateTextPDU(ASN1octetstring_t *pData, UINT choice) {
MSTextPDU * pTextPDU = NULL; UINT stringSize = 0; // Size of all the strings UNICODE
int lines = 0; // Number of text lines
//
// Calculate the size of the whole pdu
//
ULONG length = 0; if(choice == textDeletePDU_chosen) { length = sizeof(MSTextDeletePDU); } else {
//
// Calculate the size of the text
//
if(HasTextChanged()) { int i; lines = strTextArray.GetSize();
for (i = 0; i < lines ; i++) { stringSize += MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, NULL, 0) * sizeof(WCHAR); } }
length = sizeof(MSTextPDU) + sizeof(VARIABLE_STRING_HEADER)* lines + stringSize; }
DBG_SAVE_FILE_LINE pTextPDU = (MSTextPDU *) new BYTE[length];
//
// PDU choice: create, edit delete
//
pTextPDU->header.nonStandardPDU = choice;
//
// This objects handle
//
pTextPDU->header.textHandle = GetThisObjectHandle(); TRACE_DEBUG(("Text >> Text handle = %d",pTextPDU->header.textHandle ));
//
// This objects workspacehandle
//
WorkspaceObj * pWorkspace = GetMyWorkspace(); ASSERT(pWorkspace); if(pWorkspace == NULL) { delete pTextPDU; pData->value = NULL; pData->length = 0; return; } pTextPDU->header.workspaceHandle = pWorkspace->GetThisObjectHandle(); TRACE_DEBUG(("Text >> Workspace handle = %d",pTextPDU->header.workspaceHandle ));
if(choice != textDeletePDU_chosen) { //
// Get all the attributes that changed
//
pTextPDU->attrib.attributesFlag = GetPresentAttribs(); SetTextAttrib(&pTextPDU->attrib); }
//
// Set the pointer for the data that is going to be encoded
//
pData->value = (ASN1octet_t *)pTextPDU; pData->length = length; }
void TextObj::UnDraw(void) { RECT rect; GetBoundsRect(&rect); g_pDraw->InvalidateSurfaceRect(&rect,TRUE); }
void TextObj::Draw(HDC hDC, BOOL thumbNail, BOOL bPrinting) {
if(!bPrinting) {
//
// Don't draw anything if we don't belong in this workspace
//
if(GetWorkspaceHandle() != g_pCurrentWorkspace->GetThisObjectHandle()) { return; } }
RECT clipBox; BOOL dbcsEnabled = GetSystemMetrics(SM_DBCSENABLED); INT *tabArray; UINT ch; int i,j; BOOL zoomed = g_pDraw->Zoomed(); int oldBkMode = 0; int iIndex = 0; POINT pointPos; int nLastTab; ABC abc; int iLength; TCHAR * strLine;
MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::Draw");
if(hDC == NULL) { hDC = g_pDraw->m_hDCCached; }
//
// Only draw anything if the bounding rectangle intersects the current
// clip box.
//
if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); }
//
// Select the font.
//
if (thumbNail) { TRACE_MSG(("Using thumbnail font")); g_pDraw->PrimeFont(hDC, m_hFontThumb, &m_textMetrics); } else { TRACE_MSG(("Using standard font")); g_pDraw->PrimeFont(hDC, m_hFont, &m_textMetrics); }
//
// Set the color and mode for drawing.
//
COLORREF rgb; GetPenColor(&rgb); ::SetTextColor(hDC, SET_PALETTERGB(rgb));
//
// Set the background to be transparent
//
oldBkMode = ::SetBkMode(hDC, TRANSPARENT);
//
// Calculate the bounding rectangle, accounting for the new font.
//
CalculateBoundsRect();
if (!::IntersectRect(&clipBox, &clipBox, &m_rect)) { TRACE_MSG(("No clip/bounds intersection")); return; }
//
// Get the start point for the text.
//
pointPos.x = m_rect.left + m_nKerningOffset; pointPos.y = m_rect.top;
//
// Loop through the text strings drawing each as we go.
//
for (iIndex = 0; iIndex < strTextArray.GetSize(); iIndex++) { //
// Get a reference to the line to be printed for convenience.
//
strLine = (LPTSTR)strTextArray[iIndex]; iLength = lstrlen(strLine);
//
// Only draw the line if there are any characters in it.
//
if (iLength > 0) { if (zoomed) { // if new fails just skip it
DBG_SAVE_FILE_LINE tabArray = new INT[iLength+1]; if( tabArray == NULL ) { ERROR_OUT(("Failed to allocate tabArray")); continue; }
// We are zoomed. Must calculate char spacings
// ourselfs so that they end up proportionally
// in the right places. TabbedTextOut will not
// do this right so we have to use ExtTextOut with
// a tab array.
// figure out tab array
j = 0; nLastTab = 0; for (i=0; i < iLength; i++) { ch = strLine[(int)i]; //Don't worry about DBCS here...
abc = GetTextABC(strLine, 0, i);
if( j > 0 ) tabArray[j-1] = abc.abcB - nLastTab;
nLastTab = abc.abcB; j++; }
// Now, strip out any tab chars so they don't interact
// in an obnoxious manner with the tab array we just
// made and so they don't make ugly little
// blocks when they are drawn.
for (i=0; i < iLength; i++) { ch = strLine[(int)i]; if ((dbcsEnabled) && (IsDBCSLeadByte((BYTE)ch))) i++; else if(strLine[(int)i] == '\t') strLine[i] = ' '; // blow off tab, tab array
// will compensate for this
}
// do it
::ExtTextOut(hDC, pointPos.x, pointPos.y, 0, NULL, strLine, iLength, tabArray);
delete tabArray; } else { POINT ptPos;
GetAnchorPoint(&ptPos);
// Not zoomed, just do it
::TabbedTextOut(hDC, pointPos.x, pointPos.y, strLine, iLength, 0, NULL, ptPos.x); } }
//
// Move to the next line.
//
ASSERT(m_textMetrics.tmHeight != DBG_UNINIT); pointPos.y += (m_textMetrics.tmHeight); }
//
// Do NOT draw focus if clipboard or printing
//
if (WasSelectedLocally() && (hDC == g_pDraw->m_hDCCached)) { DrawRect(); }
//
// Restore the old background mode.
//
::SetBkMode(hDC, oldBkMode); g_pDraw->UnPrimeFont(hDC);
//
// If we are drawing on top of a remote pointer, draw it.
//
BitmapObj* remotePointer = NULL; WBPOSITION pos = NULL; remotePointer = g_pCurrentWorkspace->RectHitRemotePointer(&m_rect, GetPenThickness()/2, NULL); while(remotePointer) { remotePointer->Draw(); remotePointer = g_pCurrentWorkspace->RectHitRemotePointer(&m_rect, GetPenThickness()/2, remotePointer->GetMyPosition()); }
}
void TextObj::SetPenColor(COLORREF rgb, BOOL isPresent) { ChangedPenColor(); m_bIsPenColorPresent = isPresent; if(!isPresent) { return; } m_penColor.rgbtRed = GetRValue(rgb); m_penColor.rgbtGreen = GetGValue(rgb); m_penColor.rgbtBlue = GetBValue(rgb);
}
BOOL TextObj::GetPenColor(COLORREF * rgb) { if(m_bIsPenColorPresent) { *rgb = RGB(m_penColor.rgbtRed, m_penColor.rgbtGreen, m_penColor.rgbtBlue); } return m_bIsPenColorPresent; }
BOOL TextObj::GetPenColor(RGBTRIPLE* rgb) { if(m_bIsPenColorPresent) { *rgb = m_penColor; } return m_bIsPenColorPresent; }
void TextObj::SetFillColor(COLORREF rgb, BOOL isPresent) { ChangedFillColor(); m_bIsFillColorPresent = isPresent; if(!isPresent) { return; } m_fillColor.rgbtRed = GetRValue(rgb); m_fillColor.rgbtGreen = GetGValue(rgb); m_fillColor.rgbtBlue = GetBValue(rgb);
}
BOOL TextObj::GetFillColor(COLORREF* rgb) { if(m_bIsFillColorPresent && rgb !=NULL) { *rgb = RGB(m_fillColor.rgbtRed, m_fillColor.rgbtGreen, m_fillColor.rgbtBlue); } return m_bIsFillColorPresent; }
BOOL TextObj::GetFillColor(RGBTRIPLE* rgb) { if(m_bIsFillColorPresent && rgb!= NULL) { *rgb = m_fillColor; } return m_bIsFillColorPresent; }
//
// Get the encoded buffer for Drawing Create PDU
//
void TextObj::GetEncodedCreatePDU(ASN1_BUF *pBuf) { SIPDU *sipdu = NULL; DBG_SAVE_FILE_LINE sipdu = (SIPDU *) new BYTE[sizeof(SIPDU)]; if(sipdu) { sipdu->choice = siNonStandardPDU_chosen; CreateNonStandardPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction, NonStandardTextID); CreateTextPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction.data, textCreatePDU_chosen); ((MSTextPDU *)sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value)->header.nonStandardPDU = textCreatePDU_chosen; ASN1_BUF encodedPDU; g_pCoder->Encode(sipdu, pBuf); if(sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value) { delete sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value; } delete sipdu; } else { TRACE_MSG(("Failed to create penMenu")); ::PostMessage(g_pMain->m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
} }
void TextObj::SendTextPDU(UINT choice) {
if(!g_pNMWBOBJ->CanDoText()) { return; }
SIPDU *sipdu = NULL; DBG_SAVE_FILE_LINE sipdu = (SIPDU *) new BYTE[sizeof(SIPDU)]; if(sipdu) { sipdu->choice = siNonStandardPDU_chosen; CreateNonStandardPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction, NonStandardTextID); CreateTextPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction.data, choice); if(sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value == NULL) { return; }
T120Error rc = SendT126PDU(sipdu); if(rc == T120_NO_ERROR) { ResetAttrib(); SIPDUCleanUp(sipdu); } } else { TRACE_MSG(("Failed to create sipdu")); ::PostMessage(g_pMain->m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0); }
}
//
// UI Created a new Drawing Object
//
void TextObj::SendNewObjectToT126Apps(void) { SendTextPDU(textCreatePDU_chosen); }
//
// UI Edited the Drawing Object
//
void TextObj::OnObjectEdit(void) { g_bContentsChanged = TRUE; SendTextPDU(textEditPDU_chosen); }
//
// UI Deleted the Drawing Object
//
void TextObj::OnObjectDelete(void) { g_bContentsChanged = TRUE; SendTextPDU(textDeletePDU_chosen); }
//
//
// Function: TextObj::SetFont
//
// Purpose: Set the font to be used for drawing
//
//
void TextObj::SetFont(HFONT hFont) { MLZ_EntryOut(ZONE_FUNCTION, "TextObj::SetFont");
// Get the font details
LOGFONT lfont; ::GetObject(hFont, sizeof(LOGFONT), &lfont);
//
// Pass the logical font into the SetFont() function
//
SetFont(&lfont); }
//
//
// Function: TextObj::SetText
//
// Purpose: Set the text of the object
//
//
void TextObj::SetText(TCHAR * strText) { // Remove all the current stored text
strTextArray.SetSize(0);
// Scan the text for carriage return and new-line characters
int iNext = 0; int iLast = 0; int textSize = lstrlen(strText); TCHAR savedChar[1];
//
// In this case, we don't know how many lines there will be. So we
// use Add() from the StrArray class.
//
while (iNext < textSize) { // Find the next carriage return or line feed
iNext += StrCspn(strText + iNext, "\r\n");
// Extract the text before the terminator
// and add it to the current list of text lines.
savedChar[0] = strText[iNext]; strText[iNext] = 0; strTextArray.Add((strText+iLast)); strText[iNext] = savedChar[0];
if (iNext < textSize) { // Skip the carriage return
if (strText[iNext] == '\r') iNext++;
// Skip a following new line (if there is one)
if (strText[iNext] == '\n') iNext++;
// Update the index of the start of the next line
iLast = iNext; } }
if(textSize) { // Calculate the bounding rectangle for the new text
CalculateBoundsRect(); ChangedText(); } }
//
//
// Function: TextObj::SetText
//
// Purpose: Set the text of the object
//
//
void TextObj::SetText(const StrArray& _strTextArray) { // Scan the text for carriage return and new-line characters
int iSize = _strTextArray.GetSize();
//
// In this case we know how many lines, so set that # then use SetAt()
// to stick text there.
//
strTextArray.RemoveAll(); strTextArray.SetSize(iSize);
int iNext = 0; for ( ; iNext < iSize; iNext++) { strTextArray.SetAt(iNext, _strTextArray[iNext]); }
// Calculate the new bounding rectangle
CalculateBoundsRect();
}
//
//
// Function: TextObj::SetFont(metrics)
//
// Purpose: Set the font to be used for drawing
//
//
void TextObj::SetFont(LOGFONT *pLogFont, BOOL bReCalc ) { HFONT hNewFont;
MLZ_EntryOut(ZONE_FUNCTION, "TextObj::SetFont");
// Ensure that the font can be resized by the zoom function
// (proof quality prevents font scaling).
pLogFont->lfQuality = DRAFT_QUALITY;
//zap FontAssociation mode (bug 3258)
pLogFont->lfClipPrecision |= CLIP_DFA_OVERRIDE;
// Always work in cell coordinates to get scaling right
TRACE_MSG(("Setting font height %d, width %d, face %s, family %d, precis %d", pLogFont->lfHeight,pLogFont->lfWidth,pLogFont->lfFaceName, pLogFont->lfPitchAndFamily, pLogFont->lfOutPrecision));
hNewFont = ::CreateFontIndirect(pLogFont); if (!hNewFont) { // Could not create the font
ERROR_OUT(("Failed to create font")); DefaultExceptionHandler(WBFE_RC_WINDOWS, 0); return; }
// We are now guaranteed to be able to delete the old font
if (m_hFont != NULL) { DeleteFont(m_hFont); } m_hFont = hNewFont;
// Calculate the line height for this font
ASSERT(g_pDraw); g_pDraw->PrimeFont(g_pDraw->GetCachedDC(), m_hFont, &m_textMetrics);
// Set up the thumbnail font, forcing truetype if not currently TT
if (!(m_textMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) { pLogFont->lfFaceName[0] = 0; pLogFont->lfOutPrecision = OUT_TT_PRECIS; TRACE_MSG(("Non-True type font")); }
if (m_hFontThumb != NULL) { ::DeleteFont(m_hFontThumb); m_hFontThumb = NULL; } m_hFontThumb = ::CreateFontIndirect(pLogFont); if (!m_hFontThumb) { // Could not create the font
ERROR_OUT(("Failed to create thumbnail font")); }
// Calculate the bounding rectangle, accounting for the new font
if( bReCalc ) CalculateBoundsRect();
ChangedFont();
g_pDraw->UnPrimeFont(g_pDraw->m_hDCCached); }
//
//
// Function: TextObj::CalculateRect
//
// Purpose: Calculate the bounding rectangle of a portion of the object
//
//
void TextObj::CalculateRect(int iStartX, int iStartY, int iStopX, int iStopY, LPRECT lprcResult) { RECT rcResult; RECT rcT; int iIndex;
MLZ_EntryOut(ZONE_FUNCTION, "TextObj::CalculateRect");
//
// NOTE:
// We must use an intermediate rectangle, so as not to disturb the
// contents of the passed-in one until done. lprcResult may be pointing
// to the current bounds rect, and we call functions from here that
// may need its current value.
//
// Initialize the result rectangle
::SetRectEmpty(&rcResult);
if (!strTextArray.GetSize()) { // Text is empty
goto DoneCalc; }
// Allow for special limit values and ensure that the start and stop
// character positions are in range.
if (iStopY == LAST_LINE) { iStopY = strTextArray.GetSize() - 1; } iStopY = min(iStopY, strTextArray.GetSize() - 1); iStopY = max(iStopY, 0);
if (iStopX == LAST_CHAR) { iStopX = lstrlen(strTextArray[iStopY]); } iStopX = min(iStopX, lstrlen(strTextArray[iStopY])); iStopX = max(iStopX, 0);
// Loop through the text strings, adding each to the rectangle
for (iIndex = iStartY; iIndex <= iStopY; iIndex++) { int iLeftX = ((iIndex == iStartY) ? iStartX : 0); int iRightX = ((iIndex == iStopY) ? iStopX : lstrlen(strTextArray[iIndex]));
GetTextRectangle(iIndex, iLeftX, iRightX, &rcT); ::UnionRect(&rcResult, &rcResult, &rcT); }
DoneCalc: *lprcResult = rcResult; }
//
//
// Function: TextObj::CalculateBoundsRect
//
// Purpose: Calculate the bounding rectangle of the object
//
//
void TextObj::CalculateBoundsRect(void) { // Set the new bounding rectangle
CalculateRect(0, 0, LAST_CHAR, LAST_LINE, &m_rect); }
//
//
// Function: TextObj::GetTextABC
//
// Purpose: Calculate the ABC numbers for a string of text
//
// COMMENT BY RAND: The abc returned is for the whole string, not just one
// char. I.e, ABC.abcA is the offset to the first glyph in
// the string, ABC.abcB is the sum of all of the glyphs and
// ABC.abcC is the trailing space after the last glyph.
// ABC.abcA + ABC.abcB + ABC.abcC is the total rendered
// length including overhangs.
//
// Note - we never use the A spacing so it is always 0
//
ABC TextObj::GetTextABC( LPCTSTR pText, int iStartX, int iStopX) { MLZ_EntryOut(ZONE_FUNCTION, "TextObj::GetTextABC"); ABC abcResult; HDC hDC; BOOL rc = FALSE; ABC abcFirst; ABC abcLast; BOOL zoomed = g_pDraw->Zoomed(); int nCharLast; int i; LPCTSTR pScanStr; ZeroMemory( (PVOID)&abcResult, sizeof abcResult ); ZeroMemory( (PVOID)&abcFirst, sizeof abcFirst ); ZeroMemory( (PVOID)&abcLast, sizeof abcLast );
// Get the standard size measure of the text
LPCTSTR pABC = (pText + iStartX); int pABCLength = iStopX - iStartX; hDC = g_pDraw->GetCachedDC(); g_pDraw->PrimeFont(hDC, m_hFont, &m_textMetrics);
//
// We must temporarily unzoom if we are currently zoomed since the
// weird Windows font handling will not give us the same answer for
// the text extent in zoomed mode for some TrueType fonts
//
if (zoomed) { ::ScaleViewportExtEx(hDC, 1, g_pDraw->ZoomFactor(), 1, g_pDraw->ZoomFactor(), NULL); }
DWORD size = ::GetTabbedTextExtent(hDC, pABC, pABCLength, 0, NULL);
// We now have the advance width of the text
abcResult.abcB = LOWORD(size); TRACE_MSG(("Basic text width is %d",abcResult.abcB));
// Allow for C space (or overhang)
if (iStopX > iStartX) { if (m_textMetrics.tmPitchAndFamily & TMPF_TRUETYPE) { if(GetSystemMetrics( SM_DBCSENABLED )) { // have to handle DBCS on both ends
if( IsDBCSLeadByte( (BYTE)pABC[0] ) ) { // pack multi byte char into a WORD for GetCharABCWidths
WORD wMultiChar = MAKEWORD( pABC[1], pABC[0] ); rc = ::GetCharABCWidths(hDC, wMultiChar, wMultiChar, &abcFirst); } else { // first char is SBCS
rc = ::GetCharABCWidths(hDC, pABC[0], pABC[0], &abcFirst ); }
// Check for DBCS as last char. Have to scan whole string to be sure
pScanStr = pABC; nCharLast = 0; for( i=0; i<pABCLength; i++, pScanStr++ ) { nCharLast = i; if( IsDBCSLeadByte( (BYTE)*pScanStr ) ) { i++; pScanStr++; } }
if( IsDBCSLeadByte( (BYTE)pABC[nCharLast] ) ) { // pack multi byte char into a WORD for GetCharABCWidths
ASSERT( (nCharLast+1) < pABCLength ); WORD wMultiChar = MAKEWORD( pABC[nCharLast+1], pABC[nCharLast] ); rc = ::GetCharABCWidths(hDC, wMultiChar, wMultiChar, &abcLast); } else { // last char is SBCS
rc = ::GetCharABCWidths(hDC, pABC[nCharLast], pABC[nCharLast], &abcLast ); } } else { // SBCS, no special fiddling, just call GetCharABCWidths()
rc = ::GetCharABCWidths(hDC, pABC[0], pABC[0], &abcFirst );
nCharLast = pABCLength-1; rc = rc && ::GetCharABCWidths(hDC, pABC[nCharLast], pABC[nCharLast], &abcLast ); }
TRACE_MSG(("abcFirst: rc=%d, a=%d, b=%d, c=%d", rc, abcFirst.abcA, abcFirst.abcB, abcFirst.abcC) ); TRACE_MSG(("abcLast: rc=%d, a=%d, b=%d, c=%d", rc, abcLast.abcA, abcLast.abcB, abcLast.abcC) ); }
if( rc ) { // The text was trutype and we got good abcwidths
// Give the C space of the last characters from
// the string as the C space of the text.
abcResult.abcA = abcFirst.abcA; abcResult.abcC = abcLast.abcC; } else { //
// Mock up C value for a non TT font by taking some of overhang as
// the negative C value.
//
//TRACE_MSG(("Using overhang -%d as C space",m_textMetrics.tmOverhang/2));
// Adjust B by -overhang to make update rect schoot
// far enough to the left so that the toes of italic cap A's
// don't get clipped. Ignore comment above.
abcResult.abcB -= m_textMetrics.tmOverhang; } }
//
// If we temporarily unzoomed then restore it now
//
if (zoomed) { ::ScaleViewportExtEx(hDC, g_pDraw->ZoomFactor(), 1, g_pDraw->ZoomFactor(), 1, NULL); }
TRACE_MSG(("Final text width is %d, C space %d",abcResult.abcB,abcResult.abcC));
return abcResult; }
//
//
// Function: TextObj::GetTextRectangle
//
// Purpose: Calculate the bounding rectangle of a portion of the object
//
//
void TextObj::GetTextRectangle(int iStartY, int iStartX, int iStopX, LPRECT lprc) { // ABC structures for text sizing
ABC abcText1; ABC abcText2; int iLeftOffset = 0; MLZ_EntryOut(ZONE_FUNCTION, "TextObj::GetTextRectangle");
ASSERT(iStartY < strTextArray.GetSize());
// Here we calculate the width of the text glyphs in which we
// are interested. In case there are tabs involved we must start
// with position 0 and get two lengths then subtract them
abcText1 = GetTextABC(strTextArray[iStartY], 0, iStopX);
if (iStartX > 0) { // The third param used to be iStartX-1 which is WRONG. It
// has to point to the first char pos past the string
// we are using.
abcText2 = GetTextABC(strTextArray[iStartY], 0, iStartX);
// Just use B part for offset. Adding A snd/or C to it moves the update
// rectangle too far to the right and clips the char
iLeftOffset = abcText2.abcB; } else { ZeroMemory( &abcText2, sizeof abcText2 ); }
//
// We need to allow for A and C space in the bounding rectangle. Use
// ABS function just to make sure we get a large enough rectangle.
//
// Move A and C from original offset calc to here for width of update
// rectangle. Add in tmOverhang (non zero for non-tt fonts) to compensate
// for the kludge in GetTextABC()....THIS EDITBOX CODE HAS GOT TO GO...
abcText1.abcB = abcText1.abcB - iLeftOffset + abs(abcText2.abcA) + abs(abcText2.abcC) + abs(abcText1.abcA) + abs(abcText1.abcC) + m_textMetrics.tmOverhang;
TRACE_DEBUG(("Left offset %d",iLeftOffset)); TRACE_DEBUG(("B width now %d",abcText1.abcB));
// Build the result rectangle.
// Note that we never return an empty rectangle. This allows for the
// fact that the Windows rectangle functions will ignore empty
// rectangles completely. This would cause the bounding rectangle
// calculation (for instance) to go wrong if the top or bottom lines
// in a text object were empty.
ASSERT(m_textMetrics.tmHeight != DBG_UNINIT); int iLineHeight = m_textMetrics.tmHeight + m_textMetrics.tmExternalLeading;
lprc->left = 0; lprc->top = 0; lprc->right = max(1, abcText1.abcB); lprc->bottom = iLineHeight; ::OffsetRect(lprc, iLeftOffset, iLineHeight * iStartY);
// rect is the correct width at this point but it might need to be schooted to
// the left a bit to allow for kerning of 1st letter (bug 469)
if( abcText1.abcA < 0 ) { ::OffsetRect(lprc, abcText1.abcA, 0); m_nKerningOffset = -abcText1.abcA; } else m_nKerningOffset = 0;
POINT pt; GetAnchorPoint(&pt); ::OffsetRect(lprc, pt.x, pt.y); }
|