//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of docking window classes
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/docking.h>
#include <owl/controlb.h>
#include <owl/tooltip.h>
#include <owl/uihelper.h> // for TUIBorder edge painting
#include <owl/uimetric.h>
#include <owl/template.h>
#if defined(BI_MULTI_THREAD_RTL)
#include <owl/thread.h>
#endif
#if defined(__TRACE) || defined(__WARN)
# include <owl/profile.h>
#endif
#include <vector>
#include <algorithm>
using namespace std;
namespace owl {
//
// Fillin missing defines for some configurations
//
#if !defined(WM_SIZING)
# define WM_SIZING 0x0214
#endif
#if !defined(DS_SETFOREGROUND)
# define DS_SETFOREGROUND 0x200L // not in win3.1
#endif
//
// Diagnostic group for docking
//
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlCmd);
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlDocking, 1, 0);
//----------------------------------------------------------------------------
//
/// This constructor creates a dockable gadget window and sets the appropriate
/// styles for the window.
//
TDockableGadgetWindow::TDockableGadgetWindow(TWindow* parent,
TTileDirection direction,
TFont* font,
TModule* module)
:
TGadgetWindow(parent, direction, font, module),
TDockable(),
Cursor(0)
{
#if 0
Attr.Style |= WS_BORDER; // Add in border for boundries between dockables
#else
Attr.Style &= ~WS_BORDER; // Normally no borders
#endif
Attr.Style |= WS_CLIPSIBLINGS;
// Always shrink wrapped when dockable since slip fills out docking area
//
SetShrinkWrap(true, true);
}
//
//
//
TDockableGadgetWindow::~TDockableGadgetWindow()
{
delete Cursor;
}
TGadget*
TDockableGadgetWindow::GetGripGadget()
{
return new TFlatHandleGadget(IDG_FLATHANDLE);
}
//
//
//
DEFINE_RESPONSE_TABLE1(TDockableGadgetWindow, TGadgetWindow)
EV_WM_LBUTTONDOWN,
EV_WM_OWLWINDOWDOCKED,
EV_WM_SETCURSOR,
END_RESPONSE_TABLE;
/// Inserts/deletes FlatBar Grip.
void
TDockableGadgetWindow::EvOwlWindowDocked(uint loc, const TDockingSlip&)
{
TTileDirection dir = GetDirectionForLocation((TAbsLocation)loc);
if(GetFlatStyle()&FlatStandard){
if(dir == Rectangular){
delete Cursor;
Cursor = 0;
}
else if(!GadgetWithId(IDG_FLATHANDLE)){
TGadget* gadget = GetGripGadget();
if(gadget)
Insert(*gadget, Before, FirstGadget());
// set cursor
if(dir == Horizontal)
Cursor = new TCursor(0, IDC_SIZEWE);
else
Cursor = new TCursor(0, IDC_SIZENS);
LayoutSession();
}
else{
if(GetDirection() != dir){
delete Cursor;
if(dir == Horizontal)
Cursor = new TCursor(0, IDC_SIZEWE);
else
Cursor = new TCursor(0, IDC_SIZENS);
}
LayoutSession();
}
}
// if exist GridGadget
else if(GadgetWithId(IDG_FLATHANDLE)){
// set cursor
if(dir == Horizontal)
Cursor = new TCursor(0, IDC_SIZEWE);
else
Cursor = new TCursor(0, IDC_SIZENS);
LayoutSession();
}
}
//
//
//
bool
TDockableGadgetWindow::EvSetCursor(HWND hWndCursor, uint codeHitTest, TMsgId mouseMsg)
{
if(hWndCursor == GetHandle() && codeHitTest == HTCLIENT && Cursor){
TPoint point;
GetCursorPos(point);
ScreenToClient(point);
TRect boundsRect;
GetClientRect(boundsRect);
if(GetDirection()==Horizontal){
boundsRect.right = boundsRect.left+8;
boundsRect.Inflate(0,-TUIMetric::CySizeFrame);
}
else{
boundsRect.bottom = boundsRect.top+7;
boundsRect.Inflate(-TUIMetric::CxSizeFrame,0);
}
if(boundsRect.Contains(point)){
::SetCursor(*Cursor);
return true;
}
}
return TGadgetWindow::EvSetCursor(hWndCursor, codeHitTest, mouseMsg);
}
//
/// Finds out how big this dockable would be in a given location, and with a given
/// optional size hint.
//
TSize
TDockableGadgetWindow::ComputeSize(TAbsLocation loc, TSize* dim)
{
TTileDirection dir = GetDirectionForLocation(loc);
// Save current settings to restore when done, since this is a info-request
// only function
//
TTileDirection oldDir = GetDirection();
int oldWidth = RowWidth;
// If rectangular, calcuate adjust size for surrounding parent frame window
//
TSize delta(0,0);
if (dir == Rectangular) {
// If we currently are in a floating slip, use that window to calc frame delta
//
TWindow* parent = GetParentO();
if (oldDir == Rectangular && parent && parent->GetHandle()) {
delta = parent->GetWindowRect().Size() - parent->GetClientRect().Size();
// In Win95, the bottom margin and the bottom sizing border are the
// same color and not separated by a hard border, so we 'kern' them
// together to match the size of the top margin.
//
int yt = TUIMetric::CySizeFrame - TUIMetric::CyFixedFrame;
int topM, leftM, bottomM, rightM;
GetMargins(GetMargins(), leftM, topM, rightM, bottomM);
delta.cy -= std::min(yt, bottomM);
}
// No floating parent yet. Calculate the delta based on assumed frame
// styles. (This rect is only used to size the drag frame anyway.)
//
else {
TRect winRect(0, 0, 0, 0);
AdjustWindowRectEx(winRect,
WS_POPUPWINDOW|WS_VISIBLE|WS_CAPTION|WS_THICKFRAME|WS_DLGFRAME,
false, WS_EX_TOOLWINDOW|WS_EX_WINDOWEDGE);
delta = winRect.Size();
}
}
TSize sz(0, 0);
Direction = dir;
// Seek a vertical size if just a non-zero cy is given
//
if (dim && dim->cx == 0 && dim->cy != 0) {
int seekCy = dim->cy - delta.cy; // This is the height to shoot for
TSize testSz;
GetDesiredSize(testSz);
// Seek the row whose height is less than seekCy
///CH: I really would like to have a better, non-iterative algorithm for
///CH: this, but for now this works.
//
if (testSz.cy < seekCy) {
// Seek a taller row by successivly shrinking the width
//
while (1) {
int prevW = RowWidth;
sz = testSz;
RowWidth -= 5;
if (RowWidth <= 0) { // Limit reached. Give up
RowWidth = testSz.cx;
sz = testSz;
break;
}
GetDesiredSize(testSz);
if (seekCy < testSz.cy) { // Shrunk too far: use previous size
RowWidth = prevW;
break;
}
}
}
else if (testSz.cy > seekCy) {
// Seek a shorter row by successivly growing the width
//
while (1) {
int prevW = RowWidth;
sz = testSz;
RowWidth += 5;
if (RowWidth > 1000) { // CHNOTE: Limit reached. Give up
RowWidth = testSz.cx;
sz = testSz;
break;
}
GetDesiredSize(testSz);
if (seekCy > testSz.cy) {
RowWidth = prevW;
break;
}
}
}
else // seekCy == testSz.cy
sz = testSz;
}
// Change to a given horizontal size, or just the natural size for the
// direction
//
else {
if (dim) // Change the row width here because we are changing size.
RowWidth = dim->cx - delta.cx;
GetDesiredSize(sz);
}
Direction = oldDir;
RowWidth = oldWidth;
sz += delta;
return sz;
}
//
/// Gets this dockable's screen rectangle.
//
void
TDockableGadgetWindow::GetRect(TRect& rect)
{
GetWindowRect(rect);
}
//
/// Returns a gadget window tile direction code, given a docking absolute location
/// code.
//
TGadgetWindow::TTileDirection
TDockableGadgetWindow::GetDirectionForLocation(TAbsLocation loc)
{
switch(loc) {
case alTop:
case alBottom:
return Horizontal;
case alLeft:
case alRight:
return Vertical;
case alNone:
default:
return Rectangular;
}
}
//
/// Causes this dockable to lay itself out vertically, horizontally, or
/// rectangularly.
//
void
TDockableGadgetWindow::Layout(TAbsLocation loc, TSize* dim)
{
TGadgetWindow::TTileDirection dir = GetDirectionForLocation(loc);
if (GetDirection() != dir || dim) {
if (dim)
SetRectangularDimensions(dim->cx, dim->cy); // Sets RowWidth mostly
// Set the layout direction, which ends up invoking LayoutSession &
// SetWindowPos
//
SetDirection(dir);
}
}
//
/// Returns true if the mouse click point is in a spot that should move this
/// dockable around.
//
bool
TDockableGadgetWindow::ShouldBeginDrag(TPoint& pt)
{
TGadget* g = GadgetFromPoint(pt);
if (g)
// !CQ should also allow non-clickables to drag, like text!
#if 1
return !g->IsVisible(); // Allow hidden gadgets to begin a drag; separators are hidden
#else
return !g->GetEnabled(); // Allow disabled gadgets to begin a drag
#endif
return true;
}
//
/// Returns the TWindow part of this dockable object. In this case, it is just this
/// window.
//
TWindow*
TDockableGadgetWindow::GetWindow()
{
return this;
}
//
/// If the gadget window changes size when laying out a dockable gadget window, this
/// function tells the dock about it so the dock can resize too.
//
void
TDockableGadgetWindow::LayoutSession()
{
TSize sz;
GetDesiredSize(sz); // Find out how big we'd like to be
Attr.W = sz.cx; // This will allow LayoutSession to work as if
Attr.H = sz.cy; // we were the right size.
TGadgetWindow::LayoutSession();
// If we are in a slip that has been created properly, then tell it to adjust
// to our new layout
//
if (GetParentO()) {
TFloatingSlip* slip = TYPESAFE_DOWNCAST(GetParentO(),TFloatingSlip);
if (slip) {
// If this is a null-sized gadget window, hide our floating frame.
//
bool hideSlip = Attr.W == 0 || Attr.H == 0;
GetParentO()->ShowWindow(hideSlip ? SW_HIDE : SW_SHOWNA);
// Make sure the slip will adjust to our new size
//
bool stc = GetParentO()->IsFlagSet(wfShrinkToClient);
if (!stc)
GetParentO()->SetFlag(wfShrinkToClient);
SetWindowPos(0, 0, 0, Attr.W, Attr.H, SWP_NOACTIVATE | SWP_NOMOVE);
if (!stc)
GetParentO()->ClearFlag(wfShrinkToClient);
}
// If we aren't in a slip (because we haven't been inserted yet) resize
// anyway.
//
else {
SetWindowPos(0, 0, 0, Attr.W, Attr.H, SWP_NOACTIVATE | SWP_NOMOVE);
}
}
}
//
/// Returns the harbor containing the dockable object.
//
THarbor*
TDockableGadgetWindow::GetHarbor()
{
// If parent is a slip, return its harbor.
// If parent is the harbor (because the dockable is hidden), return the harbor.
//
if (!Parent)
return 0;
TDockingSlip* slip = TYPESAFE_DOWNCAST(GetParentO(),TDockingSlip);
if (slip)
return slip->GetHarbor();
THarbor* harbor = TYPESAFE_DOWNCAST(GetParentO(),THarbor);
return harbor;
}
//
/// Forwards event to slip to allow movement of gadget within the slip.
//
void
TDockableGadgetWindow::EvLButtonDown(uint modKeys, const TPoint& point)
{
TPoint pt = point;
TGadgetWindow::EvLButtonDown(modKeys, point);
// Forward the message to the parent, this is for the docking areas. To
// allow moving of the controlbars within the docking areas.
// !CQ Use OWL parent??? Or old hwnd parent?
//
TWindow* w = GetParentO();
// Check that we're parented to the right window. In OLE server situation,
// the toolbar could have been reparented to another HWND [i.e. container's
// window]
//
if (w && ::GetParent(*this) != w->GetHandle())
return;
// Forward to parent [which is a slip]
//
if (w && w->GetHandle()) {
TPoint p = MapWindowPoints(*w, point); // Transform to parent's coordinate system.
w->HandleMessage(WM_LBUTTONDOWN, modKeys, MkParam2(int16(p.x), int16(p.y)));
}
// At this point, our window may be in drag mode [i.e. being docked or
// undocked]. This means that the harbor has invoked 'SetCapture' which in
// turn implies that we won't see subsequent WM_MOUSEMOVE/WM_LBUTTONDOWN
// messages. This will leave the tooltip the impression [strong impression
// if I may add] that the mouse button never came up. The net side-effect
// will be that the tooltip will not activate until it sees an LBUTTONUP
// message. So to avoid this side-effect we fake an LBUTTONUP message here.
//
THarbor* harbor = GetHarbor();
if (harbor && GetCapture() == *harbor)
{
TTooltip* tooltip = GetTooltip();
if (tooltip && tooltip->IsWindow())
{
MSG msg;
msg.hwnd = *this;
msg.message = WM_LBUTTONUP;
msg.wParam = modKeys;
msg.lParam = MkUint32(int16(pt.x), int16(pt.y));
tooltip->RelayEvent(msg);
}
}
}
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(TDockableControlBar, TDockableGadgetWindow)
EV_WM_OWLWINDOWDOCKED,
END_RESPONSE_TABLE;
//
/// Constructs a dockable control bar.
//
TDockableControlBar::TDockableControlBar(TWindow* parent,
TTileDirection direction,
TFont* font,
TModule* module)
:
TDockableGadgetWindow(parent, direction, font, module)
{
GetMargins().Units = TMargins::BorderUnits;
WantTooltip = true;
AdjustMargins();
}
//
//
//
void
TDockableControlBar::AdjustMargins()
{
// !CQ Conrad's IDEOWL uses 6*Border,3*border & vice versa
if (GetDirection() == Horizontal) {
Margins.Left = Margins.Right = TUIMetric::CxSizeFrame + TUIMetric::CxFixedFrame;
Margins.Top = Margins.Bottom = TUIMetric::CySizeFrame;
}
else if(Direction == Vertical){
Margins.Left = Margins.Right = TUIMetric::CxSizeFrame;
Margins.Top = Margins.Bottom = TUIMetric::CySizeFrame + TUIMetric::CyFixedFrame;
}
else{
Margins.Left = Margins.Right = TUIMetric::CxSizeFrame;
Margins.Top = Margins.Bottom = TUIMetric::CySizeFrame;
}
}
//
//
//
void
TDockableControlBar::EvOwlWindowDocked(uint pos, const TDockingSlip& slip)
{
AdjustMargins();
TDockableGadgetWindow::EvOwlWindowDocked(pos, slip);
}
DEFINE_RESPONSE_TABLE1(TFloatingSlip, TFloatingFrame)
EV_WM_NCLBUTTONDOWN,
EV_WM_LBUTTONDOWN,
EV_WM_CLOSE,
EV_WM_SIZING, // Win 4.0 message only.
EV_WM_WINDOWPOSCHANGING,
EV_WM_WINDOWPOSCHANGED,
EV_WM_SETTINGCHANGE,
EV_WM_GETMINMAXINFO,
END_RESPONSE_TABLE;
//
/// Constructs a floating slip and sets the appropriate style for the window.
//
TFloatingSlip::TFloatingSlip(TWindow* parent,
int x, int y,
TWindow* clientWnd,
bool shrinkToClient,
int captionHeight,
bool popupPalette,
TModule* module)
:
TFloatingFrame(parent, clientWnd->GetCaption(), 0/*clientWnd*/, shrinkToClient,
captionHeight, popupPalette, module)
{
SetDragFrame(false);
Attr.Style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_SYSMENU);
Attr.Style &= ~(WS_VISIBLE); // Create initially hidden, show later
Attr.Style |= WS_CLIPCHILDREN; // Dont womp on children
Attr.Style |= WS_BORDER | WS_THICKFRAME; // Sizeable frame to allow reshaping
// Let tiny caption recalc margin & border dimensions using new styles. Tiny
// caption will adjust these some more as needed.
//
EnableTinyCaption(TFloatingFrame::DefaultCaptionHeight, true);
// Calculate a real initial position, & an estimated size. May resize later
// when client has reshaped.
//
TRect winRect(x, y, x+clientWnd->GetWindowAttr().W, y+clientWnd->GetWindowAttr().H);
AdjustWindowRectEx(winRect, Attr.Style, false, Attr.ExStyle);
#if 1
Attr.X = x; // Places this frame at given screen x,y
Attr.Y = y;
#else
Attr.X = winRect.left; // Places client rect at given screen x,y
Attr.Y = winRect.top;
#endif
Attr.W = winRect.Width();
Attr.H = winRect.Height();
}
//
/// Handles lbutton down bubbled up from the client to begin a dockable drag
/// operation.
//
void
TFloatingSlip::EvNCLButtonDown(uint hitTest, const TPoint& point)
{
if (hitTest == HTCAPTION) {
// Call the dockable dragging setup function in Harbor, passing this
// window as the dragged dockable and as the docking slip too
//
if (Harbor && Harbor->DockDraggingBegin(*this, point, alNone, this))
return;
}
TWindow::EvNCLButtonDown(hitTest, point);
}
//
/// Handles lbutton down bubbled up from the client to begin a dockable drag
/// operation.
//
void
TFloatingSlip::EvLButtonDown(uint modKeys, const TPoint& point)
{
// Is the mouseDown in a area where we can move the docked window?
//
TWindow* cw = GetClientWindow();
TDockable* d = TYPESAFE_DOWNCAST(cw, TDockable);
TPoint childPt = MapWindowPoints(*cw, point); // Transform to client window's coordinate system.
if (d && d->ShouldBeginDrag(childPt)) {
TPoint p = MapClientToScreen(point);
if (Harbor && Harbor->DockDraggingBegin(*this, p, alNone, this))
return; // Successfully started
}
TWindow::EvLButtonDown(modKeys, point);
}
//
/// When closing the floating slip, removes any dockable first so that it is not
/// destroyed. Dockables are owned by the harbor, not the slip, and must not be
/// destroyed when the slip is destroyed.
//
void
TFloatingSlip::EvClose()
{
TDockable* dd = TYPESAFE_DOWNCAST(GetClientWindow(), TDockable);
if (dd)
Harbor->Remove(*dd); // Will cause a close in DockableRemoved()
else
TWindow::EvClose();
}
//
/// Handles the Windows 4.0 message for the best resize user feedback.
//
bool
TFloatingSlip::EvSizing(uint side, TRect& rect)
{
// Look for size changes & make them track the dockable
//
TWindow* w = GetClientWindow();
if (w) {
TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
if (dockable) {
TSize trackSize(rect.Size());
switch (side) {
case WMSZ_BOTTOM:
case WMSZ_TOP:
trackSize.cx = 0;
break;
case WMSZ_LEFT:
case WMSZ_RIGHT:
trackSize.cy = 0;
break;
}
TSize dsz = dockable->ComputeSize(alNone, &trackSize);
// Center the fixed axis
//
switch (side) {
case WMSZ_BOTTOM:
case WMSZ_TOP:
rect.left = rect.left + (rect.Width() - dsz.cx)/2;
break;
case WMSZ_LEFT:
case WMSZ_RIGHT:
rect.top = rect.top + (rect.Height() - dsz.cy)/2;
break;
}
// Change the window size, keeping it pinned on the opposite corner
//
switch (side) {
case WMSZ_TOP:
case WMSZ_TOPRIGHT:
rect.top = rect.bottom - dsz.cy;
rect.right = rect.left + dsz.cx;
break;
case WMSZ_TOPLEFT:
rect.left = rect.right - dsz.cx;
rect.top = rect.bottom - dsz.cy;
break;
case WMSZ_BOTTOMLEFT:
case WMSZ_LEFT:
rect.left = rect.right - dsz.cx;
rect.bottom = rect.top + dsz.cy;
break;
case WMSZ_BOTTOM:
case WMSZ_BOTTOMRIGHT:
case WMSZ_RIGHT:
rect.right = rect.left + dsz.cx;
rect.bottom = rect.top + dsz.cy;
break;
}
}
}
return true;
}
//
/// Handles WM_WINDOWPOSCHANGING to make sure that the frame is properly constrained
/// by the dimensions of the dockable client.
//
bool
TFloatingSlip::EvWindowPosChanging(WINDOWPOS & windowPos)
{
TFloatingFrame::EvWindowPosChanging(windowPos);
// Look for size changes & make them track the dockable
//
if (!(windowPos.flags&SWP_NOSIZE)) {
TWindow* w = GetClientWindow();
if (w) {
TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
if (dockable) {
// !CQ Shouldn't we adjust .cx by our frame thickness to get client width?
// !CQ Maybe keep Delta around for these client<->frame size calculations...
/// TRect cr(0,0,0,0);
/// AdjustWindowRectEx(cr, Attr.Style, false, Attr.ExStyle);
TSize ts(windowPos.cx, 0);
TSize dsz = dockable->ComputeSize(alNone, &ts);
// Center the non-sizing axis
//
if (!(windowPos.flags&SWP_NOMOVE)) {
/// if (sizing horizontally)
windowPos.y = windowPos.y + (windowPos.cy - dsz.cy)/2;
/// else
/// windowPos.x = windowPos.x + (windowpos.cx - dsz.cx)/2;
}
windowPos.cx = dsz.cx;
windowPos.cy = dsz.cy;
}
}
}
windowPos.flags |= SWP_NOACTIVATE; // Not very effective, but worth a try
return false;
}
//
/// Handles WM_WINDOWPOSCHANGED to make sure that the dockable client gets a chance
/// to do final layout.
//
void
TFloatingSlip::EvWindowPosChanged(const WINDOWPOS& windowPos)
{
TFloatingFrame::EvWindowPosChanged(windowPos);
if (!(windowPos.flags&SWP_NOSIZE)) {
TWindow* w = GetClientWindow();
if (w) {
TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
if (dockable) {
/// TRect cr(0,0,0,0);
/// AdjustWindowRectEx(cr, Attr.Style, false, Attr.ExStyle);
TSize ts(windowPos.cx, 0);
TSize dsz = dockable->ComputeSize(alNone, &ts);
dockable->Layout(alNone, &dsz);
}
}
}
}
//
/// Event handler for the .INI file changed message. The window may have resized
/// because the user has changed the caption size.
//
void
TFloatingSlip::EvSettingChange(uint /*flags*/, LPCTSTR /*section*/)
{
TWindow* w = GetClientWindow();
// Resize, since our sizing borders may have changed size
//
if (w) {
TRect newWinRect = w->GetWindowRect();
AdjustWindowRectEx(newWinRect, Attr.Style, false, Attr.ExStyle);
TRect winRect = GetWindowRect();
if (winRect.Height() != newWinRect.Height() ||
winRect.Width() != newWinRect.Width()) {
SetWindowPos(0, newWinRect, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}
}
}
//
// Respond to WM_GETMINMAXINFO messages to ensure that the minTrackSize
// used by Windows is not greater than the size would be for a vertical
// floating control bar.
//
void
TFloatingSlip::EvGetMinMaxInfo(MINMAXINFO & info)
{
TSize sz(0,0);
ComputeSize( alLeft, &sz );
info.ptMinTrackSize.x = sz.cx;
info.ptMinTrackSize.y = sz.cy;
}
//
/// Overriden TDockingSlip virtual. Called by mouseup handler after a dockable is
/// dropped into this docking window.
//
void
TFloatingSlip::DockableInsert(TDockable& dockable,
const TPoint* /*topLeft*/,
TRelPosition /*position*/, TDockable* /*relDockable*/)
{
dockable.Layout(alNone); // No specific dimensions suggested
SetClientWindow(dockable.GetWindow()); // Set dockable as client & resize
// Retrieve window object of dockable
//
TWindow* dockableWindow = dockable.GetWindow();
CHECK(dockableWindow);
// Let window know it was docked [well, undocked]
//
#ifdef UNIX
if (dockableWindow->IsWindow()) //?????????????????????
dockableWindow->SendMessage(WM_OWLWINDOWDOCKED, TParam1(alNone),
TParam2((TDockingSlip*)this));
#else
dockableWindow->DispatchMsg(WM_OWLWINDOWDOCKED, 0, TParam1(alNone),
TParam2((TDockingSlip*)this));
#endif
}
//
/// Overriden TDockingSlip virtual. Called by lbutton up handler after a drag within
/// this docking window. This floating slip only moves itself.
//
void
TFloatingSlip::DockableMove(TDockable& /*dockable*/, const TPoint* topLeft,
TRelPosition /*position*/, TDockable* /*relDockable*/)
{
if (topLeft)
SetWindowPos(0, topLeft->x, topLeft->y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
}
//
/// Overriden TDockingSlip virtual. Called by lbutton up handler after a dockable
/// has been removed from this docking slip.
//
void
TFloatingSlip::DockableRemoved(const TRect& /*orgDragRect*/)
{
// Clear client window
//
SetClientWindow(0);
// !BB CloseWindow has the side-effect of leaving things around - i.e.
// !BB window object is not condemned... Can be expensive after a few
// !BB floating slips.
// !BB
// !BB CloseWindow();
SendMessage(WM_CLOSE);
}
//
/// Forward the first four TDockable virtuals to the client dockable.
//
TSize
TFloatingSlip::ComputeSize(TAbsLocation loc, TSize* dim)
{
TWindow* w = GetClientWindow();
if (w) {
TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
if (dockable)
return dockable->ComputeSize(loc, dim);
}
return TSize(0, 0);
}
//
/// Gets this dockable's screen rect.
//
void
TFloatingSlip::GetRect(TRect& rect)
{
GetWindowRect(rect);
}
//
/// Forwards the layout message over to the dockable object.
//
void
TFloatingSlip::Layout(TAbsLocation loc, TSize* dim)
{
TWindow* w = GetClientWindow();
if (w) {
TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
if (dockable)
dockable->Layout(loc, dim); // !CQ adjust dim by our frame thickness?
}
CHECK(w);
}
//
/// A given mouse down should never begin a drag for this dockable.
//
bool
TFloatingSlip::ShouldBeginDrag(TPoint& /*pt*/)
{
return false;
}
//
/// Returns the TWindow part of this dockable object. In this case it is actually
/// the client window.
//
TWindow*
TFloatingSlip::GetWindow()
{
CHECK(GetClientWindow());
return GetClientWindow();
}
//
/// Returns the associated harbor.
//
THarbor*
TFloatingSlip::GetHarbor()
{
return Harbor;
}
//
/// Returns the location of the object.
//
TAbsLocation
TFloatingSlip::GetLocation() const
{
return alNone;
}
DEFINE_RESPONSE_TABLE1(TEdgeSlip, TWindow)
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONDBLCLK, // !CQ
EV_WM_NCCALCSIZE,
EV_WM_NCPAINT,
EV_WM_ERASEBKGND,
EV_WM_PARENTNOTIFY,
EV_WM_WINDOWPOSCHANGING,
END_RESPONSE_TABLE;
//
/// Constructs an edge slip, and sets the approriate styles for the window.
//
TEdgeSlip::TEdgeSlip(TDecoratedFrame& parent, TAbsLocation location, TModule* module)
:
TWindow(&parent, _T("EdgeSlip"), module),
Location(location),
GridType(Location == alTop || Location == alBottom ? YCoord : XCoord)
{
#if defined(__TRACE) || defined(__WARN)
// Make slip dark blue for debugging purposes in diagnostic build
//
TProfile iniFile(_T("Diagnostics"), _T(OWL_INI));
bool useDiagColor = iniFile.GetInt(_T("DiagToolbarBorders")) != 0;
if (useDiagColor)
SetBkgndColor(TColor(0, 0, 128));
else
#endif
SetBkgndColor(TColor::Sys3dFace);
Attr.Style = (WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
}
//
// Type of decoration
//
struct TEachDecoration {
TWindow* Window;
int Top; // or Left if XCoord
int Bottom; // or Right if XCoord
TEachDecoration(TWindow*, TGridType);
TEachDecoration(int Top, int Bottom, TWindow* w);
bool operator <(const TEachDecoration& other) const
{return Top < other.Top;}
};
//
// Create a decoration from the grid.
//
TEachDecoration::TEachDecoration(TWindow* w, TGridType gridType)
:
Window(w)
{
TRect rect = w->GetWindowRect();
if (gridType == YCoord) {
Top = rect.top;
Bottom = rect.bottom;
}
else {
Top = rect.left;
Bottom = rect.right;
}
}
//
// Create the decoration from a specific location.
//
TEachDecoration::TEachDecoration(int top, int bottom, TWindow* w)
:
Window(w),
Top(top),
Bottom(bottom)
{
}
namespace
{
template <class TDecoration>
vector<TDecoration> MakeDecorationList_(TEdgeSlip& s, TGridType g)
{
vector<TDecoration> v;
for (TWindow* w = s.GetFirstChild(); w; w = (w == s.GetLastChild()) ? 0 : w->Next())
v.push_back(TDecoration(w, g));
sort(v.begin(), v.end());
return v;
}
} // namespace
//
/// Ensures that all decorations in the docking window are abutted against each
/// other (both horizontally and vertically); there should be no gaping holes.
//
void
TEdgeSlip::SetupWindow()
{
TWindow::SetupWindow(); // Create all children.
vector<TEachDecoration> decoList = MakeDecorationList_<TEachDecoration>(*this, GridType);
// Look for any gaping holes between the dockable windows and collapse them.
// Normally, this doesn't occur unless the to be created size is different
// from the actual created size of a gadget. This, currently, only occurs
// for TControlGadgets where the gadget is a combobox the created size
// specifies the editclass area and the drop-down area, however, the window
// rect (when the GetHandle() is valid) is just the editclass area.
//
int diff = 0;
int startWindowChange = -1;
int rowHeight = 0;
for (size_t i = 1; i < decoList.size(); ++i) {
// Are the two decorations we're going to look at on the same vertical?
//
if (decoList[i-1].Top != decoList[i].Top) {
// No, so compute possible difference.
//
if (decoList[i-1].Bottom+diff != decoList[i].Top+diff) {
// Remember the first window which is adjusted, we'll want to delay the
// SetWindowPos until all windows from this point to the end have been
// completely adjusted.
//
if (startWindowChange == -1)
startWindowChange = static_cast<int>(i);
// Found a spot. Compute the offset.
//
diff += decoList[i-1].Top + rowHeight - decoList[i].Top;
}
rowHeight = decoList[i].Bottom - decoList[i].Top;
}
else {
rowHeight = std::max(rowHeight, decoList[i].Bottom - decoList[i].Top);
}
if (diff) {
TEachDecoration& decoItem = decoList[i];
decoItem.Top += diff;
decoItem.Bottom += diff;
}
}
// Re-adjust all windows which have changed location.
//
if (startWindowChange != -1) {
for (size_t i = startWindowChange; i < decoList.size(); ++i) {
TEachDecoration& decoItem = decoList[i];
TRect r;
decoItem.Window->GetWindowRect(r);
TPoint pt(r.left, r.top);
if (GridType == YCoord)
pt.y = decoItem.Top;
else
pt.x = decoItem.Top;
// Map from global to local coord (the parent's coordinate space).
//
::MapWindowPoints(0, *this, (TPoint*)&pt, 1);
decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
i++;
}
}
// Make sure that our size is updated
//
ReLayout(true);
}
//
/// Changes the reciever to be the framewindow, not the docking window. The receiver
/// window is normally set up in Activate upon the creation of TButtonGadgetEnabler.
//
void
TEdgeSlip::EvCommandEnable(TCommandEnabler& commandEnabler)
{
if (GetParentO()) {
// Already being processed?
//
if (!commandEnabler.IsReceiver(GetParentO()->GetHandle())) {
// No, so forward it up to our parent
//
commandEnabler.SetReceiver(GetParentO()->GetHandle());
GetParentO()->EvCommandEnable(commandEnabler);
}
}
}
//
/// Forward command messages to the parent.
//
/// Changes the receiver to be the framewindow, not the docking window. The receiver
/// window is normally set up in Activate upon the creation of TButtonGadgetEnabler.
//
TResult
TEdgeSlip::EvCommand(uint id, THandle hWndCtl, uint notifyCode)
{
TRACEX(OwlCmd, 1, "TEdgeSlip::EvCommand - id(" << id << "), ctl(" <<\
static_cast<void*>(hWndCtl) << "), code(" << notifyCode << ")");
if (notifyCode == 0 && GetParentO())
return GetParentO()->EvCommand(id, hWndCtl, notifyCode);
return TWindow::EvCommand(id, hWndCtl, notifyCode);
}
//
/// Forwards the left button down message to the dockable object.
//
void
TEdgeSlip::EvLButtonDown(uint modKeys, const TPoint& point)
{
TWindow* cw = GetWindowPtr(ChildWindowFromPoint(point));
// Only allow immediate children of the docking window to be clicked on.
//
if (cw && cw->GetParentO() == this) {
// Is the mouseDown in a area where we can move the docked window?
//
TPoint childPt = MapWindowPoints(*cw, point);
TDockable* d = TYPESAFE_DOWNCAST(cw, TDockable);
if (d && d->ShouldBeginDrag(childPt)) {
TPoint p = MapClientToScreen(point);
if (Harbor && Harbor->DockDraggingBegin(*d, p, Location, this)) {
return; // Successfully started
}
}
}
TWindow::EvLButtonDown(modKeys, point);
}
//
/// Handles WM_LBUTONDBLCLICK to tell the frame to edit a toolbar.
//
void
TEdgeSlip::EvLButtonDblClk(uint, const TPoint&)
{
GetParentO()->HandleMessage(WM_OWLSLIPDBLCLK);
}
//
/// Returns the size of the client area, leaving room for the etched separators.
//
uint
TEdgeSlip::EvNCCalcSize(bool calcValidRects, NCCALCSIZE_PARAMS & calcSize)
{
uint ret = TWindow::EvNCCalcSize(calcValidRects, calcSize);
if (IsIconic())
return ret;
// Only add in space if this slip is not shrunk to nothing
//
if (calcSize.rgrc[0].bottom - calcSize.rgrc[0].top > 0) {
if (!(Attr.Style & WS_BORDER)) {
if (Location != alBottom)
calcSize.rgrc[0].top += 2;
if (Location != alTop)
calcSize.rgrc[0].bottom -= 2;
}
}
return 0;
}
//
/// Erases the background and draws in etched 'borders' within the client area.
//
void TEdgeSlip::EvNCPaint(HRGN)
{
// Non-3d style
// !CQ Always do 3d etch here?
//
if (Attr.Style & WS_BORDER) {
DefaultProcessing();
}
// Use 3-d style
//
else {
TWindowDC dc(*this);
// Paint etched line along the top for left, top & right slips, and along
// the bottom for bottom, left & right slips to separate from the menubar,
// statusbar & eachother.
//
int height(GetWindowRect().Height());
if (Location != alBottom){
TRect tr(0,0,9999,2);
TUIBorder(tr, TUIBorder::EdgeEtched, TUIBorder::Top).Paint(dc);
}
if (Location != alTop){
TRect tr(0,height-2,9999,height);
TUIBorder(tr, TUIBorder::EdgeEtched, TUIBorder::Bottom).Paint(dc);
}
}
}
//
/// Erases the background and draws in etched 'borders' within the client area.
//
bool
TEdgeSlip::EvEraseBkgnd(HDC hDC)
{
TWindow::EvEraseBkgnd(hDC); // Let TWindow erase everything
// !CQ Paint etched lines for docked children?
return true;
}
//
/// Makes sure that the slip size is updated when a child changes size.
//
void
TEdgeSlip::EvParentNotify(const TParentNotify& n)
{
if (n.Event == WM_SIZE)
ReLayout(false);
else
DefaultProcessing();
}
//
//
//
class TDecorationSpan {
public:
int Left;
int Right;
int Top;
int Bottom;
TWindow* Window;
bool Moved;
TDecorationSpan(TWindow* w, TGridType);
bool operator <(const TDecorationSpan& other) const
{return Top < other.Top || (Top == other.Top && Left < other.Left);}
};
//
// Create the decoration span from the grid.
//
TDecorationSpan::TDecorationSpan(TWindow* w, TGridType gridType)
: Moved(false)
{
TRect rect = w->GetWindowRect();
::MapWindowPoints(0, w->GetParentH(), (TPoint*)&rect, 2); // map to slip coordinates
Window = w;
if (gridType == YCoord) {
Top = rect.top;
Left = rect.left;
Right = rect.right;
Bottom = rect.bottom;
}
else {
Top = rect.left;
Left = rect.top;
Right = rect.bottom;
Bottom = rect.right;
}
}
//
/// When the slip shrinks, this function adjusts dockables where needed.
//
bool
TEdgeSlip::EvWindowPosChanging(WINDOWPOS & windowPos)
{
if (!(windowPos.flags & SWP_NOSIZE)) {
CompressParallel(windowPos.cx);
}
return TWindow::EvWindowPosChanging(windowPos);
}
//
/// Compresses or expands dockables perpendicular to the grid line.
//
void
TEdgeSlip::CompressGridLines()
{
vector<TDecorationSpan> decoList = MakeDecorationList_<TDecorationSpan>(*this, GridType);
// Tile dockables perpendicular to grid lines
//
if (!decoList.empty()) {
int gridLine = decoList[0].Top;
int lastTop = gridLine - 1;
int delta = 0;
for (size_t i = 0; i < decoList.size(); ++i) {
decoList[i].Moved = false;
int t = decoList[i].Top;
if (t != lastTop) {
delta = gridLine-t;
gridLine = t;
lastTop = t;
}
if (delta != 0) {
decoList[i].Top += delta;
decoList[i].Bottom += delta;
decoList[i].Moved = true;
}
gridLine = std::max(gridLine,decoList[i].Bottom);
}
// Move all dockables that have changed location.
//
for (size_t j = 0; j < decoList.size(); ++j) {
TPoint pt;
TDecorationSpan& decoItem = decoList[j];
if (decoItem.Moved) {
if (GridType == YCoord) {
pt.x = decoItem.Left;
pt.y = decoItem.Top;
}
else {
pt.x = decoItem.Top;
pt.y = decoItem.Left;
}
decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
}
}
ReLayout(false);
}
//
/// Compresses empty space along grid line if any dockables hang out past the end.
//
void
TEdgeSlip::CompressParallel(int width)
{
// Scan each row looking for a decoration that hangs off the end.
// If possible, pull that decoration in by collapsing empty space between
// decorations on that row.
//
vector<TDecorationSpan> decoList = MakeDecorationList_<TDecorationSpan>(*this, GridType);
size_t firstOnRow = 0;
size_t lastOnRow = 0;
while (firstOnRow < decoList.size()) {
int spaceOnRow = decoList[firstOnRow].Left;
int rowTop = decoList[firstOnRow].Top;
while (lastOnRow + 1 < decoList.size() && decoList[lastOnRow + 1].Top == rowTop) {
spaceOnRow += decoList[lastOnRow+1].Left-(decoList[lastOnRow].Right+1);
lastOnRow ++;
}
int delta = decoList[lastOnRow].Right - width;
if (delta > 0 && spaceOnRow > 0) {
if (spaceOnRow > delta) {
// if there is extra empty space, collapse from the right
//
int r = width;
size_t i = lastOnRow;
while (i >= firstOnRow && (decoList[i].Right+1) > r) {
int dx = (decoList[i].Right+1) - r;
decoList[i].Left -= dx;
decoList[i].Right -= dx;
decoList[i].Moved = true;
r = decoList[i].Left;
if (i == 0) break;
i--;
}
}
else {
// if there is not extra empty space, just collapse the space
//
int r = 0;
for (size_t i = firstOnRow; i <= lastOnRow; ++i) {
if (decoList[i].Left > r) {
int dx = decoList[i].Left - r;
decoList[i].Left -= dx;
decoList[i].Right -= dx;
decoList[i].Moved = true;
r = decoList[i].Right+1;
}
else
break;
}
}
}
firstOnRow = lastOnRow+1;
}
// Move all dockables that have changed location.
//
for (size_t i = 0; i < decoList.size(); ++i) {
TPoint pt;
TDecorationSpan& decoItem = decoList[i];
if (decoItem.Moved) {
if (GridType == YCoord) {
pt.x = decoItem.Left;
pt.y = decoItem.Top;
}
else {
pt.x = decoItem.Top;
pt.y = decoItem.Left;
}
decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
}
}
//
/// Changes the child metrics of this docking window within its decorated frame.
/// Calls when the rectangle area of the docking area is different from the computed
/// area of the dockable windows within the docking area.
//
void
TEdgeSlip::ReLayout(bool forcedLayout)
{
// Has the docking area changed in size?
//
TSize area = ComputeDockingArea();
bool sizeChanged;
if (Location == alTop || Location == alBottom) {
sizeChanged = Attr.H != area.cy;
Attr.H = area.cy;
}
else {
sizeChanged = Attr.W != area.cx;
Attr.W = area.cx;
}
// If the size has change or we need to force a layout, then do so
//
if (sizeChanged || forcedLayout) {
TDecoratedFrame* df = TYPESAFE_DOWNCAST(GetParentO(), TDecoratedFrame);
CHECK(df);
// Set the same metrics just to dirty the layout plan & force a rebuild
//
TLayoutMetrics metrics;
df->GetChildLayoutMetrics(*this, metrics);
df->SetChildLayoutMetrics(*this, metrics);
df->Layout();
}
}
//
/// This function overrides TDockingSlip::DockableInsert to insert a new dockable
/// object into this TEdgeSlip. Called by mouseup handler after a dockable object is
/// dropped into this slip.
//
void
TEdgeSlip::DockableInsert(TDockable& dockable, const TPoint* topLeft,
TRelPosition position, TDockable* relDockable)
{
// Get dockable's window & hide it in case we have to toss it around a bit
// Reparent the window to the edge slip
//
TWindow* dockableWindow = dockable.GetWindow();
CHECK(dockableWindow);
dockableWindow->ShowWindow(SW_HIDE);
dockableWindow->SetParent(this);
// Let window know it was docked...
//
#ifdef UNIX
if (dockableWindow->IsWindow()) //???????????????????????
dockableWindow->HandleMessage(WM_OWLWINDOWDOCKED, TParam1(GridType == YCoord ? alTop : alLeft), TParam2( (TDockingSlip*)this) );
#else
dockableWindow->DispatchMsg(WM_OWLWINDOWDOCKED, 0, TParam1(GridType == YCoord ? alTop : alLeft),
TParam2((TDockingSlip*)this));
#endif
// Slam dockable over to 0,0 since Move assumes we own it already
//
dockableWindow->SetWindowPos(0, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
// Make sure that the dockable is oriented the right way--horizontal layout
// for Y-gridded slips
//
dockable.Layout(GridType == YCoord ? alTop : alLeft);
/// dockableWindow->Create(); // just in case? but only if we are?
DockableMove(dockable, topLeft, position, relDockable);
dockableWindow->ShowWindow(SW_SHOWNA);
}
//
/// Overriden TDockingSlip virtual. Called by mouseup handler after a drag within
/// this docking window.
//
void
TEdgeSlip::DockableMove(TDockable& dockable, const TPoint* topLeft,
TRelPosition position, TDockable* relDockable)
{
TWindow* dockableWindow = dockable.GetWindow();
TPoint dockPos;
// If no dockable topleft given, calculate based on relative dockable &
// location
//
if (topLeft) {
dockPos = *topLeft;
}
else {
// Figure out the best relative position if none given
//
if (position == rpNone) switch (Location) {
case alNone:
break;
case alTop:
position = rpBelow;
break;
case alBottom:
position = rpAbove;
break;
case alLeft:
position = rpRightOf;
break;
case alRight:
position = rpLeftOf;
break;
default: //JJH added empty default construct
break;
}
// Get the last child inserted into this docking window and use it as the
// relative window if none was given
//
TWindow* relWindow = 0;
if (relDockable)
relWindow = relDockable->GetWindow();
if (!relWindow)
relWindow = GetLastChild();
// Get its rect if it exists, or the screen coord of this slip's client area
//
TRect relWindowRect(0,0,0,0);
if (relWindow)
relWindowRect = relWindow->GetWindowRect();
else
::MapWindowPoints(0, *this, (TPoint*)&relWindowRect, 2);
TSize dockableSize = dockableWindow->GetWindowRect().Size();
switch (position) {
case rpNone:
break;
case rpAbove:
dockPos = TPoint(relWindowRect.left, relWindowRect.top-dockableSize.cy);
break;
case rpBelow:
dockPos = TPoint(relWindowRect.left, relWindowRect.bottom);
break;
case rpLeftOf:
dockPos = TPoint(relWindowRect.left-dockableSize.cx, relWindowRect.top);
break;
case rpRightOf:
dockPos = TPoint(relWindowRect.right, relWindowRect.top);
break;
default: //JJH added default construct
break;
}
}
// Find a gridline on which to put the dockable
//
int dockableEdge;
vector<TEachDecoration> GridList = MakeDecorationList_<TEachDecoration>(*this, GridType);
CHECK(!GridList.empty());
int bottomEdge = GridList[0].Top;
if (Location == alTop || Location == alBottom)
dockableEdge = dockPos.y;
else
dockableEdge = dockPos.x;
for (size_t i = 0; i < GridList.size(); ++i) {
if (dockableEdge <= (GridList[i].Top+GridList[i].Bottom)/2)
break;
if (GridList[i].Window != dockableWindow)
bottomEdge = std::max(bottomEdge,GridList[i].Bottom);
}
// Adjust perpendicular coord to newly found spot
//
TPoint newPos(dockPos);
if (Location == alTop || Location == alBottom) {
newPos.y = bottomEdge;
}
else {
newPos.x = bottomEdge;
}
// Adjust from screen coords, but make sure rect topleft is > 0
//
::MapWindowPoints(0, *this, &newPos, 1);
if (newPos.x < 0)
newPos.x = 0;
if (newPos.y < 0)
newPos.y = 0;
// Position & show the dockable window
//
dockableWindow->SetWindowPos(0, newPos.x, newPos.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
dockableWindow->ShowWindow(SW_SHOWNA);
// Adjust all windows on the grid line
//
MoveAllOthers(dockableWindow, MoveDraggedWindow(dockableWindow));
// Squash down dockable's previous area if needed
//
CompressGridLines();
}
//
/// Overriden TDockingSlip virtual. Called by mouseup handler after a dockable has
/// been removed from this docking slip.
//
void
TEdgeSlip::DockableRemoved(const TRect& /*orgRect*/)
{
CompressGridLines();
}
//
/// All terminology in this function is referenced to the following two windows
/// Drag and LeftEdge. The LeftEdge window is only valid if the Drag windows's
/// left side is intersecting the LeftEdge window's right side.
/// \code
/// ======================
/// ------------------- Drag Window |
/// | LeftEdge Window |================
/// -------------------
/// \endcode
//1 The goal of this function is to anchor down the Drag window's left side.
/// This is accomplished by one of three ways:
/// - 1. Leave the Drag window where it is; if the Drag window's left side does
/// not intersect with any other windows (no LeftEdge window).
/// - 2. Slide the Drag window to the right of the LeftEdge window being
/// intersected. This is only done if the intersection is not beyond the
/// midpoint (less than 50% of the LeftEdge window) of the LeftEdge window.
/// - 3. Slide the Drag window to the left side position of the LeftEdge window
/// (the Drag window's left side is equal to the LeftEdge window's left
/// side). The LeftEdge window is then slid to the right side of the Drag
/// window; if the intersection is beyond the mid point of the left edge
/// window (more than 50% of the LeftEdge window).
//
TWindow*
TEdgeSlip::MoveDraggedWindow(TWindow* draggedWindow)
{
// Do we want to move the draggedWindow, if so do that.
//
TRect draggedRect = draggedWindow->GetWindowRect();
TWindow* first = GetFirstChild();
if (first) {
TWindow* w = first;
do {
if (w != draggedWindow) {
TRect currRect = w->GetWindowRect();
TPoint pt;
if (Location == alTop || Location == alBottom) {
if (draggedRect.left >= currRect.left && draggedRect.left <= currRect.right &&
currRect.top == draggedRect.top) {
int midPt = (currRect.right - currRect.left) / 2;
// If the dragged window is obscuring the window underneath by more
// than 50% then the dragged window will keep its position and the
// underneath window must moved. Otherwise, the dragged window
// will move and the window underneath will move.
//
if (currRect.left + midPt > draggedRect.left) {
// Move the draggedWindow to the same left as the window
// underneath.
//
pt.x = currRect.left;
pt.y = draggedRect.top;
::MapWindowPoints(0, *this, &pt, 1);
draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
// Move the current window to the right of the dragged window.
//
draggedRect = draggedWindow->GetWindowRect();
pt.x = draggedRect.right;
pt.y = currRect.top;
::MapWindowPoints(0, *this, &pt, 1);
w->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
return w;
}
else {
// Move the dragged window to the right of the current window (w).
//
pt.x = currRect.right;
pt.y = draggedRect.top;
::MapWindowPoints(0, *this, &pt, 1);
draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
return draggedWindow;
}
}
}
else {
// Left and right docking area.
//
if (draggedRect.top >= currRect.top && draggedRect.top <= currRect.bottom &&
currRect.left == draggedRect.left) {
int midPt = (currRect.bottom - currRect.top) / 2;
// If the dragged window is obscuring the window underneath by more
// than 50% then the dragged window will keep its position and the
// underneath window must moved. Otherwise, the dragged window will
// move and the window underneath will move.
//
if (currRect.top+midPt > draggedRect.top) {
// Move the draggedWindow to the same left as the window underneath.
//
pt.x = currRect.left;
pt.y = draggedRect.top;
::MapWindowPoints(0, *this, &pt, 1);
draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
// Move the current window to the right of the dragged window.
//
draggedRect = draggedWindow->GetWindowRect();
pt.x = draggedRect.right;
pt.y = currRect.top;
::MapWindowPoints(0, *this, &pt, 1);
w->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
return w;
}
else {
// Move the dragged window to the right of the current window (w).
//
pt.x = currRect.right;
pt.y = draggedRect.top;
::MapWindowPoints(0, *this, &pt, 1);
draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
return draggedWindow;
}
}
}
}
w = w->Next();
} while (w != first);
}
return draggedWindow;
}
//
// Structure to manage the list of windows to slide.
//
struct TWindowItem {
TWindow* DockedWindow;
int LeftSide;
TWindowItem(TWindow* dockedW = 0, int left = 0) : DockedWindow(dockedW),
LeftSide(left)
{}
// operator to satisfy TIArrayAsVector
//
bool operator == (const TWindowItem& other) {
return (other.DockedWindow == DockedWindow) &&
(other.LeftSide == LeftSide);
}
};
//
/// Builds a list (sorted by left coordinate) of windows on the same horizontal
/// (top/bottom docking) or vertical (left/right docking) of windows to the right of
/// or below the dragged window. The function then slides all windows from the first
/// one intersecting the dragged window to the last one in the list.
//
void
TEdgeSlip::MoveAllOthers(TWindow* draggedWindow, TWindow* movedWindow)
{
TObjectArray<TWindowItem> windowList;
// Include the (upto) two windows which were anchored in MoveDraggedWindow.
//
TRect draggedRect = draggedWindow->GetWindowRect() | movedWindow->GetWindowRect();
TWindow* first = GetFirstChild();
if (first) {
TWindow* w = first;
do {
// If the current window is the moved window or the draggedWindow then
// grab another window.
//
if (w != draggedWindow) {
TRect currRect = w->GetWindowRect();
// Build a list of windows on the same horizontal or vertical after
// the dragged window.
// !CQ assumes top/bottom slip -- flip axis for left/right
//
if (currRect.top == draggedRect.top && currRect.left >= draggedRect.left) {
uint i = 0;
while (i < windowList.Size()) {
if (currRect.left <= windowList[i].LeftSide)
break;
i++;
}
TWindowItem wItem(w, currRect.left); // !CQ leave to debug
windowList.AddAt(wItem, i);
}
}
w = w->Next();
} while (w != first);
}
// Are there windows after the dragged window to adjust?
//
if(windowList.Size()) {
uint i = 0;
// Find the first one, all subsequent ones (plus this one) will be slid.
//
while (i < windowList.Size()) {
if (windowList[i].LeftSide >= draggedRect.left &&
windowList[i].LeftSide < draggedRect.right)
break;
i++;
}
// Slide all windows after the dragged window.
//
if (i == 0 || i != windowList.Size()) {
TWindow* w1 = draggedWindow;
TPoint pt;
while (i < windowList.Size()) {
int offset = w1->GetWindowRect().right - windowList[i].LeftSide;
pt.x = windowList[i].LeftSide + offset;
pt.y = windowList[i].DockedWindow->GetWindowRect().top;
::MapWindowPoints(0, *this, &pt, 1);
windowList[i].DockedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
w1 = windowList[i++].DockedWindow;
}
}
}
// Iterators cannot be used when modifying the underlying container
//
// TWindowItemArrayIter iter(windowList);
// while (iter) {
// TWindowItem* item = iter.Current();
// windowList.Detach(item);
// delete item;
// iter++;
// }
}
#if 0
//
// Pull every child beyond a given coord in by a given delta (positive),
// or push out every child beyond the given coord by the given delta (negative)
//
void
TEdgeSlip::Collapse(int topCoord, int delta)
{
TWindow* first = GetFirstChild();
if (first) {
TWindow* w = first;
do {
TRect rect = w->GetWindowRect();
if (Location == alTop || Location == alBottom) {
if (rect.top > topCoord) {
rect.Offset(0, -delta);
::MapWindowPoints(0, *this, (LPPOINT)&rect, 1);
w->SetWindowPos(0, rect.left, rect.top, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
}
else {
if (rect.left > topCoord) {
rect.Offset(-delta, 0);
::MapWindowPoints(0, *this, (LPPOINT)&rect, 1);
w->SetWindowPos(0, rect.left, rect.top, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
}
w = w->Next();
} while (w != first);
}
}
#endif
//
/// Computes the docking area size based on the dockables inside (total height if
/// top/bottom docking area or total width if left/right docking area). Includes the
/// non-client area etched borders or real window borders. Will return misleading
/// Size() if dockables not 0 justified.
//
TSize
TEdgeSlip::ComputeDockingArea()
{
TRect rect(0, 0, 0, 0);
TWindow* first = GetFirstChild();
if (first) {
TWindow* w = first;
do {
// Union of all visible areas.
//
if (w->GetWindowAttr().Style & WS_VISIBLE)
rect |= w->GetWindowRect();
w = w->Next();
} while (w != first);
}
// Only add in space if this slip is not shrunk to nothing
//
if (rect.bottom > rect.top && rect.right > rect.left) {
if (!(Attr.Style & WS_BORDER)) { // if 3d style, add space for the etchings
if (Location != alBottom)
rect.bottom += 2;
if (Location != alTop)
rect.bottom += 2;
}
else {
AdjustWindowRectEx(rect, Attr.Style, false, Attr.ExStyle);
}
}
return rect.Size(); // !CQ or could return rect.BottomRight()
}
//
/// Returns the perpendicular size of the grid line with the given base coordinates.
/// Returns 0 if there are no dockables on the given grid line.
//
int
TEdgeSlip::GridSize(int baseCoord)
{
TWindow* first = GetFirstChild();
int h = 0;
if (first) {
TWindow* w = first;
do {
if (w->GetWindowAttr().Style & WS_VISIBLE) {
TRect r = w->GetWindowRect();
if (GridType == YCoord) {
if (baseCoord == r.top) {
h = std::max(h, r.Height());
}
}
else {
if (baseCoord == r.left){
h = std::max(h, r.Width());
}
}
}
w = w->Next();
} while (w != first);
}
return h;
}
//
/// Retrieves the location of the slip.
//
TAbsLocation
TEdgeSlip::GetLocation() const
{
return Location;
}
DEFINE_RESPONSE_TABLE1(THarbor, TWindow)
EV_WM_MOUSEMOVE,
EV_WM_LBUTTONDBLCLK,
EV_WM_LBUTTONUP,
END_RESPONSE_TABLE;
//
/// Creates the harbor. The harbor is where the slips can go dock.
//
THarbor::THarbor(TDecoratedFrame& df)
:
TWindow(&df, _T("Harbor")),
DecFrame(df)
{
Attr.W = 0;
Attr.H = 0;
Attr.Style = WS_CHILD; // not visible
SlipTop = 0;
SlipLeft = 0;
SlipBottom = 0;
SlipRight = 0;
DragDC = 0;
DragDockable = 0;
}
//
/// Destructor.
/// Currently does nothing.
//
THarbor::~THarbor()
{
}
//
/// Creates an edge slip at a location.
//
TEdgeSlip*
THarbor::ConstructEdgeSlip(TDecoratedFrame& df, TAbsLocation location)
{
return new TEdgeSlip(df, location);
}
//
/// Constructs a floating slip with a particular window at a location.
//
TFloatingSlip*
THarbor::ConstructFloatingSlip(TDecoratedFrame& df, int x, int y, TWindow* dockableWindow)
{
return new TFloatingSlip(&df, x, y, dockableWindow);
}
//
/// Returns the slip at the location.
//
TEdgeSlip*
THarbor::GetEdgeSlip(TAbsLocation location)
{
switch (location) {
case alNone:
return 0;
case alTop:
return SlipTop;
case alBottom:
return SlipBottom;
case alLeft:
return SlipLeft;
case alRight:
return SlipRight;
default: //JJH added empty default construct
break;
}
return 0;
}
//
/// Sets a new edge slip for a given location. Also lets the edge slip know who we
/// are.
//
void
THarbor::SetEdgeSlip(TAbsLocation location, TEdgeSlip* slip)
{
slip->SetHarbor(this);
switch (location) {
case alNone:
break;
case alTop:
SlipTop = slip;
break;
case alBottom:
SlipBottom = slip;
break;
case alLeft:
SlipLeft = slip;
break;
case alRight:
SlipRight = slip;
break;
default: //JJH added empty default construct
break;
}
}
//
/// Returns the default docking relative position for a given slip location.
//
TRelPosition
THarbor::GetSlipPosition(TAbsLocation location)
{
switch (location) {
case alNone:
return rpNone;
case alTop:
return rpBelow;
case alBottom:
return rpAbove;
case alLeft:
return rpRightOf;
case alRight:
return rpLeftOf;
default: //JJH added empty default construct
break;
}
return rpNone;
}
struct TDitherBrush
#if defined(BI_MULTI_THREAD_RTL)
: public TLocalObject
#endif
{
TDitherBrush():Brush(THatch8x8Brush::Hatch11F1)
{
}
~TDitherBrush()
{
}
THatch8x8Brush Brush;
#if defined(BI_MULTI_THREAD_RTL)
// TMRSWSection Lock;
#endif
};
//
// Static instance of the colors
//
static TDitherBrush& GetDitherBrush()
{
#if defined(BI_MULTI_THREAD_RTL)
static TTlsContainer<TDitherBrush> ditherBrush;
return ditherBrush.Get();
#else
static TDitherBrush ditherBrush;
return ditherBrush;
#endif
};
//
// Draw a fast window-type frame rect
//
static int fastWindowFrame(TDC& dc, const TRect& r, bool thick, uint32 rop)
{
int thickness = thick ? TUIMetric::CxSizeFrame // a thick
: TUIMetric::CxBorder; // or a thin frame
int x = r.left;
int y = r.top;
int cx = r.right - x - thickness;
int cy = r.bottom - y - thickness;
if (thick)
dc.SelectObject(GetDitherBrush().Brush);
else
dc.SelectStockObject(WHITE_BRUSH);
dc.PatBlt(x, y, thickness, cy, rop); // Left
dc.PatBlt(x + thickness, y, cx, thickness, rop); // Top
dc.PatBlt(x, y + cy, cx, thickness, rop); // Bottom
dc.PatBlt(x + cx, y + thickness, thickness, cy, rop); // Right
return true;
}
//
/// Begins a dockable window tracking session. Returns true if started
/// satisfactorily.
//
bool
THarbor::DockDraggingBegin(TDockable& dockable, const TPoint& point,
TAbsLocation location, TDockingSlip* dragNotificatee)
{
// Get dockable to drag, its frame rectangle & the mouse anchor point
// Compute relative position of mouse from top/left corner of window.
//
DragDockable = &dockable;
DragDockable->GetRect(DragFrame);
DragAnchor = point - DragFrame.TopLeft();
// Remember the docking area & original docking area too. Also remember which
// docking slip to notify when drag is done.
//
DragOrgSlipLoc = DragSlipLoc = location;
DragNotificatee = dragNotificatee;
// Find out the sizes of the dockable in the three orientations:
// Vertical, Horizontal & Natural
//
DragHSize = DragDockable->ComputeSize(alTop, 0);
DragVSize = DragDockable->ComputeSize(alLeft, 0);
DragNSize = DragDockable->ComputeSize(alNone, 0);
// Calculate the screen relative rectangles for the four docking areas based
// on the dec frame's client area.
//
TRect frameR = DecFrame.GetClientWindow()->GetWindowRect();
/// DecFrame.MapWindowPoints(0, frameR, 2);
if (SlipTop)
SlipTR = SlipTop->GetWindowRect();
else
SlipTR = {frameR.left, frameR.top, frameR.right, frameR.top};
if (SlipBottom)
SlipBR = SlipBottom->GetWindowRect();
else
SlipBR = {frameR.left, frameR.bottom, frameR.right, frameR.bottom};
if (SlipLeft)
SlipLR = SlipLeft->GetWindowRect();
else
SlipLR = {frameR.left, frameR.top, frameR.left, frameR.bottom};
if (SlipRight)
SlipRR = SlipRight->GetWindowRect();
else
SlipRR = {frameR.right, frameR.top, frameR.right, frameR.bottom};
// Capture the mouse & create a screen DC to use while dragging
//
SetCapture();
DragDC = new TScreenDC;
// Update dragged window here to avoid asynchronous repaint uglies when
// exposed by click. Then paint frame.
//
DragDockable->GetWindow()->UpdateWindow();
fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
return true;
}
//
/// Handles MouseMove to perform dockable dragging if a drag is in progress.
//
void
THarbor::EvMouseMove(uint modKeys, const TPoint& point)
{
if (DragDockable && DragDC) {
TPoint p = MapClientToScreen(point);
// Save un-painting the frame until the end to reduce flicker
//
TRect oldDragFrame = DragFrame;
TAbsLocation oldDragSlipLoc = DragSlipLoc;
// Compute the new horz, vert & natural frames to test for touching of
// edge slips & to eventualy update DragRect. Make sure that horz & vert
// rects surround mouse, shift as needed.
//
DragFrame.MoveTo(p.x - DragAnchor.x, p.y - DragAnchor.y);
TRect hFrame(DragFrame.TopLeft(), DragHSize);
if (!hFrame.Contains(p))
hFrame.MoveTo(hFrame.left, p.y - DragHSize.cy/2);
TRect vFrame(DragFrame.TopLeft(), DragVSize);
if (!vFrame.Contains(p))
vFrame.MoveTo(p.x - DragVSize.cx/2, vFrame.top);
TRect nFrame(DragFrame.TopLeft(), DragNSize);
if (!nFrame.Contains(p))
nFrame.Offset(p.x - DragNSize.cx/2 - nFrame.left,
p.y - DragNSize.cy/2 - nFrame.top);
if (hFrame.Touches(SlipTR))
DragSlipLoc = alTop;
else if (hFrame.Touches(SlipBR))
DragSlipLoc = alBottom;
else if (vFrame.Touches(SlipLR))
DragSlipLoc = alLeft;
else if (vFrame.Touches(SlipRR))
DragSlipLoc = alRight;
else
DragSlipLoc = alNone;
// If over a docking area and it is not the same as the last one, then
// re-compute the frame rectangle
//
if (DragSlipLoc == alNone)
DragFrame = nFrame;
else if (DragSlipLoc == alTop || DragSlipLoc == alBottom)
DragFrame = hFrame;
else
DragFrame = vFrame;
/// Adjust if needed to put frame around mouse? Check each rect? At least
/// horz & vert?
///
/// if (!DragFrame.Contains(p))
/// DragFrame.Offset(p.x - DragFrame.left - 15, p.y - DragFrame.top - 15);
// Unpaint old frame, & paint new frame
//
fastWindowFrame(*DragDC, oldDragFrame, oldDragSlipLoc == alNone, PATINVERT);
fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
}
else
TWindow::EvMouseMove(modKeys, point);
}
//
/// Handles mouse up to drop a dockable window being dragged.
//
void
THarbor::EvLButtonUp(uint modKeys, const TPoint& point)
{
if (DragDockable && DragDC) {
// Unpaint frame.
//
fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
ReleaseCapture();
TRect orgDragRect;
DragDockable->GetRect(orgDragRect);
TDockable* dragDockable = DragDockable;
DragDockable = 0;
delete DragDC;
DragDC = 0;
// We're docking into a new slip--call the Move function to do the
// work.
//
if (DragOrgSlipLoc != DragSlipLoc) {
// Get the real dockable interface for the actual dockable window--
// working dockable may have been a helper.
//
TDockable* dd = TYPESAFE_DOWNCAST(dragDockable->GetWindow(), TDockable);
CHECK(dd);
Move(*dd, DragSlipLoc, &DragFrame.TopLeft());
}
// Notifiy docking slip that the dragging resulted in a dockable move.
//
else {
if (DragNotificatee)
DragNotificatee->DockableMove(*dragDockable, &DragFrame.TopLeft());
}
}
else
TWindow::EvLButtonUp(modKeys, point);
}
//
/// Handles the left button double click and forwards the message to the dockable
/// window.
//
void
THarbor::EvLButtonDblClk(uint modKeys, const TPoint& point)
{
// We get this message when a slip is in drag mode, and mouse clicks are
// captured by the harbor.
//
if (DragDockable) {
// If dragging, stop dragging and pass the message to the dragged window.
//
TWindow* win = DragDockable->GetWindow();
EvLButtonUp(modKeys, point);
win->HandleMessage(WM_LBUTTONDBLCLK, modKeys, MkParam2(point.x, point.y));
}
else {
TWindow::EvLButtonDblClk(modKeys, point);
}
}
//
/// Inserts a dockable into the appropriate docking area indicated by the given
/// location and either a point or a relative dockable and position.
//
TDockingSlip*
THarbor::Insert(TDockable& dockable, TAbsLocation location,
const TPoint* where,
TRelPosition position, TDockable* relDockable)
{
// Get the actual window associated with the dockable
//
TWindow* dw = dockable.GetWindow();
// Making the dockable Floating.
//
if (location == alNone) {
// The 'where' is relative to the decframe's client. Make absolute when
// the floatFrame is a popup
//
TPoint floatWhere;
if (where) {
floatWhere = *where;
}
else {
// Don't know where to put the floating palette, use CW_USEDEFAULT
//
floatWhere = TPoint(CW_USEDEFAULT, CW_USEDEFAULT);
}
// Call out to the virtual to contruct a hidden floating slip
//
TFloatingSlip* slip = ConstructFloatingSlip(DecFrame,
floatWhere.x, floatWhere.y,
dw);
// Give slip pointer to harbor
//
slip->SetHarbor(this);
// If decorated frame is already created, the create floating slip
//
if (DecFrame.GetHandle()) {
slip->Create();
slip->DockableInsert(dockable, where, rpNone, 0);
slip->ShowWindow(SW_SHOWNA);
} else {
// Slip will be created latter (auto-creation) - make sure it's visible
//
slip->GetWindowAttr().Style |= WS_VISIBLE;
slip->DockableInsert(dockable, where, rpNone, 0);
}
// Return floating slip
//
return slip;
}
else {
TEdgeSlip* slip = GetEdgeSlip(location);
if (!slip) {
// Make an edge slip if not one already for the given edge
//
slip = ConstructEdgeSlip(DecFrame, location);
SetEdgeSlip(location, slip);
DecFrame.Insert(*slip, (TDecoratedFrame::TLocation)location);
// If frame is created, go ahead and create slip
//
if (DecFrame.GetHandle())
slip->Create();
}
// Insert the dockable into the slip, based on args given
//
slip->DockableInsert(dockable, where, position, relDockable);
// Return edge slip
//
return slip;
}
}
//
/// Moves a dockable from one slip to another.
//
void
THarbor::Move(TDockable& dockable, TAbsLocation location, const TPoint* where,
TRelPosition position, TDockable* relDockable)
{
// Get current slip, assuming it is the parent of the dockable's window
//
TDockingSlip* previousSlip = 0;
TWindow* dockableWindow = dockable.GetWindow();
if (dockableWindow) {
previousSlip = TYPESAFE_DOWNCAST(dockableWindow->GetParentO(), TDockingSlip);
}
// If the location hasn't changes, just tweak the position within the slip,
// otherwise insert into new & remove from old.
//
if (previousSlip && previousSlip->GetLocation() == location) {
if (where)
previousSlip->DockableMove(dockable, where);
else
;// !CQ call DockableMove when it takes position & relDockable
}
else {
TRect previousRect;
dockable.GetRect(previousRect);
// Remove from the previous slip
//
Remove(dockable);
// Insert into the new slip
//
Insert(dockable, location, where, position, relDockable);
}
}
//
/// Removes a dockable from the harbor.
//
void
THarbor::Remove(TDockable& dockable)
{
// Get the actual window associated with the dockable
//
TWindow* dockableWindow = dockable.GetWindow();
if (dockableWindow) {
TRect previousRect;
dockable.GetRect(previousRect);
// Find the slip containing the dockable
//
TDockingSlip* previousSlip = TYPESAFE_DOWNCAST(dockableWindow->GetParentO(),
TDockingSlip);
// Remove the dockable from the slip and reparent it to the harbor
//
dockableWindow->ShowWindow(SW_HIDE);
dockableWindow->SetParent(this);
// Notify original docking slip that its dockable was removed.
//
if (previousSlip)
previousSlip->DockableRemoved(previousRect);
}
}
} // OWL namespace
↑ V127 An overflow of the 32-bit 'diff' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V127 An overflow of the 32-bit 'decoItem.Top' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V127 An overflow of the 32-bit 'decoItem.Bottom' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V127 An overflow of the 32-bit 'decoList[i].Top' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V127 An overflow of the 32-bit 'decoList[i].Bottom' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: DragOrgSlipLoc, DragSlipLoc, DragNotificatee.
↑ V1004 The 'dockableWindow' pointer was used unsafely after it was verified against nullptr. Check lines: 837, 846.
↑ V1004 The 'dockableWindow' pointer was used unsafely after it was verified against nullptr. Check lines: 1521, 1522.
↑ V522 There might be dereferencing of a potential null pointer 'df'.
↑ V522 There might be dereferencing of a potential null pointer 'dd'.
↑ V584 The 'diff' value is present on both sides of the '!=' operator. The expression is incorrect or it can be simplified.
↑ V1026 The 'diff' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'decoItem.Top' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'decoItem.Bottom' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'decoList[i].Top' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'decoList[i].Bottom' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.
↑ V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.
↑ V601 The 'true' value is implicitly cast to the integer type.
↑ V821 Decreased performance. The 'pt' variable can be constructed in a lower level scope.