|
|
//
// DRAW.CPP
// Main Drawing Window
//
// Copyright Microsoft 1998-
//
// PRECOMP
#include "precomp.h"
static const TCHAR szDrawClassName[] = "WB_DRAW";
//
//
// Function: Constructor
//
// Purpose: Initialize the drawing area object
//
//
WbDrawingArea::WbDrawingArea(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::WbDrawingArea");
g_pDraw = this;
m_hwnd = NULL;
m_hDCWindow = NULL; m_hDCCached = NULL;
m_originOffset.cx = 0; m_originOffset.cy = 0;
m_posScroll.x = 0; m_posScroll.y = 0;
// Show that the drawing area is not zoomed
m_iZoomFactor = 1; m_iZoomOption = 1;
// Show that the left mouse button is up
m_bLButtonDown = FALSE; m_bIgnoreNextLClick = FALSE; m_bBusy = FALSE; m_bLocked = FALSE; m_HourGlass = FALSE;
// Indicate that the cached zoom scroll position is invalid
m_zoomRestoreScroll = FALSE;
// Show that we are not currently editing text
m_bGotCaret = FALSE; m_bTextEditorActive = FALSE; m_pActiveText = NULL;
// Show that no graphic object is in progress
m_pGraphicTracker = NULL;
// Show that the marker is not present.
m_bMarkerPresent = FALSE; m_bNewMarkedGraphic = FALSE; m_pSelectedGraphic = NULL; m_bTrackingSelectRect = FALSE;
// Show that no area is currently marked
::SetRectEmpty(&m_rcMarkedArea);
// Show we haven't got a tool yet
m_pToolCur = NULL;
// Show that we dont have a page attached yet
m_hPage = WB_PAGE_HANDLE_NULL;
m_hStartPaintGraphic = NULL;
m_pMarker = new DCWbGraphicMarker; if (!m_pMarker) { ERROR_OUT(("Failed to create m_pMarker in WbDrawingArea object constructor")); } }
//
//
// Function: Destructor
//
// Purpose: Close down the drawing area
//
//
WbDrawingArea::~WbDrawingArea(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::~WbDrawingArea");
if (m_pActiveText != NULL) { delete m_pActiveText; m_pActiveText = NULL; }
if (m_hDCWindow != NULL) { ::ReleaseDC(m_hwnd, m_hDCWindow); m_hDCWindow = NULL; }
m_hDCCached = NULL;
if (m_pMarker != NULL) { delete m_pMarker; m_pMarker = NULL; }
if (m_hwnd != NULL) { ::DestroyWindow(m_hwnd); ASSERT(m_hwnd == NULL); }
::UnregisterClass(szDrawClassName, g_hInstance);
g_pDraw = NULL;
//
// Clean the pointer lists
//
m_allPointers.EmptyList(); m_undrawnPointers.EmptyList(); }
//
// WbDrawingArea::Create()
//
BOOL WbDrawingArea::Create(HWND hwndParent, LPCRECT lprect) { WNDCLASSEX wc;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::Create");
if (!m_pMarker) { ERROR_OUT(("Failing WbDrawingArea::Create; couldn't allocate m_pMarker")); return(FALSE); }
// Get our cursor
m_hCursor = ::LoadCursor(g_hInstance, MAKEINTRESOURCE( PENFREEHANDCURSOR));
//
// Register the window class
//
ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.style = CS_OWNDC; wc.lpfnWndProc = DrawWndProc; wc.hInstance = g_hInstance; wc.hCursor = m_hCursor; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = szDrawClassName;
if (!::RegisterClassEx(&wc)) { ERROR_OUT(("WbDraw::Create register class failed")); return(FALSE); }
//
// Create our window
//
ASSERT(m_hwnd == NULL);
if (!::CreateWindowEx(WS_EX_CLIENTEDGE, szDrawClassName, NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | WS_CLIPCHILDREN, lprect->left, lprect->top, lprect->right - lprect->left, lprect->bottom - lprect->top, hwndParent, NULL, g_hInstance, this)) { ERROR_OUT(("Error creating drawing area window")); return(FALSE); }
ASSERT(m_hwnd != NULL);
//
// Initialize remaining data members
//
ASSERT(!m_bBusy); ASSERT(!m_bLocked); ASSERT(!m_HourGlass);
// Start and end points of the last drawing operation
m_ptStart.x = m_originOffset.cx; m_ptStart.y = m_originOffset.cy; m_ptEnd = m_ptStart;
// Set the width to be used for marker handles.
ASSERT(m_pMarker); m_pMarker->SetPenWidth(DRAW_HANDLESIZE);
// Get the zoom factor to be used
m_iZoomOption = DRAW_ZOOMFACTOR;
m_hDCWindow = ::GetDC(m_hwnd); m_hDCCached = m_hDCWindow;
PrimeDC(m_hDCCached); ::SetWindowOrgEx(m_hDCCached, m_originOffset.cx, m_originOffset.cy, NULL); return(TRUE); }
//
// DrawWndProc()
// Message handler for the drawing area
//
LRESULT CALLBACK DrawWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; WbDrawingArea * pDraw = (WbDrawingArea *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (message) { case WM_NCCREATE: pDraw = (WbDrawingArea *)(((LPCREATESTRUCT)lParam)->lpCreateParams); ASSERT(pDraw);
pDraw->m_hwnd = hwnd; ::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pDraw); goto DefWndProc; break;
case WM_NCDESTROY: ASSERT(pDraw); pDraw->m_hwnd = NULL; break;
case WM_PAINT: ASSERT(pDraw); pDraw->OnPaint(); break;
case WM_MOUSEMOVE: ASSERT(pDraw); pDraw->OnMouseMove((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_LBUTTONDOWN: ASSERT(pDraw); pDraw->OnLButtonDown((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_LBUTTONUP: ASSERT(pDraw); pDraw->OnLButtonUp((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_CONTEXTMENU: ASSERT(pDraw); pDraw->OnContextMenu(LOWORD(lParam), HIWORD(lParam)); break;
case WM_SIZE: ASSERT(pDraw); pDraw->OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_HSCROLL: ASSERT(pDraw); pDraw->OnHScroll(GET_WM_HSCROLL_CODE(wParam, lParam), GET_WM_HSCROLL_POS(wParam, lParam)); break;
case WM_VSCROLL: ASSERT(pDraw); pDraw->OnVScroll(GET_WM_VSCROLL_CODE(wParam, lParam), GET_WM_VSCROLL_POS(wParam, lParam)); break;
case WM_CTLCOLOREDIT: ASSERT(pDraw); lResult = pDraw->OnEditColor((HDC)wParam); break;
case WM_SETFOCUS: ASSERT(pDraw); pDraw->OnSetFocus(); break;
case WM_ACTIVATE: ASSERT(pDraw); pDraw->OnActivate(GET_WM_ACTIVATE_STATE(wParam, lParam)); break;
case WM_SETCURSOR: ASSERT(pDraw); lResult = pDraw->OnCursor((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_CANCELMODE: ASSERT(pDraw); pDraw->OnCancelMode(); break;
default: DefWndProc: lResult = DefWindowProc(hwnd, message, wParam, lParam); break; }
return(lResult); }
//
//
// Function: RealizePalette
//
// Purpose: Realize the drawing area palette
//
//
void WbDrawingArea::RealizePalette( BOOL bBackground ) { UINT entriesChanged; HDC hdc = m_hDCCached;
if (m_hPage != WB_PAGE_HANDLE_NULL) { HPALETTE hPalette = PG_GetPalette(); if (hPalette != NULL) { // get our 2cents in
m_hOldPalette = ::SelectPalette(hdc, hPalette, bBackground); entriesChanged = ::RealizePalette(hdc);
// if mapping changes go repaint
if (entriesChanged > 0) ::InvalidateRect(m_hwnd, NULL, TRUE); } } }
LRESULT WbDrawingArea::OnEditColor(HDC hdc) { HPALETTE hPalette = PG_GetPalette();
if (hPalette != NULL) { ::SelectPalette(hdc, hPalette, FALSE); ::RealizePalette(hdc); }
::SetTextColor(hdc, SET_PALETTERGB( m_textEditor.m_clrPenColor ) );
return((LRESULT)::GetSysColorBrush(COLOR_WINDOW)); }
//
//
// Function: OnPaint
//
// Purpose: Paint the window. This routine is called whenever Windows
// issues a WM_PAINT message for the Whiteboard window.
//
//
void WbDrawingArea::OnPaint(void) { RECT rcUpdate; RECT rcTmp; RECT rcBounds; HDC hSavedDC; HPEN hSavedPen; HBRUSH hSavedBrush; HPALETTE hSavedPalette; HPALETTE hPalette; HFONT hSavedFont;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::OnPaint");
// Get the update rectangle
::GetUpdateRect(m_hwnd, &rcUpdate, FALSE);
if (Zoomed()) { ::InflateRect(&rcUpdate, 1, 1); InvalidateSurfaceRect(&rcUpdate); }
// Can only do any painting if we have a valid page
if (m_hPage != WB_PAGE_HANDLE_NULL) { // Determine whether any of the remote pointers are to be redrawn.
// If they are they must be added to the update region to allow them
// to redraw correctly. This is because they save the bits underneath
// them and blit them back onto the screen as they are moved.
if (m_allPointers.IsEmpty() == FALSE) { TRACE_MSG(("Remote pointer is dispayed")); POSITION pos = m_allPointers.GetHeadPosition(); while (pos != NULL) { DCWbGraphicPointer* pPointer = (DCWbGraphicPointer*) m_allPointers.GetNext(pos);
pPointer->GetBoundsRect(&rcBounds); if (::IntersectRect(&rcTmp, &rcBounds, &rcUpdate)) { TRACE_MSG(("Invalidating remote pointer")); InvalidateSurfaceRect(&rcBounds); } } } }
// Start painting
PAINTSTRUCT ps;
::BeginPaint(m_hwnd, &ps);
hSavedDC = m_hDCCached; hSavedFont = m_hOldFont; hSavedPen = m_hOldPen; hSavedBrush = m_hOldBrush; hSavedPalette = m_hOldPalette;
TRACE_MSG(("Flipping cache to paint DC")); m_hDCCached = ps.hdc; PrimeDC(m_hDCCached);
// Only draw anything if we have a valid page attached
if (m_hPage != WB_PAGE_HANDLE_NULL) { // set palette
hPalette = PG_GetPalette(); if (hPalette != NULL) { m_hOldPalette = ::SelectPalette(m_hDCCached, hPalette, FALSE ); ::RealizePalette(m_hDCCached); }
//
// Draw the graphic objects
//
DCWbGraphic* pGraphic; WB_GRAPHIC_HANDLE hStart;
if( m_hStartPaintGraphic != NULL ) { hStart = m_hStartPaintGraphic; m_hStartPaintGraphic = NULL;
pGraphic = DCWbGraphic::ConstructGraphic(m_hPage, hStart); } else { pGraphic = PG_First(m_hPage, &hStart, &rcUpdate); }
while (pGraphic != NULL) { ASSERT(pGraphic->Handle() == hStart);
// Do not draw the active text graphic yet (it is drawn topmost)
if (!m_bTextEditorActive || (hStart != m_textEditor.Handle())) { TRACE_MSG(("Drawing a normal graphic")); pGraphic->Draw(m_hDCCached, this); }
// Release the current graphic
delete pGraphic;
// Get the next one
pGraphic = PG_Next(m_hPage, &hStart, &rcUpdate); }
//
// Draw the marker
//
if (GraphicSelected() == TRUE) { TRACE_MSG(("Drawing the marker")); DrawMarker(m_hDCCached); }
//
// Draw the remote pointers that are on this page
//
if (m_allPointers.IsEmpty() == FALSE) { POSITION pos = m_allPointers.GetHeadPosition(); while (pos != NULL) { DCWbGraphicPointer* pPointer = (DCWbGraphicPointer*) m_allPointers.GetNext(pos);
pPointer->GetBoundsRect(&rcTmp); if (::IntersectRect(&rcTmp, &rcTmp, &rcUpdate)) { TRACE_MSG(("Drawing remote pointer")); pPointer->DrawSave(m_hDCCached, this); } } }
//
// Draw the tracking graphic
// But not if it is a remote pointer since this has already been done
// above and Draw() is not the correct function to use for Rem Ptr
//
if ((m_pGraphicTracker != NULL) && !EqualPoint(m_ptStart, m_ptEnd) && !(m_pGraphicTracker->IsGraphicTool() == enumGraphicPointer)) { TRACE_MSG(("Drawing the tracking graphic")); m_pGraphicTracker->Draw(m_hDCCached, this); }
if (hPalette != NULL) { ::SelectPalette(m_hDCCached, m_hOldPalette, TRUE); }
// fixes painting problems for bug 2185
if( TextEditActive() ) { RedrawTextEditbox(); } }
//
// Restore the DC to its original state
//
UnPrimeDC(m_hDCCached);
m_hOldFont = hSavedFont; m_hOldPen = hSavedPen; m_hOldBrush = hSavedBrush; m_hOldPalette = hSavedPalette; m_hDCCached = hSavedDC;
// Finish painting
::EndPaint(m_hwnd, &ps); }
//
// Selects all graphic objs contained in rectSelect. If rectSelect is
// NULL then ALL objs are selected
//
void WbDrawingArea::SelectMarkerFromRect(LPCRECT lprcSelect) { BOOL bSomethingWasPicked = FALSE; DCWbGraphic* pGraphic; WB_GRAPHIC_HANDLE hStart; RECT rc;
if (g_pwbCore->WBP_PageCountGraphics(m_hPage) <= 0 ) return;
m_HourGlass = TRUE; SetCursorForState();
RemoveMarker( NULL );
pGraphic = PG_First(m_hPage, &hStart, lprcSelect, TRUE); while (pGraphic != NULL) { // add obj to marker list if its not locked - bug 2185
pGraphic->GetBoundsRect(&rc);
ASSERT(m_pMarker); if (m_pMarker->SetRect(&rc, pGraphic, FALSE)) { m_pSelectedGraphic = pGraphic; bSomethingWasPicked = TRUE; }
// Get the next one
pGraphic = PG_Next(m_hPage, &hStart, lprcSelect, TRUE ); }
if( bSomethingWasPicked ) PutMarker( NULL );
m_HourGlass = FALSE; SetCursorForState(); }
//
//
// Function: OnTimer
//
// Purpose: Process a timer event. These are used to update freehand and
// text objects while they are being drawn/edited and to
// update the remote pointer position when the mouse stops.
//
//
void WbDrawingArea::OnTimer(UINT idTimer) { TRACE_TIMER(("WbDrawingArea::OnTimer"));
// We are only interested if the user is drawing something or editing
if (m_bLButtonDown == TRUE) { // If the user is dragging an object or drawing a freehand line
if (m_pGraphicTracker != NULL) { // If the user is drawing a freehand line
if (m_pGraphicTracker->IsGraphicTool() == enumGraphicFreeHand) {
// The update only writes the new version if changes have been made
if (m_pGraphicTracker->Handle() == NULL) { m_pGraphicTracker->AddToPageLast(m_hPage); } else { m_pGraphicTracker->Replace(); } }
//
// If the user is dragging a remote pointer (have to check
// m_pGraphicTracker for NULL again in case OnLButtonUp was
// called (bug 4685))
//
if ( m_pGraphicTracker != NULL ) { if (m_pGraphicTracker->IsGraphicTool() == enumGraphicPointer) { // The update only writes the new version if changes have been made
m_pGraphicTracker->Update(); } } } } }
//
//
// Function: OnSize
//
// Purpose: The window has been resized.
//
//
void WbDrawingArea::OnSize(UINT nType, int cx, int cy) { // Only process this message if the window is not minimized
if ( (nType == SIZEFULLSCREEN) || (nType == SIZENORMAL)) { if (TextEditActive()) { TextEditParentResize(); }
// Set the new scroll range (based on the new client area)
SetScrollRange(cx, cy);
// Ensure that the scroll position lies in the new scroll range
ValidateScrollPos();
// make page move if needed
ScrollWorkspace();
// Update the scroll bars
::SetScrollPos(m_hwnd, SB_HORZ, m_posScroll.x, TRUE); ::SetScrollPos(m_hwnd, SB_VERT, m_posScroll.y, TRUE); } }
//
//
// Function: SetScrollRange
//
// Purpose: Set the current scroll range. The range is based on the
// work surface size and the size of the client area.
//
//
void WbDrawingArea::SetScrollRange(int cx, int cy) { SCROLLINFO scinfo;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::SetScrollRange");
// If we are in zoom mode, then allow for the magnification
ASSERT(m_iZoomFactor != 0); cx /= m_iZoomFactor; cy /= m_iZoomFactor;
ZeroMemory( &scinfo, sizeof (SCROLLINFO) ); scinfo.cbSize = sizeof (SCROLLINFO); scinfo.fMask = SIF_PAGE | SIF_RANGE| SIF_DISABLENOSCROLL;
// Set the horizontal scroll range and proportional thumb size
scinfo.nMin = 0; scinfo.nMax = DRAW_WIDTH - 1; scinfo.nPage = cx; ::SetScrollInfo(m_hwnd, SB_HORZ, &scinfo, FALSE);
// Set the vertical scroll range and proportional thumb size
scinfo.nMin = 0; scinfo.nMax = DRAW_HEIGHT - 1; scinfo.nPage = cy; ::SetScrollInfo(m_hwnd, SB_VERT, &scinfo, FALSE); }
//
//
// Function: ValidateScrollPos
//
// Purpose: Ensure that the current scroll position is within the bounds
// of the current scroll range. The scroll range is set to
// ensure that the window on the worksurface never extends
// beyond the surface boundaries.
//
//
void WbDrawingArea::ValidateScrollPos() { int iMax; SCROLLINFO scinfo;
// Validate the horixontal scroll position using proportional settings
scinfo.cbSize = sizeof(scinfo); scinfo.fMask = SIF_ALL; ::GetScrollInfo(m_hwnd, SB_HORZ, &scinfo); iMax = scinfo.nMax - scinfo.nPage + 1; m_posScroll.x = max(m_posScroll.x, 0); m_posScroll.x = min(m_posScroll.x, iMax);
// Validate the vertical scroll position using proportional settings
scinfo.cbSize = sizeof(scinfo); scinfo.fMask = SIF_ALL; ::GetScrollInfo(m_hwnd, SB_VERT, &scinfo); iMax = scinfo.nMax - scinfo.nPage + 1; m_posScroll.y = max(m_posScroll.y, 0); m_posScroll.y = min(m_posScroll.y, iMax); }
//
//
// Function: ScrollWorkspace
//
// Purpose: Scroll the workspace to the position set in the member
// variable m_posScroll.
//
//
void WbDrawingArea::ScrollWorkspace(void) { RECT rc;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::ScrollWorkspace");
//
// Determine whether any of the remote pointers are to be redrawn. If
// they are they must be added to the update region to allow them to
// redraw correctly. This is because they save the bits underneath them
// and blit them back onto the screen as they are moved.
//
if (!m_allPointers.IsEmpty()) { TRACE_MSG(("Remote pointer is dispayed - invalidate before scroll")); POSITION pos = m_allPointers.GetHeadPosition();
while (pos != NULL) { DCWbGraphicPointer* pPointer = (DCWbGraphicPointer*) m_allPointers.GetNext(pos);
TRACE_MSG(("Invalidating remote pointer")); pPointer->GetBoundsRect(&rc); InvalidateSurfaceRect(&rc); } }
// Do the scroll
DoScrollWorkspace();
// Tell the parent that the scroll position has changed
HWND hwndParent;
hwndParent = ::GetParent(m_hwnd); if (hwndParent != NULL) { ::PostMessage(hwndParent, WM_USER_PRIVATE_PARENTNOTIFY, WM_VSCROLL, 0L); } }
//
//
// Function: DoScrollWorkspace
//
// Purpose: Scroll the workspace to the position set in the member
// variable m_posScroll.
//
//
void WbDrawingArea::DoScrollWorkspace() { // Validate the scroll position
ValidateScrollPos();
// Set the scroll box position
::SetScrollPos(m_hwnd, SB_HORZ, m_posScroll.x, TRUE); ::SetScrollPos(m_hwnd, SB_VERT, m_posScroll.y, TRUE);
// Only update the screen if the scroll position has changed
if ( (m_originOffset.cy != m_posScroll.y) || (m_originOffset.cx != m_posScroll.x) ) { // Calculate the amount to scroll
INT iVScrollAmount = m_originOffset.cy - m_posScroll.y; INT iHScrollAmount = m_originOffset.cx - m_posScroll.x;
// Save the new position (for UpdateWindow)
m_originOffset.cx = m_posScroll.x; m_originOffset.cy = m_posScroll.y;
::SetWindowOrgEx(m_hDCCached, m_originOffset.cx, m_originOffset.cy, NULL);
// Scroll and redraw the newly invalidated portion of the window
::ScrollWindow(m_hwnd, iHScrollAmount, iVScrollAmount, NULL, NULL); ::UpdateWindow(m_hwnd); } }
//
//
// Function: GotoPosition
//
// Purpose: Move the top-left corner of the workspace to the specified
// position in the workspace.
//
//
void WbDrawingArea::GotoPosition(int x, int y) { // Set the new scroll position
m_posScroll.x = x; m_posScroll.y = y;
// Scroll to the new position
DoScrollWorkspace();
// Invalidate the zoom scroll cache if we scroll when unzoomed.
if (!Zoomed()) { m_zoomRestoreScroll = FALSE; } }
//
//
// Function: OnVScroll
//
// Purpose: Process a WM_VSCROLL messages.
//
//
void WbDrawingArea::OnVScroll(UINT nSBCode, UINT nPos) { RECT rcClient;
// Get the current client rectangle HEIGHT
::GetClientRect(m_hwnd, &rcClient); ASSERT(rcClient.top == 0); rcClient.bottom -= rcClient.top;
// Act on the scroll code
switch(nSBCode) { // Scroll to bottom
case SB_BOTTOM: m_posScroll.y = DRAW_HEIGHT - rcClient.bottom; break;
// Scroll down a line
case SB_LINEDOWN: m_posScroll.y += DRAW_LINEVSCROLL; break;
// Scroll up a line
case SB_LINEUP: m_posScroll.y -= DRAW_LINEVSCROLL; break;
// Scroll down a page
case SB_PAGEDOWN: m_posScroll.y += rcClient.bottom / m_iZoomFactor; break;
// Scroll up a page
case SB_PAGEUP: m_posScroll.y -= rcClient.bottom / m_iZoomFactor; break;
// Scroll to the top
case SB_TOP: m_posScroll.y = 0; break;
// Track the scroll box
case SB_THUMBPOSITION: case SB_THUMBTRACK: m_posScroll.y = nPos; // don't round
break;
default: break; }
// Validate the scroll position
ValidateScrollPos(); ::SetScrollPos(m_hwnd, SB_VERT, m_posScroll.y, TRUE);
// If this message is informing us of the end of scrolling,
// update the window
if (nSBCode == SB_ENDSCROLL) { // Scroll the window
ScrollWorkspace(); }
// Invalidate the zoom scroll cache if we scroll when unzoomed.
if (!Zoomed()) { m_zoomRestoreScroll = FALSE; } }
//
//
// Function: OnHScroll
//
// Purpose: Process a WM_HSCROLL messages.
//
//
void WbDrawingArea::OnHScroll(UINT nSBCode, UINT nPos) { RECT rcClient;
// Get the current client rectangle WIDTH
::GetClientRect(m_hwnd, &rcClient); ASSERT(rcClient.left == 0); rcClient.right -= rcClient.left;
switch(nSBCode) { // Scroll to the far right
case SB_BOTTOM: m_posScroll.x = DRAW_WIDTH - rcClient.right; break;
// Scroll right a line
case SB_LINEDOWN: m_posScroll.x += DRAW_LINEHSCROLL; break;
// Scroll left a line
case SB_LINEUP: m_posScroll.x -= DRAW_LINEHSCROLL; break;
// Scroll right a page
case SB_PAGEDOWN: m_posScroll.x += rcClient.right / m_iZoomFactor; break;
// Scroll left a page
case SB_PAGEUP: m_posScroll.x -= rcClient.right / m_iZoomFactor; break;
// Scroll to the far left
case SB_TOP: m_posScroll.x = 0; break;
// Track the scroll box
case SB_THUMBPOSITION: case SB_THUMBTRACK: m_posScroll.x = nPos; // don't round
break;
default: break; }
// Validate the scroll position
ValidateScrollPos(); ::SetScrollPos(m_hwnd, SB_HORZ, m_posScroll.x, TRUE);
// If this message is informing us of the end of scrolling,
// update the window
if (nSBCode == SB_ENDSCROLL) { // Scroll the window
ScrollWorkspace(); }
// Invalidate the zoom scroll cache if we scroll when unzoomed.
if (!Zoomed()) { m_zoomRestoreScroll = FALSE; } }
//
//
// Function: AutoScroll
//
// Purpose: Auto-scroll the window to bring the position passed as
// parameter into view.
//
//
BOOL WbDrawingArea::AutoScroll ( int xSurface, int ySurface, BOOL bMoveCursor, int xCaret, int yCaret ) { int nXPSlop, nYPSlop; int nXMSlop, nYMSlop; int nDeltaHScroll, nDeltaVScroll; BOOL bDoScroll = FALSE;
nXPSlop = 0; nYPSlop = 0; nXMSlop = 0; nYMSlop = 0;
if( TextEditActive() ) { POINT ptDirTest;
ptDirTest.x = xSurface - xCaret; ptDirTest.y = ySurface - yCaret;
// set up for text editbox
if( ptDirTest.x > 0 ) nXPSlop = m_textEditor.m_textMetrics.tmMaxCharWidth; else if( ptDirTest.x < 0 ) nXMSlop = -m_textEditor.m_textMetrics.tmMaxCharWidth;
if( ptDirTest.y > 0 ) nYPSlop = m_textEditor.m_textMetrics.tmHeight; else if( ptDirTest.y < 0 ) nYMSlop = -m_textEditor.m_textMetrics.tmHeight;
nDeltaHScroll = m_textEditor.m_textMetrics.tmMaxCharWidth; nDeltaVScroll = m_textEditor.m_textMetrics.tmHeight; } else { // set up for all other objects
nDeltaHScroll = DRAW_LINEHSCROLL; nDeltaVScroll = DRAW_LINEVSCROLL; }
// Get the current visible surface rectangle
RECT visibleRect; GetVisibleRect(&visibleRect);
// Check for pos + slop being outside visible area
if( (xSurface + nXPSlop) >= visibleRect.right ) { bDoScroll = TRUE; m_posScroll.x += (((xSurface + nXPSlop) - visibleRect.right) + nDeltaHScroll); }
if( (xSurface + nXMSlop) < visibleRect.left ) { bDoScroll = TRUE; m_posScroll.x -= ((visibleRect.left - (xSurface + nXMSlop)) + nDeltaHScroll); }
if( (ySurface + nYPSlop) >= visibleRect.bottom) { bDoScroll = TRUE; m_posScroll.y += (((ySurface + nYPSlop) - visibleRect.bottom) + nDeltaVScroll); }
if( (ySurface + nYMSlop) < visibleRect.top) { bDoScroll = TRUE; m_posScroll.y -= ((visibleRect.top - (ySurface + nYMSlop)) + nDeltaVScroll); }
if( !bDoScroll ) return( FALSE );
// Indicate that scrolling has completed (in both directions)
ScrollWorkspace();
// Update the mouse position (if required)
if (bMoveCursor) { POINT screenPos;
screenPos.x = xSurface; screenPos.y = ySurface;
SurfaceToClient(&screenPos); ::ClientToScreen(m_hwnd, &screenPos); ::SetCursorPos(screenPos.x, screenPos.y); }
return( TRUE ); }
//
//
// Function: OnCursor
//
// Purpose: Process a WM_SETCURSOR messages.
//
//
LRESULT WbDrawingArea::OnCursor(HWND hwnd, UINT uiHit, UINT uMsg) { BOOL bResult = FALSE;
// Check that this message is for the main window
if (hwnd == m_hwnd) { // If the cursor is now in the client area, set the cursor
if (uiHit == HTCLIENT) { bResult = SetCursorForState(); } else { // Restore the cursor to the standard arrow. Set m_hCursor to NULL
// to indicate that we have not set a special cursor.
m_hCursor = NULL; ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); bResult = TRUE; } }
// Return result indicating whether we processed the message or not
return bResult; }
//
//
// Function: SetCursorForState
//
// Purpose: Set the cursor for the current state
//
//
BOOL WbDrawingArea::SetCursorForState(void) { BOOL bResult = FALSE;
m_hCursor = NULL;
// If the drawing area is locked, use the "locked" cursor
if (m_HourGlass) { m_hCursor = ::LoadCursor( NULL, IDC_WAIT ); } else if (m_bLocked) { // Return the cursor for the tool
m_hCursor = ::LoadCursor(g_hInstance, MAKEINTRESOURCE( LOCKCURSOR )); } else if (m_pToolCur != NULL) { // Get the cursor for the tool currently in use
m_hCursor = m_pToolCur->GetCursorForTool(); }
if (m_hCursor != NULL) { ::SetCursor(m_hCursor); bResult = TRUE; }
// Return result indicating whether we set the cursor or not
return bResult; }
//
//
// Function: Lock
//
// Purpose: Lock the drawing area, preventing further updates
//
//
void WbDrawingArea::Lock(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::Lock");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Stop any drawing we are doing.
CancelDrawingMode();
// Deselect any selected graphic
ClearSelection();
// Show that we are now locked
m_bLocked = TRUE; TRACE_MSG(("Drawing area is now locked"));
// Set the cursor for the drawing mode, but only if we should be drawing
// a special cursor (if m_hCursor != the current cursor, then the cursor
// is out of the client area).
if (::GetCursor() == m_hCursor) { SetCursorForState(); } }
//
//
// Function: Unlock
//
// Purpose: Unlock the drawing area, preventing further updates
//
//
void WbDrawingArea::Unlock(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::Unlock");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Show that we are now unlocked
m_bLocked = FALSE; TRACE_MSG(("Drawing area is now UNlocked"));
// Set the cursor for the drawing mode, but only if we should be drawing
// a special cursor (if m_hCursor != the current cursor, then the cursor
// is out of the client area).
if (::GetCursor() == m_hCursor) { SetCursorForState(); } }
//
//
// Function: GraphicAdded
//
// Purpose: A graphic has been added to the page - update the drawing
// area.
//
//
void WbDrawingArea::GraphicAdded(DCWbGraphic* pAddedGraphic) { HPALETTE hPal; HPALETTE hOldPal = NULL; HDC hDC; RECT rcUpdate; RECT rcBounds; RECT rcT;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::GraphicAdded");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Get the current update rectangle
::GetUpdateRect(m_hwnd, &rcUpdate, FALSE);
// Check if the object is the uppermost in the page,
// if it is we can draw it onto the window without
// playing the whole contents of the page.
// (If there the invalid part of the window touches the rectangle of
// the graphic which has just been added, we just invalidate the area
// occupied by the new graphic to get it drawn.)
pAddedGraphic->GetBoundsRect(&rcBounds); if ((pAddedGraphic->IsTopmost()) && !::IntersectRect(&rcT, &rcUpdate, &rcBounds)) { // Get a device context for drawing
hDC = m_hDCCached;
// set up palette
if ( (m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE); ::RealizePalette(hDC); }
// Remove the remote pointers from the affected area
RemovePointers(hDC, &rcBounds);
// Remove the marker and save whether it is to be restored later
BOOL bSaveMarkerPresent = m_bMarkerPresent; RemoveMarker(NULL);
// Play the new graphic into the context
pAddedGraphic->Draw(hDC);
// Restore the marker (if necessary)
if (bSaveMarkerPresent == TRUE) { PutMarker(NULL); }
// Restore the remote pointers
PutPointers(hDC);
// If we are editting some text, make editbox redraw
if (m_bTextEditorActive && (m_textEditor.Handle() != NULL)) { RECT rcText;
m_textEditor.GetBoundsRect(&rcText);
// Include the client border
InflateRect(&rcText, ::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE)); InvalidateSurfaceRect(&rcText); }
if (hOldPal != NULL) { ::SelectPalette(hDC, hOldPal, TRUE); } } else { // Update the area occupied by the object
InvalidateSurfaceRect(&rcBounds); } }
//
//
// Function: PointerUpdated
//
// Purpose: A remote pointer has been added, removed or updated - make
// the change on the screen.
//
//
void WbDrawingArea::PointerUpdated ( DCWbGraphicPointer* pPointer, BOOL bForcedRemove ) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::PointerUpdated");
ASSERT(pPointer != NULL);
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Determine whether the pointer has been added, removed, or just updated
DCWbGraphicPointer* pUndrawFrom = pPointer; POSITION posBefore = m_allPointers.Lookup(pPointer); POSITION posAfter = NULL; if (posBefore == NULL) { // The pointer is not currently drawn
// Check whether the pointer is active
if ((pPointer->IsActive()) && !bForcedRemove) { // Determine where the pointer should go on the drawn list
if ( (pPointer->IsLocalPointer()) || (m_allPointers.IsEmpty() == TRUE)) { // The local pointer always goes at the end
posAfter = m_allPointers.AddTail(pPointer); } else { // Find the next active pointer on the page (we already
// know that allPointers is not empty from the test above).
posAfter = m_allPointers.GetTailPosition(); pUndrawFrom = (DCWbGraphicPointer*) m_allPointers.GetFromPosition(posAfter); if (!pUndrawFrom->IsLocalPointer()) { pUndrawFrom = PG_NextPointer(m_hPage, pPointer); }
posAfter = m_allPointers.AddTail(pPointer); } } } else { // The pointer is already in our list
pUndrawFrom = pPointer; }
// If we have something to do
if ((posBefore != NULL) || (posAfter != NULL)) { if (pUndrawFrom != NULL) { // Undraw all pointers in the vicinity of the updated pointer
RECT rcT; RECT rcBounds;
pPointer->GetDrawnRect(&rcT); pPointer->GetBoundsRect(&rcBounds); ::UnionRect(&rcT, &rcT, &rcBounds); RemovePointers(NULL, pUndrawFrom, &rcT); }
// If the updated pointer is no longer active we do not want
// to redraw it, and want to remove it from the active pointer
// list.
POSITION posUndrawn = m_undrawnPointers.Lookup(pPointer); if ((pPointer->IsActive() == FALSE) || (bForcedRemove == TRUE)) { // Remove it from the undrawn pointers list (so it does not
// get drawn again).
if (posUndrawn != NULL) { m_undrawnPointers.RemoveAt(posUndrawn); }
// Remove it from the list of all active pointers on the page.
posUndrawn = m_allPointers.Lookup(pPointer); if (posUndrawn != NULL) { m_allPointers.RemoveAt(posUndrawn); } } else { // If this pointer was not previously active it will not
// be in the undrawn list and will therefore not get redrawn. So
// add it to the list to get it drawn. (It goes at the head of the
// list because we have undrawn all pointers above it.)
if (posUndrawn == NULL) { m_undrawnPointers.AddTail(pPointer); } }
// Restore all the remote pointers that were removed
PutPointers(NULL); } }
//
//
// Function: RemovePointers
//
// Purpose: Remove all remote pointers that are above and overlap
// the specified pointer.
//
//
void WbDrawingArea::RemovePointers ( HDC hPassedDC, DCWbGraphicPointer* pPointerUpdate ) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::RemovePointers");
// Show that we have not removed any pointers yet
m_undrawnPointers.EmptyList();
// Do nothing if the pointer specified is NULL
if (pPointerUpdate != NULL) { RECT rcUpdate;
::SetRectEmpty(&rcUpdate); RemovePointers(hPassedDC, pPointerUpdate, &rcUpdate); } }
//
//
// Function: RemovePointers
//
// Purpose: Remove all remote pointers that overlap a rectangle on the
// surface.
//
//
void WbDrawingArea::RemovePointers ( HDC hPassedDC, LPCRECT lprc ) { RECT rcT;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::RemovePointers");
// Show that we have not removed any pointers yet
m_undrawnPointers.EmptyList();
// Only do anything if the rectangle given is visible
GetVisibleRect(&rcT); if (::IntersectRect(&rcT, &rcT, lprc)) { RemovePointers(hPassedDC, NULL, lprc); } }
//
//
// Function: RemovePointers
//
// Purpose: Remove all remote pointers that overlap a rectangle on the
// surface.
//
//
void WbDrawingArea::RemovePointers ( HDC hDC, DCWbGraphicPointer* pPointerStart, LPCRECT lprcOverlap ) { RECT rcT; RECT rcT2; RECT rcDrawn;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::RemovePointers");
// Show that we have not removed any pointers yet
m_undrawnPointers.EmptyList();
// Get our own DC (if necessary)
if (!hDC) hDC = m_hDCCached;
// We needn't do anything if the pointer and rectangles given
// are both off-screen.
GetVisibleRect(&rcT);
BOOL bNeedCheck = FALSE; if (pPointerStart != NULL) { pPointerStart->GetDrawnRect(&rcT2); if (::IntersectRect(&rcT2, &rcT2, &rcT)) { bNeedCheck = TRUE; } }
// A NULL overlap rect means empty
if (::IntersectRect(&rcT, &rcT, lprcOverlap)) { bNeedCheck = TRUE; }
if (bNeedCheck) { // Get a list of all pointers on the page (with the local
// pointer last in the list).
POSITION allPos = m_allPointers.GetHeadPosition();
// We must undraw pointers in decreasing Z-order.
// With more than two pointers the effect of removing
// a pointer can require another pointer to be removed also:
// (pointer A overlaps pointer B, and B overlaps C without A and
// C overlapping each other directly). To get round this we build
// a list of pointers to be removed (and redrawn) as we go.
// If we are starting from a pointer
if (pPointerStart != NULL) { // Get the position of the start pointer
POSITION startPos = m_allPointers.Lookup(pPointerStart);
// If the pointer was not found, this is an error
ASSERT(startPos != NULL);
// Save the start position for the search
m_allPointers.GetNext(startPos); allPos = startPos;
// Add the updated pointer to the remove list
m_undrawnPointers.AddTail(pPointerStart);
// If the rectangle passed in is empty, set it to the rectangle
// of the pointer passed in.
if (::IsRectEmpty(lprcOverlap)) { pPointerStart->GetDrawnRect(&rcDrawn); lprcOverlap = &rcDrawn; } }
// For each pointer above the start, check whether it overlaps
// any pointer in the list already built, or the rectangle passed in.
DCWbGraphicPointer* pPointerCheck; while (allPos != NULL) { // Get the pointer to be tested
pPointerCheck = (DCWbGraphicPointer*) m_allPointers.GetNext(allPos);
// Get the rectangle it is currently occupying on the surface
// Check for overlap with the passed rectangle
pPointerCheck->GetDrawnRect(&rcT2); if (::IntersectRect(&rcT, &rcT2, lprcOverlap)) { m_undrawnPointers.AddTail(pPointerCheck); } }
// Create a reversed list
CWBOBLIST worklist; DCWbGraphicPointer* pPointer;
POSITION pos = m_undrawnPointers.GetHeadPosition(); while (pos != NULL) { pPointer = (DCWbGraphicPointer*) m_undrawnPointers.GetNext(pos); worklist.AddHead(pPointer); }
// Now remove the pointers, walking through the reverde list
pos = worklist.GetHeadPosition(); while (pos != NULL) { // Remove it
pPointer = (DCWbGraphicPointer*) worklist.GetNext(pos); pPointer->Undraw(hDC, this); }
worklist.EmptyList(); } }
//
//
// Function: PutPointers
//
// Purpose: Draw all remote pointers in the pointer redraw list.
//
//
void WbDrawingArea::PutPointers ( HDC hDC, COBLIST* pUndrawList ) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::PutPointers");
if (!hDC) hDC = m_hDCCached;
// Get the start position in the list for drawing
if (pUndrawList == NULL) { pUndrawList = &m_undrawnPointers; }
// Do the redrawing
DCWbGraphicPointer* pPointer; POSITION pos = pUndrawList->GetHeadPosition(); while (pos != NULL) { // Get the next pointer
pPointer = (DCWbGraphicPointer*) pUndrawList->GetNext(pos); pPointer->Redraw(hDC, this); } }
//
//
// Function: PageCleared
//
// Purpose: The page has been cleared
//
//
void WbDrawingArea::PageCleared(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::PageCleared");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Discard any text being edited
if (m_bTextEditorActive) { if (m_bLocked) { DeactivateTextEditor(); } else { EndTextEntry(FALSE); } }
// Remove the copy of the marked graphic and the marker
ClearSelection();
// Invalidate the whole window
::InvalidateRect(m_hwnd, NULL, TRUE); }
//
//
// Function: GraphicDeleted
//
// Purpose: A graphic has been removed from the page - update the
// drawing area.
//
//
void WbDrawingArea::GraphicDeleted(DCWbGraphic* pDeletedGraphic) { DCWbGraphic* pDeletedMarker; RECT rcBounds;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::GraphicDeleted");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// Check whether the graphic being deleted is selected
ASSERT(m_pMarker);
if( GraphicSelected() && ((pDeletedMarker = m_pMarker->HasAMarker( pDeletedGraphic )) != NULL) ) { // remove marker corresponding to the deleted graphic
delete pDeletedMarker;
// if deleted graphic was also the last selection, use prev selection
// (carefull...m_pSelectedGraphic is invalid now if this is true)
if( m_pSelectedGraphic == pDeletedMarker ) //only safe comparision
m_pSelectedGraphic = m_pMarker->LastMarker(); }
// Invalidate the area occupied by the object
pDeletedGraphic->GetBoundsRect(&rcBounds); InvalidateSurfaceRect(&rcBounds); }
//
//
// Function: GraphicUpdated
//
// Purpose: A graphic in the page has been updated - update the
// drawing area.
//
//
void WbDrawingArea::GraphicUpdated ( DCWbGraphic* pUpdatedGraphic, BOOL bUpdateMarker, BOOL bErase ) { DCWbGraphic* pUpdatedMarker; BOOL bWasEqual; RECT rcBounds;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::GraphicUpdated");
// Check whether the drawing area is busy - this is not allowed
ASSERT(!m_bBusy);
// If the graphic being updated is selected update marker status
ASSERT(m_pMarker);
if( bUpdateMarker && GraphicSelected() && ((pUpdatedMarker = m_pMarker->HasAMarker( pUpdatedGraphic )) != NULL) ) { // must zap lock flag for old object to prevent UnLock loops
pUpdatedMarker->ClearLockFlag(); delete pUpdatedMarker;
// If the graphic is now locked deselect it
if (pUpdatedGraphic->Locked() == TRUE) { if( m_pSelectedGraphic == pUpdatedMarker ) //only safe comparision
m_pSelectedGraphic = m_pMarker->LastMarker(); } else { // the graphic isn't locked, re-select it
bWasEqual = (m_pSelectedGraphic == pUpdatedMarker); pUpdatedMarker = DCWbGraphic::ConstructGraphic(m_hPage, pUpdatedGraphic->Handle());
pUpdatedMarker->GetBoundsRect(&rcBounds); m_pMarker->SetRect(&rcBounds, pUpdatedMarker, FALSE );
if( bWasEqual ) //only safe comparision
m_pSelectedGraphic = pUpdatedMarker; } }
if (TextEditActive() && (m_textEditor.Handle() == pUpdatedGraphic->Handle()) ) { return; // skip update if object is currently being text edited
// (fix for bug 3059)
}
pUpdatedGraphic->GetBoundsRect(&rcBounds); InvalidateSurfaceRect(&rcBounds, bErase); }
//
//
// Function : GraphicFreehandUpdated
//
// Purpose : A freehand graphic has been updated
//
//
void WbDrawingArea::GraphicFreehandUpdated(DCWbGraphic* pGraphic) { HPALETTE hPal; HPALETTE hOldPal = NULL; RECT rc;
// Draw the object
HDC hDC = m_hDCCached;
if ((m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE ); ::RealizePalette(hDC); }
// Remove the remote pointers from the affected area
pGraphic->GetBoundsRect(&rc); RemovePointers(hDC, &rc);
// Play the new graphic into the context
pGraphic->Draw(hDC);
// Restore the remote pointers
PutPointers(hDC);
// Get the intersection of the graphic and any objects covering it - if
// there are any objects over the freehand object, we have to redraw them
PG_GetObscuringRect(m_hPage, pGraphic, &rc); if (!::IsRectEmpty(&rc)) { // The graphic is at least partially obscured - force an update
InvalidateSurfaceRect(&rc, TRUE); }
if (hOldPal != NULL ) { ::SelectPalette(hDC, hOldPal, TRUE); } }
//
//
// Function: InvalidateSurfaceRect
//
// Purpose: Invalidate the window rectangle corresponding to the given
// drawing surface rectangle.
//
//
void WbDrawingArea::InvalidateSurfaceRect(LPCRECT lprc, BOOL bErase) { RECT rc;
// Convert the surface co-ordinates to client window and invalidate
// the rectangle.
rc = *lprc; SurfaceToClient(&rc); ::InvalidateRect(m_hwnd, &rc, bErase); }
//
//
// Function: UpdateRectangles
//
// Purpose: Updates have affected a region of the drawing area - force
// a redraw now.
//
//
void WbDrawingArea::UpdateRectangles ( LPCRECT lprc1, LPCRECT lprc2, BOOL bRepaint ) { // Remove the marker and save whether it is to be restored later
BOOL bSaveMarkerPresent = m_bMarkerPresent; RemoveMarker(NULL);
// Invalidate the bounding rectangles specifying that the background
// is to be erased when painted.
if (!::IsRectEmpty(lprc1)) { InvalidateSurfaceRect(lprc1, bRepaint); }
if (!::IsRectEmpty(lprc2)) { InvalidateSurfaceRect(lprc2, bRepaint); }
// Repaint the invalidated regions
::UpdateWindow(m_hwnd);
// Restore the marker (if necessary)
if (bSaveMarkerPresent) { PutMarker(NULL); } }
//
//
// Function: PrimeFont
//
// Purpose: Insert the supplied font into our DC and return the
// text metrics
//
//
void WbDrawingArea::PrimeFont(HDC hDC, HFONT hFont, TEXTMETRIC* pTextMetrics) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::PrimeFont");
//
// temporarily unzoom to get the font that we want
//
if (Zoomed()) { ::ScaleViewportExtEx(m_hDCCached, 1, m_iZoomFactor, 1, m_iZoomFactor, NULL); }
HFONT hOldFont = SelectFont(hDC, hFont); if (hOldFont == NULL) { WARNING_OUT(("Failed to select font into DC")); }
if (pTextMetrics != NULL) { ::GetTextMetrics(hDC, pTextMetrics); }
//
// restore the zoom state
//
if (Zoomed()) { ::ScaleViewportExtEx(m_hDCCached, m_iZoomFactor, 1, m_iZoomFactor, 1, NULL); } }
//
//
// Function: UnPrimeFont
//
// Purpose: Remove the specified font from the DC and clear cache
// variable
//
//
void WbDrawingArea::UnPrimeFont(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::UnPrimeFont");
if (hDC != NULL) { SelectFont(hDC, ::GetStockObject(SYSTEM_FONT)); } }
//
//
// Function: PrimeDC
//
// Purpose: Set up a DC for drawing
//
//
void WbDrawingArea::PrimeDC(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::PrimeDC");
::SetMapMode(hDC, MM_ANISOTROPIC);
::SetBkMode(hDC, TRANSPARENT);
::SetTextAlign(hDC, TA_LEFT | TA_TOP); }
//
//
// Function: UnPrimeDC
//
// Purpose: Reset the DC to default state
//
//
void WbDrawingArea::UnPrimeDC(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::UnPrimeDC");
SelectPen(hDC, (HPEN)::GetStockObject(BLACK_PEN)); SelectBrush(hDC, (HBRUSH)::GetStockObject(BLACK_BRUSH));
UnPrimeFont(hDC); }
//
// WbDrawingArea::OnContextMenu()
//
void WbDrawingArea::OnContextMenu(int xScreen, int yScreen) { POINT pt; RECT rc;
pt.x = xScreen; pt.y = yScreen; ::ScreenToClient(m_hwnd, &pt);
::GetClientRect(m_hwnd, &rc); if (::PtInRect(&rc, pt)) { // Complete drawing action, if any
OnLButtonUp(0, pt.x, pt.y);
// Ask main window to put up context menu
g_pMain->PopupContextMenu(pt.x, pt.y); } }
//
// WbDrawingArea::OnLButtonDown()
//
void WbDrawingArea::OnLButtonDown(UINT flags, int x, int y) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::OnLButtonDown");
if( m_bIgnoreNextLClick ) { TRACE_MSG( ("Ignoring WM_LBUTTONDOWN") ); return; }
// Set the focus to this window. This is done to ensure that we trap
// the text edit keys and the delete key when they are used.
::SetFocus(m_hwnd);
// Save the operation start point (and current end point)
// Adjust the mouse position to allow for the zoom factor
m_ptStart.x = x; m_ptStart.y = y; ClientToSurface(&m_ptStart); m_ptEnd = m_ptStart;
// Show that the mouse button is now down
m_bLButtonDown = TRUE;
// Show that the drawing area is now busy
m_bBusy = TRUE;
// User's can drag their own remote pointer even if the drawing area
// is locked. So we check before the test for the lock.
if (m_pToolCur->ToolType() == TOOLTYPE_SELECT) { if (RemotePointerSelect(m_ptStart)) { return; } }
// Only allow the action to take place if the drawing area is unlocked,
// and we have a valid tool
if (m_bLocked || (m_pToolCur == NULL)) { // Tidy up the state and leave now
m_bLButtonDown = FALSE; m_bBusy = FALSE; return; }
// Call the relevant initialization routine
if (m_pToolCur->ToolType() != TOOLTYPE_SELECT) { // dump selection if not select tool
ClearSelection(); }
switch (m_pToolCur->ToolType()) { case TOOLTYPE_SELECT: BeginSelectMode(m_ptStart); break;
case TOOLTYPE_ERASER: BeginDeleteMode(m_ptStart); break;
case TOOLTYPE_TEXT: break;
case TOOLTYPE_HIGHLIGHT: case TOOLTYPE_PEN: BeginFreehandMode(m_ptStart); break;
case TOOLTYPE_LINE: BeginLineMode(m_ptStart); break;
case TOOLTYPE_BOX: case TOOLTYPE_FILLEDBOX: BeginRectangleMode(m_ptStart); break;
case TOOLTYPE_ELLIPSE: case TOOLTYPE_FILLEDELLIPSE: BeginEllipseMode(m_ptStart); break;
// Do nothing if we do not recognise the pen type
default: ERROR_OUT(("Bad tool type")); break; }
// Clamp the cursor to the drawing window
RECT rcClient;
::GetClientRect(m_hwnd, &rcClient); ::MapWindowPoints(m_hwnd, NULL, (LPPOINT)&rcClient.left, 2); ::InflateRect(&rcClient, 1, 1); ::ClipCursor(&rcClient); }
//
//
// Function: RemotePointerSelect
//
// Purpose: Check for the user clicking inside their own remote pointer.
//
//
BOOL WbDrawingArea::RemotePointerSelect ( POINT surfacePos ) { BOOL bResult = FALSE; DCWbGraphicPointer* pPointer;
// Check we have a valid page
if (m_hPage == WB_PAGE_HANDLE_NULL) { return(bResult); }
// Assume we do not start dragging a graphic
m_pGraphicTracker = NULL;
// Check if we are clicking in the local user's pointer
pPointer = PG_LocalPointer(m_hPage); if ( (pPointer != NULL) && (pPointer->PointInBounds(surfacePos))) { // The user is clicking in their pointer
m_pGraphicTracker = pPointer;
// Save the current time (used to determine when to update
// the external remote pointer information).
m_dwTickCount = ::GetTickCount();
// Hide the mouse (helps prevent flicker)
::ShowCursor(FALSE);
// Get all mouse input directed to the this window
::SetCapture(m_hwnd);
// Start the timer for updating the pointer (this is only for updating
// the pointer position when the user stops moving the pointer but
// keeps the mouse button down).
::SetTimer(m_hwnd, TIMER_GRAPHIC_UPDATE, DRAW_GRAPHICUPDATEDELAY, NULL);
// Show that we have selected a pointer
bResult = TRUE; }
return(bResult); }
//
//
// Function: SelectPreviousGraphicAt
//
// Purpose: Select the previous graphic (in the Z-order) at the position
// specified, and starting at a specified graphic. If the
// graphic pointer given is NULL the search starts from the
// top. If the point specified is outside the bounding
// rectangle of the specified graphic the search starts at the
// top and chooses the first graphic which contains the point.
//
// The search process will loop back to the top of the Z-order
// if it gets to the bottom having failed to find a graphic.
//
// Graphics which are locked are ignored by the search.
//
//
DCWbGraphic* WbDrawingArea::SelectPreviousGraphicAt ( DCWbGraphic* pStartGraphic, POINT point ) { // Set the result to "none found" initially
DCWbGraphic* pResultGraphic = NULL;
// If a starting point has been specified
if (pStartGraphic != NULL) { RECT rectHit;
MAKE_HIT_RECT(rectHit, point);
// If the reference point is within the start graphic
if ( pStartGraphic->PointInBounds(point) && pStartGraphic->CheckReallyHit( &rectHit ) ) { // Start from the specified graphic
pResultGraphic = pStartGraphic;
// Look for the previous one (that is not locked)
do { pResultGraphic = PG_SelectPrevious(m_hPage, *pResultGraphic, point); } while ( (pResultGraphic != NULL) && (pResultGraphic->Locked())); } else { // We are not looking within the currently selected graphic.
// Deselect the current one. The start pointer and handle are
// left at NULL.
; } }
// If we have not got a result graphic yet. (This catches two cases:
// - where no start graphic has been given so that we want to start
// from the top,
// - where we have searched back from the start graphic and reached
// the bottom of the Z-order without finding a suitable graphic.
if (pResultGraphic == NULL) { // Get the topmost graphic that contains the point specified
pResultGraphic = PG_SelectLast(m_hPage, point);
// Ensure that we have not got a locked graphic
while ( (pResultGraphic != NULL) && (pResultGraphic->Locked())) { pResultGraphic = PG_SelectPrevious(m_hPage, *pResultGraphic, point); } }
// If we have found an object, draw the marker
if (pResultGraphic != NULL) { // Select the new one
SelectGraphic(pResultGraphic); }
return pResultGraphic; }
//
//
// Function: BeginSelectMode
//
// Purpose: Process a mouse button down in select mode
//
//
void WbDrawingArea::BeginSelectMode(POINT surfacePos, BOOL bDontDrag ) { RECT rc;
// Assume we do not start dragging a graphic
m_pGraphicTracker = NULL;
// Assume that we do not mark a new graphic
m_bNewMarkedGraphic = FALSE;
// turn off TRACK-SELECT-RECT
m_bTrackingSelectRect = FALSE;
// Check whether there is currently an object marked, and
// whether we are clicking inside the same object. If we are then
// we do nothing here - the click will be handled by the tracking or
// completion routines for select mode.
ASSERT(m_pMarker);
if ( (GraphicSelected() == FALSE) || (m_pMarker->PointInBounds(surfacePos) == FALSE)) { // We are selecting a new object if bDontDrag == FALSE, find it.
// otherwise just turn on the select rect
DCWbGraphic* pGraphic; if( bDontDrag ) pGraphic = NULL; else pGraphic = SelectPreviousGraphicAt(NULL, surfacePos);
// If we have found an object, draw the marker
if (pGraphic != NULL) { // Show that a new graphic has now been marked.
m_bNewMarkedGraphic = TRUE; } else { if( (GetAsyncKeyState( VK_SHIFT ) >= 0) && (GetAsyncKeyState( VK_CONTROL ) >= 0) ) { // clicked on dead air, remove all selections
ClearSelection(); }
//TRACK-SELECT-RECT
m_bTrackingSelectRect = TRUE;
BeginRectangleMode(surfacePos);
return; } }
// If we now have a selected graphic, and we are clicking inside it
if ( (GraphicSelected()) && (m_pMarker->PointInBounds(surfacePos))) { // Create a rectangle object for tracking the drag
DCWbGraphicSelectTrackingRectangle* pRectangle = new DCWbGraphicSelectTrackingRectangle();
m_pSelectedGraphic->GetBoundsRect(&rc);
if (!pRectangle) { ERROR_OUT(("BeginSelectMode failed; couldn't create tracking rect object")); } else { pRectangle->SetRect(&rc); pRectangle->SetColor(RGB(0, 0, 0)); pRectangle->SetPenWidth(1); }
m_pGraphicTracker = pRectangle;
// We do not draw the tracking rectangle yet as the user has not yet
// dragged it anywhere. A single click within an object will then
// not cause a tracking rectangle to flash on the screen.
}
// Get all mouse input directed to the this window
::SetCapture(m_hwnd); }
void WbDrawingArea::BeginDeleteMode(POINT mousePos ) { // turn off object dragging
BeginSelectMode( mousePos, TRUE ); }
//
//
// Function: BeginTextMode
//
// Purpose: Process a mouse button down in text mode
//
//
void WbDrawingArea::BeginTextMode(POINT surfacePos) { RECT rc;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::BeginTextMode");
//
// Get a DC for passing into the text editor
//
HDC hDC = m_hDCCached;
// If we are already editing a text object, we just move the text cursor
if (m_bTextEditorActive) { // If the mouse has been clicked in the currently active object
// we just move the cursor within the object, otherwise we end the
// edit for the current object and move to a new one.
m_textEditor.GetBoundsRect(&rc); if (::PtInRect(&rc, surfacePos)) { // Set the new position for the cursor
m_textEditor.SetCursorPosFromPoint(surfacePos); } else { // Complete the text entry accepting the changes
EndTextEntry(TRUE);
// LAURABU BOGUS:
// It would be cooler to now return, that way you don't get
// another text object just cuz you ended the current editing
// session.
} }
// If we are not editing an object we check to see whether there is
// a text object under the cursor or whether we must start a new one.
if (!m_bTextEditorActive) { // Check whether we are clicking over a text object. If we are
// start editing the object, otherwise we start a new text object.
// Look back through the Z-order for a text object
DCWbGraphic* pGraphic = PG_SelectLast(m_hPage, surfacePos); DCWbGraphic* pNextGraphic = NULL; while ( (pGraphic != NULL) && (pGraphic->IsGraphicTool() != enumGraphicText)) { // Get the next one
pNextGraphic = PG_SelectPrevious(m_hPage, *pGraphic, surfacePos);
// Release the previous graphic
delete pGraphic;
// Use the next one
pGraphic = pNextGraphic; }
if (pGraphic != NULL) { // Check whether this graphic object is already being edited by
// another user in the call.
if (!pGraphic->Locked()) { // We found a text object under the mouse pointer...
// ...edit it
m_pActiveText = (DCWbGraphicText*) pGraphic;
// Transfer the text from the object into the text editor
if (!m_textEditor.SetTextObject(m_pActiveText)) { DefaultExceptionHandler(WBFE_RC_WINDOWS, 0); return; }
// Make sure the tool reflects the new information
if (m_pToolCur != NULL) { m_pToolCur->SelectGraphic(pGraphic); }
HWND hwndParent = ::GetParent(m_hwnd);; if (hwndParent != NULL) { ::PostMessage(hwndParent, WM_USER_UPDATE_ATTRIBUTES, 0, 0L); }
// Lock the graphic to prevent other users editing it.
// (This is not currently a real lock but a flag in the object
// header. There is a window in which two users can start editing
// the same text object at the same time.)
m_textEditor.Lock(); m_textEditor.Update();
// Show that we are now gathering text but dont put up cursor
// yet. Causes cursor droppings later (bug 2505)
//ActivateTextEditor( FALSE );
ActivateTextEditor( TRUE );
// Set the initial cursor position for the edit
m_textEditor.SetCursorPosFromPoint(surfacePos);
// If this is not the topmost object we must redraw to get
// it to the top so it is visible for editing
if (PG_IsTopmost(m_hPage, m_pActiveText)) { m_pActiveText->GetBoundsRect(&rc); InvalidateSurfaceRect(&rc); ::UpdateWindow(m_hwnd); } } else delete pGraphic; } else { // There are no text objects under the mouse pointer...
// ...start a new one
// Clear any old text out of the editor, and reset its graphic
// handle. This prevents us from replacing an old text object when
// we next save the text editor contents.
if (!m_textEditor.New()) { DefaultExceptionHandler(WBFE_RC_WINDOWS, 0); return; }
// Lock the text editor to prevent other users editing the object.
// (The object will be added to the page when the update timer pops
// or when the user hits space or return.)
m_textEditor.Lock();
// Set the attributes of the text
m_textEditor.SetFont(m_pToolCur->GetFont()); m_textEditor.SetColor(m_pToolCur->GetColor()); m_textEditor.GraphicTool(m_pToolCur->ToolType());
// We need to reselect a font now into our DC
SelectFont(hDC, m_textEditor.GetFont());
// Set the position of the new object
SIZE sizeCursor; m_textEditor.GetCursorSize(&sizeCursor); m_textEditor.CalculateBoundsRect(); m_textEditor.MoveTo(m_ptEnd.x, m_ptEnd.y - sizeCursor.cy);
// We are not editing an active text object
ASSERT(m_pActiveText == NULL);
// Show that we are now gathering text
ActivateTextEditor( TRUE ); } } }
//
//
// Function: BeginFreehandMode
//
// Purpose: Process a mouse button down event in draw mode
//
//
void WbDrawingArea::BeginFreehandMode(POINT surfacePos) { // Tracking in draw mode is a special case. We draw directly to the client
// area of the window and create an object to record the points on the
// line that we are drawing.
m_pGraphicTracker = new DCWbGraphicFreehand();
if (!m_pGraphicTracker) { ERROR_OUT(("BeginFreehandMode failing; can't create graphic freehand object")); } else { ((DCWbGraphicFreehand*) m_pGraphicTracker)->AddPoint(surfacePos); m_pGraphicTracker->SetColor(m_pToolCur->GetColor()); m_pGraphicTracker->SetPenWidth(m_pToolCur->GetWidth()); m_pGraphicTracker->SetROP(m_pToolCur->GetROP()); m_pGraphicTracker->GraphicTool(m_pToolCur->ToolType()); m_pGraphicTracker->Lock(); }
// Get all mouse input directed to the this window
::SetCapture(m_hwnd);
// Start the timer for updating the graphic (this is only for updating
// the graphic when the user stops moving the pointer but keeps the
// mouse button down).
::SetTimer(m_hwnd, TIMER_GRAPHIC_UPDATE, DRAW_GRAPHICUPDATEDELAY, NULL);
// Save the current time (used to determine when to update
// the external graphic pointer information while the mouse is
// being moved).
m_dwTickCount = ::GetTickCount(); }
//
//
// Function: BeginLineMode
//
// Purpose: Process a mouse button down event in line mode
//
//
void WbDrawingArea::BeginLineMode(POINT surfacePos) { // Get all mouse input directed to the this window
::SetCapture(m_hwnd);
// Create the object to be used for tracking
DCWbGraphicTrackingLine* pGraphicLine = new DCWbGraphicTrackingLine(); if (!pGraphicLine) { ERROR_OUT(("BeginLineMode failing; can't create tracking line object")); } else { pGraphicLine->SetColor(m_pToolCur->GetColor()); pGraphicLine->SetPenWidth(1);
pGraphicLine->SetStart(surfacePos); pGraphicLine->SetEnd(surfacePos); }
m_pGraphicTracker = pGraphicLine; }
//
//
// Function: BeginRectangleMode
//
// Purpose: Process a mouse button down event in box mode
//
//
void WbDrawingArea::BeginRectangleMode(POINT surfacePos) { // Get all mouse input directed to the this window
::SetCapture(m_hwnd);
// Create the object to be used for tracking
DCWbGraphicTrackingRectangle* pGraphicRectangle = new DCWbGraphicTrackingRectangle(); if (!pGraphicRectangle) { ERROR_OUT(("BeginRectangleMode failing; can't create tracking rect object")); } else { pGraphicRectangle->SetColor( CLRPANE_BLACK ); pGraphicRectangle->SetPenWidth(1); pGraphicRectangle->SetRectPts(surfacePos, surfacePos); }
m_pGraphicTracker = pGraphicRectangle; }
//
//
// Function: BeginEllipseMode
//
// Purpose: Process a mouse button down event in ellipse mode
//
//
void WbDrawingArea::BeginEllipseMode(POINT surfacePos) { // Get all mouse input directed to the this window
::SetCapture(m_hwnd);
// Create the object to be used for tracking
DCWbGraphicTrackingEllipse* pGraphicEllipse = new DCWbGraphicTrackingEllipse(); if (!pGraphicEllipse) { ERROR_OUT(("BeginEllipseMode failing; can't create tracking ellipse object")); } else { pGraphicEllipse->SetColor(m_pToolCur->GetColor()); pGraphicEllipse->SetPenWidth(1); pGraphicEllipse->SetRectPts(surfacePos, surfacePos); }
m_pGraphicTracker = pGraphicEllipse; }
//
// WbDrawingArea::OnMouseMove
//
void WbDrawingArea::OnMouseMove(UINT flags, int x, int y) { POINT surfacePos;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::OnMouseMove");
surfacePos.x = x; surfacePos.y = y;
// Check if the left mouse button is down
if (m_bLButtonDown) { // Calculate the worksurface position
// Adjust the mouse position to allow for the zoom factor
ClientToSurface(&surfacePos);
// Make sure the point is a valid surface position
MoveOntoSurface(&surfacePos);
// Check whether the window needs to be scrolled to get the
// current position into view.
AutoScroll(surfacePos.x, surfacePos.y, TRUE, 0, 0);
// Action taken depends on the tool type
switch(m_pToolCur->ToolType()) { case TOOLTYPE_HIGHLIGHT: case TOOLTYPE_PEN: TrackFreehandMode(surfacePos); break;
case TOOLTYPE_LINE: TrackLineMode(surfacePos); break;
case TOOLTYPE_BOX: case TOOLTYPE_FILLEDBOX: TrackRectangleMode(surfacePos); break;
case TOOLTYPE_ELLIPSE: case TOOLTYPE_FILLEDELLIPSE: TrackEllipseMode(surfacePos); break;
case TOOLTYPE_SELECT: TrackSelectMode(surfacePos); break;
case TOOLTYPE_ERASER: TrackDeleteMode(surfacePos); break;
case TOOLTYPE_TEXT: break;
default: ERROR_OUT(("Unknown tool type")); break; } } }
//
//
// Function: CancelDrawingMode
//
// Purpose: Cancels a drawing operation after an error.
//
//
void WbDrawingArea::CancelDrawingMode(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::CancelDrawingMode");
//
// Quit if there's nothing to cancel.
//
if (!m_bBusy && !m_bTextEditorActive) { TRACE_DEBUG(("Drawing area not busy and text editor not active...")); return; }
// The drawing area is no longer busy
m_bBusy = FALSE;
//
// Redraw the object - we need to discard any local updates which we
// weren't able to write to the object we are editing. Ideally we should
// just invalidate the object itself but because some of the co-ordinates
// we have already drawn on the page may have been lost, we dont know
// exactly how big the object is.
//
::InvalidateRect(m_hwnd, NULL, TRUE);
m_bLButtonDown = FALSE;
// Release the mouse capture
if (::GetCapture() == m_hwnd) { ::ReleaseCapture(); }
//
// Perform any tool specific processing.
//
switch(m_pToolCur->ToolType()) { case TOOLTYPE_HIGHLIGHT: case TOOLTYPE_PEN: CompleteFreehandMode(); break;
case TOOLTYPE_SELECT: // Stop the pointer update timer
::KillTimer(m_hwnd, TIMER_GRAPHIC_UPDATE); break;
case TOOLTYPE_TEXT: if (m_bTextEditorActive) { m_textEditor.AbortEditGently(); } break;
default: break; }
// Show that we are no longer tracking an object
if (m_pGraphicTracker != NULL) { delete m_pGraphicTracker; m_pGraphicTracker = NULL; } }
//
//
// Function: TrackSelectMode
//
// Purpose: Process a mouse move event in select mode
//
//
void WbDrawingArea::TrackSelectMode(POINT surfacePos) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::TrackSelectMode");
// If an object is being dragged
if (m_pGraphicTracker != NULL) { // Get a device context for the window
HDC hDC = m_hDCCached;
// Check whether the marked object is the local pointer
if (m_pGraphicTracker->IsGraphicTool() == enumGraphicPointer) { DCWbGraphicPointer* pPointer = (DCWbGraphicPointer*) m_pGraphicTracker;
// Move the pointer to its new position
pPointer->MoveBy(surfacePos.x - m_ptEnd.x, surfacePos.y - m_ptEnd.y);
// Draw the new pointer
pPointer->Redraw(hDC, this);
// Save the new box end point (top right)
m_ptEnd = surfacePos;
// Check whether we need to update the external remote pointer
// information. (Based on the time. We will miss one update
// when the time wraps.)
DWORD dwNewTickCount = ::GetTickCount(); if (dwNewTickCount > m_dwTickCount + DRAW_REMOTEPOINTERDELAY) { TRACE_DEBUG(("Updating pointer - tick count exceeded"));
// Update the pointer
pPointer->Update();
// Set the saved tick count to the new count
m_dwTickCount = dwNewTickCount; } } else { if( m_bTrackingSelectRect ) TrackRectangleMode(surfacePos); else {
// In this case we must be dragging a marked object
ASSERT(GraphicSelected());
// We never draw the tracking rectangle in the start position of
// the graphic. This gives the user some feedback when they have
// positioned the graphic back at its original place.
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Erase the last box (using XOR property)
m_pGraphicTracker->Draw(hDC); }
// Save the new box end point (top left)
m_pGraphicTracker->MoveBy(surfacePos.x - m_ptEnd.x, surfacePos.y - m_ptEnd.y); m_ptEnd = surfacePos;
// Draw the new rectangle (XORing it onto the display)
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Draw the rectangle
m_pGraphicTracker->Draw(hDC); } } } } }
void WbDrawingArea::TrackDeleteMode( POINT mousePos ) { TrackSelectMode( mousePos ); }
//
//
// Function: TrackFreehandMode
//
// Purpose: Process a mouse move event in draw mode
//
//
void WbDrawingArea::TrackFreehandMode(POINT surfacePos) { HPALETTE hPal = NULL; HPALETTE hOldPal = NULL; HPEN hPen = NULL; HPEN hOldPen = NULL;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::TrackFreehandMode");
// Get a device context for the client area
HDC hDC = m_hDCCached;
// set up palette
if ((m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE); ::RealizePalette(hDC); }
// Tracking in draw mode is a special case. We draw directly to the client
// area of the window and to the recording device context.
// Save the point, checking there aren't too many points
if (!m_pGraphicTracker || (((DCWbGraphicFreehand*) m_pGraphicTracker)->AddPoint(surfacePos) == FALSE)) { // too many points so end the freehand object
OnLButtonUp(0, surfacePos.x, surfacePos.y); goto TrackFreehandCleanup; }
// Set the DC attributes
ASSERT(m_pGraphicTracker != NULL);
hPen = ::CreatePen(m_pGraphicTracker->GetPenStyle(), m_pGraphicTracker->GetPenWidth(), m_pGraphicTracker->GetColor()); if (!hPen) { ERROR_OUT(("Couldn't create pen in track freehand mode")); goto TrackFreehandCleanup; }
hOldPen = SelectPen(hDC, hPen); if (hOldPen != NULL) { int iOldROP = ::SetROP2(hDC, m_pGraphicTracker->GetROP());
// Draw the next segment of the freehand line into the recording context
// and the client area, and save the new start point.
::MoveToEx(hDC, m_ptStart.x, m_ptStart.y, NULL); ::LineTo(hDC, surfacePos.x, surfacePos.y);
// Update the start point for the next line segment
m_ptStart = surfacePos;
// Restore the DC attributes
::SetROP2(hDC, iOldROP);
// Check whether we need to update the external graphic information.
// (Based on the time. We will miss one update when the time wraps.)
DWORD dwNewTickCount = ::GetTickCount(); if (dwNewTickCount > m_dwTickCount + DRAW_GRAPHICUPDATEDELAY) { TRACE_DEBUG(("Updating freehand - tick count exceeded"));
// Update the pointer
if (m_pGraphicTracker->Handle() == NULL) { m_pGraphicTracker->AddToPageLast(m_hPage); } else { m_pGraphicTracker->Replace(); }
// Set the saved tick count to the new count
m_dwTickCount = dwNewTickCount; } }
TrackFreehandCleanup:
if (hOldPen != NULL) { SelectPen(hDC, hOldPen); }
if (hPen != NULL) { ::DeletePen(hPen); }
if (hOldPal != NULL) { ::SelectPalette(hDC, hOldPal, TRUE); } }
//
//
// Function: TrackLineMode
//
// Purpose: Process a mouse move event in line mode
//
//
void WbDrawingArea::TrackLineMode(POINT surfacePos) { HPALETTE hPal; HPALETTE hOldPal = NULL;
// Get a device context for tracking
HDC hDC = m_hDCCached;
// set up palette
if ((m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE ); ::RealizePalette(hDC); }
// Erase the last line drawn (using XOR property)
if (!EqualPoint(m_ptStart, m_ptEnd)) { if (m_pGraphicTracker != NULL) { m_pGraphicTracker->Draw(hDC); } }
// Draw the new line (XORing it onto the display)
if (!EqualPoint(m_ptStart, surfacePos)) { m_ptEnd = surfacePos;
if (m_pGraphicTracker != NULL) { ((DCWbGraphicTrackingLine*) m_pGraphicTracker)->SetEnd(m_ptEnd); m_pGraphicTracker->Draw(hDC); } }
if (hOldPal != NULL) { ::SelectPalette(hDC, hOldPal, TRUE); } }
//
//
// Function: TrackRectangleMode
//
// Purpose: Process a mouse move event in box or filled box mode
//
//
void WbDrawingArea::TrackRectangleMode(POINT surfacePos) { HPALETTE hPal; HPALETTE hOldPal = NULL;
// Get a device context for tracking
HDC hDC = m_hDCCached;
// set up palette
if ((m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE ); ::RealizePalette(hDC); }
// Erase the last ellipse (using XOR property)
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Draw the rectangle
if (m_pGraphicTracker != NULL) { m_pGraphicTracker->Draw(hDC); } }
// Draw the new rectangle (XORing it onto the display)
if (!EqualPoint(m_ptStart, surfacePos)) { // Save the new box end point (top right)
m_ptEnd = surfacePos;
// Draw the rectangle
if (m_pGraphicTracker != NULL) { ((DCWbGraphicTrackingRectangle*) m_pGraphicTracker)->SetRectPts(m_ptStart, m_ptEnd); m_pGraphicTracker->Draw(hDC); } }
if (hOldPal != NULL) { ::SelectPalette(hDC, hOldPal, TRUE); } }
//
//
// Function: TrackEllipseMode
//
// Purpose: Process a mouse move event in ellipse or filled ellipse mode
//
//
void WbDrawingArea::TrackEllipseMode(POINT surfacePos) { HPALETTE hPal; HPALETTE hOldPal = NULL;
// Get a device context for tracking
HDC hDC = m_hDCCached;
// set up palette
if( (m_hPage != WB_PAGE_HANDLE_NULL) && ((hPal = PG_GetPalette()) != NULL) ) { hOldPal = ::SelectPalette(hDC, hPal, FALSE); ::RealizePalette(hDC); }
// Erase the last ellipse (using XOR property)
if (!EqualPoint(m_ptStart, m_ptEnd)) { if (m_pGraphicTracker != NULL) { m_pGraphicTracker->Draw(hDC); } }
// Draw the new ellipse (XORing it onto the display)
if (!EqualPoint(m_ptStart, surfacePos)) { // Update the end point of the operation
m_ptEnd = surfacePos;
if (m_pGraphicTracker != NULL) { ((DCWbGraphicTrackingEllipse*) m_pGraphicTracker)->SetRectPts(m_ptStart, m_ptEnd); m_pGraphicTracker->Draw(hDC); } }
if (hOldPal != NULL) { ::SelectPalette(hDC, hOldPal, TRUE ); } }
//
// WbDrawingArea::OnLButtonUp()
//
void WbDrawingArea::OnLButtonUp(UINT flags, int x, int y) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::OnLButtonUp");
if (m_bIgnoreNextLClick) { TRACE_MSG( ("Ignoring WM_LBUTTONUP") ); m_bIgnoreNextLClick = FALSE; return; }
// Only process the event if we saw the button down event
if (m_bLButtonDown) { TRACE_MSG(("End of drawing operation"));
m_bLButtonDown = FALSE;
// The drawing area is no longer busy
m_bBusy = FALSE;
if (m_pGraphicTracker == NULL) { // Calculate the work surface position
// Adjust the mouse position to allow for the zoom factor
POINT surfacePos;
surfacePos.x = x; surfacePos.y = y; ClientToSurface(&surfacePos); MoveOntoSurface(&surfacePos); m_ptEnd = surfacePos; }
// Release the mouse capture
if (::GetCapture() == m_hwnd) { ::ReleaseCapture(); }
// Check the page is valid - might not be if it has been deleted
// while the object was being drawn - we would not have been
// alerted to this because m_bBusy was true.
if (m_hPage != WB_PAGE_HANDLE_NULL) { // surround in an exception handler in case of lock errors, etc -
// we need to remove the graphic tracker
// Action taken depends on the current tool type
ASSERT(m_pToolCur != NULL);
switch(m_pToolCur->ToolType()) { case TOOLTYPE_HIGHLIGHT: case TOOLTYPE_PEN: CompleteFreehandMode(); break;
case TOOLTYPE_LINE: CompleteLineMode(); break;
case TOOLTYPE_BOX: CompleteRectangleMode(); break;
case TOOLTYPE_FILLEDBOX: CompleteFilledRectangleMode(); break;
case TOOLTYPE_ELLIPSE: CompleteEllipseMode(); break;
case TOOLTYPE_FILLEDELLIPSE: CompleteFilledEllipseMode(); break;
case TOOLTYPE_SELECT: CompleteSelectMode(); break;
case TOOLTYPE_ERASER: CompleteDeleteMode(); break;
case TOOLTYPE_TEXT: m_ptStart.x = x; m_ptStart.y = y; ClientToSurface(&m_ptStart); BeginTextMode(m_ptStart); break;
default: ERROR_OUT(("Unknown pen type")); break; } }
// Show that we are no longer tracking an object
if (m_pGraphicTracker != NULL) { delete m_pGraphicTracker; m_pGraphicTracker = NULL; } }
// unclamp cursor (bug 589)
ClipCursor(NULL); }
//
//
// Function: CompleteSelectMode
//
// Purpose: Complete a select mode operation
//
//
void WbDrawingArea::CompleteSelectMode() { // If an object is being dragged
if (m_pGraphicTracker != NULL) { // Check if we were dragging a pointer. Pointers track
// themselves i.e. the original copy of the pointer is not
// left on the page. We want to leave the last drawn image on
// the page as this is the new pointer position.
if (m_pGraphicTracker->IsGraphicTool() == enumGraphicPointer) { DCWbGraphicPointer* pPointer = (DCWbGraphicPointer*) m_pGraphicTracker;
// Show the mouse
::ShowCursor(TRUE);
// Update the object's position (if necessary)
if (!EqualPoint(m_ptStart, m_ptEnd)) { pPointer->Update(); }
// We do not want to delete the graphic pointer (it belongs to
// the page object that created it). So reset the graphic tracker
// pointer to prevent it being deleted in OnLButtonUp.
m_pGraphicTracker = NULL;
// Stop the pointer update timer
::KillTimer(m_hwnd, TIMER_GRAPHIC_UPDATE); } else { if( m_bTrackingSelectRect && (!EqualPoint(m_ptStart, m_ptEnd))) { CompleteMarkAreaMode(); SelectMarkerFromRect( &m_rcMarkedArea ); } else { // The select item is a real graphic - not a pointer
// If we need to remove the rubber band box
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Erase the last box (using XOR property).
// Get a device context for tracking
HDC hDC = m_hDCCached;
// Draw the rectangle
m_pGraphicTracker->Draw(hDC);
// Move selection
m_HourGlass = TRUE; SetCursorForState();
RemoveMarker( NULL ); m_pMarker->MoveBy(m_ptEnd.x - m_ptStart.x, m_ptEnd.y - m_ptStart.y); m_pMarker->Update();
PutMarker( NULL, FALSE );
m_HourGlass = FALSE; SetCursorForState();
// The tracking object will be deleted by OnLButtonUp
} else { // Start and end points were the same, in this case the object has
// not been moved. We treat this as a request to move the marker
// back through the stack of objects.
if (m_bNewMarkedGraphic == FALSE) { SelectPreviousGraphicAt(m_pSelectedGraphic, m_ptEnd); } } } } } }
void WbDrawingArea::CompleteDeleteMode() { // select object(s)
CompleteSelectMode();
// nuke 'em
::PostMessage(g_pMain->m_hwnd, WM_COMMAND, MAKELONG(IDM_DELETE, BN_CLICKED), 0); }
//
//
// Function: CompleteMarkAreaMode
//
// Purpose: Process a mouse button up event in mark area mode
//
//
void WbDrawingArea::CompleteMarkAreaMode(void) { // Get a device context for tracking
HDC hDC = m_hDCCached;
// Erase the last ellipse (using XOR property)
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Draw the rectangle
if (m_pGraphicTracker != NULL) { m_pGraphicTracker->Draw(hDC); }
// Use normalized coords
if (m_ptEnd.x < m_ptStart.x) { m_rcMarkedArea.left = m_ptEnd.x; m_rcMarkedArea.right = m_ptStart.x; } else { m_rcMarkedArea.left = m_ptStart.x; m_rcMarkedArea.right = m_ptEnd.x; }
if (m_ptEnd.y < m_ptStart.y) { m_rcMarkedArea.top = m_ptEnd.y; m_rcMarkedArea.bottom = m_ptStart.y; } else { m_rcMarkedArea.top = m_ptStart.y; m_rcMarkedArea.bottom = m_ptEnd.y; } } }
//
//
// Function: CompleteTextMode
//
// Purpose: Complete a text mode operation
//
//
void WbDrawingArea::CompleteTextMode() { // Not much to for text mode. Main text mode actions are taken
// as a result of a WM_CHAR message and not on mouse events.
// Just deselect our font if it is still selected
UnPrimeFont(m_hDCCached); }
//
//
// Function: CompleteFreehandMode
//
// Purpose: Complete a draw mode operation
//
//
void WbDrawingArea::CompleteFreehandMode(void) {
// Add the freehand object created during the drawing to the page
if (m_pGraphicTracker != NULL) { if (m_pGraphicTracker->Handle() == NULL) { m_pGraphicTracker->ClearLockFlag(); m_pGraphicTracker->AddToPageLast(m_hPage); } else { // clear lock flag and let ForceReplace propagate it (fix
// for NT bug 4744(new bug#... )
m_pGraphicTracker->ClearLockFlag(); m_pGraphicTracker->ForceReplace(); } }
// Stop the update timer
::KillTimer(m_hwnd, TIMER_GRAPHIC_UPDATE); }
//
//
// Function: CompleteLineMode
//
// Purpose: Complete a line mode operation
//
//
void WbDrawingArea::CompleteLineMode(void) { // Only draw the line if it has non-zero length
if (!EqualPoint(m_ptStart, m_ptEnd)) { DCWbGraphicLine line; line.SetStart(m_ptStart); line.SetEnd(m_ptEnd); line.SetColor(m_pToolCur->GetColor()); line.SetPenWidth(m_pToolCur->GetWidth()); line.SetROP(m_pToolCur->GetROP()); line.GraphicTool(m_pToolCur->ToolType());
// Add the object to the list of recorded graphics
line.AddToPageLast(m_hPage); } }
//
//
// Function: CompleteRectangleMode
//
// Purpose: Complete a box mode operation
//
//
void WbDrawingArea::CompleteRectangleMode(void) { // Only draw the box if it is not null
if (!EqualPoint(m_ptStart, m_ptEnd)) { DCWbGraphicRectangle rectangle; rectangle.SetRectPts(m_ptStart, m_ptEnd); rectangle.SetPenWidth(m_pToolCur->GetWidth()); rectangle.SetColor(m_pToolCur->GetColor()); rectangle.SetROP(m_pToolCur->GetROP()); rectangle.GraphicTool(m_pToolCur->ToolType());
// Add the object to the list of recorded graphics
rectangle.AddToPageLast(m_hPage); } }
//
//
// Function: CompleteFilledRectangleMode
//
// Purpose: Complete a filled box mode operation
//
//
void WbDrawingArea::CompleteFilledRectangleMode(void) { // Draw the new rectangle
if (!EqualPoint(m_ptStart, m_ptEnd)) { DCWbGraphicFilledRectangle rectangle;
rectangle.SetRectPts(m_ptStart, m_ptEnd); rectangle.SetPenWidth(m_pToolCur->GetWidth()); rectangle.SetColor(m_pToolCur->GetColor()); rectangle.SetROP(m_pToolCur->GetROP()); rectangle.GraphicTool(m_pToolCur->ToolType());
// Add the object to the list of recorded graphics
rectangle.AddToPageLast(m_hPage); } }
//
//
// Function: CompleteEllipseMode
//
// Purpose: Complete an ellipse mode operation
//
//
void WbDrawingArea::CompleteEllipseMode(void) { // Only draw the ellipse if it is not null
if (!EqualPoint(m_ptStart, m_ptEnd)) { // The ellipse was defined by taking using start point as the center
// but was changed to use the bounding tracking rectangle - bug 1608
// Create the ellipse object
DCWbGraphicEllipse ellipse;
ellipse.SetRectPts(m_ptStart, m_ptEnd); ellipse.SetColor(m_pToolCur->GetColor()); ellipse.SetPenWidth(m_pToolCur->GetWidth()); ellipse.SetROP(m_pToolCur->GetROP()); ellipse.GraphicTool(m_pToolCur->ToolType());
// Add the object to the list of recorded graphics
ellipse.AddToPageLast(m_hPage); } }
//
//
// Function: CompleteFilledEllipseMode
//
// Purpose: Complete a filled ellipse mode operation
//
//
void WbDrawingArea::CompleteFilledEllipseMode(void) { // Only draw the ellipse if it is not null
if (!EqualPoint(m_ptStart, m_ptEnd)) { // Create the ellipse object
DCWbGraphicFilledEllipse ellipse;
ellipse.SetRectPts(m_ptStart, m_ptEnd); ellipse.SetColor(m_pToolCur->GetColor()); ellipse.SetPenWidth(m_pToolCur->GetWidth()); ellipse.SetROP(m_pToolCur->GetROP()); ellipse.GraphicTool(m_pToolCur->ToolType());
// Add the object to the list of recorded graphics
ellipse.AddToPageLast(m_hPage); } }
//
//
// Function: EndTextEntry
//
// Purpose: The user has finished entering a text object. The parameter
// indicates whether the changes are to be accepted or
// discarded.
//
//
void WbDrawingArea::EndTextEntry(BOOL bAccept) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::EndTextEntry");
// Only take action if the text editor is active
if (m_bTextEditorActive) { RECT rcBounds;
// We must at least redraw the bounding rectangle of the
// text object as it now stands (as it will not longer be
// on top).
m_textEditor.GetBoundsRect(&rcBounds);
// If we are editing an existing text object
if (m_pActiveText != NULL) { TRACE_MSG(("Editing an existing object"));
//
// If we are not accepting the edited text we must redraw
// both the old and new rectangles to ensure that everything
// is shown correctly.
//
if (!bAccept) { //
// Write the active text object back to restore it. This object
// will have the same handle as the text editor object if we have
// written it to the page - we must not delete the text editor
// object.
//
m_pActiveText->ForceReplace(); m_textEditor.ZapHandle(); // prevent editor from stepping on m_pActiveText
} else { // If the object is now empty
if (m_textEditor.IsEmpty()) { // Remove the object from the list
PG_GraphicDelete(m_hPage, *m_pActiveText); m_textEditor.ZapHandle(); // text object is gone now, invalidate
} else { // Do a replace to save the final version
m_textEditor.Replace(); } }
// We have finished with the text object now so get rid of it
// and the fonts it holds
TRACE_MSG(("Deleting the active object")); delete m_pActiveText; m_pActiveText = NULL; } else { // We were adding a new text object
TRACE_MSG(("Adding a new object"));
// If we want to discard the object, or it is empty
if (!bAccept || (m_textEditor.IsEmpty())) { // If we have added the text editor to the page, remove it
if (m_textEditor.Handle() != NULL) { m_textEditor.Delete(); } } else { // Check whether we have already added the object to the page
if (m_textEditor.Handle() == NULL) { // Create and add a new object to the page
// (No redrawing is required)
m_textEditor.AddToPageLast(m_hPage); } else { // Replace the object to send the final version
m_textEditor.Replace(); } } }
// Deactivate the text editor
DeactivateTextEditor();
// Redraw any altered parts of the screen
InvalidateSurfaceRect(&rcBounds); } }
//
//
// Function: Zoom
//
// Purpose: Toggle the zoom state of the drawing area
//
//
void WbDrawingArea::Zoom(void) { RECT rcClient;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::Zoom");
// We zoom focusing on the centre of the window
::GetClientRect(m_hwnd, &rcClient); long xOffset = (rcClient.right - (rcClient.right / m_iZoomOption)) / 2; long yOffset = (rcClient.bottom - (rcClient.bottom / m_iZoomOption)) / 2;
if (m_iZoomFactor != 1) { // We are already zoomed move back to unzoomed state
// First save the scroll position in case we return to zoom immediately
m_posZoomScroll = m_posScroll; m_zoomRestoreScroll = TRUE;
m_posScroll.x -= xOffset; m_posScroll.y -= yOffset; ::ScaleViewportExtEx(m_hDCCached, 1, m_iZoomFactor, 1, m_iZoomFactor, NULL); m_iZoomFactor = 1; } else { // We are not zoomed so do it
if (m_zoomRestoreScroll) { m_posScroll = m_posZoomScroll; } else { m_posScroll.x += xOffset; m_posScroll.y += yOffset; }
m_iZoomFactor = m_iZoomOption; ::ScaleViewportExtEx(m_hDCCached, m_iZoomFactor, 1, m_iZoomFactor, 1, NULL);
// ADDED BY RAND - don't allow text editing in zoom mode
if( (m_pToolCur == NULL) || (m_pToolCur->ToolType() == TOOLTYPE_TEXT) ) ::SendMessage(g_pMain->m_hwnd, WM_COMMAND, IDM_TOOLS_START, 0 ); }
TRACE_MSG(("Set zoom factor to %d", m_iZoomFactor));
// Update the scroll information
SetScrollRange(rcClient.right, rcClient.bottom); ValidateScrollPos();
::SetScrollPos(m_hwnd, SB_HORZ, m_posScroll.x, TRUE); ::SetScrollPos(m_hwnd, SB_VERT, m_posScroll.y, TRUE);
// Update the origin offset from the scroll position
m_originOffset.cx = m_posScroll.x; m_originOffset.cy = m_posScroll.y; ::SetWindowOrgEx(m_hDCCached, m_originOffset.cx, m_originOffset.cy, NULL);
// Tell the parent that the scroll position has changed
::PostMessage(g_pMain->m_hwnd, WM_USER_PRIVATE_PARENTNOTIFY, WM_VSCROLL, 0L);
g_pMain->SetMenuStates(::GetSubMenu(::GetMenu(g_pMain->m_hwnd), 3));
// Redraw the window
::InvalidateRect(m_hwnd, NULL, TRUE); }
//
//
// Function: SelectTool
//
// Purpose: Set the current tool
//
//
void WbDrawingArea::SelectTool(WbTool* pToolNew) { // If we are leaving text mode, complete the text entry
if (m_bTextEditorActive && (m_pToolCur->ToolType() == TOOLTYPE_TEXT) && (pToolNew->ToolType() != TOOLTYPE_TEXT)) { // End text entry accepting the changes
EndTextEntry(TRUE); }
ASSERT(m_pMarker);
// If we are no longer in select mode, and the marker is present,
// then remove it and let the tool know it's no longer selected
if ( (m_pToolCur != NULL) && (m_pToolCur->ToolType() == TOOLTYPE_SELECT) && (pToolNew->ToolType() != TOOLTYPE_SELECT)) { m_pToolCur->DeselectGraphic();
RemoveMarker(NULL); m_pMarker->DeleteAllMarkers( m_pSelectedGraphic );
delete m_pSelectedGraphic; m_pSelectedGraphic = NULL; } else if ( (m_pToolCur != NULL) && (m_pToolCur->ToolType() == TOOLTYPE_ERASER) && (pToolNew->ToolType() != TOOLTYPE_ERASER)) { m_pToolCur->DeselectGraphic();
RemoveMarker(NULL); m_pMarker->DeleteAllMarkers( m_pSelectedGraphic );
delete m_pSelectedGraphic; m_pSelectedGraphic = NULL; }
// Save the new tool
m_pToolCur = pToolNew; }
//
//
// Function: SetSelectionColor
//
// Purpose: Set the color of the selected object
//
//
void WbDrawingArea::SetSelectionColor(COLORREF clr) { RECT rc;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::SetSelectionColor");
// If the text editor is active - redraw the text in the new color
if (m_bTextEditorActive) { // Change the color being used by the editor
m_textEditor.SetColor(clr);
// Update the screen
m_textEditor.GetBoundsRect(&rc); InvalidateSurfaceRect(&rc); }
// If there is a currently marked object
if (GraphicSelected()) { // Change color of the selected objects
ASSERT(m_pMarker);
m_pMarker->SetColor(clr);
// Update the objects
m_pMarker->Update(); }
m_textEditor.ForceUpdate();
}
//
//
// Function: SetSelectionWidth
//
// Purpose: Set the nib width used to draw the currently selected object
//
//
void WbDrawingArea::SetSelectionWidth(UINT uiWidth) { // If there is a currently marked object
if (GraphicSelected()) { ASSERT(m_pMarker);
// Change the width of the object
m_pMarker->SetPenWidth(uiWidth);
// Update the object
m_pMarker->Update(); } }
//
//
// Function: SetSelectionFont
//
// Purpose: Set the font used by the currently selected object
//
//
void WbDrawingArea::SetSelectionFont(HFONT hFont) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::SetSelectionFont");
// Define rectangles for redrawing
RECT rcOldBounds; RECT rcNewBounds;
m_textEditor.GetBoundsRect(&rcOldBounds);
// Pass the font onto the text editor
// If the text editor is active - redraw the text in the new font
if (m_bTextEditorActive) { m_textEditor.SetFont(hFont);
// Get the new rectangle of the text
m_textEditor.GetBoundsRect(&rcNewBounds);
// Remove and destroy the text cursor to ensure that it
// gets re-drawn with the new size for the font
// Update the screen
InvalidateSurfaceRect(&rcOldBounds); InvalidateSurfaceRect(&rcNewBounds);
// get the text cursor back
ActivateTextEditor( TRUE ); }
// If there is a currently marked object
if (GraphicSelected()) { ASSERT(m_pMarker);
m_pMarker->SetSelectionFont(hFont); } }
//
//
// Function: OnSetFocus
//
// Purpose: The window is getting the focus
//
//
void WbDrawingArea::OnSetFocus(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::OnSetFocus");
//
// If we are in text mode, we must make the text cursor visible.
//
if (m_bTextEditorActive && (m_pToolCur->ToolType() == TOOLTYPE_TEXT)) { ActivateTextEditor(TRUE); } }
//
//
// Function: OnActivate
//
// Purpose: The window is being activated or deactivated
//
//
void WbDrawingArea::OnActivate(UINT uiState) { // Check if we are being activated or deactivated
if (uiState) { // We are being activated, get the focus as well
::SetFocus(m_hwnd);
// If we are in text mode, we must make the text cursor visible
if (m_bTextEditorActive && (m_pToolCur->ToolType() == TOOLTYPE_TEXT)) { ActivateTextEditor(TRUE); } } else { // We are being deactivated
DeactivateTextEditor(); } }
//
//
// Function: DeleteGraphic
//
// Purpose: Remove an object from the page.
//
//
void WbDrawingArea::DeleteGraphic(DCWbGraphic* pObject) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::DeleteGraphic");
ASSERT(pObject != NULL);
// Delete the object from the recorded list. This is an asynchronous
// function, completed when a WBP_EVENT_GRAPHIC_DELETED event is received.
PG_GraphicDelete(m_hPage, *pObject);
// The caller is responsible for deleting the graphic object.
}
//
//
// Function: DeleteSelection
//
// Purpose: Delete the currently selected object
//
//
void WbDrawingArea::DeleteSelection() { // If there is an object currently selected...
if (GraphicSelected()) { ASSERT(m_pMarker);
// ...delete it
m_pMarker->DeleteSelection(); m_pSelectedGraphic = NULL; } }
//
//
// Function: GetSelection
//
// Purpose: Return the currently selected graphic (or NULL if none).
//
//
DCWbGraphic* WbDrawingArea::GetSelection() { DCWbGraphic* pGraphic = NULL;
// If there is an object currently selected...
if (GraphicSelected()) { // ...return it
pGraphic = m_pSelectedGraphic; }
return pGraphic; }
//
//
// Function: BringToTopSelection
//
// Purpose: Bring the currently selected object to the top
//
//
void WbDrawingArea::BringToTopSelection() { // If there is an object currently selected...
if (GraphicSelected()) { ASSERT(m_pMarker);
// Bring it to the top
m_pMarker->BringToTopSelection();
// The update will be made in the window from the event generated
// by the change to the page.
} }
//
//
// Function: SendToBackSelection
//
// Purpose: Send the currently marked object to the back
//
//
void WbDrawingArea::SendToBackSelection() { // If there is an object currently selected...
if (GraphicSelected()) { ASSERT(m_pMarker);
// Send it to the back
m_pMarker->SendToBackSelection();
// The update will be made in the window from the event generated
// by the change to the page.
} }
//
//
// Function: Clear
//
// Purpose: Clear the drawing area.
//
//
void WbDrawingArea::Clear() { // Remove the recorded objects
PG_Clear(m_hPage);
// The page will be redrawn after an event generated by the clear request
}
//
//
// Function: Attach
//
// Purpose: Change the page the window is displaying
//
//
void WbDrawingArea::Attach(WB_PAGE_HANDLE hPage) { // Remove any pointers on the current page. We are really only doing this
// to tell the pointers they are no longer drawn as they keep a record
// of whether they are in order to undraw correctly.
if (m_allPointers.IsEmpty() == FALSE) { // Get a DC for drawing
HDC hDC = m_hDCCached;
// Remove the pointers, reversing through the list
DCWbGraphicPointer* pPointer; POSITION pos = m_allPointers.GetHeadPosition();
while (pos != NULL) { // Remove it
pPointer = (DCWbGraphicPointer*) m_allPointers.GetNext(pos); pPointer->Undraw(hDC, this); } }
m_allPointers.EmptyList(); m_undrawnPointers.EmptyList();
// Accept any text being edited
if (m_bTextEditorActive) { EndTextEntry(TRUE); }
// finish any drawing operation now
if (m_bLButtonDown) { OnLButtonUp(0, m_ptStart.x, m_ptStart.y); }
// Get rid of the selection
ClearSelection();
// Save the new page details
m_hPage = hPage;
// If the new page we are attaching is not the empty page, set up
// the list of pointers for the new page.
if (m_hPage != WB_PAGE_HANDLE_NULL) { // Get the list of active pointers on the new page. The local
// pointer must be last in the list so that it is drawn topmost.
POM_OBJECT hUserNext;
DCWbGraphicPointer* pPointer = PG_FirstPointer(m_hPage, &hUserNext);
while (pPointer != NULL) { // Check whether we should add this pointer to the list
if (!pPointer->IsLocalPointer()) { m_allPointers.AddTail(pPointer); }
// Get the next pointer
pPointer = PG_NextPointer(m_hPage, &hUserNext); }
// Check if the local pointer should also be added
pPointer = PG_LocalPointer(m_hPage);
if (pPointer != NULL) { m_allPointers.AddTail(pPointer); } }
// Force a redraw of the window to show the new contents
::InvalidateRect(m_hwnd, NULL, TRUE); }
//
//
// Function: DrawMarker
//
// Purpose: Draw the graphic object marker
//
//
void WbDrawingArea::DrawMarker(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::DrawMarker");
ASSERT(m_pMarker);
if (!hDC) hDC = m_hDCCached;
// Draw the marker
m_pMarker->Draw(hDC); }
//
//
// Function: PutMarker
//
// Purpose: Draw the graphic object marker
//
//
void WbDrawingArea::PutMarker(HDC hDC, BOOL bDraw) { ASSERT(m_pMarker);
// If the marker is not already present, draw it
if (!m_bMarkerPresent) { m_pMarker->Present( TRUE );
// Draw the marker (using XOR)
if( bDraw ) DrawMarker(hDC);
// Show that the marker is present
m_bMarkerPresent = TRUE; } }
//
//
// Function: RemoveMarker
//
// Purpose: Remove the graphic object marker
//
//
void WbDrawingArea::RemoveMarker(HDC hDC) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::RemoveMarker");
ASSERT(m_pMarker);
if (!hDC) hDC = m_hDCCached;
// If the marker is not already present, draw it
if (m_bMarkerPresent) { // Draw the marker (it is XORed so this removes it)
m_pMarker->Undraw(hDC, this);
m_pMarker->Present( FALSE );
// Show that the marker is no longer present
m_bMarkerPresent = FALSE; } }
//
//
// Function: ActivateTextEditor
//
// Purpose: Start a text editing session
//
//
void WbDrawingArea::ActivateTextEditor( BOOL bPutUpCusor ) { // Record that the editor is now active
m_bTextEditorActive = TRUE;
// show editbox
m_textEditor.ShowBox( SW_SHOW );
// reset our DBCS sync
// Start the timer for updating the text
m_textEditor.SetTimer( DRAW_GRAPHICUPDATEDELAY); }
//
//
// Function: DeactivateTextEditor
//
// Purpose: End a text editing session
//
//
void WbDrawingArea::DeactivateTextEditor(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::DeactivateTextEditor");
// Stop the update timer
m_textEditor.KillTimer();
// Ensure the object is unlocked, if it was ever added to the page
if (m_textEditor.Handle() != NULL) { m_textEditor.Unlock();
// Sync up across all connections - FIXES BUG 521
m_textEditor.ForceReplace();
UINT uiReturn; uiReturn = g_pwbCore->WBP_GraphicMove(m_hPage, m_textEditor.Handle(), LAST); if (uiReturn != 0) { DefaultExceptionHandler(WBFE_RC_WB, uiReturn); return; } //////////////////////////
}
// Show that we are not editing any text
m_bTextEditorActive = FALSE;
// hide editbox
m_textEditor.ShowBox( SW_HIDE ); }
//
//
// Function: SurfaceToClient
//
// Purpose: Convert a point in surface co-ordinates to client
// co-ordinates (taking account of the current zoom factor).
//
//
void WbDrawingArea::SurfaceToClient(LPPOINT lppoint) { lppoint->x -= m_originOffset.cx; lppoint->x *= m_iZoomFactor;
lppoint->y -= m_originOffset.cy; lppoint->y *= m_iZoomFactor; }
//
//
// Function: ClientToSurface
//
// Purpose: Convert a point in client co-ordinates to surface
// co-ordinates (taking account of the current zoom factor).
//
//
void WbDrawingArea::ClientToSurface(LPPOINT lppoint) { ASSERT(m_iZoomFactor != 0);
lppoint->x /= m_iZoomFactor; lppoint->x += m_originOffset.cx;
lppoint->y /= m_iZoomFactor; lppoint->y += m_originOffset.cy; }
//
//
// Function: SurfaceToClient
//
// Purpose: Convert a rectangle in surface co-ordinates to client
// co-ordinates (taking account of the current zoom factor).
//
//
void WbDrawingArea::SurfaceToClient(LPRECT lprc) { SurfaceToClient((LPPOINT)&lprc->left); SurfaceToClient((LPPOINT)&lprc->right); }
//
//
// Function: ClientToSurface
//
// Purpose: Convert a rectangle in client co-ordinates to surface
// co-ordinates (taking account of the current zoom factor).
//
//
void WbDrawingArea::ClientToSurface(LPRECT lprc) { ClientToSurface((LPPOINT)&lprc->left); ClientToSurface((LPPOINT)&lprc->right); }
//
//
// Function: GraphicSelected
//
// Purpose: Return TRUE if a graphic is currently selected
//
//
BOOL WbDrawingArea::GraphicSelected(void) { ASSERT(m_pMarker);
BOOL bSelected = (m_bMarkerPresent) && (m_pMarker->GetNumMarkers() > 0);
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::GraphicSelected");
if( bSelected ) { ASSERT(m_pSelectedGraphic != NULL); }
return( bSelected ); }
//
//
// Function: SelectGraphic
//
// Purpose: Select a graphic - save the pointer to the graphic and
// draw the marker on it.
//
//
void WbDrawingArea::SelectGraphic(DCWbGraphic* pGraphic, BOOL bEnableForceAdd, BOOL bForceAdd ) { BOOL bZapCurrentSelection; RECT rc;
MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::SelectGraphic");
ASSERT(m_pMarker);
if (pGraphic->Locked() == FALSE) { // Save the pointer to the selected graphic
m_pSelectedGraphic = pGraphic;
if( (pGraphic = m_pMarker->HasAMarker( m_pSelectedGraphic )) != NULL ) { // toggle marker (unselect pGraphic)
delete pGraphic; delete m_pSelectedGraphic; m_pSelectedGraphic = m_pMarker->LastMarker(); } else {
// new selection, add to list or replace list?
if( bEnableForceAdd ) bZapCurrentSelection = !bForceAdd; else bZapCurrentSelection = ((GetAsyncKeyState( VK_SHIFT ) >= 0) && (GetAsyncKeyState( VK_CONTROL ) >= 0));
if( bZapCurrentSelection ) { // replace list
RemoveMarker(NULL); m_pMarker->DeleteAllMarkers( m_pSelectedGraphic, TRUE ); }
// Add the object rect to the marker rect list
m_pSelectedGraphic->GetBoundsRect(&rc); m_pMarker->SetRect(&rc, m_pSelectedGraphic, FALSE ); }
// Draw the marker
PutMarker(NULL);
// Update the attributes window to show graphic is selected
if( m_pSelectedGraphic != NULL ) m_pToolCur->SelectGraphic(m_pSelectedGraphic);
HWND hwndParent = ::GetParent(m_hwnd); if (hwndParent != NULL) { ::PostMessage(hwndParent, WM_USER_UPDATE_ATTRIBUTES, 0, 0L); } } else { // we can delete the graphic now, because we're not selecting it
delete pGraphic; m_pSelectedGraphic = NULL; TRACE_MSG(("Tried to select a locked graphic - ignored")); } }
//
//
// Function: DeselectGraphic
//
// Purpose: Deselect a graphic - remove the marker and delete the
// graphic object associated with it.
//
//
void WbDrawingArea::DeselectGraphic(void) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::DeselectGraphic");
//
// Quit if no graphic selected.
//
if( m_pSelectedGraphic == NULL ) { return; }
// Remove the marker
RemoveMarker(NULL);
// Delete the graphic object
delete m_pSelectedGraphic; m_pSelectedGraphic = NULL;
// Update the attributes window to show graphic is unselected
m_pToolCur->DeselectGraphic();
HWND hwndParent = ::GetParent(m_hwnd); if (hwndParent != NULL) { ::PostMessage(hwndParent, WM_USER_UPDATE_ATTRIBUTES, 0, 0L); } }
//
//
// Function: GetVisibleRect
//
// Purpose: Return the rectangle of the surface currently visible in the
// drawing area window.
//
//
void WbDrawingArea::GetVisibleRect(LPRECT lprc) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::VisibleRect");
// Get the client rectangle
::GetClientRect(m_hwnd, lprc);
// Convert to surface co-ordinates
ClientToSurface(lprc); }
//
//
// Function: MoveOntoSurface
//
// Purpose: If a given point is outwith the surface rect, move it on
//
//
void WbDrawingArea::MoveOntoSurface(LPPOINT lppoint) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::MoveOntoSurface");
//
// Make sure that the position is within the surface rect
//
if (lppoint->x < 0) { lppoint->x = 0; } else if (lppoint->x >= DRAW_WIDTH) { lppoint->x = DRAW_WIDTH - 1; }
if (lppoint->y < 0) { lppoint->y = 0; } else if (lppoint->y >= DRAW_HEIGHT) { lppoint->y = DRAW_HEIGHT - 1; } }
//
//
// Function: GetOrigin
//
// Purpose: Provide current origin of display
//
//
void WbDrawingArea::GetOrigin(LPPOINT lppoint) { lppoint->x = m_originOffset.cx; lppoint->y = m_originOffset.cy; }
void WbDrawingArea::ShutDownDC(void) { UnPrimeDC(m_hDCCached);
if (m_hDCWindow != NULL) { ::ReleaseDC(m_hwnd, m_hDCWindow); m_hDCWindow = NULL; }
m_hDCCached = NULL; }
void WbDrawingArea::ClearSelection( void ) { MLZ_EntryOut(ZONE_FUNCTION, "WbDrawingArea::ClearSelection");
ASSERT(m_pMarker);
RemoveMarker( NULL ); m_pMarker->DeleteAllMarkers( m_pSelectedGraphic ); DeselectGraphic(); }
void WbDrawingArea::OnCancelMode( void ) { // We were dragging but lost mouse control, gracefully end the drag (NM4db:573)
POINT pt;
::GetCursorPos(&pt); ::ScreenToClient(m_hwnd, &pt); OnLButtonUp(0, pt.x, pt.y); m_bLButtonDown = FALSE; }
|