You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4676 lines
123 KiB
4676 lines
123 KiB
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
|
|
|