// //************************************************************************** // // Bhatch.cpp - An implementation of the hatched border around inplace // active windows. // // Modifications to OLE2UI version: // - C++ version // - removed restriction that parent must be non-null in CreateHatchWindow // - changed window from a child to a popup to allow null parent // - "extra bytes" removed in favor of member data // - added Paint() and InitWidth() to unclutter wnd proc // - added hit testing and correct cursors for handles // - added resizing for handles // - added NegotiateSize() to mediate inplace size negotiations between // the container and the server // // Copyright (c) 1993,94 by Borland International, Inc. All rights reserved // //************************************************************************** #include "BOle.h" // #define STRICT 1 #define STRICT #include "OLE2UI.h" #include "BHatch.h" // class name of hatch window #ifdef ANSI #define CLASS_HATCH "BOle Hatch Window" #else #define CLASS_HATCH OLESTR("BOle Hatch Window") #endif // local function prototypes LRESULT FAR PASCAL EXPORT BOleHatchWndProc (HWND, UINT, WPARAM, LPARAM); // static initializer UINT BOleHatchWindow::hatchWidth = 0; // global functions void SetRectExt (LPRECT r, UINT x, UINT y, UINT extent) { r->left = x; r->top = y; r->right = x + extent; r->bottom = y + extent; } BOleHatchWindow::BOleHatchWindow(HWND hWndParent, HINSTANCE hInst, BOleSite *p) { pBack = p; // assign BOleSite #ifdef ANSI hWndHatch = ::CreateWindowExA( 0, CLASS_HATCH, // Class name "Hatch Window", // Window name WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, GetDesktopWindow(), (HMENU)NULL, hInst, this ); #else hWndHatch = ::CreateWindowEx( 0, CLASS_HATCH, // Class name OLESTR("Hatch Window"), // Window name WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, GetDesktopWindow(), (HMENU)NULL, hInst, this ); #endif hatchRect.left = hatchRect.top = hatchRect.bottom = hatchRect.right = 0; MoveRect.left = MoveRect.top = MoveRect.bottom = MoveRect.right = 0; nClippedFlag = 0; pSite = NULL; fInMouseDown = FALSE; } //*************************************************************************** // Destructor: ~BOleHatchWindow // // Purpose: // Public destructor to destroy the hWnd // //*************************************************************************** BOleHatchWindow::~BOleHatchWindow () { if (hWndHatch) ::DestroyWindow (hWndHatch); } //*************************************************************************** // Static member function: Register // // Purpose: // Register the hatch window // // Parameters: // hInst Process instance // // Return Value: // TRUE if successful // FALSE if failed // //*************************************************************************** BOOL BOleHatchWindow::Register (HINSTANCE hInst) { #ifdef ANSI WNDCLASSA wc; #else WNDCLASS wc; #endif // Register Hatch Window Class wc.style = CS_BYTEALIGNWINDOW; wc.lpfnWndProc = BOleHatchWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(BOleHatchWindow*); // 'this' in extra bytes wc.hInstance = hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = CLASS_HATCH; #ifdef ANSI if (!::RegisterClassA(&wc)) #else if (!::RegisterClass(&wc)) #endif return FALSE; else return TRUE; } //*************************************************************************** // Static member function: InitWidth // // Purpose: // Initialize hatch border with from WIN.INI // //*************************************************************************** void BOleHatchWindow::InitWidth () { #ifdef ANSI hatchWidth = GetProfileIntA("windows", "oleinplaceborderwidth", DEFAULT_HATCHBORDER_WIDTH); #else hatchWidth = GetProfileInt(OLESTR("windows"), OLESTR("oleinplaceborderwidth"), DEFAULT_HATCHBORDER_WIDTH); #endif } //*************************************************************************** // Member function: GetWidth // // Purpose: // Get width of hatch border // // Parameters: None // // Return Value: // UINT width of the hatch border //*************************************************************************** UINT BOleHatchWindow::GetWidth () { if (!::IsWindow(hWndHatch)) return 0; return hatchWidth; } //**************************************************************** // window messages related member functions // // Purpose: // Window Procedure dispatches all messages here. // Each member functions got the message name. // Do messages processing here. // Refer to windows help to get information about messages // //**************************************************************** //**************************************************************** // Member function: wmCreate // // Parameters: // LPCREATESTRUCT struct containing information about the window // to be created. Not used // // Return Value: // LRESULT 0L to continue creation process // //**************************************************************** #ifdef ANSI LRESULT BOleHatchWindow::wmCreate (LPCREATESTRUCTA) #else LRESULT BOleHatchWindow::wmCreate (LPCREATESTRUCT) #endif { BOleHatchWindow::InitWidth (); return 0L; } //*************************************************************************** // Member function: wmPaint // // Purpose: // Draw hatched border and handles // // Parameters: None // // Return Value: // LRESULT 0L // //*************************************************************************** LRESULT BOleHatchWindow::wmPaint () { HDC hDC; PAINTSTRUCT ps; RECT rcHatchRect; hDC = ::BeginPaint(hWndHatch, &ps); // get hatchRect to paint hatch border rcHatchRect = hatchRect; SetRect((LPRECT)&rcHatchRect,rcHatchRect.left, rcHatchRect.top, rcHatchRect.right, rcHatchRect.bottom); OleUIDrawShading(&rcHatchRect, hDC, OLEUI_SHADE_BORDERIN, hatchWidth); // get the real visible rect to draw handles GetWindowRect(hWndHatch, (LPRECT)&rcHatchRect); SetRect((LPRECT)&rcHatchRect,0, 0, rcHatchRect.right-rcHatchRect.left, rcHatchRect.bottom-rcHatchRect.top); ::InflateRect ((LPRECT)&rcHatchRect, -hatchWidth, -hatchWidth); OleUIDrawHandles (&rcHatchRect, hDC, OLEUI_HANDLES_OUTSIDE, hatchWidth+1, TRUE); ::EndPaint (hWndHatch, &ps); return 0L; } //**************************************************************** // Member function: wmNCHitTest // // Purpose: // Find out which handle the cursor is over // // Parameters: // LPPOINT mouse position // // Return Value: // UINT a value indicating the position of the cursor // //**************************************************************** UINT BOleHatchWindow::wmNCHitTest (LPPOINT lpPoint) { RECT handleRect; RECT rc; // contain hatch window rect // we want lpPoint to be in client coordinates ::ScreenToClient (hWndHatch, lpPoint); GetWindowRect(hWndHatch, (LPRECT)&rc); SetRect((LPRECT)&rc,0,0,rc.right-rc.left,rc.bottom-rc.top); SetRectExt (&handleRect, rc.left, rc.top, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTTOPLEFT; SetRectExt (&handleRect, rc.left, (rc.top+rc.bottom-hatchWidth + 1)/2, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTLEFT; SetRectExt (&handleRect, rc.left, rc.bottom - hatchWidth + 1, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTBOTTOMLEFT; SetRectExt (&handleRect, (rc.left+rc.right-hatchWidth + 1)/2, rc.top, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTTOP; SetRectExt (&handleRect, (rc.left+rc.right-hatchWidth + 1)/2, rc.bottom - hatchWidth + 1, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTBOTTOM; SetRectExt (&handleRect, rc.right-hatchWidth + 1, rc.top, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTTOPRIGHT; SetRectExt (&handleRect, rc.right-hatchWidth + 1, (rc.top+rc.bottom-hatchWidth + 1)/2, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTRIGHT; SetRectExt (&handleRect, rc.right-hatchWidth + 1, rc.bottom-hatchWidth + 1, hatchWidth + 1); if (::PtInRect(&handleRect, *lpPoint)) return HTBOTTOMRIGHT; // If we've gotten this far, the cursor is not over a handle, but it // is over the hatch window (because we got the message). This means // the cursor is over the hatched border. It can't be over our client // area because the server object takes up the client area. // // So, by telling Windows that the cursor is over the caption bar, we // can piggy-back on Windows' code for dragging windows around. // return HTCAPTION; } //**************************************************************** // Member function: wmSetCursor // // Purpose: // Set the cursor bitmap to match the handle it's over. // // Parameters: // HWND handle of the window with the cursor // UINT only used. Contains the value returned from wmNCHitTest // UINT Mouse message number // // Return Value: // LRESULT TRUE, to stop further processing // //**************************************************************** LRESULT BOleHatchWindow::wmSetCursor (HWND, UINT hitTestCode, UINT) { switch (hitTestCode) { case HTTOPLEFT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENWSE))); break; case HTLEFT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZEWE))); break; case HTBOTTOMLEFT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENESW))); break; case HTTOP: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENS))); break; case HTBOTTOM: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENS))); break; case HTTOPRIGHT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENESW))); break; case HTRIGHT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZEWE))); break; case HTBOTTOMRIGHT: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENWSE))); break; default: ::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_ARROW))); } return (LRESULT)TRUE; } //**************************************************************** // Member function: wmMouseActivate // // Parameters: // HWND handle of the top level parent. Not used // UINT hit test code. Not used // UINT Mouse message identifier. Not used // // Return Value: // UINT MA_NOACTIVATE does not activate the window // //**************************************************************** UINT BOleHatchWindow::wmMouseActivate (HWND, UINT, UINT) { return MA_NOACTIVATE; } //**************************************************************** // Member function: wmNCLButtonDown // // Parameters: // UINT hit test code as returned from WM_NCHITTEST // LPOINT cursor coordinates // // Return Value: // LRESULT let it process by DefWindowProc // //**************************************************************** LRESULT BOleHatchWindow::wmNCLButtonDown (UINT nTestCode, LPPOINT lpPoint) { // first resize window if needed if (nClippedFlag & CLIPPED) { RECT WinRect; // get the real (visible) window in client coordinates GetWindowRect( hWndHatch, (LPRECT)&WinRect); ScreenToClient( GetParent( hWndHatch), (LPPOINT)&WinRect); // if clipped left or top hatchRect adjust top and/or left if (nClippedFlag & CLIPPED_LEFT) WinRect.left += hatchRect.left; if (nClippedFlag & CLIPPED_TOP) WinRect.top += hatchRect.top; // move window MoveWindow( hWndHatch, WinRect.left, WinRect.top, hatchRect.right - hatchRect.left, hatchRect.bottom - hatchRect.top, FALSE); } // set flag indicating the user is moving the window and initiating // all space negotiation procedure fInMouseDown=TRUE; return ::DefWindowProc (hWndHatch, WM_NCLBUTTONDOWN, nTestCode, *((long *)lpPoint)); } //**************************************************************** // Member function: wmGetMinMaxInfo // // Parameters: // LPMINMAXINFO struct containing resizing information // // Return Value: // LRESULT 0L when processing this message // //**************************************************************** LRESULT BOleHatchWindow::wmGetMinMaxInfo (MINMAXINFO* pMMI) { int minWidth = (GetWidth() * 2) + 15; pMMI->ptMinTrackSize.x = minWidth; pMMI->ptMinTrackSize.y = minWidth; return 0L; } //**************************************************************** // Member function: wmWindowPosChanging // // Purpose: // Call negotiate size to activate the space negotiation // procedure and, in case, changes the WINDOWPOS values // so to accomplish the new size // // Parameters: // LPWINDOWPOS structure containing information about // resizing values // // Return Value: // LRESULT // //**************************************************************** LRESULT BOleHatchWindow::wmWindowPosChanging (LPWINDOWPOS pWP) { if (hWndHatch == GetCapture()) { RECT TempRect; fInMouseDown = TRUE; ::DefWindowProc (hWndHatch, WM_WINDOWPOSCHANGING, 0, (DWORD)(LPVOID)pWP); ::InvalidateRect(hWndHatch, NULL, FALSE); ::CopyRect((LPRECT)&TempRect, (LPRECT)&hatchRect); ::InflateRect((LPRECT)&TempRect, -hatchWidth, -hatchWidth); ::ValidateRect(hWndHatch, (LPRECT)&TempRect); return 0L; } if (fInMouseDown) { int minWidth = (GetWidth() * 2) + 15; if (pWP->cx < minWidth) { pWP->cx = minWidth; } if (pWP->cy < minWidth) { pWP->cy = minWidth; } NegotiateSize (pWP); if (nClippedFlag & CLIPPED) { pWP->x = MoveRect.left; pWP->y = MoveRect.top; pWP->cx = MoveRect.right; pWP->cy = MoveRect.bottom; pWP->flags = 0; } ::InvalidateRect(hWndHatch, NULL, TRUE); fInMouseDown = FALSE; return 0L; } return ::DefWindowProc (hWndHatch, WM_WINDOWPOSCHANGING, 0, (DWORD)(LPVOID)pWP); } //*************************************************************************** // Member function: Show // // Purpose: // Show or hide the hatch window; provide access to the site // // Parameters: // BOOL TRUE to show the hatch window, FALSE to hide it // LPOLEINPLACESITE Pointer to the in place site, which is used to // negotiate new size when a handle is dragged // //*************************************************************************** void BOleHatchWindow::Show (BOOL fShow, LPOLEINPLACESITE pS) { pSite = pS; ::ShowWindow (hWndHatch, fShow ? SW_SHOW : SW_HIDE); } //*************************************************************************** // Member function: SetSize // // Purpose: // Move/size the HatchWindow correctly given the rect required by the // in-place server object window and the lprcClipRect imposed by the // in-place container. both rect's are expressed in the client coord. // of the in-place container's window (which is the parent of the // HatchWindow). When fInMouseDown is TRUE must not use MoveWindow // but change the values in the WINDOWPOS struct inside the // WM_WINDOWPOSCHANGING message (it seems Windows code is not re-entrant // there) // // OLE2NOTE: the in-place server must honor the lprcClipRect specified // by its in-place container. it must NOT draw outside of the ClipRect. // in order to achieve this, the hatch window is sized to be // exactly the size that should be visible (rcVisRect). the // rcVisRect is defined as the intersection of the full size of // the HatchRect window and the lprcClipRect. // the ClipRect could infact clip the HatchRect on the // right/bottom and/or on the top/left. if it is clipped on the // right/bottom then it is sufficient to simply resize the hatch // window. but if the HatchRect is clipped on the top/left then // in-place server document window (child of HatchWindow) must be moved // by the delta that was clipped. the window origin of the // in-place server window will then have negative coordinates relative // to its parent HatchWindow. // // Parameters: // LPRECT full size of in-place server object window // LPRECT clipping rect imposed by in-place container // LPPOINT offset required to position in-place server object // window properly. caller should call: // OffsetRect(&rcObjRect,lpptOffset->x,lpptOffset->y) // // Return Value: None // //*************************************************************************** void BOleHatchWindow::SetSize( LPRECT lprcIPObjRect, LPRECT lprcClipRect, LPPOINT lpptOffset) { RECT rcHatchRect; RECT rcVisRect; UINT uHatchWidth; POINT ptOffset; if (!::IsWindow(hWndHatch)) return; rcHatchRect = *lprcIPObjRect; uHatchWidth = GetWidth(); ::InflateRect((LPRECT)&rcHatchRect, uHatchWidth + 1, uHatchWidth + 1); // find the intersection between hatch (server) window and // clipping area (area where the object is allowed to paint) ::IntersectRect (&rcVisRect, &rcHatchRect, lprcClipRect); // move the hatch window at the intersection rectangle if you are not // dragging the window around if (!fInMouseDown) { ::MoveWindow( hWndHatch, rcVisRect.left, rcVisRect.top, rcVisRect.right-rcVisRect.left, rcVisRect.bottom-rcVisRect.top, TRUE /* fRepaint */ ); ::InvalidateRect(hWndHatch, NULL, TRUE); } else { // this information will be used in the WM_WINDOWPOSCHANGING // message to change WINDOWPOS structure values ::SetRect(&MoveRect, rcVisRect.left, rcVisRect.top, rcVisRect.right-rcVisRect.left, rcVisRect.bottom-rcVisRect.top); } nClippedFlag = 0; // clear clipped flag // store clipping information if (!(::EqualRect(&rcVisRect,&rcHatchRect))) { nClippedFlag |= CLIPPED; if (rcVisRect.left != rcHatchRect.left ) { nClippedFlag |= CLIPPED_LEFT; } if (rcVisRect.top != rcHatchRect.top) { nClippedFlag |= CLIPPED_TOP; } } // convert the rcHatchRect into the client coordinate system of the // HatchWindow itself ptOffset.x = - rcVisRect.left; ptOffset.y = - rcVisRect.top; ::OffsetRect(&rcHatchRect, ptOffset.x, ptOffset.y); hatchRect = rcHatchRect; // calculate offset required to position in-place // server doc window (output value) lpptOffset->x = ptOffset.x; lpptOffset->y = ptOffset.y; return; } //*************************************************************************** // Member function: NegotiateSize // // Purpose: // When the user drags a handle of the hatch window, Windows attempts // to resize the window. This routine catches that attempt and validates // it with the client. We don't need to validate it with the server // because the client will call the server back from OnPosRectChange to // BOleSite::SetObjectRects. // // Parameters: // WINDOWPOS* Pointer to Windows' WINDOWPOS structure // //*************************************************************************** void BOleHatchWindow::NegotiateSize (WINDOWPOS *pwp) { RECT rcWnd; // The pre-resize size of the hatch window // As an optimization, get the current size of the hatch window // and bail out if it's the same as the new size. This saves the // potentially expensive renegotiation. // ::GetWindowRect (hWndHatch, &rcWnd); HWND parent = ::GetParent (hWndHatch); ::ScreenToClient (parent, (LPPOINT) &rcWnd.left); ::ScreenToClient (parent, (LPPOINT) &rcWnd.right); //if ( rcWnd.left == pwp->x && rcWnd.top == pwp->y && // (rcWnd.bottom - rcWnd.top == pwp->cy) && // (rcWnd.right - rcWnd.left == pwp->cx)) // return; // Ask the client to resize to the new size of the window // RECT rcNew; // The new size of the server's inplace window ::SetRect (&rcNew, pwp->x, pwp->y, pwp->x + pwp->cx, pwp->y + pwp->cy); ::InflateRect (&rcNew, -(hatchWidth + 1), -(hatchWidth + 1)); if (pSite) { // Ask the server to show more/less of object // if (!SUCCEEDED(pBack->SetSiteRect(&rcNew))) { SIZE size; // undo resize of the server size.cx = rcWnd.right - rcWnd.left; size.cy = rcWnd.bottom - rcWnd.top; pBack->pPart->SetPartSize(&size); // If the client refuses the new size, give the pre-resize // size of the hatch window, back to Windows through the WINDOWPOS // pwp->x = rcWnd.left; pwp->y = rcWnd.top; pwp->cx = rcWnd.right - rcWnd.left; pwp->cy = rcWnd.bottom - rcWnd.top; } } } //*************************************************************************** // BOleHatchWndProc // // Purpose: // WndProc for hatch window // // Parameters: // hWnd // Message // wParam // lParam // // Return Value: // message dependent //*************************************************************************** LRESULT FAR PASCAL EXPORT BOleHatchWndProc (HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) { #ifdef ANSI BOleHatchWindow *pHW = (BOleHatchWindow*) ::GetWindowLongA(hWnd, 0); #else BOleHatchWindow *pHW = (BOleHatchWindow*) ::GetWindowLong(hWnd, 0); #endif // if pHW is NULL, assign it if WM_NCCREATE otherwise // return DefWindowProc if (!pHW) { if (Message==WM_NCCREATE) { #ifdef ANSI CREATESTRUCTA FAR* lpcs = (CREATESTRUCTA FAR*)lParam; ::SetWindowLongA(hWnd, 0, (DWORD)(LPVOID)(lpcs->lpCreateParams)); #else CREATESTRUCT FAR* lpcs = (CREATESTRUCT FAR*)lParam; ::SetWindowLong(hWnd, 0, (DWORD)(LPVOID)(lpcs->lpCreateParams)); #endif } return ::DefWindowProc (hWnd, Message, wParam, lParam); } switch (Message) { case WM_CREATE: #ifdef ANSI return pHW->wmCreate((LPCREATESTRUCTA)lParam); #else return pHW->wmCreate((LPCREATESTRUCT)lParam); #endif case WM_PAINT: return pHW->wmPaint (); case WM_GETMINMAXINFO: return pHW->wmGetMinMaxInfo((MINMAXINFO*)lParam); // The cursor setting strategy here is to catch the WM_NCHITTEST // first to figure out which hit-test area the cursor is over. // Then Windows calls us back with WM_SETCURSOR so we can make // the cursor the correct shape for that hit-test area. // case WM_NCHITTEST: if (pHW) { POINT pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); return pHW->wmNCHitTest ((LPPOINT) &pt); } // Any window that is used during in-place activation // must handle the WM_SETCURSOR message or else the cursor // of the in-place parent will be used. if WM_SETCURSOR is // not handled, then DefWindowProc sends the message to the // window's parent. // case WM_SETCURSOR: return pHW->wmSetCursor((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_MOUSEACTIVATE: return pHW->wmMouseActivate((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); // The window move/resize strategy here is: // - Keep control so we can allow the OLE2 client app to refuse // the new dimensions. Don't wait until the window has already // been moved and then move it back. // - In order to keep control, we catch the WM_WINDOWPOSCHANGING // and set appropriate flags in the WINDOWPOS struct. // - Since WM_WINDOWPOSCHANGING is sent for both user-interface // initiated actions and for ::MoveWindow-initiated changes, we // keep a flag which prevents changes during mousedowns // case WM_NCLBUTTONDOWN: return pHW->wmNCLButtonDown(wParam, (LPPOINT)&lParam); case WM_WINDOWPOSCHANGING: return pHW->wmWindowPosChanging((WINDOWPOS*) lParam); default: return ::DefWindowProc (hWnd, Message, wParam, lParam); } }