/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// windowserrorstring.hxx includes postwin.h, which #undef OPAQUE, so "#redef" it #include <comphelper/windowserrorstring.hxx> #ifdef OPAQUE #error OPAQUE should not be defined!? #endif #define OPAQUE 2
// the struct below is used to determine the qualities of a particular handle struct AFX_HANDLEINFO
{
size_t nOffsetX; // offset within RECT for X coordinate
size_t nOffsetY; // offset within RECT for Y coordinate int nCenterX; // adjust X by Width()/2 * this number int nCenterY; // adjust Y by Height()/2 * this number int nHandleX; // adjust X by handle size * this number int nHandleY; // adjust Y by handle size * this number int nInvertX; // handle converts to this when X inverted int nInvertY; // handle converts to this when Y inverted
};
// the struct below gives us information on the layout of a RECT struct and // the relationship between its members struct AFX_RECTINFO
{
size_t nOffsetAcross; // offset of opposite point (ie. left->right) int nSignAcross; // sign relative to that point (ie. add/subtract)
};
}
// this array is indexed by the offset of the RECT member / sizeof(int) const AFX_RECTINFO afxRectInfo[] =
{
{ offsetof(RECT, right), +1 },
{ offsetof(RECT, bottom), +1 },
{ offsetof(RECT, left), -1 },
{ offsetof(RECT, top), -1 },
};
static HBRUSH HalftoneBrush()
{ if (afxHalftoneBrush == nullptr)
{
WORD grayPattern[8]; for (int i = 0; i < 8; i++)
grayPattern[i] = static_cast<WORD>(0x5555 << (i & 1));
HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); if (grayBitmap != nullptr)
{
afxHalftoneBrush = CreatePatternBrush(grayBitmap);
DeleteObject(grayBitmap);
}
} return afxHalftoneBrush;
}
staticvoid DrawDragRect(
HDC hDC,LPRECT lpRect,SIZE size,
LPRECT lpRectLast,SIZE sizeLast,
HBRUSH hBrush = nullptr,HBRUSH hBrushLast = nullptr)
{ // first, determine the update region and select it
HRGN rgnNew;
HRGN rgnOutside,rgnInside;
rgnOutside = CreateRectRgnIndirect(lpRect);
RECT rect = *lpRect;
InflateRect(&rect,-size.cx, -size.cy);
IntersectRect(&rect,&rect,lpRect);
rgnInside = CreateRectRgnIndirect(&rect);
rgnNew = CreateRectRgn(0, 0, 0, 0);
CombineRgn(rgnNew,rgnOutside,rgnInside,RGN_XOR);
HBRUSH hBrushOld = nullptr; if (hBrush == nullptr)
hBrush = HalftoneBrush(); if (hBrushLast == nullptr)
hBrushLast = hBrush;
HRGN rgnLast(nullptr);
HRGN rgnUpdate(nullptr); if (lpRectLast != nullptr)
{ // find difference between new region and old region
rgnLast = CreateRectRgn(0, 0, 0, 0);
SetRectRgn(
rgnOutside,
lpRectLast->left,
lpRectLast->top,
lpRectLast->right,
lpRectLast->bottom);
rect = *lpRectLast;
InflateRect(&rect,-sizeLast.cx, -sizeLast.cy);
IntersectRect(&rect,&rect, lpRectLast);
SetRectRgn(rgnInside,rect.left,rect.top,rect.right,rect.bottom);
CombineRgn(rgnLast,rgnOutside,rgnInside, RGN_XOR);
// // only diff them if brushes are the same if (hBrush == hBrushLast)
{
rgnUpdate = CreateRectRgn(0, 0, 0, 0);
CombineRgn(rgnUpdate,rgnLast,rgnNew, RGN_XOR);
}
} if (hBrush != hBrushLast && lpRectLast != nullptr)
{ // brushes are different -- erase old region first
SelectClipRgn(hDC,rgnLast);
GetClipBox(hDC,&rect);
hBrushOld = static_cast<HBRUSH>(SelectObject(hDC,static_cast<HGDIOBJ>(hBrushLast)));
PatBlt(hDC,rect.left,rect.top,(rect.right-rect.left),(rect.bottom-rect.top),PATINVERT);
int Tracker::HitTest(POINT point) const
{
TrackerHit hitResult = hitNothing;
RECT rectTrue;
GetTrueRect(&rectTrue);
NormalizeRect(&rectTrue); if (PtInRect(&rectTrue,point))
{ if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
hitResult = static_cast<TrackerHit>(HitTestHandles(point)); else
hitResult = hitMiddle;
} return hitResult;
}
BOOL Tracker::SetCursor(HWND pWnd, UINT nHitTest) const
{ // trackers should only be in client area if (nHitTest != HTCLIENT) returnFALSE;
// convert cursor position to client co-ordinates
POINT point;
GetCursorPos(&point);
ScreenToClient(pWnd,&point);
// do hittest and normalize hit int nHandle = HitTestHandles(point); if (nHandle < 0) returnFALSE;
// need to normalize the hittest such that we get proper cursors
nHandle = NormalizeHit(nHandle);
// handle special case of hitting area between handles // (logically the same -- handled as a move -- but different cursor) if (nHandle == hitMiddle && !PtInRect(&m_rect,point))
{ // only for trackers with hatchedBorder (ie. in-place resizing) if (m_nStyle & hatchedBorder)
nHandle = TrackerHit(9);
}
::SetCursor(afxCursors[nHandle]); returnTRUE;
}
BOOL Tracker::Track(HWND hWnd,POINT point,BOOL bAllowInvert,
HWND hWndClipTo)
{ // perform hit testing on the handles int nHandle = HitTestHandles(point); if (nHandle < 0)
{ // didn't hit a handle, so just return FALSE returnFALSE;
}
// otherwise, call helper function to do the tracking
m_bAllowInvert = bAllowInvert;
SetCursor(hWnd,nHandle); return TrackHandle(nHandle, hWnd, point, hWndClipTo);
}
BOOL Tracker::TrackHandle(int nHandle,HWND hWnd,POINT point,HWND hWndClipTo)
{ // don't handle if capture already set if (GetCapture() != nullptr) returnFALSE;
// save original width & height in pixels int nWidth = m_rect.right - m_rect.left; int nHeight = m_rect.bottom - m_rect.top;
// set capture to the window which received this message
SetCapture(hWnd);
UpdateWindow(hWnd); if (hWndClipTo != nullptr)
UpdateWindow(hWndClipTo);
RECT rectSave = m_rect;
// find out what x/y coords we are supposed to modify int *px, *py; int xDiff, yDiff;
GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
xDiff = point.x - xDiff;
yDiff = point.y - yDiff;
// get DC for drawing
HDC hDrawDC; if (hWndClipTo != nullptr)
{ // clip to arbitrary window by using adjusted Window DC
hDrawDC = GetDCEx(hWndClipTo,nullptr, DCX_CACHE);
} else
{ // otherwise, just use normal DC
hDrawDC = GetDC(hWnd);
}
RECT rectOld; bool bMoved = false;
// get messages until capture lost or cancelled/accepted for (;;)
{
MSG msg; intconst bRet = GetMessageW(&msg, nullptr, 0, 0);
SAL_WARN_IF(-1 == bRet, "embedserv", "GetMessageW failed: " << comphelper::WindowsErrorString(GetLastError())); if (-1 == bRet || 0 == bRet) break;
if (GetCapture() != hWnd) break;
switch (msg.message)
{ // handle movement/accept messages case WM_LBUTTONUP: case WM_MOUSEMOVE:
rectOld = m_rect; // handle resize cases (and part of move) if (px != nullptr)
*px = static_cast<int>(static_cast<short>(LOWORD(msg.lParam))) - xDiff; if (py != nullptr)
*py = static_cast<int>(static_cast<short>(HIWORD(msg.lParam))) - yDiff;
// handle move case if (nHandle == hitMiddle)
{
m_rect.right = m_rect.left + nWidth;
m_rect.bottom = m_rect.top + nHeight;
} // allow caller to adjust the rectangle if necessary
AdjustRect(nHandle,&m_rect);
// only redraw and callback if the rect actually changed!
m_bFinalErase = (msg.message == WM_LBUTTONUP); if (!EqualRect(&rectOld,&m_rect) || m_bFinalErase)
{ if (bMoved)
{
m_bErase = TRUE;
DrawTrackerRect(&rectOld,hWndClipTo,hDrawDC,hWnd);
}
OnChangedRect(rectOld); if (msg.message != WM_LBUTTONUP)
bMoved = true;
} if (m_bFinalErase) goto ExitLoop;
if (!EqualRect(&rectOld,&m_rect))
{
m_bErase = FALSE;
DrawTrackerRect(&m_rect,hWndClipTo,hDrawDC,hWnd);
} break;
// handle cancel messages case WM_KEYDOWN: if (msg.wParam != VK_ESCAPE) break;
[[fallthrough]]; case WM_RBUTTONDOWN: if (bMoved)
{
m_bErase = m_bFinalErase = TRUE;
DrawTrackerRect(&m_rect, hWndClipTo, hDrawDC, hWnd);
}
m_rect = rectSave; goto ExitLoop;
// just dispatch rest of the messages default:
DispatchMessageW(&msg); break;
}
}
ExitLoop: if (hWndClipTo != nullptr)
ReleaseDC(hWndClipTo,hDrawDC); else
ReleaseDC(hWnd,hDrawDC);
ReleaseCapture();
// restore rect in case bMoved is still FALSE if (!bMoved)
m_rect = rectSave;
m_bFinalErase = FALSE;
m_bErase = FALSE;
// return TRUE only if rect has changed return !EqualRect(&rectSave,&m_rect);
}
void Tracker::DrawTrackerRect(
LPRECT lpRect,HWND pWndClipTo,HDC pDC,HWND pWnd)
{ // first, normalize the rectangle for drawing
RECT rect = *lpRect;
NormalizeRect(&rect);
// convert to client coordinates if (pWndClipTo != nullptr)
TransformRect(&rect,pWnd,pWndClipTo);
void Tracker::Draw(HDC hDC) const
{ // set initial DC state
SetMapMode(hDC,MM_TEXT);
SetViewportOrgEx(hDC,0, 0,nullptr);
SetWindowOrgEx(hDC,0, 0,nullptr);
// get normalized rectangle
RECT rect = m_rect;
NormalizeRect(&rect);
// if hatchBrush is going to be used, need to unrealize it if ((m_nStyle & (hatchInside|hatchedBorder)) != 0)
UnrealizeObject(static_cast<HGDIOBJ>(afxHatchBrush));
int Tracker::GetHandleSize(LPRECT lpRect) const
{
LPCRECT rect = lpRect == nullptr ? &m_rect : lpRect;
int size = m_nHandleSize; if (!(m_nStyle & resizeOutside))
{ // make sure size is small enough for the size of the rect int sizeMax = std::min(abs(rect->right - rect->left),
abs(rect->bottom - rect->top)); if (size * 2 > sizeMax)
size = sizeMax / 2;
} return size;
}
int Tracker::HitTestHandles(POINT point) const
{
RECT rect;
UINT mask = GetHandleMask();
// see if hit anywhere inside the tracker
GetTrueRect(&rect); if (!PtInRect(&rect,point)) return hitNothing; // totally missed
// see if we hit a handle for (int i = 0; i < 8; ++i)
{ if (mask & (1<<i))
{
GetHandleRect(static_cast<TrackerHit>(i), &rect); if (PtInRect(&rect,point)) returnstatic_cast<TrackerHit>(i);
}
}
// last of all, check for non-hit outside of object, between resize handles if ((m_nStyle & hatchedBorder) == 0)
{
rect = m_rect;
NormalizeRect(&rect); if ((m_nStyle & (dottedLine|solidLine)) != 0)
InflateRect(&rect,+1, +1); if (!PtInRect(&rect,point)) return hitNothing; // must have been between resize handles
} return hitMiddle; // no handle hit, but hit object (or object border)
}
void Tracker::GetModifyPointers( int nHandle, int** ppx, int** ppy, int* px, int* py)
{ if (nHandle == hitMiddle)
nHandle = hitTopLeft; // same as hitting top-left
*ppx = nullptr;
*ppy = nullptr;
// fill in the part of the rect that this handle modifies // (Note: handles that map to themselves along a given axis when that // axis is inverted don't modify the value on that axis)
const AFX_HANDLEINFO* pHandleInfo = &afxHandleInfo[nHandle]; if (pHandleInfo->nInvertX != nHandle)
{
*ppx = reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pHandleInfo->nOffsetX); if (px != nullptr)
*px = **ppx;
} else
{ // middle handle on X axis if (px != nullptr)
*px = m_rect.left + (m_rect.left-m_rect.right) / 2;
} if (pHandleInfo->nInvertY != nHandle)
{
*ppy = reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pHandleInfo->nOffsetY); if (py != nullptr)
*py = **ppy;
} else
{ // middle handle on Y axis if (py != nullptr)
*py = m_rect.top + (m_rect.top-m_rect.bottom) / 2;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.