// // GROBJ.CPP // Graphic Objects // // Copyright Microsoft 1998- // // PRECOMP #include "precomp.h" #define DECIMAL_PRECISION 10000 // // Local macros // #define min4(x1,x2,x3,x4) min((min((x1),(x2))),(min((x3),(x4)))) #define max4(x1,x2,x3,x4) max((max((x1),(x2))),(max((x3),(x4)))) // // CircleHit() // // Checks for overlap between circle at PcxPcy with uRadius and // lpHitRect. If overlap TRUE is returned, otherwise FALSE. // BOOL CircleHit( LONG Pcx, LONG Pcy, UINT uRadius, LPCRECT lpHitRect, BOOL bCheckPt ) { RECT hr = *lpHitRect; RECT ellipse; ellipse.left = Pcx - uRadius; ellipse.right= Pcx + uRadius; ellipse.bottom = Pcy + uRadius; ellipse.top = Pcy - uRadius; // check the easy thing first (don't use PtInRect) if( bCheckPt &&(lpHitRect->left >= ellipse.left)&&(ellipse.right >= lpHitRect->right)&& (lpHitRect->top >= ellipse.top)&&(ellipse.bottom >= lpHitRect->bottom)) { return( TRUE ); } // // The circle is just a boring ellipse // return EllipseHit(&ellipse, bCheckPt, uRadius, lpHitRect ); } // // EllipseHit() // // Checks for overlap between ellipse defined by lpEllipseRect and // lpHitRect. If overlap TRUE is returned, otherwise FALSE. // BOOL EllipseHit(LPCRECT lpEllipseRect, BOOL bBorderHit, UINT uPenWidth, LPCRECT lpHitRect ) { RECT hr = *lpHitRect; // Check easy thing first. If lpEllipseRect is inside lpHitRect // then we have a hit (no duh...) if( (hr.left <= lpEllipseRect->left)&&(hr.right >= lpEllipseRect->right)&& (hr.top <= lpEllipseRect->top)&&(hr.bottom >= lpEllipseRect->bottom) ) return( TRUE ); // If this is an ellipse.... // // * * ^ // * | b | Y // * | a +-------> X // *-------+-------- // | // // // Look for the ellipse hit. (x/a)^2 + (y/b)^2 = 1 // If it is > 1 than the point is outside the ellipse // If it is < 1 it is inside // LONG a,b,aOuter, bOuter, x, y, xCenter, yCenter; BOOL bInsideOuter = FALSE; BOOL bOutsideInner = FALSE; // // Calculate a and b // a = (lpEllipseRect->right - lpEllipseRect->left)/2; b = (lpEllipseRect->bottom - lpEllipseRect->top)/2; // // Get the center of the ellipse // xCenter = lpEllipseRect->left + a; yCenter = lpEllipseRect->top + b; // // a and b generates a inner ellipse // aOuter and bOuter generates a outer ellipse // aOuter = a + uPenWidth + 1; bOuter = b + uPenWidth + 1; a = a - 1; b = b - 1; // // Make our coordinates relative to the center of the ellipse // y = abs(hr.bottom - yCenter); x = abs(hr.right - xCenter); // // Be carefull not to divide by 0 // if((a && b && aOuter && bOuter) == 0) { return FALSE; } // // We are using LONG instead of double and we need to have some precision // that is why we multiply the equation of the ellipse // ((x/a)^2 + (y/b)^2 = 1) by DECIMAL_PRECISION // Note that the multiplication has to be done before the division, if we didn't do that // we will always get 0 or 1 for x/a // if(x*x*DECIMAL_PRECISION/(aOuter*aOuter) + y*y*DECIMAL_PRECISION/(bOuter*bOuter) <= DECIMAL_PRECISION) { bInsideOuter = TRUE; } if(x*x*DECIMAL_PRECISION/(a*a)+ y*y*DECIMAL_PRECISION/(b*b) >= DECIMAL_PRECISION) { bOutsideInner = TRUE; } // // If we are checking for border hit, // we need to be inside the outer ellipse and inside the inner // if( bBorderHit ) { return( bInsideOuter & bOutsideInner ); } // just need to be inside the outer ellipse else { return( bInsideOuter ); } } // // LineHit() // // Checks for overlap (a "hit") between lpHitRect and the line // P1P2 accounting for line width. If bCheckP1End or bCheckP2End is // TRUE then a circle of radius 0.5 * uPenWidth is also checked for // a hit to account for the rounded ends of wide lines. // // If a hit is found TRUE is returned, otherwise FALSE. // BOOL LineHit( LONG P1x, LONG P1y, LONG P2x, LONG P2y, UINT uPenWidth, BOOL bCheckP1End, BOOL bCheckP2End, LPCRECT lpHitRect ) { LONG uHalfPenWidth = uPenWidth/2; LONG a,b,x,y; x = lpHitRect->left + (lpHitRect->right - lpHitRect->left)/2; y = lpHitRect->bottom + (lpHitRect->top - lpHitRect->bottom)/2; if( (P1x == P2x)&&(P1y == P2y) ) { // just check one end point's circle return( CircleHit( P1x, P1y, uHalfPenWidth, lpHitRect, TRUE ) ); } // check rounded end at P1 if( bCheckP1End && CircleHit( P1x, P1y, uHalfPenWidth, lpHitRect, FALSE ) ) return( TRUE ); // check rounded end at P2 if( bCheckP2End && CircleHit( P2x, P2y, uHalfPenWidth, lpHitRect, FALSE ) ) return( TRUE ); // // The function of a line is Y = a.X + b // // a = (Y1-Y2)/(X1 -X2) // if we found a we get b = y1 -a.X1 // if(P1x == P2x) { a=0; b = DECIMAL_PRECISION*P1x; } else { a = (P1y - P2y)*DECIMAL_PRECISION/(P1x - P2x); b = DECIMAL_PRECISION*P1y - a*P1x; } // // Paralel to Y // if(P1x == P2x && ((x >= P1x - uHalfPenWidth) && x <= P1x + uHalfPenWidth)) { return TRUE; } // // Paralel to X // if(P1y == P2y && ((y >= P1y - uHalfPenWidth) && y <= P1y + uHalfPenWidth)) { return TRUE; } // // General line // return(( y*DECIMAL_PRECISION <= a*x + b + DECIMAL_PRECISION*uHalfPenWidth) & ( y*DECIMAL_PRECISION >= a*x + b - DECIMAL_PRECISION*uHalfPenWidth)); } // // // Function: ConstructGraphic // // Purpose: Construct a graphic from a page and handle // // DCWbGraphic* DCWbGraphic::ConstructGraphic(WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic) { PWB_GRAPHIC pHeader; DCWbGraphic* pGraphic; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ConstructGraphic(page, handle)"); // Get a pointer to the external graphic data // (Throws an exception if any errors occur) pHeader = PG_GetData(hPage, hGraphic); // Construct the graphic pGraphic = DCWbGraphic::ConstructGraphic(pHeader); // If we got the graphic, set its page and handle if (pGraphic != NULL) { pGraphic->m_hPage = hPage; pGraphic->m_hGraphic = hGraphic; } g_pwbCore->WBP_GraphicRelease(hPage, hGraphic, pHeader); return pGraphic; } DCWbGraphic* DCWbGraphic::ConstructGraphic(WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, PWB_GRAPHIC pHeader) { DCWbGraphic* pGraphic; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ConstructGraphic(page, pHeader)"); pGraphic = DCWbGraphic::ConstructGraphic(pHeader); // If we got the graphic, set its page and handle if (pGraphic != NULL) { pGraphic->m_hPage = hPage; pGraphic->m_hGraphic = hGraphic; } return pGraphic; } DCWbGraphic* DCWbGraphic::ConstructGraphic(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ConstructGraphic(data)"); TRACE_DEBUG(("Constructing graphic of type %hd", pHeader->type)); TRACE_DEBUG(("Length of graphic = %ld", pHeader->length)); TRACE_DEBUG(("Data offset = %hd", pHeader->dataOffset)); // Construct the internal representation of the graphic DCWbGraphic* pGraphic = NULL; if (pHeader == NULL) { return NULL; } switch (pHeader->type) { case TYPE_GRAPHIC_LINE: pGraphic = new DCWbGraphicLine(pHeader); break; case TYPE_GRAPHIC_FREEHAND: pGraphic = new DCWbGraphicFreehand(pHeader); break; case TYPE_GRAPHIC_RECTANGLE: pGraphic = new DCWbGraphicRectangle(pHeader); break; case TYPE_GRAPHIC_FILLED_RECTANGLE: pGraphic = new DCWbGraphicFilledRectangle(pHeader); break; case TYPE_GRAPHIC_ELLIPSE: pGraphic = new DCWbGraphicEllipse(pHeader); break; case TYPE_GRAPHIC_FILLED_ELLIPSE: pGraphic = new DCWbGraphicFilledEllipse(pHeader); break; case TYPE_GRAPHIC_TEXT: pGraphic = new DCWbGraphicText(pHeader); break; case TYPE_GRAPHIC_DIB: pGraphic = new DCWbGraphicDIB(pHeader); break; default: // Do nothing, the object pointer is already set to NULL break; } if (!pGraphic) { ERROR_OUT(("ConstructGraphic failing; can't allocate object of type %d", pHeader->type)); } return pGraphic; } // // // Function: CopyGraphic // // Purpose: Construct a graphic from a pointer. This function makes a // complete internal copy of the graphic data. // // DCWbGraphic* DCWbGraphic::CopyGraphic(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::CopyGraphic(PWB_GRAPHIC)"); // Construct the graphic DCWbGraphic* pGraphic = DCWbGraphic::ConstructGraphic(pHeader); // Copy the extra data if (pGraphic != NULL) { pGraphic->CopyExtra(pHeader); } return pGraphic; } // // // Function: DCWbGraphic constructor // // Purpose: Construct a new graphic object. // // DCWbGraphic::DCWbGraphic(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::DCWbGraphic"); // Do basic initialization Initialize(); // Convert the external data header to the internal member variables if (pHeader != NULL) { ReadHeader(pHeader); // Convert the extra data for the specific object // (not all objects have extra data). ReadExtra(pHeader); } } DCWbGraphic::DCWbGraphic(WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::DCWbGraphic"); // Do the basic initialization Initialize(); ASSERT(hPage != WB_PAGE_HANDLE_NULL); m_hPage = hPage; ASSERT(hGraphic != NULL); m_hGraphic = hGraphic; // Read the header data ReadExternal(); } DCWbGraphic::~DCWbGraphic( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphic::ReadExternal // // Purpose: Read the graphic data from an externally stored graphic. // The external graphic to be used is specified by the // hGraphic member. // // void DCWbGraphic::ReadExternal(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ReadExternal"); ASSERT(m_hPage != WB_PAGE_HANDLE_NULL); ASSERT(m_hGraphic != NULL); // Lock the object data in the page PWB_GRAPHIC pHeader = PG_GetData(m_hPage, m_hGraphic); // Convert the external data header to the internal member variables ReadHeader(pHeader); // Convert the extra data for the specific object // (not all objects have extra data). ReadExtra(pHeader); // Release the data in the page g_pwbCore->WBP_GraphicRelease(m_hPage, m_hGraphic, pHeader); // Show that we are no longer changed since last read/write m_bChanged = FALSE; } // // // Function: DCWbGraphic::ReadHeader // // Purpose: Convert the external representation of the graphic's header // to the internal format. // // void DCWbGraphic::ReadHeader(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ReadHeader"); // Get the length of the object m_dwExternalLength = pHeader->length; // Convert the external data header to the internal member variables // Bounding rectangle m_boundsRect.left = pHeader->rectBounds.left; m_boundsRect.top = pHeader->rectBounds.top; m_boundsRect.right = pHeader->rectBounds.right; m_boundsRect.bottom = pHeader->rectBounds.bottom; // Defining rectangle m_rect.left = pHeader->rect.left; m_rect.top = pHeader->rect.top; m_rect.right = pHeader->rect.right; m_rect.bottom = pHeader->rect.bottom; // Pen color m_clrPenColor = RGB(pHeader->color.red, pHeader->color.green, pHeader->color.blue); m_clrPenColor = SET_PALETTERGB( m_clrPenColor ); // make it do color matching // Pen width m_uiPenWidth = pHeader->penWidth; // Pen style m_iPenStyle = pHeader->penStyle; // Raster operation m_iPenROP = pHeader->rasterOp; // Get the lock indication m_uiLockState = pHeader->locked; // Get the drawing tool type if (pHeader->toolType == WBTOOL_TEXT) m_toolType = TOOLTYPE_TEXT; else m_toolType = TOOLTYPE_PEN; } // // // Function: DCWbGraphic::WriteExternal // // Purpose: Write the graphic's details to a flat WB_GRAPHIC structure // // void DCWbGraphic::WriteExternal(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::WriteExternal"); // Write the header WriteHeader(pHeader); // Write the extra data WriteExtra(pHeader); } // // // Function: DCWbGraphic::WriteHeader // // Purpose: Write the graphic's header details to a flat WB_GRAPHIC // structure. // // void DCWbGraphic::WriteHeader(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::WriteHeader"); // Convert the internal data to the external header format // Init struct FillMemory(pHeader, sizeof (WB_GRAPHIC), 0 ); // Calculate the external length pHeader->length = CalculateExternalLength(); // Set the type of graphic pHeader->type = (TSHR_UINT16)Type(); // Assume that there is no extra data pHeader->dataOffset = sizeof(WB_GRAPHIC); // Bounding rectangle pHeader->rectBounds.left = (short)m_boundsRect.left; pHeader->rectBounds.top = (short)m_boundsRect.top; pHeader->rectBounds.right = (short)m_boundsRect.right; pHeader->rectBounds.bottom = (short)m_boundsRect.bottom; // Defining rectangle pHeader->rect.left = (short)m_rect.left; pHeader->rect.top = (short)m_rect.top; pHeader->rect.right = (short)m_rect.right; pHeader->rect.bottom = (short)m_rect.bottom; // Pen color pHeader->color.red = GetRValue(m_clrPenColor); pHeader->color.green = GetGValue(m_clrPenColor); pHeader->color.blue = GetBValue(m_clrPenColor); // Pen width pHeader->penWidth = (TSHR_UINT16)m_uiPenWidth; // Pen style pHeader->penStyle = (TSHR_UINT16)m_iPenStyle; // Raster operation pHeader->rasterOp = (TSHR_UINT16)m_iPenROP; // Set the lock indicator pHeader->locked = (BYTE) m_uiLockState; // Set the drawing method pHeader->smoothed = FALSE; // Set the drawing tool type if (m_toolType == TOOLTYPE_TEXT) pHeader->toolType = WBTOOL_TEXT; else pHeader->toolType = WBTOOL_PEN; } // // // Function: Initialize // // Purpose: Initialize the member variables // // void DCWbGraphic::Initialize(void) { m_hPage = WB_PAGE_HANDLE_NULL; m_hGraphic = NULL; m_bChanged = TRUE; m_uiLockState = WB_GRAPHIC_LOCK_NONE; // // Set default graphic attributes // ::SetRectEmpty(&m_boundsRect); ::SetRectEmpty(&m_rect); m_clrPenColor = RGB(0, 0, 0); // Black pen color m_uiPenWidth = 1; // One unit width m_iPenROP = R2_COPYPEN; // Standard drawing ROP m_iPenStyle = PS_INSIDEFRAME; // Solid pen to be used m_toolType = TOOLTYPE_PEN; } // // // Function: Copy // // Purpose: Return a copy of the graphic. The graphic returned has all // its data read into local memory. The returned graphic has // the same page as the copied graphic, but a NULL handle. // // DCWbGraphic* DCWbGraphic::Copy(void) const { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Copy"); // Get a pointer to the external graphic data // (Throws an exception if any errors occur) PWB_GRAPHIC pHeader = PG_GetData(m_hPage, m_hGraphic); // Construct the graphic DCWbGraphic* pGraphic = DCWbGraphic::CopyGraphic(pHeader); // If we got the graphic, set its page and handle if (pGraphic != NULL) { pGraphic->m_hPage = m_hPage; pGraphic->m_hGraphic = NULL; } // Release the data g_pwbCore->WBP_GraphicRelease(m_hPage, m_hGraphic, pHeader); return pGraphic; } // // // Function: DCWbGraphic::SetBoundsRect // // Purpose: Set the bounding rectangle of the object // // void DCWbGraphic::SetBoundsRect(LPCRECT lprc) { m_boundsRect = *lprc; } // // // Function: DCWbGraphic::SetRect // // Purpose: Set the defining rectangle of the object // // void DCWbGraphic::SetRect(LPCRECT lprc) { m_rect = *lprc; NormalizeRect(&m_rect); // Show that we have been changed m_bChanged = TRUE; } void DCWbGraphic::SetRectPts(POINT point1, POINT point2) { RECT rc; rc.left = point1.x; rc.top = point1.y; rc.right = point2.x; rc.bottom = point2.y; SetRect(&rc); } // // // Function: DCWbGraphic::PointInBounds // // Purpose: Return TRUE if the specified point lies in the bounding // rectangle of the graphic object. // // BOOL DCWbGraphic::PointInBounds(POINT point) { return(::PtInRect(&m_boundsRect, point)); } // // // Function: DCWbGraphic::MoveBy // // Purpose: Translate the object by the offset specified // // void DCWbGraphic::MoveBy(int cx, int cy) { // Move the bounding rectangle ::OffsetRect(&m_boundsRect, cx, cy); // Show that we have been changed m_bChanged = TRUE; } // // // Function: DCWbGraphic::MoveTo // // Purpose: Move the object to an absolute position // // void DCWbGraphic::MoveTo(int x, int y) { // Calculate the offset needed to translate the object from its current // position to the required position. x -= m_boundsRect.left; y -= m_boundsRect.top; MoveBy(x, y); } // // // Function: DCWbGraphic::GetPosition // // Purpose: Return the top left corner of the object's bounding // rectangle // // void DCWbGraphic::GetPosition(LPPOINT lppt) { lppt->x = m_boundsRect.left; lppt->y = m_boundsRect.top; } // // // Function: DCWbGraphic::NormalizeRect // // Purpose: Normalize a rectangle ensuring that the top left is above // and to the left of the bottom right. // // void DCWbGraphic::NormalizeRect(LPRECT lprc) { int tmp; if (lprc->right < lprc->left) { tmp = lprc->left; lprc->left = lprc->right; lprc->right = tmp; } if (lprc->bottom < lprc->top) { tmp = lprc->top; lprc->top = lprc->bottom; lprc->bottom = tmp; } } // // // Function: DCWbGraphic::SetColor // // Purpose: Set the object color. // // void DCWbGraphic::SetColor(COLORREF color) { color = SET_PALETTERGB( color ); // make it use color matching if (m_clrPenColor != color) { // Save the new color m_clrPenColor = color; // Show that we have been changed m_bChanged = TRUE; } } // // // Function: DCWbGraphic::SetROP // // Purpose: Set the object raster operation // // void DCWbGraphic::SetROP(int iPenROP) { // If the new ROP is different if (m_iPenROP != iPenROP) { // Save the new ROP m_iPenROP = iPenROP; // Show that we have been changed m_bChanged = TRUE; } } // // // Function: DCWbGraphic::SetPenStyle // // Purpose: Set the object pen style // // void DCWbGraphic::SetPenStyle(int iPenStyle) { // If the new style is different if (m_iPenStyle != iPenStyle) { // Save the new pen style m_iPenStyle = iPenStyle; // Show that the graphic has been changed m_bChanged = TRUE; } } // // // Function: DCWbGraphic::SetPenWidth // // Purpose: Set the pen width for the object. // // void DCWbGraphic::SetPenWidth(UINT uiWidth) { // If the new width is different if (m_uiPenWidth != uiWidth) { // Save the width given m_uiPenWidth = uiWidth; // Update the bounding rectangle CalculateBoundsRect(); // Show that we have been changed m_bChanged = TRUE; } } // // // Function: IsTopmost // // Purpose: Return TRUE if this graphic is topmost on its page // // BOOL DCWbGraphic::IsTopmost(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::IsTopmost"); ASSERT(m_hGraphic != NULL); return PG_IsTopmost(m_hPage, this); } // // // Function: AddToPageLast // // Purpose: Add the graphic to the specified page // // void DCWbGraphic::AddToPageLast(WB_PAGE_HANDLE hPage) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::AddToPageLast"); ASSERT(m_hGraphic == NULL); // Get the length of the flat representation DWORD length = CalculateExternalLength(); // Allocate memory for the graphic PWB_GRAPHIC pHeader = PG_AllocateGraphic(hPage, length); if(pHeader == NULL) { return; } // Write the graphic details to the memory WriteExternal(pHeader); // Add the flat representation to the page WB_GRAPHIC_HANDLE hGraphic = NULL; UINT uiReturn; uiReturn = g_pwbCore->WBP_GraphicAddLast(hPage, pHeader, &hGraphic); if (uiReturn != 0) { DefaultExceptionHandler(WBFE_RC_WB, uiReturn); return; } // Show that we have not changed since the last write m_bChanged = FALSE; // Save the page to which this graphic now belongs m_hPage = hPage; m_hGraphic = hGraphic; } // // // Function: ForceReplace // // Purpose: Write the object to external storage, replacing what is // already there, even if the object hasn't changed. // // void DCWbGraphic::ForceReplace(void) { if( Type() != 0 ) { m_bChanged = TRUE; this->Replace(); } } // // // Function: Replace // // Purpose: Write the object to external storage, replacing what is // already there. // // void DCWbGraphic::Replace(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Replace"); ASSERT(m_hGraphic != NULL); // Only do the replace if we have been changed if (m_bChanged == TRUE) { TRACE_MSG(("Replacing the graphic in the page")); // Get the length of the flat representation DWORD length = CalculateExternalLength(); // Allocate memory for the graphic PWB_GRAPHIC pHeader = PG_AllocateGraphic(m_hPage, length); if(pHeader == NULL) { return; } // Write the graphic details to the memory WriteExternal(pHeader); // Replace the graphic PG_GraphicReplace(m_hPage, &m_hGraphic, pHeader); // Show that we have not changed since the last update m_bChanged = FALSE; } } // // // Function: ReplaceConfirm // // Purpose: Confirm the replace of the graphic // // void DCWbGraphic::ReplaceConfirm(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::ReplaceConfirm"); ASSERT(m_hGraphic != NULL); // Confirm the update g_pwbCore->WBP_GraphicReplaceConfirm(m_hPage, m_hGraphic); // Read the new details ReadExternal(); } void DCWbGraphic::ForceUpdate(void) { if ((Type() != 0) && m_hGraphic) { m_bChanged = TRUE; this->Update(); } } // // // Function: Update // // Purpose: Write the header of the graphic to external storage // // void DCWbGraphic::Update(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Update"); ASSERT(m_hGraphic != NULL); // Only make the update if the graphic has changed if (m_bChanged) { // Allocate memory for the update graphic TRACE_MSG(("Graphic has changed")); DWORD length = sizeof(WB_GRAPHIC); PWB_GRAPHIC pHeader; if( (pHeader = PG_AllocateGraphic(m_hPage, length)) != NULL ) { // Write the header details to the allocated memory pHeader->type = (TSHR_UINT16)Type(); WriteHeader(pHeader); // Update the header in the page PG_GraphicUpdate(m_hPage, &m_hGraphic, pHeader); } // Show that we have not changed since the last update m_bChanged = FALSE; } } // // // Function: UpdateConfirm // // Purpose: Confirm the update of the graphic // // void DCWbGraphic::UpdateConfirm(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::UpdateConfirm"); ASSERT(m_hGraphic != NULL); // Confirm the update g_pwbCore->WBP_GraphicUpdateConfirm(m_hPage, m_hGraphic); // Read the new details ReadExternal(); } // // // Function: Delete // // Purpose: Remove the graphic from its page // // void DCWbGraphic::Delete(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Delete"); ASSERT(m_hPage != WB_PAGE_HANDLE_NULL); ASSERT(m_hGraphic != NULL); // Delete the graphic PG_GraphicDelete(m_hPage, *this); // Reset the handles for this graphic - it is now deleted m_hPage = WB_PAGE_HANDLE_NULL; m_hGraphic = NULL; // Show that we have changed (an add is required to save the graphic) m_bChanged = TRUE; } // // // Function: DeleteConfirm // // Purpose: Confirm the delete of the graphic // // void DCWbGraphic::DeleteConfirm(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::DeleteConfirm"); ASSERT(m_hGraphic != NULL); // Confirm the update g_pwbCore->WBP_GraphicDeleteConfirm(m_hPage, m_hGraphic); // Reset the graphic page and handle (they are no longer useful) m_hPage = WB_PAGE_HANDLE_NULL; m_hGraphic = NULL; } // // // Function: Lock // // Purpose: Lock the graphic // // void DCWbGraphic::Lock(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Lock"); // If we are not already locked if( Type() != 0 ) { if (m_uiLockState == WB_GRAPHIC_LOCK_NONE) { m_bChanged = TRUE; m_uiLockState = WB_GRAPHIC_LOCK_LOCAL; } } } // // // Function: Unlock // // Purpose: Unlock the graphic // // void DCWbGraphic::Unlock(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphic::Unlock"); // If we are currently locked if( Type() != 0 ) { if (m_uiLockState == WB_GRAPHIC_LOCK_LOCAL) { // Lock & release PWB_GRAPHIC pHeader = PG_GetData(m_hPage, m_hGraphic); g_pwbCore->WBP_GraphicRelease(m_hPage, m_hGraphic, pHeader); m_uiLockState = WB_GRAPHIC_LOCK_NONE; g_pwbCore->WBP_GraphicUnlock(m_hPage, m_hGraphic); } } } // // // Function: DCWbGraphicMarker::DCWbGraphicMarker // // Purpose: Constructors for marker objects // // DCWbGraphicMarker::DCWbGraphicMarker() { HBITMAP hBmpMarker; // Set up a checked pattern to draw the marker rect with WORD bits[] = {204, 204, 51, 51, 204, 204, 51, 51}; // Create the brush to be used to draw the marker rectangle hBmpMarker = ::CreateBitmap(8, 8, 1, 1, bits); m_hMarkerBrush = ::CreatePatternBrush(hBmpMarker); ::DeleteBitmap(hBmpMarker); MarkerList.EmptyList(); ::SetRectEmpty(&m_rect); m_bMarkerPresent = FALSE; } DCWbGraphicMarker::~DCWbGraphicMarker() { if (m_hMarkerBrush != NULL) { DeleteBrush(m_hMarkerBrush); m_hMarkerBrush = NULL; } } // // // Function: DCWbGraphicMarker::SetRect // // Purpose: Set the rectangle for the object // // BOOL DCWbGraphicMarker::SetRect(LPCRECT lprc, DCWbGraphic *pGraphic, BOOL bRedraw, BOOL bLockObject ) { DCWbGraphic *pMarker; BOOL bGraphicAdded = FALSE; LPRECT pmMarker; // Save the new rectangle m_rect = *lprc; NormalizeRect(&m_rect); // Calculate the new bounding rectangle of the entire marker CalculateBoundsRect(); // Calculate the marker rectangles CalculateMarkerRectangles(); if( (pMarker = HasAMarker( pGraphic )) != NULL ) delete pMarker; // allow select only if object is not locked - bug 2185 if( !pGraphic->Locked()) { // add/replace pGraphic|markerrect pair to list pmMarker = new RECT; if (!pmMarker) { ERROR_OUT(("Failed to create RECT object")); } else { *pmMarker = m_markerRect; MarkerList.SetAt( (void *)pGraphic, pmMarker); ASSERT(g_pDraw); DrawRect(g_pDraw->GetCachedDC(), pmMarker, FALSE, NULL ); bGraphicAdded = TRUE; } if( bLockObject ) { // lock the object if we don't already have it locked // to keep anybody else from selecting it if( !pGraphic->GotLock() ) { pGraphic->Lock(); if( pGraphic->Handle() != NULL ) pGraphic->ForceUpdate(); // if valid object force lock NOW } } } if( bRedraw && m_bMarkerPresent ) { ASSERT(g_pDraw); ::UpdateWindow(g_pDraw->m_hwnd); } // set m_boundsRect to real bounds GetBoundsRect(&m_boundsRect); return( bGraphicAdded ); } // // // Function: DCWbGraphicMarker::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicMarker::CalculateBoundsRect(void) { // Generate the new bounding rectangle m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); ::InflateRect(&m_boundsRect, m_uiPenWidth, m_uiPenWidth); } // // // Function: DCWbGraphicMarker::CalculateMarkerRectangles // // Purpose: Calculate the rectangles for the marker handles // // void DCWbGraphicMarker::CalculateMarkerRectangles(void) { m_markerRect = m_boundsRect; ::InflateRect(&m_markerRect, 1-m_uiPenWidth, 1-m_uiPenWidth); } // // // Function: DCWbGraphicMarker::PointInMarker // // Purpose: Calculate whether the given point is in one of the marker // rectangles. // // int DCWbGraphicMarker::PointInMarker(POINT point) { return(NO_HANDLE); } void DCWbGraphicMarker::DrawRect ( HDC hDC, LPCRECT pMarkerRect, BOOL bDrawObject, DCWbGraphic * pGraphic ) { int nOldROP; COLORREF crOldTextColor; COLORREF crOldBkColor; nOldROP = ::SetROP2(hDC, R2_COPYPEN); crOldTextColor = ::SetTextColor(hDC, RGB(0, 0, 0)); ASSERT(g_pDraw); crOldBkColor = ::SetBkColor(hDC, ::GetSysColor(COLOR_WINDOW)); if (pMarkerRect != NULL) { if( bDrawObject ) pGraphic->Draw(hDC ); // draw object instead of rect else ::FrameRect(hDC, pMarkerRect, m_hMarkerBrush); // draw rect } ::SetROP2(hDC, nOldROP); ::SetTextColor(hDC, crOldTextColor); ::SetBkColor(hDC, crOldBkColor); } // // // Function: DCWbGraphicMarker::Draw // // Purpose: Draw the marker object // // void DCWbGraphicMarker::Draw(HDC hDC, BOOL bDrawObjects) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; // if marker is not up, do nuthin if( !m_bMarkerPresent ) return; posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); DrawRect(hDC, pMarkerRect, bDrawObjects, pGraphic ); } } void DCWbGraphicMarker::UndrawRect ( HDC hDC, WbDrawingArea * pDrawingArea, LPCRECT pMarkerRect ) { int nOldROP; COLORREF crOldTextColor; COLORREF crOldBkColor; if (pMarkerRect != NULL) { // set up context to erase marker rect nOldROP = ::SetROP2(hDC, R2_COPYPEN); ASSERT(g_pDraw); crOldTextColor = ::SetTextColor(hDC, ::GetSysColor(COLOR_WINDOW)); crOldBkColor = ::SetBkColor(hDC, ::GetSysColor(COLOR_WINDOW)); ::FrameRect(hDC, pMarkerRect, m_hMarkerBrush); UndrawMarker( pMarkerRect ); // invalidate so underlying objects will repair window ::SetROP2(hDC, nOldROP); ::SetTextColor(hDC, crOldTextColor); ::SetBkColor(hDC, crOldBkColor); } } // // // Function: DCWbGraphicMarker::Undraw // // Purpose: Undraw the marker object // // void DCWbGraphicMarker::Undraw(HDC hDC, WbDrawingArea * pDrawingArea) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); UndrawRect(hDC, pDrawingArea, pMarkerRect); } } void DCWbGraphicMarker::DeleteAllMarkers( DCWbGraphic *pLastSelectedGraphic, BOOL bLockLastSelectedGraphic ) { POSITION posFirst; DCWbGraphic *pGraphic; LPRECT pMarkerRect; BOOL bAddLastBack = FALSE; if( MarkerList.IsEmpty() ) return; // nuthin to do // let each object clean itself up posFirst = MarkerList.GetHeadPosition(); while( posFirst != NULL ) { MarkerList.GetNextAssoc( posFirst, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { if( pGraphic == pLastSelectedGraphic ) { // have to put this one back since somebody up there needs it bAddLastBack = TRUE; // delete key but don't delete object DeleteMarker( pGraphic ); } else { // obj will call DeleteMarker() delete pGraphic; } } else { // nobody home, remove key ourselves DeleteMarker( pGraphic ); } } // put last selected object back if needed if( bAddLastBack && (pLastSelectedGraphic != NULL) ) { RECT rcT; pLastSelectedGraphic->GetBoundsRect(&rcT); SetRect(&rcT, pLastSelectedGraphic, FALSE, bLockLastSelectedGraphic ); } // if marker is not up, don't redraw immediately if( !m_bMarkerPresent ) return; ASSERT(g_pDraw); if (g_pDraw->m_hwnd != NULL ) ::UpdateWindow(g_pDraw->m_hwnd); } // // Deletes DCWbGraphic/LPRECT pair corresponding to pGraphic // void DCWbGraphicMarker::DeleteMarker( DCWbGraphic *pGraphic ) { LPRECT pMarkerRect; if( MarkerList.IsEmpty() ) return; if( MarkerList.Lookup( (void *)pGraphic, (void *&)pMarkerRect ) ) { if( pMarkerRect != NULL ) { ASSERT(g_pDraw); UndrawRect(g_pDraw->GetCachedDC(), g_pDraw, pMarkerRect ); delete pMarkerRect; } MarkerList.RemoveKey( (void *)pGraphic ); // set m_boundsRect to real bounds GetBoundsRect(&m_boundsRect); // pGraphic should be locked by us since it was selected // but check to be sure since this might be coming from // another user that beat us to the lock. if( pGraphic->GotLock() ) { pGraphic->Unlock(); if( pGraphic->Handle() != NULL ) pGraphic->ForceUpdate(); } } // if marker is not up, don't redraw immediately if( !m_bMarkerPresent ) return; } // // Sees if pGraphic->Handle() is in marker list and returns obj // DCWbGraphic *DCWbGraphicMarker::HasAMarker( DCWbGraphic *pGraphic ) { POSITION posNext; DCWbGraphic *pSearchGraphic; LPRECT pMarkerRect; if( MarkerList.IsEmpty() ) return( NULL ); posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pSearchGraphic, (void *&)pMarkerRect ); if( (pSearchGraphic != NULL)&& (pSearchGraphic->Handle() == pGraphic->Handle()) ) { return( pSearchGraphic ); } } return( NULL ); } // // Gets last marker // DCWbGraphic *DCWbGraphicMarker::LastMarker( void ) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; pGraphic = NULL; if( !MarkerList.IsEmpty() ) { // this isn't eactly right, just return head of list for now posNext = MarkerList.GetHeadPosition(); if( posNext != NULL ) MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); } return( pGraphic ); } void DCWbGraphicMarker::UndrawMarker(LPCRECT pMarkerRect ) { RECT rect; ASSERT(g_pDraw); if( (pMarkerRect != NULL) && (g_pDraw->m_hwnd != NULL) ) { rect = *pMarkerRect; g_pDraw->SurfaceToClient(&rect); ::InvalidateRect(g_pDraw->m_hwnd, &rect, FALSE); } } int DCWbGraphicMarker::GetNumMarkers( void ) { int count = 0; POSITION pos; pos = MarkerList.GetHeadPosition(); while(pos) { count ++; MarkerList.GetNext(pos); } return count; } void DCWbGraphicMarker::MoveBy(int cx, int cy) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call MoveBy for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { pGraphic->MoveBy(cx, cy); } } } DCWbGraphic::MoveBy(cx, cy); // move marker too } void DCWbGraphicMarker::Update( void ) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) pGraphic->Update(); } } } BOOL DCWbGraphicMarker::PointInBounds(POINT pt) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; RECT rectHit; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { MAKE_HIT_RECT(rectHit, pt ); if( pGraphic->PointInBounds(pt)&& pGraphic->CheckReallyHit( &rectHit ) ) return( TRUE ); } } } return( FALSE ); } // // Returns a rect that is the union of all the items in the marker // void DCWbGraphicMarker::GetBoundsRect(LPRECT lprc) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; RECT rc; ::SetRectEmpty(lprc); if( !MarkerList.IsEmpty()) { posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { pGraphic->GetBoundsRect(&rc); ::UnionRect(lprc, lprc, &rc); } } } } void DCWbGraphicMarker::SetColor(COLORREF color) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) pGraphic->SetColor( color ); } } } void DCWbGraphicMarker::SetPenWidth(UINT uiWidth) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) pGraphic->SetPenWidth(uiWidth); } } } void DCWbGraphicMarker::SetSelectionFont(HFONT hFont) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( (pGraphic != NULL)&& pGraphic->IsGraphicTool() == enumGraphicText) { // Change the font of the object ((DCWbGraphicText*)pGraphic)->SetFont(hFont); // Replace the object pGraphic->Replace(); } } } } // // Deletes each marker obj for all connections // void DCWbGraphicMarker::DeleteSelection( void ) { POSITION posNext; DCWbGraphic *pGraphic; DCWbGraphic *pGraphicCopy; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { // make a copy for trash can pGraphicCopy = pGraphic->Copy(); // throw in trash if( pGraphicCopy != NULL ) { g_pMain->m_LastDeletedGraphic.CollectTrash( pGraphicCopy ); } // delete obj g_pDraw->DeleteGraphic( pGraphic ); } } DeleteAllMarkers( NULL ); } } // // Brings eaach marker obj to top // void DCWbGraphicMarker::BringToTopSelection( void ) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { // move obj to top UINT uiReturn; uiReturn = g_pwbCore->WBP_GraphicMove(g_pDraw->Page(), pGraphic->Handle(), LAST); if (uiReturn != 0) { DefaultExceptionHandler(WBFE_RC_WB, uiReturn); return; } } } } } // // Sends each marker object to back // void DCWbGraphicMarker::SendToBackSelection( void ) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; if( !MarkerList.IsEmpty() ) { // Call Update for each selected obj posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pGraphic != NULL ) { UINT uiReturn; // move obj to top uiReturn = g_pwbCore->WBP_GraphicMove(g_pDraw->Page(), pGraphic->Handle(), FIRST); if (uiReturn != 0) { DefaultExceptionHandler(WBFE_RC_WB, uiReturn); return; } } } } } // // Copy marker to clipboard using CLIPBOARD_PRIVATE_MULTI_OBJ format: // [ RECT : marker rect ] // [ DWORD : number of objects ] // [ DWORD : byte length of 1st object ] // [ WB_GRAPHIC : header data for first object ] // [ DWORD : byte length of 2nd object ] // [ WB_GRAPHIC : header data for 2nd object ] // : // : // [ DWORD : byte length of last object ] // [ WB_GRAPHIC : header data for last object ] // [ DWORD : 0 (marks end of object data) ] // BOOL DCWbGraphicMarker::RenderPrivateMarkerFormat( void ) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; DWORD nBufSize; DWORD nObjSize; DWORD nNumObjs; BYTE *buf; BYTE *pbuf; HANDLE hbuf; PWB_GRAPHIC pHeader; WB_GRAPHIC_HANDLE hGraphic; if( MarkerList.IsEmpty() ) return( TRUE ); // nuthin to do // Have to make two passes. The first one figures out how much // data we have, the second copies the data. // figure out how much data we've got nBufSize = sizeof (RECT) + sizeof (DWORD); // marker rect and object // count are first nNumObjs = 0; posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( (pGraphic != NULL)&& ((hGraphic = pGraphic->Handle()) != NULL)&& ((pHeader = PG_GetData(pGraphic->Page(), hGraphic )) != NULL) ) { nBufSize += (DWORD)(pHeader->length + sizeof(DWORD)); g_pwbCore->WBP_GraphicRelease(pGraphic->Page(), hGraphic, pHeader); // count objects instead of using MarkerList.GetCount() // in case we have an error or something (bad object, // leaky core, who knows...) nNumObjs++; } } // Add one more DWORD at end. This will be set to 0 below // to mark the end of the buffer. nBufSize += sizeof(DWORD); // Make object buffer. Use GlobalDiddle instead of new so we // can pass a mem handle to the clipboard later. hbuf = ::GlobalAlloc( GHND, nBufSize ); if( hbuf == NULL ) return( FALSE ); // couldn't make room buf = (BYTE *)::GlobalLock( hbuf ); if( buf == NULL ) { ::GlobalFree( hbuf ); return( FALSE ); // couldn't find the room } pbuf = buf; // set marker rect CopyMemory(pbuf, &m_boundsRect, sizeof(RECT)); pbuf += sizeof (RECT); // set number of objects *((DWORD *)pbuf) = nNumObjs; pbuf += sizeof (DWORD); // copy each obj to buf + a length DWORD posNext = MarkerList.GetHeadPosition(); while( posNext != NULL ) { MarkerList.GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( (pGraphic != NULL)&& ((hGraphic = pGraphic->Handle()) != NULL)&& ((pHeader = PG_GetData(pGraphic->Page(), hGraphic )) != NULL) ) { // save length of this obj first nObjSize = (DWORD)pHeader->length; *((DWORD *)pbuf) = nObjSize; pbuf += sizeof (DWORD); // copy obj to buf CopyMemory( pbuf, (CONST VOID *)pHeader, nObjSize ); // make sure copy isn't "locked" (bug 474) ((PWB_GRAPHIC)pbuf)->locked = WB_GRAPHIC_LOCK_NONE; // set up for next obj pbuf += nObjSize; g_pwbCore->WBP_GraphicRelease(pGraphic->Page(), hGraphic, pHeader ); } } // cork it up *((DWORD *)pbuf) = 0; // give it to the clipboard ::GlobalUnlock( hbuf ); if( ::SetClipboardData( g_ClipboardFormats[ CLIPBOARD_PRIVATE_MULTI_OBJ ], hbuf ) == NULL ) { // clipboard choked, clean up mess ::GlobalFree( hbuf ); return( FALSE ); } // zillions of shared clipboards all over the planet are receiving this // thing about now... return( TRUE ); } // // Decodes CLIPBOARD_PRIVATE_MULTI_OBJ format and pastes objects // to Whiteboard. See DCWbGraphicMarker::RenderPrivateMarkerFormat // for details of format. // void DCWbGraphicMarker::Paste( HANDLE handle ) { BYTE *pbuf; DWORD nNumObjs; DWORD nObjSize; DCWbGraphic *pGraphic; DCWbGraphic *pSelectedGraphic; SIZE PasteOffset; RECT rectMarker; // blow off current selection g_pMain->m_drawingArea.RemoveMarker(NULL); DeleteAllMarkers( NULL ); pSelectedGraphic = NULL; // get data pbuf = (BYTE *)::GlobalLock( handle ); if( pbuf == NULL ) return; // can't get the door open // get marker's original coords and figure offset CopyMemory( &rectMarker, (CONST VOID *)pbuf, sizeof (RECT) ); pbuf += sizeof (RECT); RECT rcVis; g_pMain->m_drawingArea.GetVisibleRect(&rcVis); PasteOffset.cx = rcVis.left - rectMarker.left; PasteOffset.cy = rcVis.top - rectMarker.top; // get num objects nNumObjs = *((DWORD *)pbuf); pbuf += sizeof (DWORD); // get each object while( (nObjSize = *((DWORD *)pbuf)) != 0 ) { pbuf += sizeof (DWORD); // Add the object to the page and current selection pGraphic = DCWbGraphic::CopyGraphic( (PWB_GRAPHIC)pbuf ); pbuf += nObjSize; if( pGraphic != NULL ) { pGraphic->MoveBy( PasteOffset.cx, PasteOffset.cy ); pGraphic->AddToPageLast( g_pMain->GetCurrentPage() ); g_pMain->m_drawingArea.SelectGraphic( pGraphic, TRUE, TRUE ); } } ::GlobalUnlock( handle ); GetBoundsRect(&m_boundsRect); } DCWbGraphicLine::~DCWbGraphicLine( void ) { // Have to make sure marker is cleaned up before we vanish // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicLine::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the line // // void DCWbGraphicLine::CalculateBoundsRect() { // Create the basic bounding rectangle from the start and end points m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); // Expand the rectangle by the pen width used for drawing int iInflate = (m_uiPenWidth + 1) / 2; ::InflateRect(&m_boundsRect, iInflate, iInflate); } // // // Function: DCWbGraphicLine::SetStart // // Purpose: Set the start point of the line // // void DCWbGraphicLine::SetStart(POINT pointFrom) { // Only do anything if the start point has changed if (!EqualPoint(*((LPPOINT)&m_rect.left), pointFrom)) { // Save the new start point m_rect.left = pointFrom.x; m_rect.top = pointFrom.y; // Show that the graphic has changed m_bChanged = TRUE; } // Update the bounding rectangle CalculateBoundsRect(); } // // // Function: DCWbGraphicLine::SetEnd // // Purpose: Set the start point of the line // // void DCWbGraphicLine::SetEnd(POINT pointTo) { // Only do anything if the end point has changed if (!EqualPoint(*((LPPOINT)&m_rect.right), pointTo)) { // Save the new end point m_rect.right = pointTo.x; m_rect.bottom = pointTo.y; // Show that the graphic has changed m_bChanged = TRUE; } // Update the bounding rectangle CalculateBoundsRect(); } // // // Function: DCWbGraphicLine::Draw // // Purpose: Draw the line. // // void DCWbGraphicLine::Draw(HDC hDC) { HPEN hPen; HPEN hOldPen; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicLine::Draw"); // Select the required pen hPen = ::CreatePen(m_iPenStyle, m_uiPenWidth, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); if (hOldPen != NULL) { // Select the raster operation int iOldROP = ::SetROP2(hDC, m_iPenROP); // Draw the line ::MoveToEx(hDC, m_rect.left, m_rect.top, NULL); ::LineTo(hDC, m_rect.right, m_rect.bottom); // De-select the pen and ROP ::SetROP2(hDC, iOldROP); SelectPen(hDC, hOldPen); } if (hPen != NULL) { ::DeletePen(hPen); } } // // // Function: DCWbGraphicLine::MoveBy // // Purpose: Move the line. // // void DCWbGraphicLine::MoveBy(int cx, int cy) { // Move the start and end points ::OffsetRect(&m_rect, cx, cy); // Move the other object attributes DCWbGraphic::MoveBy(cx, cy); } // // Checks object for an actual overlap with pRectHit. Assumes m_boundsRect // has already been compared. // BOOL DCWbGraphicLine::CheckReallyHit(LPCRECT pRectHit) { return(LineHit(m_rect.left, m_rect.top, m_rect.right, m_rect.bottom, m_uiPenWidth, TRUE, TRUE, pRectHit)); } // // // Function: DCWbGraphicFreehand::DCWbGraphicFreehand // // Purpose: Constructor // // DCWbGraphicFreehand::DCWbGraphicFreehand(void) : DCWbGraphic() { } DCWbGraphicFreehand::DCWbGraphicFreehand(PWB_GRAPHIC pHeader) : DCWbGraphic() { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehand::DCWbGraphicFreehand"); // Note that we do everything in this constructor because of the // call to ReadExternal. If we let the DCWbGraphic base constructor // do it the wrong version of ReadExtra will be called (the one // in DCWbGraphic instead of the one in DCWbGraphicFreehand); // Do the basic initialization Initialize(); // Set up the page and graphic handle ASSERT(pHeader != NULL); // Read the header data ReadHeader(pHeader); // Read the extra data ReadExtra(pHeader); } DCWbGraphicFreehand::DCWbGraphicFreehand ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) : DCWbGraphic() { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehand::DCWbGraphicFreehand"); // Note that we do everything in this constructor because of the // call to ReadExternal. If we let the DCWbGraphic base constructor // do it the wrong version of ReadExtra will be called (the one // in DCWbGraphic instead of the one in DCWbGraphicFreehand); // Do the basic initialization Initialize(); ASSERT(hPage != WB_PAGE_HANDLE_NULL); m_hPage = hPage; ASSERT(hGraphic != NULL); m_hGraphic = hGraphic; // Read the header data ReadExternal(); } DCWbGraphicFreehand::~DCWbGraphicFreehand( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicFreehand::MoveBy // // Purpose: Move the polyline. // // void DCWbGraphicFreehand::MoveBy(int cx, int cy) { // Move the base point of the freehand object m_rect.left += cx; m_rect.top += cy; // Move the other object attributes DCWbGraphic::MoveBy(cx, cy); } // // // Function: DCWbGraphicFreehand::Draw // // Purpose: Draw the polyline. // // void DCWbGraphicFreehand::Draw(HDC hDC) { RECT clipBox; int iOldROP; HPEN hPen; HPEN hOldPen; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehand:Draw"); // NFC, SFR 5922. Check the return code from GetClipBox. // If we fail to get it, just draw everything if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Select the required pen hPen = ::CreatePen(m_iPenStyle, m_uiPenWidth, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); // Select the raster operation iOldROP = ::SetROP2(hDC, m_iPenROP); if (hOldPen != NULL) { // All points are relative to the first point in the list. // We update the origin of the DC temporarily to account for this. POINT origin; ::GetWindowOrgEx(hDC, &origin); ::SetWindowOrgEx(hDC, origin.x - m_rect.left, origin.y - m_rect.top, NULL); // Call the appropriate drawing function, according to whether // we're smooth or not DrawUnsmoothed(hDC); // Restore the origin ::SetWindowOrgEx(hDC, origin.x, origin.y, NULL); ::SetROP2(hDC, iOldROP); SelectPen(hDC, hOldPen); } if (hPen != NULL) { ::DeletePen(hPen); } } // // // Function: DCWbGraphicFreehand::DrawUnsmoothed // // Purpose: Draw the complete graphic, not using smoothing. // // void DCWbGraphicFreehand::DrawUnsmoothed(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehandDrawUnsmoothed"); // Set up the count and pointer to the points data. We use the // external data if we have a handle, otherwise internal data is used. int iCount = points.GetSize(); if (iCount < 2) { POINT point; point.x = points[0]->x; point.y = points[0]->y; points.Add(point); iCount = points.GetSize(); } RECT clipBox; if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } // Draw all the line segments stored ::MoveToEx(hDC, points[0]->x, points[0]->y, NULL); for ( int iIndex = 1; iIndex < iCount; iIndex++) { // Draw the line ::LineTo(hDC, points[iIndex]->x, points[iIndex]->y); } } // // // Function: DCWbGraphicFreehand::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the line // // void DCWbGraphicFreehand::CalculateBoundsRect(void) { // Reset the bounds rectangle ::SetRectEmpty(&m_boundsRect); // Add each of the points in the line to the bounding rectangle int iCount = points.GetSize(); for ( int iIndex = 0; iIndex < iCount; iIndex++) { AddPointToBounds(points[iIndex]->x, points[iIndex]->y); } // // Since the points are inclusive, we need to add one to the top & // bottom sides. // ::InflateRect(&m_boundsRect, 0, 1); ::OffsetRect(&m_boundsRect, m_rect.left, m_rect.top); } // // // Function: DCWbGraphicFreehand::AddPointToBounds // // Purpose: Add a single point into the bounding rectangle. The point is // expected to be in surface co-ordinates. // // void DCWbGraphicFreehand::AddPointToBounds(int x, int y) { // Create a rectangle containing the point just added (expanded // by the width of the pen being used). RECT rect; int iInflate = (m_uiPenWidth + 1) / 2; rect.left = x - iInflate; rect.top = y - iInflate; rect.right = x + iInflate; rect.bottom = y + iInflate; ::UnionRect(&m_boundsRect, &m_boundsRect, &rect); } // // // Function: DCWbGraphicFreehand::AddPoint // // Purpose: Add a point to the poly line, returning BOOL indicating // success. // // BOOL DCWbGraphicFreehand::AddPoint(POINT point) { BOOL bSuccess = TRUE; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehand::AddPoint"); // if we've reached the maximum number of points then quit with failure if (points.GetSize() >= MAX_FREEHAND_POINTS) { bSuccess = FALSE; TRACE_MSG(("Maximum number of points for freehand object reached.")); return(bSuccess); } // If this is the first point - all others are taken relative to it. if (points.GetSize() == 0) { // Save the first point here. m_rect.left = point.x; m_rect.top = point.y; } // Add the new point to the array - surround with exception handler // to catch memory errors POINT newpoint; newpoint.x = point.x - m_rect.left; newpoint.y = point.y - m_rect.top; points.Add((newpoint)); // Add the new point into the accumulated bounds rectangle. AddPointToBounds(point.x, point.y); // Show that the graphic has changed m_bChanged = TRUE; return(bSuccess); } // // // Function: DCWbGraphicFreehand::CalculateExternalLength // // Purpose: Return the length of the external representation of the // graphic. // // DWORD DCWbGraphicFreehand::CalculateExternalLength(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFreehand::CalculateExternalLength"); // Calculate the total length of the flat representation of the graphic return (DWORD) ( sizeof(WB_GRAPHIC_FREEHAND) + (points.GetSize() * sizeof(POINT))); } // // // Function: DCWbGraphicFreehand::WriteExtra // // Purpose: Write the extra (non-header) data to the flat representation // of the graphic. // // void DCWbGraphicFreehand::WriteExtra(PWB_GRAPHIC pHeader) { // Allocate the memory PWB_GRAPHIC_FREEHAND pFreehand = (PWB_GRAPHIC_FREEHAND) pHeader; // Copy the extra details into place pFreehand->pointCount = (TSHR_UINT16)points.GetSize(); for ( int iIndex = 0; iIndex < pFreehand->pointCount; iIndex++) { pFreehand->points[iIndex].x = (short)points[iIndex]->x; pFreehand->points[iIndex].y = (short)points[iIndex]->y; } } // // // Function: DCWbGraphicFreehand::ReadExtra // // Purpose: Read the extra (non-header) data from the flat // representation of the graphic. // // void DCWbGraphicFreehand::ReadExtra(PWB_GRAPHIC pHeader) { // Allocate the memory PWB_GRAPHIC_FREEHAND pFreehand = (PWB_GRAPHIC_FREEHAND) pHeader; // Get the number of points int iCount = pFreehand->pointCount; // Set the size of the points array points.SetSize(iCount); // Copy the points from the external memory to internal int iPointIndex = 0; while (iPointIndex < iCount) { points[iPointIndex]->x = pFreehand->points[iPointIndex].x; points[iPointIndex]->y = pFreehand->points[iPointIndex].y; iPointIndex++; } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicFreehand::CheckReallyHit(LPCRECT pRectHit) { POINT *lpPoints; int iCount; int i; POINT ptLast; UINT uRadius; RECT rectHit; iCount = points.GetSize(); lpPoints = (POINT *)points.GetBuffer(); if( iCount == 0 ) return( FALSE ); // addjust hit rect to lpPoints coord space. rectHit = *pRectHit; ::OffsetRect(&rectHit, -m_rect.left, -m_rect.top); if( (iCount > 0)&&(iCount < 2) ) { // only one point, just hit check it uRadius = m_uiPenWidth >> 1; // m_uiPenWidth/2 return( CircleHit( lpPoints->x, lpPoints->y, uRadius, &rectHit, TRUE ) ); } // look for a hit on each line segment body ptLast = *lpPoints++; for( i=1; ix, lpPoints->y, m_uiPenWidth, FALSE, FALSE, &rectHit ) ) return( TRUE ); // got a hit ptLast = *lpPoints++; } // now, look for a hit on the line endpoints if m_uiPenWidth > 1 if( m_uiPenWidth > 1 ) { uRadius = m_uiPenWidth >> 1; // m_uiPenWidth/2 lpPoints = (POINT *)points.GetBuffer(); for( i=0; ix, lpPoints->y, uRadius, &rectHit, FALSE ) ) return( TRUE ); // got a hit } } return( FALSE ); // no hits } DCWbGraphicRectangle::~DCWbGraphicRectangle( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicRectangle::SetRect // // Purpose: Set the rectangle size/position // // void DCWbGraphicRectangle::SetRect(LPCRECT lprect) { DCWbGraphic::SetRect(lprect); // Generate the new bounding rectangle CalculateBoundsRect(); } // // // Function: DCWbGraphicRectangle::MoveBy // // Purpose: Move the rectangle // // void DCWbGraphicRectangle::MoveBy(int cx, int cy) { // Move the rectangle ::OffsetRect(&m_rect, cx, cy); // Move the other object attributes DCWbGraphic::MoveBy(cx, cy); } // // // Function: DCWbGraphicRectangle::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicRectangle::CalculateBoundsRect(void) { // Generate the new bounding rectangle m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); ::InflateRect(&m_boundsRect, m_uiPenWidth, m_uiPenWidth); } // // // Function: DCWbGraphicRectangle::Draw // // Purpose: Draw the rectangle // // void DCWbGraphicRectangle::Draw(HDC hDC) { int iOldROP; RECT clipBox; HPEN hPen; HPEN hOldPen; HBRUSH hOldBrush; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicRectangle::Draw"); // Only draw anything if the bounding rectangle intersects // the current clip box. if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Select the pen hPen = ::CreatePen(m_iPenStyle, m_uiPenWidth, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); hOldBrush = SelectBrush(hDC, ::GetStockObject(NULL_BRUSH)); // Select the raster operation iOldROP = ::SetROP2(hDC, m_iPenROP); // Draw the rectangle ::Rectangle(hDC, m_boundsRect.left, m_boundsRect.top, m_boundsRect.right, m_boundsRect.bottom); ::SetROP2(hDC, iOldROP); SelectPen(hDC, hOldPen); if (hPen != NULL) { ::DeletePen(hPen); } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicRectangle::CheckReallyHit(LPCRECT pRectHit) { RECT rectEdge; RECT rectHit; // check left edge rectEdge.left = m_rect.left - m_uiPenWidth; rectEdge.top = m_rect.top - m_uiPenWidth; rectEdge.right = m_rect.left; rectEdge.bottom = m_rect.bottom + m_uiPenWidth; if (::IntersectRect(&rectHit, &rectEdge, pRectHit)) return( TRUE ); // check right edge rectEdge.left = m_rect.right; rectEdge.right = m_rect.right + m_uiPenWidth; if (::IntersectRect(&rectHit, &rectEdge, pRectHit)) return( TRUE ); // check top edge rectEdge.left = m_rect.left; rectEdge.right = m_rect.right; rectEdge.bottom = m_rect.top; if (::IntersectRect(&rectHit, &rectEdge, pRectHit)) return( TRUE ); // check bottom edge rectEdge.top = m_rect.bottom; rectEdge.bottom = m_rect.bottom + m_uiPenWidth; if (::IntersectRect(&rectHit, &rectEdge, pRectHit)) return( TRUE ); return( FALSE ); } DCWbGraphicFilledRectangle::~DCWbGraphicFilledRectangle( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicFilledRectangle::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicFilledRectangle::CalculateBoundsRect(void) { // Generate the new bounding rectangle // This is one greater than the rectangle to include the drawing rectangle m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); ::InflateRect(&m_boundsRect, 1, 1); } // // // Function: DCWbGraphicFilledRectangle::Draw // // Purpose: Draw the rectangle // // void DCWbGraphicFilledRectangle::Draw(HDC hDC) { HPEN hPen; HPEN hOldPen; HBRUSH hBrush; HBRUSH hOldBrush; int iOldROP; RECT clipBox; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFilledRectangle::Draw"); // Only draw anything if the bounding rectangle intersects // the current clip box. if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Select the pen hPen = ::CreatePen(m_iPenStyle, 2, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); hBrush = ::CreateSolidBrush(m_clrPenColor); hOldBrush = SelectBrush(hDC, hBrush); // Select the raster operation iOldROP = ::SetROP2(hDC, m_iPenROP); // Draw the rectangle ::Rectangle(hDC, m_boundsRect.left, m_boundsRect.top, m_boundsRect.right, m_boundsRect.bottom); // Restore the ROP mode ::SetROP2(hDC, iOldROP); SelectBrush(hDC, hOldBrush); if (hBrush != NULL) { ::DeleteBrush(hBrush); } SelectPen(hDC, hOldPen); if (hPen != NULL) { ::DeletePen(hPen); } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicFilledRectangle::CheckReallyHit(LPCRECT pRectHit) { return( TRUE ); } // // Draws a tracking rect for every marker obj in marker // (DCWbGraphicSelectTrackingRectangle is a friend of DCWbGraphicMarker // and WbDrawingArea) // void DCWbGraphicSelectTrackingRectangle::Draw(HDC hDC) { POSITION posNext; DCWbGraphic *pGraphic; LPRECT pMarkerRect; RECT rectTracker; CPtrToPtrList *pMList; // don't draw at start point or XOR will get out of sync if( (m_Offset.cx == 0)&&(m_Offset.cy == 0) ) return; ASSERT(g_pDraw); pMList = &(g_pDraw->m_pMarker->MarkerList); if( pMList->IsEmpty() ) return; posNext = pMList->GetHeadPosition(); while( posNext != NULL ) { pMList->GetNextAssoc( posNext, (void *&)pGraphic, (void *&)pMarkerRect ); if( pMarkerRect != NULL ) { rectTracker = *pMarkerRect; ::OffsetRect(&rectTracker, m_Offset.cx, m_Offset.cy); SetRect(&rectTracker); DCWbGraphicRectangle::Draw(hDC); } } } void DCWbGraphicSelectTrackingRectangle::MoveBy(int cx, int cy) { m_Offset.cx += cx; m_Offset.cy += cy; } DCWbGraphicEllipse::~DCWbGraphicEllipse( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicEllipse::SetRect // // Purpose: Set the ellipse size/position // // void DCWbGraphicEllipse::SetRect(LPCRECT lprc) { DCWbGraphic::SetRect(lprc); // Generate the new bounding rectangle CalculateBoundsRect(); } // // // Function: DCWbGraphicEllipse::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicEllipse::CalculateBoundsRect(void) { // Generate the new bounding rectangle // This includes all the line, since we draw inside the bounds m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); ::InflateRect(&m_boundsRect, m_uiPenWidth, m_uiPenWidth); } // // // Function: DCWbGraphicEllipse::MoveBy // // Purpose: Move the ellipse // // void DCWbGraphicEllipse::MoveBy(int cx, int cy) { // Move the ellipse ::OffsetRect(&m_rect, cx, cy); // Move the other object attributes DCWbGraphic::MoveBy(cx, cy); } // // // Function: DCWbGraphicEllipse::Draw // // Purpose: Draw the ellipse // // void DCWbGraphicEllipse::Draw(HDC hDC) { HPEN hPen; HPEN hOldPen; HBRUSH hOldBrush; int iOldROP; RECT clipBox; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicEllipse::Draw"); // Only draw anything if the bounding rectangle intersects // the current clip box. if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Select the pen hPen = ::CreatePen(m_iPenStyle, m_uiPenWidth, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); hOldBrush = SelectBrush(hDC, ::GetStockObject(NULL_BRUSH)); // Select the raster operation iOldROP = ::SetROP2(hDC, m_iPenROP); // Draw the rectangle ::Ellipse(hDC, m_boundsRect.left, m_boundsRect.top, m_boundsRect.right, m_boundsRect.bottom); ::SetROP2(hDC, iOldROP); SelectBrush(hDC, hOldBrush); SelectPen(hDC, hOldPen); if (hPen != NULL) { ::DeletePen(hPen); } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicEllipse::CheckReallyHit(LPCRECT pRectHit) { return( EllipseHit( &m_rect, TRUE, m_uiPenWidth, pRectHit ) ); } DCWbGraphicFilledEllipse::~DCWbGraphicFilledEllipse( void ) { // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } } // // // Function: DCWbGraphicFilledEllipse::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicFilledEllipse::CalculateBoundsRect(void) { // Generate the new bounding rectangle // This is one greater than the rectangle to include the drawing rectangle m_boundsRect = m_rect; NormalizeRect(&m_boundsRect); ::InflateRect(&m_boundsRect, 1, 1); } // // // Function: DCWbGraphicFilledEllipse::Draw // // Purpose: Draw the ellipse // // void DCWbGraphicFilledEllipse::Draw(HDC hDC) { RECT clipBox; HPEN hPen; HPEN hOldPen; HBRUSH hBrush; HBRUSH hOldBrush; int iOldROP; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicFilledEllipse::Draw"); // Only draw anything if the bounding rectangle intersects // the current clip box. if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Select the pen hPen = ::CreatePen(m_iPenStyle, 2, m_clrPenColor); hOldPen = SelectPen(hDC, hPen); hBrush = ::CreateSolidBrush(m_clrPenColor); hOldBrush = SelectBrush(hDC, hBrush); // Select the raster operation iOldROP = ::SetROP2(hDC, m_iPenROP); // Draw the rectangle ::Ellipse(hDC, m_boundsRect.left, m_boundsRect.top, m_boundsRect.right, m_boundsRect.bottom); ::SetROP2(hDC, iOldROP); SelectBrush(hDC, hOldBrush); if (hBrush != NULL) { ::DeleteBrush(hBrush); } SelectPen(hDC, hOldPen); if (hPen != NULL) { ::DeletePen(hPen); } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicFilledEllipse::CheckReallyHit(LPCRECT pRectHit) { return( EllipseHit( &m_rect, FALSE, 0, pRectHit ) ); } // // // Function: DCWbGraphicText::DCWbGraphicText // // Purpose: Initialize a new drawn text object. // // DCWbGraphicText::DCWbGraphicText(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::DCWbGraphicText"); m_hFontThumb = 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); // Add an empty line to the text array strTextArray.Add(_T("")); // Show that the graphic has not changed m_bChanged = FALSE; m_nKerningOffset = 0; // added for bug 469 } DCWbGraphicText::DCWbGraphicText(PWB_GRAPHIC pHeader) : DCWbGraphic() { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::DCWbGraphicText"); ASSERT(pHeader != NULL); m_hFont = NULL; m_hFontThumb = NULL; // Note that we do everything in this constructor because of the // calls to ReadHeader and ReadExtra. If we let the DCWbGraphic base // constructor do it the wrong version of ReadExtra will be called // (the one in DCWbGraphic instead of the one in DCWbGraphicText). // Add an empty line to the text array strTextArray.Add(_T("")); // Read the data ReadHeader(pHeader); ReadExtra(pHeader); // Show that the graphic has not changed m_bChanged = FALSE; } DCWbGraphicText::DCWbGraphicText ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) : DCWbGraphic() { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::DCWbGraphicText"); // Note that we do everything in this constructor because of the // call to ReadExternal. If we let the DCWbGraphic base constructor // do it the wrong version of ReadExtra will be called (the one // in DCWbGraphic instead of the one in DCWbGraphicText); // Set up the page and graphic handle ASSERT(hPage != WB_PAGE_HANDLE_NULL); m_hPage = hPage; ASSERT(hGraphic != NULL); m_hGraphic = hGraphic; m_hFont = NULL; m_hFontThumb = NULL; // Add an empty line to the text array strTextArray.Add(_T("")); // Read the data ReadExternal(); // Show that the graphic has not changed m_bChanged = FALSE; } // // // Function: DCWbGraphicText:: ~DCWbGraphicText // // Purpose: Destruct a text object // // DCWbGraphicText::~DCWbGraphicText() { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::~DCWbGraphicText"); // don't know if we are selected or not so just delete anyway if(g_pDraw != NULL && g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } // Ensure that the DC does not contain our fonts if(g_pDraw != NULL) { g_pDraw->UnPrimeFont(g_pDraw->GetCachedDC()); } if (m_hFontThumb != NULL) { ::DeleteFont(m_hFontThumb); m_hFontThumb = NULL; } if (m_hFont != NULL) { ::DeleteFont(m_hFont); m_hFont = NULL; } } StrCspn(char * string, char * control) { char *str = string; char *ctrl = control; unsigned char map[32]; int count; /* Clear out bit map */ for (count=0; count<32; count++) map[count] = 0; /* Set bits in control map */ while (*ctrl) { map[*ctrl >> 3] |= (1 << (*ctrl & 7)); ctrl++; } count=0; map[0] |= 1; /* null chars not considered */ while (!(map[*str >> 3] & (1 << (*str & 7)))) { count++; str++; } return(count); } // // // Function: DCWbGraphicText::SetText // // Purpose: Set the text of the object // // void DCWbGraphicText::SetText(TCHAR * strText) { // Remove all the current stored text strTextArray.RemoveAll(); // 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; } } // Calculate the bounding rectangle for the new text CalculateBoundsRect(); // Show that the graphic has not changed m_bChanged = TRUE; } // // // Function: DCWbGraphicText::SetText // // Purpose: Set the text of the object // // void DCWbGraphicText::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(); // Show that the graphic has changed m_bChanged = TRUE; } // // // Function: DCWbGraphicText::SetFont // // Purpose: Set the font to be used for drawing // // void DCWbGraphicText::SetFont(HFONT hFont) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::SetFont"); // Get the font details LOGFONT lfont; ::GetObject(hFont, sizeof(LOGFONT), &lfont); // // Pass the logical font into the SetFont() function // SetFont(&lfont); } // // // Function: DCWbGraphicText::SetFont(metrics) // // Purpose: Set the font to be used for drawing // // void DCWbGraphicText::SetFont(LOGFONT *pLogFont, BOOL bReCalc ) { HFONT hOldFont; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::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)); hOldFont = m_hFont; m_hFont = ::CreateFontIndirect(pLogFont); if (!m_hFont) { // Could not create the font ERROR_OUT(("Failed to create font")); DefaultExceptionHandler(WBFE_RC_WINDOWS, 0); return; } // Calculate the line height for this font if(g_pDraw != NULL) { HDC hDC = g_pDraw->GetCachedDC(); g_pDraw->PrimeFont(hDC, m_hFont, &m_textMetrics); } // We are now guaranteed to be able to delete the old font if (hOldFont != NULL) { ::DeleteFont(hOldFont); } // 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 = ::CreateFontIndirect(pLogFont); if (!m_hFontThumb) { // Could not create the font ERROR_OUT(("Failed to create thumbnail font")); DefaultExceptionHandler(WBFE_RC_WINDOWS, 0); return; } // Calculate the bounding rectangle, accounting for the new font if( bReCalc ) CalculateBoundsRect(); // Show that the graphic has changed m_bChanged = TRUE; } // // // Function: DCWbGraphicText::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 DCWbGraphicText::GetTextABC( LPCTSTR pText, int iStartX, int iStopX) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::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; iZoomFactor(), 1, g_pDraw->ZoomFactor(), 1, NULL); } TRACE_MSG(("Final text width is %d, C space %d",abcResult.abcB,abcResult.abcC)); return abcResult; } // // // Function: DCWbGraphicText::GetTextRectangle // // Purpose: Calculate the bounding rectangle of a portion of the object // // void DCWbGraphicText::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, "DCWbGraphicText::GetTextRect"); // 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. 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; GetPosition(&pt); ::OffsetRect(lprc, pt.x, pt.y); } // // // Function: DCWbGraphicText::CalculateRect // // Purpose: Calculate the bounding rectangle of a portion of the object // // void DCWbGraphicText::CalculateRect(int iStartX, int iStartY, int iStopX, int iStopY, LPRECT lprcResult) { RECT rcResult; RECT rcT; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::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); // 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 (int 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); } *lprcResult = rcResult; } // // // Function: DCWbGraphicText::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the object // // void DCWbGraphicText::CalculateBoundsRect(void) { // Set the new bounding rectangle CalculateRect(0, 0, LAST_CHAR, LAST_LINE, &m_boundsRect); } // // // Function: DCWbGraphicText::Draw // // Purpose : Draw the object onto the specified DC // // void DCWbGraphicText::Draw(HDC hDC, BOOL thumbNail) { 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"); // // Only draw anything if the bounding rectangle intersects the current // clip box. // if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // // 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. // ::SetTextColor(hDC, m_clrPenColor); // // Set the background to be transparent // oldBkMode = ::SetBkMode(hDC, TRANSPARENT); // // Calculate the bounding rectangle, accounting for the new font. // CalculateBoundsRect(); // // Get the start point for the text. // pointPos.x = m_boundsRect.left + m_nKerningOffset; pointPos.y = m_boundsRect.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 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; GetPosition(&ptPos); // Not zoomed, just do it ::TabbedTextOut(hDC, pointPos.x, pointPos.y, strLine, iLength, 0, NULL, ptPos.x); } } // // Move to the next line. // pointPos.y += (m_textMetrics.tmHeight); } // // Restore the old background mode. // ::SetBkMode(hDC, oldBkMode); g_pDraw->UnPrimeFont(hDC); } // // // Function: DCWbGraphicText::CalculateExternalLength // // Purpose: Return the length of the external representation of the // graphic. // // DWORD DCWbGraphicText::CalculateExternalLength(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::CalculateExternalLength"); // Loop through the text strings, adding the size of each as we go DWORD length = sizeof(WB_GRAPHIC_TEXT); int iCount = strTextArray.GetSize(); for (int iIndex = 0; iIndex < iCount; iIndex++) { // Allow extra bytes per string for NULL term length += lstrlen(strTextArray[iIndex]) + 2; } return length; } // // // Function: DCWbGraphicText::WriteExtra // // Purpose: Write the extra (non-header) data to the flat representation // of the graphic. // // void DCWbGraphicText::WriteExtra(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::WriteExtra"); // Allocate the memory PWB_GRAPHIC_TEXT pText = (PWB_GRAPHIC_TEXT) pHeader; // Get the font face name LOGFONT lfont; ::GetObject(m_hFont, sizeof(LOGFONT), &lfont); // Copy the face name into the flat object representation // The other information comes from the logical font details TRACE_MSG(("Font details height %d, avwidth %d, family %d, face %s", lfont.lfHeight, lfont.lfWidth, lfont.lfPitchAndFamily, lfont.lfFaceName)); _tcscpy(pText->faceName, lfont.lfFaceName); pText->charHeight = (short)lfont.lfHeight; pText->averageCharWidth = (short)lfont.lfWidth; pText->strokeWeight = (short)lfont.lfWeight; pText->italic = lfont.lfItalic; pText->underline = lfont.lfUnderline; pText->strikeout = lfont.lfStrikeOut; pText->pitch = lfont.lfPitchAndFamily; //COMMENT BY RAND // Original DCL apps ignore WB_GRAPHIC_TEXT::codePage. I am using it here // to pass around the fonts script (character set). This might change later. // Apps that ignore this have set it to 0 which will be interpreted as an // ANSI_CHARSET. pText->codePage = lfont.lfCharSet; // Loop through the text strings, adding each as we go char* pDest = pText->text; int iCount = strTextArray.GetSize(); for (int iIndex = 0; iIndex < iCount; iIndex++) { _tcscpy(pDest, strTextArray[iIndex]); pDest += lstrlen(strTextArray[iIndex]); // Add the null terminator *pDest++ = '\0'; } // Save the number of strings pText->stringCount = (TSHR_UINT16)iCount; } // // // Function: DCWbGraphicText::ReadExtra // // Purpose: Read the extra (non-header) data from the flat // representation of the graphic. // // void DCWbGraphicText::ReadExtra(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::ReadExtra"); // Allocate the memory PWB_GRAPHIC_TEXT pText = (PWB_GRAPHIC_TEXT) pHeader; // Get the font details LOGFONT lfont; lfont.lfHeight = (short)pText->charHeight; // lfont.lfWidth = pText->averageCharWidth; lfont.lfEscapement = 0; lfont.lfOrientation = 0; lfont.lfWeight = pText->strokeWeight; lfont.lfItalic = pText->italic; lfont.lfUnderline = pText->underline; lfont.lfStrikeOut = pText->strikeout; //COMMENT BY RAND // Original DCL apps ignore WB_GRAPHIC_TEXT::codePage. I am using it here // to pass around the fonts script (character set). This might change later. // Apps that ignore this have set it to 0 which will be interpreted as an // ANSI_CHARSET. lfont.lfCharSet = (BYTE)pText->codePage; lfont.lfOutPrecision = OUT_DEFAULT_PRECIS; lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS | CLIP_DFA_OVERRIDE; lfont.lfQuality = DRAFT_QUALITY; lfont.lfPitchAndFamily = pText->pitch; _tcscpy(lfont.lfFaceName, pText->faceName); TRACE_MSG(("Setting height to %d, width %d, pitch %d, face %s", pText->charHeight, pText->averageCharWidth, pText->pitch, pText->faceName)); // Loop through the text strings, retrieving each as we go TCHAR* pString = pText->text; int iCount = pText->stringCount; // Remove all the current stored text strTextArray.RemoveAll(); strTextArray.SetSize(iCount); for (int iIndex = 0; iIndex < iCount; iIndex++) { strTextArray.SetAt(iIndex, pString); pString += lstrlen(pString); // Skip the null terminator pString++; } // Set the current font SetFont(&lfont); } // // // Function: InvalidateMetrics // // Purpose: Mark the metrics need retrieving again // // void DCWbGraphicText::InvalidateMetrics(void) { } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicText::CheckReallyHit(LPCRECT pRectHit ) { return( TRUE ); } // version of Position() that compensates for kerning (bug 469) void DCWbGraphicText::GetPosition(LPPOINT lppt) { lppt->x = m_boundsRect.left + m_nKerningOffset; lppt->y = m_boundsRect.top; } // // // Function: DCWbGraphicDIB::DCWbGraphicDIB // // Purpose: Initialize a new drawn bitmap object. // // DCWbGraphicDIB::DCWbGraphicDIB(void) { // Show that we have no internal image m_lpbiImage = NULL; } DCWbGraphicDIB::DCWbGraphicDIB(PWB_GRAPHIC pHeader) : DCWbGraphic(pHeader) { // Show that we have no internal image m_lpbiImage = NULL; } DCWbGraphicDIB::DCWbGraphicDIB ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) : DCWbGraphic(hPage, hGraphic) { // Show that we have no internal image m_lpbiImage = NULL; } // // // Function: DCWbGraphicDIB::~DCWbGraphicDIB // // Purpose: Destruct a drawn bitmap object. // // DCWbGraphicDIB::~DCWbGraphicDIB(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::~DCWbGraphicDIB"); // don't know if we are selected or not so just delete anyway if(g_pDraw->m_pMarker != NULL) { g_pDraw->m_pMarker->DeleteMarker( this ); } DeleteImage(); } // // // Function: DCWbGraphicDIB::SetImage // // Purpose: Set the image of the object // // void DCWbGraphicDIB::SetImage(LPBITMAPINFOHEADER lpbi) { // Delete any current bits DeleteImage(); // Save the DIB bits--this is a COPY we now own m_lpbiImage = lpbi; // Update the bounds rectangle CalculateBoundsRect(); // Show that the graphic has changed m_bChanged = TRUE; } // // // Function: DCWbGraphicDIB::CalculateBoundsRect // // Purpose: Calculate the bounding rectangle of the bitmap // // void DCWbGraphicDIB::CalculateBoundsRect() { // If there is no bitmap set up, the bounding rectangle is empty if (m_lpbiImage == NULL) { ::SetRectEmpty(&m_boundsRect); } else { // Calculate the bounding rectangle from the size of the bitmap m_boundsRect.right = m_boundsRect.left + m_lpbiImage->biWidth; m_boundsRect.bottom = m_boundsRect.top + m_lpbiImage->biHeight; } } // // // Function: DCWbGraphicDIB::CalculateExternalLength // // Purpose: Return the length of the external representation of the // graphic. // // DWORD DCWbGraphicDIB::CalculateExternalLength(void) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::CalculateExternalLength"); // Use the internal representation to calculate the external length. DWORD dwLength = sizeof(WB_GRAPHIC_DIB); if (m_lpbiImage != NULL) { dwLength += DIB_TotalLength(m_lpbiImage); } else { // If we have got an external form already, use its length if (m_hGraphic != NULL) { dwLength = m_dwExternalLength; } } return dwLength; } // // // Function: DCWbGraphicDIB::WriteExtra // // Purpose: Write the data above and beyond the header to the pointer // passed. // // void DCWbGraphicDIB::WriteExtra(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::WriteExtra"); // Nothing more to do if we do not have an image if (m_lpbiImage != NULL) { // Copy the data into place memcpy(((BYTE *) pHeader) + pHeader->dataOffset, m_lpbiImage, DIB_TotalLength(m_lpbiImage)); } } // // // Function: DCWbGraphicDIB::ReadExtra // // Purpose: Read the data above and beyond the header to the pointer // passed. // // // // DCWbGraphicDIB does not have a ReadExtra function. The Draw function // uses the external data (if there is any) and the local data if there is // not. // // // // Function: DCWbGraphicDIB::CopyExtra // // Purpose: Copy the data above and beyond the header into this object. // // void DCWbGraphicDIB::CopyExtra(PWB_GRAPHIC pHeader) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::CopyExtra"); // Get a pointer to the DIB data LPBITMAPINFOHEADER lpbi; lpbi = (LPBITMAPINFOHEADER) (((BYTE *) pHeader) + pHeader->dataOffset); // Make a DIB copy ASSERT(m_lpbiImage == NULL); m_lpbiImage = DIB_Copy(lpbi); // Show that the graphic has changed m_bChanged = TRUE; } // // // Function: DCWbGraphicDIB::FromScreenArea // // Purpose: Set the content of the object from an area of the screen // // void DCWbGraphicDIB::FromScreenArea(LPCRECT lprcScreen) { LPBITMAPINFOHEADER lpbiNew; lpbiNew = DIB_FromScreenArea(lprcScreen); if (lpbiNew != NULL) { // Set this as our current bits SetImage(lpbiNew); } else { ::Message(NULL, (UINT)IDS_MSG_CAPTION, (UINT)IDS_CANTGETBMP, (UINT)MB_OK ); } } // // // Function: DCWbGraphicDIB::DeleteImage // // Purpose: Delete the internal image // // void DCWbGraphicDIB::DeleteImage(void) { // If we have DIB bits, delete if (m_lpbiImage != NULL) { ::GlobalFree((HGLOBAL)m_lpbiImage); m_lpbiImage = NULL; } // Show our contents have changed m_bChanged = TRUE; } // // // Function: DCWbGraphicDIB::GetDIBData // // Purpose: Return a pointer to the DIB data // // BOOL DCWbGraphicDIB::GetDIBData(HOLD_DATA& hold) { MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::GetDIBData"); // Pointer to image data (set up below depending on whether // we have an internal or external image). hold.lpbi = NULL; hold.pHeader = NULL; // Draw depending on whether the DIB data is internal or external if (m_hGraphic == NULL) { // Do nothing if we do not have an image at all if (m_lpbiImage != NULL) { hold.lpbi = m_lpbiImage; } } else { // Lock the object data in the page hold.pHeader = (PWB_GRAPHIC) PG_GetData(m_hPage, m_hGraphic); if (hold.pHeader != NULL) { hold.lpbi = (LPBITMAPINFOHEADER) (((BYTE *) hold.pHeader) + hold.pHeader->dataOffset); } } return (hold.lpbi != NULL); } // // // Function: DCWbGraphicDIB::ReleaseDIBData // // Purpose: Release DIB data previously obtained with GetDIBData // // void DCWbGraphicDIB::ReleaseDIBData(HOLD_DATA& hold) { if ((m_hGraphic != NULL) && (hold.pHeader != NULL)) { // Release external memory g_pwbCore->WBP_GraphicRelease(m_hPage, m_hGraphic, hold.pHeader); hold.pHeader = NULL; } // Reset the hold bitmap info pointer hold.lpbi = NULL; } // // // Function: DCWbGraphicDIB::Draw // // Purpose: Draw the object onto the specified DC // // void DCWbGraphicDIB::Draw(HDC hDC) { RECT clipBox; MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicDIB::Draw"); // Only draw anything if the bounding rectangle intersects // the current clip box. if (::GetClipBox(hDC, &clipBox) == ERROR) { WARNING_OUT(("Failed to get clip box")); } else if (!::IntersectRect(&clipBox, &clipBox, &m_boundsRect)) { TRACE_MSG(("No clip/bounds intersection")); return; } // Pointer to image data (set up below depending on whether // we have an internal or external image. HOLD_DATA hold; if (GetDIBData(hold)) { // Set the stretch mode to be used so that scan lines are deleted // rather than combined. This will tend to preserve color better. int iOldStretchMode = ::SetStretchBltMode(hDC, STRETCH_DELETESCANS); // Draw the bitmap BOOL bResult = ::StretchDIBits(hDC, m_boundsRect.left, m_boundsRect.top, m_boundsRect.right - m_boundsRect.left, m_boundsRect.bottom - m_boundsRect.top, 0, 0, (UINT) hold.lpbi->biWidth, (UINT) hold.lpbi->biHeight, (VOID FAR *) DIB_Bits(hold.lpbi), (LPBITMAPINFO)hold.lpbi, DIB_RGB_COLORS, SRCCOPY); // Restore the stretch mode ::SetStretchBltMode(hDC, iOldStretchMode); // Release external memory ReleaseDIBData(hold); } } // // Checks object for an actual overlap with pRectHit. This // function assumes that the boundingRect has already been // compared with pRectHit. // BOOL DCWbGraphicDIB::CheckReallyHit(LPCRECT pRectHit) { return( TRUE ); } ObjectTrashCan::~ObjectTrashCan(void) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::~ObjectTrashCan"); BurnTrash(); } BOOL ObjectTrashCan::GotTrash( void ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::GotTrash"); return(!Trash.IsEmpty()); } void ObjectTrashCan::BurnTrash( void ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::BurnTrash"); int nObjects; int i; // zap objects POSITION pos = Trash.GetHeadPosition(); while (pos != NULL) { delete Trash.GetNext(pos); } // zap pointers EmptyTrash(); } void ObjectTrashCan::CollectTrash( DCWbGraphic *pGObj ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::CollectTrash"); Trash.AddTail(pGObj); // stuff it in the sack m_hPage = pGObj->Page(); } void ObjectTrashCan::EmptyTrash( void ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::EmptyTrash"); // zap pointers but leave objects scattered about the room Trash.EmptyList(); } void ObjectTrashCan::AddToPageLast ( WB_PAGE_HANDLE hPage ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::AddToPageLast"); int nObjects; int i; POSITION posNext = Trash.GetHeadPosition(); while( posNext != NULL ) { ((DCWbGraphic *)(Trash.GetNext(posNext)))->AddToPageLast(hPage); } } void ObjectTrashCan::SelectTrash( void ) { MLZ_EntryOut(ZONE_FUNCTION, "ObjectTrashCan::SelectTrash"); int nObjects; int i; BOOL bForceAdd; DCWbGraphic *pGObj; // Zap current selection with first object and then add remaining // objects to current selection bForceAdd = FALSE; POSITION posNext = Trash.GetHeadPosition(); while( posNext != NULL ) { pGObj = (DCWbGraphic *)(Trash.GetNext(posNext)); g_pMain->m_drawingArea.SelectGraphic( pGObj, TRUE, bForceAdd ); bForceAdd = TRUE; } } CPtrToPtrList::CPtrToPtrList( void ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::CPtrToPtrList"); }// CPtrToPtrList::CPtrToPtrList CPtrToPtrList::~CPtrToPtrList( void ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::~CPtrToPtrList"); RemoveAll(); }// CPtrToPtrList::~CPtrToPtrList void CPtrToPtrList::RemoveAll( void ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::RemoveAll"); POSITION pos; stPtrPair *pPp; // clean up pairs pos = GetHeadPosition(); while( pos != NULL ) { pPp = (stPtrPair *)GetNext( pos ); if( pPp != NULL ) delete pPp; } COBLIST::EmptyList(); }// CPtrToPtrList::~CPtrToPtrList void CPtrToPtrList::SetAt( void *key, void *newValue ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::SetAt"); stPtrPair *pPp; // see if key is already there pPp = FindMainThingPair( key, NULL ); if( pPp != NULL ) { // it's there, we're just updating its value pPp->pRelatedThing = newValue; } else { // this is a new entry pPp = new stPtrPair; if( pPp != NULL ) { pPp->pMainThing = key; pPp->pRelatedThing = newValue; AddTail(pPp); } else { ERROR_OUT( ("CPtrToPtrList: can't alloc stPtrPair") ); } } }// CPtrToPtrList::SetAt BOOL CPtrToPtrList::RemoveKey( void *key ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::RemoveKey"); POSITION pos; stPtrPair *pPp; pPp = FindMainThingPair( key, &pos ); if( pPp != NULL ) { RemoveAt( pos ); delete pPp; return( TRUE ); } else return( FALSE ); }// CPtrToPtrList::RemoveKey void CPtrToPtrList::GetNextAssoc( POSITION &rNextPosition, void *&rKey, void *&rValue ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::GetNextAssoc"); stPtrPair *pPp; pPp = (stPtrPair *)GetNext( rNextPosition ); if( pPp != NULL ) { rKey = pPp->pMainThing; rValue = pPp->pRelatedThing; } else { rKey = NULL; rValue = NULL; } }// CPtrToPtrList::GetNextAssoc BOOL CPtrToPtrList::Lookup( void *key, void *&rValue ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::Lookup"); stPtrPair *pPp; pPp = FindMainThingPair( key, NULL ); if( pPp != NULL ) { rValue = pPp->pRelatedThing; return( TRUE ); } else { rValue = NULL; return( FALSE ); } }// CPtrToPtrList::Lookup CPtrToPtrList::stPtrPair * CPtrToPtrList::FindMainThingPair( void *pMainThing, POSITION *pPos ) { MLZ_EntryOut(ZONE_FUNCTION, "CPtrToPtrList::FindMainThingPair"); POSITION pos; POSITION lastpos; stPtrPair *pPp; if( pPos != NULL ) *pPos = NULL; // look for pair containing pMainThing pos = GetHeadPosition(); while( pos != NULL ) { lastpos = pos; pPp = (stPtrPair *)GetNext( pos ); if( pPp->pMainThing == pMainThing ) { if( pPos != NULL ) *pPos = lastpos; return( pPp ); } } // didn't find it return( NULL ); }// CPtrToPtrList::FindMainThingPair #define ARRAY_INCREMENT 0x200 DCDWordArray::DCDWordArray() { MLZ_EntryOut(ZONE_FUNCTION, "DCDWordArray::DCDWordArray"); m_Size = 0; m_MaxSize = ARRAY_INCREMENT; m_pData = new POINT[ARRAY_INCREMENT]; if (!m_pData) { ERROR_OUT(("Failed to allocate m_pData POINT array")); } } DCDWordArray::~DCDWordArray() { MLZ_EntryOut(ZONE_FUNCTION, "DCDWordArray::~DCDWordArray"); delete[] m_pData; } // // We need to increase the size of the array // BOOL DCDWordArray::ReallocateArray(void) { POINT *pOldArray = m_pData; m_pData = new POINT[m_MaxSize]; if(m_pData) { TRACE_DEBUG((">>>>>Increasing size of array to hold %d points", m_MaxSize)); // copy new data from old memcpy( m_pData, pOldArray, (m_Size) * sizeof(POINT)); TRACE_DEBUG(("Deleting array of points %x", pOldArray)); delete[] pOldArray; return TRUE; } else { ERROR_OUT(("Failed to allocate new POINT array of size %d", m_MaxSize)); m_pData = pOldArray; return FALSE; } } // // Add a new point to the array // void DCDWordArray::Add(POINT point) { MLZ_EntryOut(ZONE_FUNCTION, "DCDWordArray::Add"); TRACE_DEBUG(("Adding point(%d,%d) at %d", point.x, point.y, m_Size)); TRACE_DEBUG(("Adding point at %x", &m_pData[m_Size])); if(m_pData == NULL) { return; } m_pData[m_Size].x = point.x; m_pData[m_Size].y = point.y; m_Size++; // // if we want more points, we need to re allocate the array // if(m_Size == m_MaxSize) { m_MaxSize +=ARRAY_INCREMENT; if(ReallocateArray() == FALSE) { m_Size--; } } } // // Return the number of points in the array // UINT DCDWordArray::GetSize(void) { return m_Size; } // // Sets the size of the array // void DCDWordArray::SetSize(UINT size) { int newSize; // // if we want more points, we need to re allocate the array // if (size > m_MaxSize) { m_MaxSize= ((size/ARRAY_INCREMENT)+1)*ARRAY_INCREMENT; if(ReallocateArray() == FALSE) { return; } } m_Size = size; }