Click here to Skip to main content
15,887,386 members
Articles / Desktop Programming / MFC

MFC/C++ Helper Class for Window Resizing

Rate me:
Please Sign up or sign in to vote.
4.91/5 (139 votes)
21 Oct 2014CPOL25 min read 660.1K   18.2K   385   249
Gives you total control when it comes to resizing windows

Introduction

In MFC, resizing or repositioning controls could be quite bothersome. If you are familiar with the .NET platform, things are much more simplified with the use of the Anchor and Dock properties of the Control class and design-time support for adding child controls to container controls. I tried to mimic some of these features of .NET, but the C++ way.

Background

There are other solutions available online (also on CodeProject.com) for this purpose. I think my solution stands out on its design, simplicity, and feature richness.

This solution allows you to do the following:

  • Anchor dialog controls
  • Create panels which contain other panels or UI controls
  • Restrict the size of dialog controls or panels by using minimum size and maximum size properties
  • Restrict the size of dialogs by using minimum size and maximum size properties
  • Create horizontal or vertical split containers
  • Freeze split containers so that users cannot use mouse to resize the panels
  • Set a panel of split containers to be fixed so that when the dialog resizes, the fixed panel will remain unchanged
  • Set the parent of dialog controls or panels (not related to Win32 SetParent API)
  • Show a resize grip on the lower right corner of the dialog using Visual Style (if the application supports it)
  • Show resize grips for splitter of split containers
  • Dock dialog control within its parent panel
  • Create flow layout panels

Let's take a brief look at how .NET does its resizing of child controls:

  • Anchor - It allows a child control to be anchored to the left, top, right, or bottom edge, or any combination of these four options.
  • Dock - It allows a child control to be docked to the left, top, right, and bottom edges.
  • Visual Studio Designer Support - If you place a frame control on a Windows Form and then drag a button control on top of the frame, the button becomes the actual child of the frame and grandchild of the Windows Form. But in the resource editor of MFC, if you place the frame on the dialog template and then drag a button on top of the frame control, the button is actually an immediate child of the dialog template and not the frame. This means if you move the frame, the button will not move.
  • SplitContainer - Creating splitter windows has never been this simple since the invention of this control. It has two panels which can host other controls inside.
  • FlowLayout - Represents a panel that dynamically lays out its contents horizontally or vertically.

So in .NET, all controls are children or grandchildren of the Form; this creates a hierarchical structure of controls. When a parent is resized or repositioned, all its children are resized or repositioned according to their Anchor or Dock property settings.

In my solution, I create a hierarchical structure of rectangles (CRect) instead. I have implemented the Anchor, Panel, SplitContainer, Dock, and FlowLayout concepts.

There are several classes in this solution, but CWndResizer is the only class that a developer will work with.

Typically, you will design your dialog template in the Visual Studio Resource Editor, and then in the dialog class implementation, you will have a member variable like this:

C++
private:
    CWndResizer m_resizer;

The samples included with this article uses the CDialog class to demonstrate many features of this class. But this class can be used with any class derived from CWnd (CDialog, CPropertyPage, CPropertySheet, CFrmWnd, CFormView, etc.).

Before this class can do anything, you must call the Hook method like this:

C++
BOOL CExample1Dlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  BOOL bOk = m_resizer.Hook(this);
  ASSERT(bOk == TRUE);
}

In this article, I will refer to this window (that is passed to the Hook method) as "hooked-window".

By calling this method, it places a window procedure hook in the WndProc chain.

When you call the Hook method, it stores the client area of the hooked-window in a structure called CPanel. A panel is mainly a rectangle area given in client coordinates of the hooked-window. A panel can have zero or more panels as children. During the creation of a panel, you assign a unique name for the panel. The name is used to refer to the panel or find a panel. The client area of the hooked-window is the root of the hierarchy, and it is named _root.

Each panel has the Anchor, MinSize, and MaxSize properties (along with some other properties). But you cannot directly set or get the properties of a panel; instead, you will use member methods of the CWndResizer class.

The idea is that when a CPanel is resized or repositioned, all its children are also resized and repositioned relatively.

Now, let's look at some code snippets.

The following code will anchor the OK and Cancel buttons of your dialog to the bottom-right corner:

C++
BOOL CExample1Dlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  BOOL bOk = m_resizer.Hook(this);
  ASSERT(bOk == TRUE);

  bOk = m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
  ASSERT(bOk == TRUE);

  bOk = m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);
  ASSERT(bOk == TRUE);
}

If you want to create a panel and set its Anchor property to ANCHOR_HORIZONTALLY, you will do this:

C++
BOOL CExample1Dlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  BOOL bOk = m_resizer.Hook(this);
  ASSERT(bOk == TRUE);
  CRect rc(40, 40, 240, 240);
  bOk = m_resizer.CreatePanel(_T("MyNewPanel"), &rc);

  bOk = m_resizer.SetAnchor(_T("MyNewPanel"), ANCHOR_HORIZONTALLY);
  ASSERT(bOk == TRUE);

  ASSERT(bOk == TRUE);
}

About Clipping (Important)

This solution cannot clip any part of a dialog control or any part of a panel. A CPanel object does not have any window (HWND) associated with it in general. If a child panel is larger than its parent panel, the child's area that is outside the parent will be visible since neither the parent panel nor the child panel is actually a window. To circumvent this issue, we should set a reasonable minimum size of the parent panel by calling the SetMinimumSize method so that the parent is never smaller than the child's minimum size. You should also consider setting the minimum size of the hooked-window as well such that it can contain all panels within its client area.

Using the Code

The sample application, created in VS2008, demonstrates many features of this class.

The CWndResizer class has the following members:

Here are the details:

Method

BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel);

BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

Description Creates a flow layout panel in the client area of the hooked-window (.NET analogy).
Parameters panelName A unique name for this panel. This name is used to refer to this panel later on.
prcPanel A rectangle given in the hooked-window's client coordinate.
parrID An array of dialog control IDs. This method creates a panel with the combined area of the dialog controls specified in this array.
setAsChildren If this is TRUE, then this method will create panels for each of the dialog controls specified in parrID and make them children of its own.
Return values TRUE Succeeded.
FALSE Failed.

CWndResizer::Hook method hasn't been called.

-or-

panelName is not unique or is NULL.

-or-

parrID contains an invalid ID.

Remarks

The FlowLayout panel arranges its contents in a horizontal or vertical flow direction. Its contents can be wrapped from one row to the next, or from one column to the next.

You can specify the flow direction by calling the value of the SetFlowDirection method.

Other panels can be a child of the FlowLayout panel. With this capability, you can construct sophisticated layouts that adapt to your dialog's dimensions at run time.

Method

BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);

BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

BOOL CreatePanel(UINT panelID);

Description Creates a panel in the client area of the hooked-window.
Parameters panelName An unique name for this panel. This name is used to refer to this panel later on.
prcPanel A rectangle given in the hooked-window's client coordinate.
parrID An array of dialog control IDs. This method creates a panel with the combined area of the dialog controls specified in this array.
setAsChildren If this is TRUE, then this method will create panels for each of the dialog controls specified in parrID and make them children of its own.
panelID An ID of the dialog control.
Return values TRUE Succeeded.
FALSE Failed.

CWndResizer::Hook method hasn't been called.

-or-

panelName is not unique or is NULL.

-or-

parrID contains an invalid ID.

-or-

panelID is invalid.

Remarks

After a panel is created, by default, it will have the following initial settings:

  • Parent name: _T("_root")
  • Anchor: ANCHOR_LEFT | ANCHOR_TOP
  • Dock: DOCK_NONE
  • MinimumSize: cx = 10, cy = 10
  • MaximumSize: cx = 100000, cy = 100000

About BOOL CreatePanel(UINT panelID):

You would typically use this overloaded method for ActiveX or OLE controls. Panels created by this overloaded method are resized/repositioned using the MoveWindow API. For other panels, CWndResizer uses BeginDeferWindowPos, DeferWindowPos, and EndDeferWindowPos to resize/reposition UI controls.

Note: CWndResizer creates panels internally for dialog controls when the dialog controls are referenced for the first time.

About BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE):

This method works as a shortcut for some techniques. Suppose you have a very simple dialog with just the OK and the Cancel button, and you want to anchor both buttons at the bottom-right corner of the dialog, then you can do any one of the three techniques below to achieve this:

Technique 1:

C++
m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);

Technique 2:

C++
CRect rcArea; // a combine area of the two buttons
CRect rcBtn; // area of a single button

GetDlgItem(IDOK)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
GetDlgItem(IDCANCEL)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
ScreenToClient(&rcArea);
// create the panel
m_resizer.CreatePanel(_T("My_OK_Cancel_Panel"), &rcArea);

// set the buttons as children of the newly created panel
m_resizer.SetParent(IDOK, _T("My_OK_Cancel_Panel") );
m_resizer.SetParent(IDCANCEL, _T("My_OK_Cancel_Panel") );

// set the anchor of the newly created panel
m_resizer.SetAnchor(_T("My_OK_Cancel_Panel"),
                    ANCHOR_RIGHT | ANCHOR_BOTTOM);

Technique 3:

C++
CUIntArray arrID;
arrID.Add(IDOK);
arrID.Add(IDCANCEL);
m_resizer.CreatePanel(_T("My_OK_Cancel_Panel"),
                      &arrID, TRUE);
m_resizer.SetAnchor(_T("My_OK_Cancel_Panel"),
                    ANCHOR_RIGHT | ANCHOR_BOTTOM);

Technique 2 and Technique 3 produce exactly the same hierarchy of panels. But Technique 3 is way shorter than Technique 2.

Method

BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, LPCTSTR panelNameB);

BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, UINT panelIDB);

BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, UINT panelIDB);

BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, LPCTSTR panelNameB);

Description Creates a split container (.NET analogy).
Parameters splitContainerName A unique name for this split container. This name is used to refer to this panel later on.
panelNameA Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer.

If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container.

panelIDA An ID of a child control (window) of the hooked-window.

If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container.

panelNameB

Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer.

If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container.

panelIDB

An ID of a child control (window) of the hooked-window.

If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container

Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

splitContainerName, panelNameA, panelIDA, panelNameB, or panelIDB is not unique or is NULL.

-or-

The specified panels (rectangles) overlapped.

-or-

One or all of the specified panels have already been used to create the split container.

Remarks

CWndResizer determines whether the split container should be horizontal or vertical.

If the right member of the first rectangle (the second parameter) is less than the left member of the second rectangle (the third parameter), this method creates a horizontal split container. Otherwise, if the bottom member of the first rectangle (the second parameter) is less than the top member of the second rectangle (the third parameter), this method creates a vertical split container.

Definition of CSplitContainer

A CSplitContainer is comprised of two CSplitPanels and one CSplitterPanel. A CSplitterPanel contains a CSplitterGripperPanel.

If you move your mouse over the space (CSpitterPanel) between the two panels (CSplitPanel) of CSplitContainer, the mouse pointer will change. Then you can press down the left mouse button and drag to resize the panels. If the parent of the panel is resized, the two panels of the splitter panel will resize proportionately.

Method

BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);

BOOL GetAnchor(UINT panelID, UINT & anchor);

Description Gets the anchor of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
anchor Receive the anchor value.
Return values TRUE Succeeded. anchor contains a valid value.
FALSE

Failed. anchor value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks Retrieves the anchor value of a panel. See SetAnchor for details.
Method

BOOL GetAutoHandlePaint();

Description Gets whether or not paint should be automatically handled by CWndResizer.
Return values TRUE Succeeded. anchor contains a valid value.
FALSE

Failed. anchor value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks Please see SetAutoHandlePaint for details.
Method

BOOL GetDock(LPCTSTR panelName, UINT & uDock);

BOOL GetDock(UINT panelID, UINT & uDock);

Description Gets the anchor of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
uDock Receive the dock value.
Return values TRUE Succeeded. anchor contains a valid value.
FALSE

Failed. anchor value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks Retrieves the dock value of a panel. See SetDock for details.
Method BOOL GetFixedPanel(LPCTSTR splitContainer, short & panel);
Description Gets the name of the panel that is fixed (if any) (.NET analogy).
Parameters splitContainerName Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer.
panel Receives the ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2, then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel.
Return values TRUE Succeeded. panel contains a valid value.
FALSE

Failed. panel value should be ignored.

The CWndResizer::Hook method hasn't been called.

-or-

splitContainerName is not valid.

Remarks Retrieves the fixed panel ID of a a split container. See SetSetFixedPanel for details.
Method BOOL GetFlowDirection(LPCTSTR flowPanelName, short & direction);
Description Gets the flow direction of the FlowPanel (.NET analogy).
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
direction Receives the flow direction of the FlowPanel.
Return values TRUE Succeeded. direction contains a valid value.
FALSE

Failed. direction value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks See SetFlowDirection for details.
Method BOOL GetFlowItemSpacingX(LPCTSTR flowPanelName, int & nSpace);
Description Gets the spacing between items in x-direction in the FlowPanel.
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
nSpace Receives the space in pixels.
Return values TRUE Succeeded. panel contains a valid value.
FALSE

Failed. The nSpace value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks See SetFlowItemSpacingX for details.
Method BOOL GetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace);
Description Gets the spacing between items in y-direction in the FlowPanel.
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
nSpace Receives the space in pixels.
Return values TRUE Succeeded. panel contains a valid value.
FALSE

Failed. nSpace value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks See SetFlowItemSpacingY for details.
Method BOOL GetIsSplitterFixed(LPCTSTR splitContainerName , BOOL &fixed);
Description Gets whether or not the splitter is set to fixed for a split container (.NET analogy).
Parameters splitContainerName Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer.
fixed Receives a boolean value. If this is TRUE, then the splitter is fixed; otherwise, the splitter is free to be moved by mouse.
Return values TRUE Succeeded. fixed contains a valid value.
FALSE

Failed. The fixed value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

splitContainerName is not valid.

Remarks Retrieves a flag that indicates whether or not the splitter is free to be moved by mouse for a split container. See SetIsSplitterFixed for details.
Method

BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;

BOOL GetMaximumSize(UINT panelID, CSize & size) ;

Description Gets the maximum size of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
Return values TRUE Succeeded. size contains the valid value.
FALSE

Failed. The size value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks If panelName or panelID refers to a panel that is part of the horizontal split container, then the CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of the vertical split container, then the CSize::cx member should be ignored.
Method

BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;

BOOL GetMinimumSize(UINT panelID, CSize & size) ;

Description Gets the minimum size of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
Return values TRUE Succeeded. size contains the valid value.
FALSE Failed. The size value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks If panelName or panelID refers to a panel that is part of a horizontal split container, then the CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the CSize::cx member should be ignored.
Method

BOOL GetParent(LPCTSTR panelName, CString & parentName);

BOOL GetParent(UINT panelID, CString & parentName);

Description Gets the name of the parent of the specified panel.
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
parentName Receives the name of the parent.
Return values TRUE Succeeded. parentName contains a valid value.
FALSE

Failed. The size value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks If parentName is _root, it indicates that the panel is an immediate child of the hooked-window.
Method BOOL GetShowResizeGrip();
Description Gets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window.
Parameters N/A No parameters.
Return values TRUE It will draw a resize grip at the bottom-right corner of the dialog/window.
FALSE It will not draw a resize grip at the bottom-right corner of the dialog/window.
Remarks This method never fails.
Method BOOL GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL & bShow);
Description Gets whether or not the splitter grip is visible for a split container.
Parameters splitContainerName Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer.
bShow Receives a boolean value. If this is TRUE, then the splitter grip is visible, otherwise the splitter grip is hidden.
Return values TRUE Succeeded.
FALSE

Failed. The bShow value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

splitContainerName is not valid.

Remarks N/A
Method BOOL Hook(CWnd * pWnd);
Description Places a hook into the WndProc chain.
Parameters pWnd A pointer to a CWnd that you want to resize and its child controls.
Return values TRUE Succeeded.
FALSE

Failed.

pWnd is not valid.

-or-

This method already has been called once.

Remarks

You must call this method before calling any other method.

The window that the pWnd points to is called the "hooked-window".

Method BOOL InvokeOnResized();
Description Simulates resizing of the hooked-window.
Parameters N/A No parameters.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

Remarks

This method sends a WM_SIZE message to the hooked-window.

After creating the necessary panels and settings anchors, you would want to call this to apply the settings initially.

Method

BOOL SetAnchor(LPCTSTR panelName, UINT & anchor);

BOOL SetAnchor(UINT panelID, UINT & anchor);

Description Sets the anchor of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
anchor Anchor value. See remarks.
Return values TRUE Succeeded. anchor contains the valid value.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks

anchor could be one or more of the following:

Anchor values Description
ANCHOR_LEFT Anchors the left edge of the panel to its parent. This is the default for a panel.
ANCHOR_TOP Anchors the top edge of the panel to its parent. This is the default for a panel.
ANCHOR_RIGHT Anchors the right edge of the panel to its parent.
ANCHOR_BOTTOM Anchors the bottom edge of the panel to its parent.
ANCHOR_CENTER_HORIZONTALLY The panel is horizontally centered within the parent rectangle. It has higher priority than ANCHOR_ALL. .NET does not have similar support.
ANCHOR_CENTER_VERTICALLY The panel is vertically centered within the parent rectangle. It has higher priority than ANCHOR_ALL. .NET does not have similar support.
ANCHOR_PRIORITY_RIGHT This is only useful if used with ANCHOR_HORIZONTALLY. If the width of the panel grows bigger than the maximum width allowed, by default, the panel's left edge will be anchored and right edge will be freed to adjust to the maximum size. If this is specified, then the right edge will be anchored and the left edge will be freed to adjust to the maximum size. .NET does not have similar support.
ANCHOR_PRIORITY_BOTTOM This is only useful if used with ANCHOR_VERTICALLY. If the height of the panel grows bigger than the maximum height allowed, by default, the panel's top edge will be anchored and bottom edge will be freed to adjust to the maximum size. If this is specified, then the bottom edge will be anchored and the top edge will be freed to adjust to the maximum size. .NET does not have similar support.
ANCHOR_VERTICALLY (ANCHOR_TOP | ANCHOR_BOTTOM)
ANCHOR_HORIZONTALLY (ANCHOR_LEFT | ANCHOR_RIGHT)
ANCHOR_ALL (ANCHOR_VERTICALLY | ANCHOR_HORIZONTALLY)

Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size.

Method

BOOL SetAutoHandlePaint(BOOL bHandle);

Description Sets whether or not paint should be automatically handled by CWndResizer.
Parameters bHandle TRUE if CWndResizer is to automatically handle WM_PAINT message. Otherwise FALSE.
Return values TRUE Always returns TRUE
FALSE Never returns FALSE.
Remarks Here is an example:
C++
BOOL CTestDlg::OnInitDialog()
{
  CDialog::OnInitDialog();

  BOOL bOk = FALSE;
  bOk = m_resizer.Hook(this);
  ASSERT( bOk);

  // turning off auto paint handle
  m_resizer.SetAutoHandlePaint(FALSE); 
  m_resizer.SetShowResizeGrip(TRUE);

  bOk = m_resizer.InvokeOnResized();
  ASSERT( bOk);

  return TRUE;
}

void CTestDlg::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // Your custom drawing
    CRect rect(10, 20, 60, 60);
    CBrush brush(RGB(0,0,0));
    dc.FillRect(rect, &brush);
    // call CWndResizer::Draw method so it can draw
    // any classes derived from CVisualPanel
    m_resizer.Draw(&dc);  // <= calling Draw method
}
Method

BOOL SetDock(LPCTSTR panelName, UINT & uDock);

BOOL SetDock(UINT panelID, UINT & uDock);

Description Sets the dock of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
uDock Dock value. See remarks.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks

uDock could be one of the following:

Dock values Description
DOCK_NONE Panel will not be docked. This is the default for a panel.
DOCK_LEFT Panel will be docked on the left edge of its parent.
DOCK_TOP Panel will be docked on the top edge of its parent.
DOCK_RIGHT Panel will be docked on the right edge of its parent.
DOCK_BOTTOM Panel will be docked on the bottom edge of its parent.
Method BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel);
Description Sets one of the panels of a split container to be fixed (.NET analogy).
Parameters splitContainerName Name of a panel that was created by a prior call to CWndResizer::CreateSplitContainer.
panel ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2, then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel.
Return values TRUE Succeeded.
FALSE Failed.

CWndResizer::Hook method hasn't been called.

Remarks

As the hooked-window is resized, the fixed panel remains the same and the other panel resizes.

Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size.

Method BOOL SetFlowDirection(LPCTSTR flowPanelName, short direction);
Description Sets the flow direction of the FlowPanel (.NET analogy).
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
direction Flow direction of the FlowPanel.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks Set direction to 1 to indicate left-to-right direction, any other value will indicate top-to-bottom direction.
Method BOOL SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpace);
Description Sets spacing between items in x-direction in FlowPanel.
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
nSpace Space in pixels.
Return values TRUE Succeeded.
FALSE

Failed. The nSpace value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks If the flow direction is left-to-right, nSpace indicates the space between two items in the x-direction, otherwise it indicates the space between two columns of items.
Method BOOL SetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace);
Description Gets the spacing between items in y-direction in the FlowPanel.
Parameters flowPanelName Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel.
nSpace Space in pixels.
Return values TRUE Succeeded.
FALSE

Failed. nSpace value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

flowPanelName is not valid.

Remarks If the flow direction is left-to-right, nSpace indicates the space between two items in the y-direction, otherwise it indicates the space between two rows of items.
Method BOOL SetIsSplitterFixed(LPCTSTR splitContainerName , BOOL fixed);
Description Sets whether or not the splitter is set to fixed for a split container (.NET analogy).
Parameters splitContainerName Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer.
fixed A boolean value. If this is TRUE, then the splitter is fixed, otherwise the splitter is free to be moved by mouse.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

Remarks If the splitter is set to fixed, user cannot move the splitter using mouse.
Method

BOOL SetMaximumSize(LPCTSTR panelName, CSize & size) ;

BOOL SetMaximumSize(UINT panelID, CSize & size) ;

Description Sets the maximum size of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
size Minimum size of the panel.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

size is smaller than CWndResizer::GetMinimumSize.

Remarks If panelName or panelID refers to a panel that is part of a horizontal split container, then the cy member of size should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the cx member of size should be ignored.
Method

<code>BOOL SetMinimumSize(LPCTSTR panelName, CSize & size);

BOOL SetMinimumSize(UINT panelID, CSize & size) ;

Description Sets the minimum size of the specified panel (.NET analogy).
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

-or-

panelName or panelID is not valid.

Remarks If panelName or panelID refers to a panel that is part of a horizontal split container, then the cy member of size should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the cx member of size should be ignored.
Method

BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);

BOOL SetParent(UINT panelID, LPCTSTR parentName);

BOOL SetParent(LPCTSTR panelName, UINT parentID);

BOOL SetParent(UINT panelID, UINT parentID);

Description Sets the name of the parent of the specified panel.
Parameters panelName Name of a panel that was created by a prior call to CWndResizer::CreatePanel.
panelID An ID of a child control (window) of the hooked-window.
parentID Name of the parent.
parentName Name of the parent.
Return values TRUE Succeeded. parentName contains a valid value.
FALSE Failed. The size value should be ignored.

CWndResizer::Hook method hasn't been called.

-or-

panelName, panelID, parentName, or parentName is not valid.

Remarks

This should not be thought of as the Win32 SetParent API.

Set parentName to _root to indicate that the panel is an immediate child of the hooked-window.

Method void SetShowResizeGrip(BOOL show = TRUE);
Description Sets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window.
Parameters show TRUE if the resize grip should be drawn, otherwise FALSE.
Return values N/A No return values.
Remarks This method never fails.
Method BOOL SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow);
Description Sets a flag indication whether to show or hide a gripper for the splitter of a split container.
Parameters splitContainerName Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer.
bShow TRUE if the resize grip should be drawn, otherwise hidden.
Return values TRUE Succeeded.
FALSE Failed.

CWndResizer::Hook method hasn't been called.

-or-

splitContainerName is invalid.

Remarks This method never fails.
Method BOOL Unhook();
Parameters N/A No parameters.
Return values TRUE Succeeded.
FALSE

Failed.

CWndResizer::Hook method hasn't been called.

Remarks

You do not have to call this method.

This method is atomically called when the hooked-window receives a WM_DESTROY message.

Points of Interest

This solution does not require you to calculate any numbers on your part. This solution encapsulates all the functionalities in the CWndResizer.

One can re-design it such that it exposes other objects (CPanel, CSplitContainer, etc.). The reason I designed it this way is because I did not want the user to have to know too many objects, and to simplify memory allocation and de-allocation.

Another thing is that it depends on MFC classes (CWnd etc.); again, you can remove all references to MFC classes and use pure Win32 APIs.

History

  • 13 Jan 2011: Article updated to include new features, Docking and FlowLayoutPanel
  • 25 Feb 2011: CWndResizer::UnHook method changed as per member 2272507's comments
  • 16 Mar 2011: Updated demo and source code
  • 23 May 2011: Updated demo and source code
  • 1 Aug 2011: Updated demo and source code
  • 17 Aug 2011: Updated demo and source code - now supports scrolling on CFormView
  • 7 Dec 2011: Updated demo and source code
  • 25 Jan 2013: New example added (Example 11) and a bug fixed
  • 28 Jun 2013: Updated source code
  • 9 Nov 2013: Error in Example 9 was fixed after reported by users
  • 20 Oct 2014: Added three new methods: Draw(CPaintDC * pDC), GetAutoHandlePaint() and SetAutoHandlePaint(BOOL bHandle). These help when you have your custom painting. See documentation above for details.
  • 20 Oct 2014: Reported bugs fixed

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: When I iinclude WndResizer.cpp and WndResizer.h in my VC2003 project, I can't compile it. Pin
Mizan Rahman8-Jul-12 7:38
Mizan Rahman8-Jul-12 7:38 
QuestionSupport for hiding/showing controls Pin
mskalski31-May-12 5:57
mskalski31-May-12 5:57 
AnswerRe: Support for hiding/showing controls Pin
Mizan Rahman31-May-12 22:21
Mizan Rahman31-May-12 22:21 
GeneralRe: Support for hiding/showing controls Pin
mskalski1-Jun-12 0:51
mskalski1-Jun-12 0:51 
GeneralRe: Support for hiding/showing controls Pin
Mizan Rahman4-Jun-12 2:38
Mizan Rahman4-Jun-12 2:38 
GeneralRe: Support for hiding/showing controls Pin
mskalski4-Jun-12 3:51
mskalski4-Jun-12 3:51 
GeneralRe: Support for hiding/showing controls Pin
MaxMax146-Aug-13 9:21
MaxMax146-Aug-13 9:21 
GeneralRe: Support for hiding/showing controls Pin
mskalski10-Sep-14 3:40
mskalski10-Sep-14 3:40 
Sorry, I didn't notice question. I hardly had time for looking to codeproject Smile | :)

If you still need this info:

CPanel::PtInTriangle() has default second argument set to ANCHOR_BOTTOMRIGHT:

C++
CPanel::PtInTriangle( CPoint point, UINT nEdge = ANCHOR_BOTTOMRIGHT) const;


and ResizeGripperPanelName was defined as used before but declared as public static member, along with RootPanelName.


C++
class WndResizer
{
public:
	static const TCHAR ResizeGripperPanelName[];
	static const TCHAR RootPanelName[];
...
};

and in WndResizer.cpp:

C++
const TCHAR CWndResizer::ResizeGripperPanelName[] = _T("_resizeGrip");
const TCHAR CWndResizer::RootPanelName[] = _T("_root");


and below is full implementation i case someone will need it.

WndResizer.h

C++
/*
DISCLAIMER

Auther: Mizan Rahman
Original publication location: http://www.codeproject.com/KB/dialog/WndResizer.aspx

This work is provided under the terms and condition described in The Code Project Open License (CPOL)
(http://www.codeproject.com/info/cpol10.aspx)

This disclaimer should not be removed and should exist in any reproduction of this work.
*/

#pragma once

#define ANCHOR_LEFT                   1
#define ANCHOR_TOP                    2
#define ANCHOR_RIGHT                  4
#define ANCHOR_BOTTOM                 8
#define ANCHOR_HORIZONTALLY_CENTERED  16
#define ANCHOR_VERTICALLY_CENTERED    32
#define ANCHOR_PRIORITY_RIGHT         64   // by defualt, left has higher priority
#define ANCHOR_PRIORITY_BOTTOM        128  // by defualt, top has higher priority

#define ANCHOR_IGNORE                 256  // if set, panel is ignored in layout computations

#define ANCHOR_TOPLEFT                (ANCHOR_LEFT | ANCHOR_TOP)
#define ANCHOR_TOPRIGHT               (ANCHOR_RIGHT | ANCHOR_TOP)
#define ANCHOR_BOTTOMLEFT             (ANCHOR_LEFT | ANCHOR_BOTTOM)
#define ANCHOR_BOTTOMRIGHT            (ANCHOR_RIGHT | ANCHOR_BOTTOM)

#define ANCHOR_VERTICALLY             (ANCHOR_TOP | ANCHOR_BOTTOM)
#define ANCHOR_HORIZONTALLY           (ANCHOR_LEFT | ANCHOR_RIGHT)

#define ANCHOR_ALL                    (ANCHOR_VERTICALLY | ANCHOR_HORIZONTALLY)

#define DOCK_NONE   0
#define DOCK_LEFT   1
#define DOCK_TOP    2
#define DOCK_RIGHT  3
#define DOCK_BOTTOM 4
#define DOCK_FILL   5

class CPanel;
class CUIPanel;

typedef CList<CPanel *, CPanel *> CPanelList;

class CPanel : public CRect
{
public:
	CPanel();
	CPanel(const CRect * prc);
	virtual ~CPanel();

	UINT Anchor;
	UINT Dock;
	CString Name;
	CPanel * Parent;
	CPanelList Children;
	int LeftOffset;
	int TopOffset;
	int RightOffset;
	int BottomOffset; 
	CSize MinSize;
	CSize MaxSize;
	BOOL m_bVisible;

	virtual void OnResized();
	virtual BOOL AddChild(CPanel * prc);
	virtual BOOL RemoveChild(CPanel * prc);
	virtual BOOL SetAnchor(UINT anchor);
	virtual BOOL SetMinSize(CSize & size);
	virtual BOOL SetMaxSize(CSize & size);
	virtual BOOL SetOffset(UINT anchor, int offset);
	virtual BOOL SetVisible(BOOL bVisible);
	virtual BOOL GetVisible(BOOL &bVisible);
	virtual BOOL IsVisible();
	
	virtual CString ToString();
	virtual CString GetTypeName();
	virtual CWnd * GetHookedWnd();

	BOOL PtInTriangle( CPoint point, UINT nEdge = ANCHOR_BOTTOMRIGHT ) const;
	static BOOL PtInTriangle( CPoint point, CPoint v1, CPoint v2, CPoint v3 );

private:
	void Init();
};

class CRootPanel : public CPanel
{
public:
	CRootPanel();
	~CRootPanel();
	virtual CWnd * GetHookedWnd();
	CWnd * m_pHookWnd;
	virtual CString GetTypeName();
}; 

class CWndResizer 
{
public:
	static const TCHAR ResizeGripperPanelName[];
	static const TCHAR RootPanelName[];

	enum SplitterOrientation 
	{
		// a horizontal splitter container panel
		SPLIT_CONTAINER_H = 1,
		// a vertical splitter contianer panel
		SPLIT_CONTAINER_V = 2, 
	};

	enum FlowDirection 
	{
		// left to right flow direction
		LEFT_TO_RIGHT = 3,
		// top to bottom flow direction
		TOP_TO_BOTTOM = 4, 
	};

	enum FixedPanel
	{
		FIXED_NONE = 0,
		FIXED_TOPLEFT = 1,
		FIXED_BOTTOMRIGHT = 2,
		FIXED_TOPLEFT_HIDDEN = 3,
		FIXED_BOTTOMRIGHT_HIDDEN = 4,

		// aliases
		FIXED_LEFT = FIXED_TOPLEFT,
		FIXED_TOP = FIXED_TOPLEFT,
		FIXED_RIGHT = FIXED_BOTTOMRIGHT,
		FIXED_BOTTOM = FIXED_BOTTOMRIGHT,
		FIXED_LEFT_HIDDEN = FIXED_TOPLEFT_HIDDEN,
		FIXED_TOP_HIDDEN = FIXED_TOPLEFT_HIDDEN,
		FIXED_RIGHT_HIDDEN = FIXED_BOTTOMRIGHT_HIDDEN,
		FIXED_BOTTOM_HIDDEN = FIXED_BOTTOMRIGHT_HIDDEN,
	};

	enum ResizeGrip
	{
		RESIZE_GRIP_HIDDEN = 0,
		RESIZE_GRIP_VISIBLE = 1,
		RESIZE_GRIP_VISIBLE_ACTIVE_RECT = 2,
		RESIZE_GRIP_VISIBLE_ACTIVE_TRIANGLE = 2
	};

public:
	CWndResizer(void);
	~CWndResizer(void);

	BOOL Hook(CWnd * pWnd);
	BOOL Hook(CWnd * pWnd, CSize & size);
	BOOL Unhook();

	BOOL CreatePanel(UINT uID);

	BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);
	/// Last of parrID should be 0.
	BOOL CreatePanel(LPCTSTR panelName, const UINT * parrID, BOOL setAsChildren = FALSE);
	BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);

	BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, LPCTSTR panelNameB);		
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, UINT panelIDB);	
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, UINT panelIDB);	
	BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, LPCTSTR panelNameB); 

	BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);
	/// Last of parrID should be 0.
	BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const UINT * parrID, BOOL setAsChildren = FALSE);
	BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel);

	BOOL SetAnchor(LPCTSTR panelName, UINT anchor);
	BOOL SetAnchor(UINT panelID, UINT anchor);
	BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);
	BOOL GetAnchor(UINT panelID, UINT & anchor);

	BOOL SetOffset(LPCTSTR panelName, UINT anchor, int offset);
	BOOL SetOffset(UINT panelID, UINT anchor, int offset);

	BOOL SetDock(LPCTSTR panelName, UINT anchor);
	BOOL SetDock(UINT panelID, UINT anchor);
	BOOL GetDock(LPCTSTR panelName, UINT & anchor);
	BOOL GetDock(UINT panelID, UINT & anchor);

	BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);
	BOOL SetParent(UINT panelID, LPCTSTR parentName);
	BOOL SetParent(LPCTSTR panelName, UINT parentID);
	BOOL SetParent(UINT panelID, UINT parentID);
	BOOL GetParent(LPCTSTR panelName, CString & parentName);
	BOOL GetParent(UINT panelID, CString & parentName);

	BOOL SetMinimumSize(LPCTSTR panelName, CSize & size);
	BOOL SetMinimumSize(UINT panelID, CSize & size);
	BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;
	BOOL GetMinimumSize(UINT panelID, CSize & size) ;

	BOOL SetMaximumSize(LPCTSTR panelName, CSize & size);
	BOOL SetMaximumSize(UINT panelID, CSize & size);
	BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;
	BOOL GetMaximumSize(UINT panelID, CSize & size) ;

	BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel);
	BOOL GetFixedPanel(LPCTSTR splitContainerName, short & panel);

	BOOL SetIsSplitterFixed(LPCTSTR splitContainerName, BOOL fixed);
	BOOL GetIsSplitterFixed(LPCTSTR splitContainerName, BOOL &fixed);

	BOOL SetFlowDirection(LPCTSTR flowPanelName, short direction);
	BOOL GetFlowDirection(LPCTSTR flowPanelName, short &direction);

	BOOL SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpacing);
	BOOL GetFlowItemSpacingX(LPCTSTR flowPanelName, int &nSpacing);

	BOOL SetFlowItemSpacingY(LPCTSTR flowPanelName, int nSpacing);
	BOOL GetFlowItemSpacingY(LPCTSTR flowPanelName, int &nSpacing);

	BOOL SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow);
	BOOL GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL & bShow);

	void SetShowResizeGrip(ResizeGrip show = RESIZE_GRIP_VISIBLE_ACTIVE_TRIANGLE);
	void SetShowResizeGrip(BOOL show);
	ResizeGrip GetShowResizeGrip();

	// useful for persisting UI layout
	BOOL SetSplitterPosition(LPCTSTR splitContainerName, UINT position);	
	BOOL GetSplitterPosition(LPCTSTR splitContainerName, UINT & position);	

	BOOL InvokeOnResized();
	
	CString GetDebugInfo();
	
private:
	CWnd * m_pHookedWnd;
	CRootPanel root;
	CPanel * m_pCaptured;
	HCURSOR m_hOldCursor;
	int m_splitterOffset;	

	CUIPanel * CreateUIPanel(UINT uID);
	CUIPanel * GetUIPanel(UINT uID);
	CString IdToName(UINT uID);

	CPanel * FindPanelByName(CPanel * pRoot, LPCTSTR name);
	CPanel * FindSplitterFromPoint(CPanel * pRoot, CPoint point);
	void GetUIPanels(CPanel * pRoot, CPanelList * pList, BOOL bOle);
	void GetVisualPanels(CPanel * pRoot, CPanelList * pList);

	void ResizeUI(CPanel * pRoot);

	void GetTrueClientRect(CWnd * pWnd, CRect * prc);
	void EnsureRootMinMax();
	void UpdateSplitterOffset(CPoint ptCurr);

	void GetDebugInfo(CPanel * pRoot, CString & info, CString indent);

	void OnPaint();
	void OnSize(UINT nType, int cx, int cy);
	void OnSizing(UINT fwSide, LPRECT pRect);
	void OnScroll();
	void OnDestroy();
	void OnLButtonDown(UINT nFlags, CPoint point);
	void OnMouseMove(UINT nFlags, CPoint point);
	void OnLButtonUp(UINT nFlags, CPoint point);
	LRESULT OnNcHitTest( CPoint point ); // return HTNOWHERE to pass to default processing
	void OnThemeChanged();

	WNDPROC m_pfnWndProc;
	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};


WndResizer.cpp

C++
/*
DISCLAIMER

Auther: Mizan Rahman
Original publication location: http://www.codeproject.com/KB/dialog/WndResizer.aspx

This work is provided under the terms and condition described in The Code Project Open License (CPOL)
(http://www.codeproject.com/info/cpol10.aspx)

This disclaimer should not be removed and should exist in any reproduction of this work.
*/

#include "StdAfx.h"
#include "WndResizer.h"

#include "uxtheme.h"
#pragma comment(lib, "UxTheme.lib")


static CMap<HWND, HWND, CWndResizer *, CWndResizer *> WndResizerData;

class CUIPanel : public CPanel
{
public:
	CUIPanel(const CRect * prc, UINT uID);
	~CUIPanel();
	UINT m_uID;   // could be a resource ID or a HWND
	BOOL m_bOle;  // TRUE= m_uID is an ID of an ActiveX or OLE control, FALSE=regular windows control
	virtual BOOL SetVisible(BOOL bVisible);
	virtual BOOL GetVisible(BOOL &bVisible);
	virtual BOOL IsVisible();
	virtual CString GetTypeName();
};


class CVisualPanel : public CPanel
{
public:
	CVisualPanel();
	CVisualPanel(const CRect * prc);
	~CVisualPanel();
	virtual void Draw(CDC * pDC);
	virtual CString GetTypeName();
	virtual void OnResized();
private:
	CRect m_rcPrev;
};

class CGripperPanel : public CVisualPanel
{
public:
	CGripperPanel();
	CGripperPanel(const CRect * prc);
	~CGripperPanel();
	virtual void Draw(CDC * pDC);
	virtual CString GetTypeName();
 private:
	HTHEME m_hTheme;
};

class CSplitterGripperPanel : public CVisualPanel
{
public:
	CSplitterGripperPanel(CWndResizer::SplitterOrientation type);
	~CSplitterGripperPanel();
	virtual void Draw(CDC * pDC);
	virtual CString GetTypeName();
private:
	CWndResizer::SplitterOrientation m_OrienType;
};

#ifdef USE_SPLIT_PANEL
class CSplitPanel : public CPanel
{
public:
	CSplitPanel(CPanel * pPanel);
	~CSplitPanel();
	virtual BOOL SetAnchor(UINT anchor);
	virtual BOOL AddChild(CPanel * prc);
	virtual BOOL RemoveChild(CPanel * prc);
	virtual CString GetTypeName();
	virtual BOOL SetMinSize(CSize & size);
	virtual BOOL SetMaxSize(CSize & size);
private:
	CPanel * m_pOriginalPanel;
};
#else
typedef CPanel CSplitPanel;
#endif

class CSpitterPanel : public CPanel
{
public:
	CSpitterPanel(CWndResizer::SplitterOrientation type);
	CSpitterPanel(const CRect * prc, CWndResizer::SplitterOrientation type);
	~CSpitterPanel();
	virtual CString GetTypeName();
	CSplitterGripperPanel * m_pGrippePanel;
private:
	CWndResizer::SplitterOrientation m_OrienType;
};

class CSplitContainer : public CPanel
{
public:
	CSplitContainer(CSplitPanel * pPanelA, CSplitPanel * pPanelB, CWndResizer::SplitterOrientation type);
	~CSplitContainer();
	virtual void OnResized();
	virtual BOOL AddChild(CPanel * prc);
	virtual BOOL RemoveChild(CPanel * prc);
	virtual CString GetTypeName();
	void SetSplitterPosition(int leftOfSpliter /* or topOfSpliter if vertical */);
	int GetSplitterPosition();
	void SetFixedPanel(short nFixedPanel /* 1=left or top; 2=right or bototm; other=no fixed panel */);
	short GetFixedPanel();
	void SetIsSplitterFixed(BOOL bFixed);
	BOOL GetIsSplitterFixed();
	void SetShowSplitterGrip(BOOL bShow);
	BOOL GetShowSplitterGrip();

	static CSplitContainer * Create(CPanel * pPanelA, CPanel * pPanelB);
	
	CWndResizer::SplitterOrientation m_Orientation;
private:
	BOOL m_IsSplitterFixed;
	short m_FixedPanel; // 1=left or top panel; 2=right or bottom panel, otherwise no fixed panel

	double m_nRatio;
	int m_nSplitterSize; // for horizontal, it is the splitter width, otherwise it is the splitter height
	void GetSplitArea(CRect * pSplitterPanel);
	int GetSplitterSize(CPanel * pLeftPanel, CPanel * pRightPanel);
	void UpdateRatio();

	CSplitPanel * m_pPanelA;
	CSplitPanel * m_pPanelB;
	CSpitterPanel * m_pSplitter;
};


class CFlowLayoutPanel : public CPanel
{
public:
	CFlowLayoutPanel();
	CFlowLayoutPanel(const CRect * prc);
	~CFlowLayoutPanel();
	virtual void OnResized();
	virtual CString GetTypeName();
	void SetFlowDirection(CWndResizer::FlowDirection direction);
	CWndResizer::FlowDirection GetFlowDirection();
	void SetItemSpacingX(int nSpace);
	int GetItemSpacingX();
	void SetItemSpacingY(int nSpace);
	int GetItemSpacingY();

private:
	int m_nItemSpacingX;
	int m_nItemSpacingY;
	CWndResizer::FlowDirection m_nFlowDirection;
};


/////////////////////////////  CWndResizer
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const TCHAR CWndResizer::ResizeGripperPanelName[] = _T("_resizeGrip");
const TCHAR CWndResizer::RootPanelName[] = _T("_root");

CWndResizer::CWndResizer(void)
{
	m_pHookedWnd = NULL;
	m_pfnWndProc = NULL;
	root.Name = RootPanelName;
	m_pCaptured = NULL;
	m_hOldCursor = NULL;
	m_splitterOffset = 0;
}

CWndResizer::~CWndResizer(void)
{
}

static BOOL CALLBACK MakeGroupBoxesTransparent(HWND hwnd, LPARAM) 
{
	TCHAR className[128] = _T("");
	int iRet = GetClassName(hwnd, className, ARRAYSIZE(className) - 1);

	LONG style = GetWindowLong(hwnd, GWL_STYLE);

	if ( 0 == _tcscmp(className, _T("Button")) && BS_GROUPBOX == (style & BS_GROUPBOX) ){
		CWnd::ModifyStyleEx(hwnd, NULL, WS_EX_TRANSPARENT, NULL);
	}
	return TRUE;
}

BOOL CWndResizer::Hook(CWnd * pParent)
{
	ASSERT( m_pHookedWnd == NULL );

	m_pHookedWnd = pParent;

	GetTrueClientRect(m_pHookedWnd, &root);
	
	root.m_pHookWnd = m_pHookedWnd;

	// create the resize gripper panel
	CRect rcResziGrip(&root);
	int cx = ::GetSystemMetrics(SM_CXHSCROLL);
	int cy = ::GetSystemMetrics(SM_CYVSCROLL);
	rcResziGrip.DeflateRect(root.Width() - cx, root.Height() - cy, 0, 0);
	CGripperPanel * pResizeGripper = new CGripperPanel(&rcResziGrip);
	pResizeGripper->SetAnchor( ANCHOR_RIGHT | ANCHOR_BOTTOM );
	pResizeGripper->Name = ResizeGripperPanelName;
	root.AddChild( pResizeGripper );

	WndResizerData.SetAt(m_pHookedWnd->m_hWnd, this);
	CWnd::ModifyStyle(pParent->GetSafeHwnd(), NULL, WS_CLIPCHILDREN | WS_CLIPSIBLINGS, NULL);
	EnumChildWindows(pParent->GetSafeHwnd(), MakeGroupBoxesTransparent, NULL);

	m_pfnWndProc = (WNDPROC)::SetWindowLongPtr(m_pHookedWnd->m_hWnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);
	return TRUE;
}

BOOL CWndResizer::Hook(CWnd * pParent, CSize &  size)
{
	ASSERT( m_pHookedWnd == NULL );

	m_pHookedWnd = pParent;

	GetTrueClientRect(m_pHookedWnd, &root);
	root.right = root.left + size.cx;
	root.bottom = root.top + size.cy;
	root.m_pHookWnd = m_pHookedWnd;

	// create the resize gripper panel
	CRect rcResziGrip(&root);
	int cx = ::GetSystemMetrics(SM_CXHSCROLL);
	int cy = ::GetSystemMetrics(SM_CYVSCROLL);
	rcResziGrip.DeflateRect(root.Width() - cx, root.Height() - cy, 0, 0);
	CGripperPanel * pResizeGripper = new CGripperPanel(&rcResziGrip);
	pResizeGripper->SetAnchor( ANCHOR_RIGHT | ANCHOR_BOTTOM );
	pResizeGripper->Name = ResizeGripperPanelName;
	root.AddChild( pResizeGripper );

	WndResizerData.SetAt(m_pHookedWnd->m_hWnd, this);

	CWnd::ModifyStyle(pParent->GetSafeHwnd(), NULL, WS_CLIPCHILDREN | WS_CLIPSIBLINGS, NULL);
	EnumChildWindows(pParent->GetSafeHwnd(), MakeGroupBoxesTransparent, NULL);

	m_pfnWndProc = (WNDPROC)::SetWindowLongPtr(m_pHookedWnd->m_hWnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);
	return TRUE;
}

BOOL CWndResizer::Unhook()
{
	if (m_pHookedWnd == NULL )
		return FALSE; //hasn't been hooked

	WNDPROC pWndProc = (WNDPROC)::SetWindowLongPtr(m_pHookedWnd->m_hWnd, GWLP_WNDPROC, (LONG_PTR)m_pfnWndProc);
	WndResizerData.RemoveKey(m_pHookedWnd->m_hWnd); 
	root.m_pHookWnd = NULL;
	m_pHookedWnd = NULL;

	// destroy all chilldren
	while(root.Children.GetCount() > 0 )
	{
		CPanel * pChild = root.Children.RemoveHead();
		delete pChild;
	}
	return TRUE;
}



/////////////////////////////////
BOOL CWndResizer::CreatePanel(UINT uID)
{
	ASSERT(m_pHookedWnd != NULL);
	if (FindPanelByName(&root, IdToName(uID)) != NULL)
	{
		return FALSE;
	}
	CUIPanel * pPanel = GetUIPanel(uID);
	ASSERT(pPanel != NULL);

	pPanel->m_bOle = TRUE;

	return TRUE;
}

BOOL CWndResizer::CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren)
{
	ASSERT(m_pHookedWnd != NULL);

	CRect rcFinal(0, 0, 0, 0);
	for(int i = 0; i < parrID->GetCount(); i++)
	{
		CRect rc(0, 0, 0, 0);
		m_pHookedWnd->GetDlgItem(parrID->GetAt(i))->GetWindowRect(&rc);
		m_pHookedWnd->ScreenToClient(&rc);
		rcFinal.UnionRect(&rcFinal, &rc);
	}

	BOOL bOk = CreatePanel(panelName, &rcFinal);
	if (bOk == FALSE)
	{
		return FALSE;
	}

	if ( setAsChildren )
	{
		CPanel * pPanel = FindPanelByName(&root, panelName);
		for(int i = 0; i < parrID->GetCount(); i++)
		{
			if (FindPanelByName(&root, IdToName(parrID->GetAt(i))) != NULL)  
			{
				bOk = root.RemoveChild(pPanel);
				 ASSERT( bOk );
				delete pPanel;
				return FALSE;
			}
			CUIPanel * pUIPanel = GetUIPanel(parrID->GetAt(i));
			ASSERT( pUIPanel != NULL);
			bOk = pPanel->AddChild( pUIPanel );
			 ASSERT( bOk );
		}
	}
	return TRUE;
}


BOOL CWndResizer::CreatePanel(LPCTSTR panelName, const UINT * parrID, BOOL setAsChildren)
{
	ASSERT(m_pHookedWnd != NULL);

	CRect rcFinal(0, 0, 0, 0);
	for(int i = 0; parrID[i]; i++)
	{
		CRect rc(0, 0, 0, 0);
		m_pHookedWnd->GetDlgItem(parrID[i])->GetWindowRect(&rc);
		m_pHookedWnd->ScreenToClient(&rc);
		rcFinal.UnionRect(&rcFinal, &rc);
	}

	BOOL bOk = CreatePanel(panelName, &rcFinal);
	if (bOk == FALSE)
	{
		return FALSE;
	}

	if ( setAsChildren )
	{
		CPanel * pPanel = FindPanelByName(&root, panelName);
		for(int i = 0; parrID[i]; i++)
		{
			if (FindPanelByName(&root, IdToName(parrID[i])) != NULL)  
			{
				bOk = root.RemoveChild(pPanel);
				 ASSERT( bOk );
				delete pPanel;
				return FALSE;
			}
			CUIPanel * pUIPanel = GetUIPanel(parrID[i]);
			ASSERT( pUIPanel != NULL);
			bOk = pPanel->AddChild( pUIPanel );
			 ASSERT( bOk );
		}
	}
	return TRUE;
}


BOOL CWndResizer::CreatePanel(LPCTSTR panelName, const CRect * prcPanel)
{
	if (FindPanelByName(&root, panelName) != NULL)  
		return FALSE;

	CPanel * pPanel = new CPanel(prcPanel);
	pPanel->Name = panelName;
	return root.AddChild(pPanel);
 
}
/////////////////////////////////
BOOL CWndResizer::CreateSplitContainer(LPCTSTR panelName, LPCTSTR panelNameA, LPCTSTR panelNameB)
{
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) != NULL)  
		return FALSE;

	CPanel * pPanelA = NULL;
	if ((pPanelA = FindPanelByName(&root, panelNameA)) == NULL)  
		return FALSE;

	CPanel * pPanelB = NULL;
	if ((pPanelB = FindPanelByName(&root, panelNameB)) == NULL)  
		return FALSE;

	if (pPanelA == pPanelB) // two panel cannot be same
		return FALSE;

	CPanel * pSplitterContainer = CSplitContainer::Create(pPanelA, pPanelB);
	if (pSplitterContainer == NULL)
		return FALSE;
	pSplitterContainer->Name = panelName;

	return root.AddChild(pSplitterContainer);
}

BOOL CWndResizer::CreateSplitContainer(LPCTSTR panelName, LPCTSTR panelNameA, UINT panelIDB)
{
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) != NULL)  
		return FALSE;

	CPanel * pPanelA = NULL;
	if ((pPanelA = FindPanelByName(&root, panelNameA)) == NULL)  
		return FALSE;
	
	CPanel * pPanelB = GetUIPanel(panelIDB);
	if (pPanelB == NULL )
		return FALSE;

	if (pPanelA == pPanelB) // two panel cannot be same
		return FALSE;

	// first lets make sure the two CRect are properly set
	CPanel * pSplitterContainer = CSplitContainer::Create(pPanelA, pPanelB);
	if (pSplitterContainer == NULL)
		return FALSE;

	pSplitterContainer->Name = panelName;

	return root.AddChild(pSplitterContainer);
}


BOOL CWndResizer::CreateSplitContainer(LPCTSTR panelName, UINT panelIDA, LPCTSTR panelNameB)
{
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) != NULL)  
		return FALSE;

	CPanel * pPanelA = GetUIPanel(panelIDA);
	if (pPanelA == NULL )
		return FALSE;

	CPanel * pPanelB = NULL;
	if ((pPanelB = FindPanelByName(&root, panelNameB)) == NULL)  
		return FALSE;

	if (pPanelA == pPanelB) // two panel cannot be same
		return FALSE;
	
	CPanel * pSplitterContainer = CSplitContainer::Create(pPanelA, pPanelB);
	if (pSplitterContainer == NULL)
		return FALSE;
	pSplitterContainer->Name.Append(panelName);

	return root.AddChild(pSplitterContainer);
}

BOOL CWndResizer::CreateSplitContainer(LPCTSTR panelName, UINT panelIDA, UINT panelIDB)
{
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) != NULL)  
		return FALSE;

	CPanel * pPanelA = GetUIPanel(panelIDA);
	if (pPanelA == NULL )
		return FALSE;

	CPanel * pPanelB = GetUIPanel(panelIDB);
	if (pPanelB == NULL )
		return FALSE;
	
	if (pPanelA == pPanelB) // two panel cannot be same
		return FALSE;
 
	CPanel * pSplitterContainer = CSplitContainer::Create(pPanelA, pPanelB);
	if (pSplitterContainer == NULL)
		return FALSE;

	pSplitterContainer->Name = panelName;

	return root.AddChild(pSplitterContainer);
}


/////////////////////////////////
BOOL CWndResizer::CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren)
{
	ASSERT(m_pHookedWnd != NULL);

	CRect rcFinal(0, 0, 0, 0);
	for(int i = 0; i < parrID->GetCount(); i++)
	{
		CRect rc(0, 0, 0, 0);
		m_pHookedWnd->GetDlgItem(parrID->GetAt(i))->GetWindowRect(&rc);
		m_pHookedWnd->ScreenToClient(&rc);
		rcFinal.UnionRect(&rcFinal, &rc);
	}

	BOOL bOk = CreateFlowLayoutPanel(panelName, &rcFinal);
	if (bOk == FALSE)
	{
		return FALSE;
	}

	if ( setAsChildren )
	{
		CPanel * pPanel = FindPanelByName(&root, panelName);
		for(int i = 0; i < parrID->GetCount(); i++)
		{
			if (FindPanelByName(&root, IdToName(parrID->GetAt(i))) != NULL)  
			{
				bOk = root.RemoveChild(pPanel);
				 ASSERT( bOk );
				delete pPanel;
				return FALSE;
			}
			CUIPanel * pUIPanel = GetUIPanel(parrID->GetAt(i));
			ASSERT( pUIPanel != NULL);
			bOk = pPanel->AddChild( pUIPanel );
			 ASSERT( bOk );
		}
	}
	return TRUE;
}

BOOL CWndResizer::CreateFlowLayoutPanel(LPCTSTR panelName, const UINT * parrID, BOOL setAsChildren)
{
	ASSERT(m_pHookedWnd != NULL);

	CRect rcFinal(0, 0, 0, 0);
	for(int i = 0; parrID[i] != 0; i++)
	{
		CRect rc(0, 0, 0, 0);
		m_pHookedWnd->GetDlgItem(parrID[i])->GetWindowRect(&rc);
		m_pHookedWnd->ScreenToClient(&rc);
		rcFinal.UnionRect(&rcFinal, &rc);
	}

	BOOL bOk = CreateFlowLayoutPanel(panelName, &rcFinal);
	if (bOk == FALSE)
		return FALSE;

	if ( setAsChildren )
	{
		CPanel * pPanel = FindPanelByName(&root, panelName);
		for(int i = 0; parrID[i] != 0; i++)
		{
			if (FindPanelByName(&root, IdToName(parrID[i])) != NULL)  
			{
				bOk = root.RemoveChild(pPanel);
				 ASSERT( bOk );
				delete pPanel;
				return FALSE;
			}
			CUIPanel * pUIPanel = GetUIPanel(parrID[i]);
			ASSERT( pUIPanel != NULL);
			bOk = pPanel->AddChild( pUIPanel );
			 ASSERT( bOk );
		}
	}
	return TRUE;
}
BOOL CWndResizer::CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel)
{
	if (FindPanelByName(&root, panelName) != NULL)  
	{
		return FALSE;
	}

	CPanel * pPanel = new CFlowLayoutPanel(prcPanel);
	pPanel->Name = panelName;
	return root.AddChild(pPanel);
 
}

/////////////////////////////////
BOOL CWndResizer::SetAnchor(LPCTSTR panelName, UINT uAnchor)
{
	// container must already exist
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  
	{
		return FALSE;
	}

	return pPanel->SetAnchor(uAnchor);
}

BOOL CWndResizer::SetAnchor(UINT uID, UINT uAnchor)
{
	ASSERT(m_pHookedWnd != NULL);

	CUIPanel * pPanel = GetUIPanel(uID);
	if (pPanel == NULL)
		return FALSE;

	pPanel->SetAnchor(uAnchor);  
	return TRUE;
}

BOOL CWndResizer::GetAnchor(LPCTSTR panelName, UINT & anchor)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  // name of parent must already exist
		return FALSE;

	anchor = pPanel->Anchor;
	return TRUE;
}

BOOL CWndResizer::GetAnchor(UINT uID, UINT & anchor)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)  // name of parent must already exist
		return FALSE;

	anchor = pPanel->Anchor;
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CWndResizer::SetOffset(LPCTSTR panelName, UINT anchor, int offset)
{
	// container must already exist
	CPanel * pPanel = FindPanelByName(&root, panelName);
	if (pPanel == NULL)  
		return FALSE;

	return pPanel->SetOffset( anchor, offset );
}

BOOL CWndResizer::SetOffset(UINT uID, UINT anchor, int offset)
{
	ASSERT(m_pHookedWnd != NULL);

	CUIPanel * pPanel = GetUIPanel(uID);
	if (pPanel == NULL)
		return FALSE;

	return pPanel->SetOffset( anchor, offset );
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CWndResizer::SetDock(LPCTSTR panelName, UINT uDock)
{
	// container must already exist
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  
	{
		return FALSE;
	}

	pPanel->Dock = uDock ;
	return TRUE;
}

BOOL CWndResizer::SetDock(UINT uID, UINT uDock)
{
	ASSERT(m_pHookedWnd != NULL);

	CUIPanel * pPanel = GetUIPanel(uID);

	if (pPanel == NULL)
	{
		return FALSE;
	}
	pPanel->Dock = uDock;  
	
	return TRUE;
}

BOOL CWndResizer::GetDock(LPCTSTR panelName, UINT & uDock)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  // name of parent must already exist
	{
		return FALSE;
	}

	uDock = pPanel->Dock;
	return TRUE;
}

BOOL CWndResizer::GetDock(UINT uID, UINT & uDock)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)  // name of parent must already exist
	{
		return FALSE;
	}

	uDock = pPanel->Dock;
	return TRUE;
}


//////////////////////
BOOL CWndResizer::SetParent(LPCTSTR panelName, LPCTSTR parentName)
{
	ASSERT(m_pHookedWnd != NULL);

	// now make sure parentName is OK
	CPanel * pParent = NULL;
	if ( (pParent = FindPanelByName(&root, parentName)) == NULL)  // name of parent must already exist
	{
		return FALSE;
	}

	// make sure panelName exist
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) == NULL)
	{
		return FALSE;
	}

	return pParent->AddChild(pPanel);
}
BOOL CWndResizer::SetParent(UINT uID, LPCTSTR parentName)
{
	ASSERT(m_pHookedWnd != NULL);

	// now make sure parentName is OK
	CPanel * pParent = NULL;
	if ( (pParent = FindPanelByName(&root, parentName)) == NULL)  // name of parent must already exist
	{
		return FALSE;
	}
	CPanel * pPanel = NULL;

	// first see if it is already defined
	if ((pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)
	{
		if ((pPanel = CreateUIPanel(uID)) == NULL)
		{
			return FALSE;
		}
	}

	return pParent->AddChild(pPanel);

}

BOOL CWndResizer::SetParent(LPCTSTR panelName, UINT uParentID)
{
	ASSERT(m_pHookedWnd != NULL);

	// now make sure parentName is OK
	CPanel * pParent = GetUIPanel(uParentID);
	if ( pParent == NULL)  // name of parent must already exist
	{
		return FALSE;
	}

	// make sure panelName exist
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, panelName)) == NULL)
	{
		return FALSE;
	}

	return pParent->AddChild(pPanel);

}
BOOL CWndResizer::SetParent(UINT uID, UINT uParentID)
{
	CPanel * pParent = GetUIPanel(uParentID);
	if (pParent == NULL)
	{
		return FALSE;
	}
	CPanel * pPanel = GetUIPanel(uID);;
	if (pParent == NULL)
	{
		return FALSE;
	}

	return pParent->AddChild( pPanel );

}


BOOL CWndResizer::GetParent(LPCTSTR panelName, CString & parentName)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL) 
	{
		return FALSE;
	}

	parentName = pPanel->Parent->Name;
	return TRUE;
}
BOOL CWndResizer::GetParent(UINT uID, CString & parentName)
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)  // name of parent must already exist
	{
		return FALSE;
	}

	parentName = pPanel->Parent->Name;
	return TRUE;
}

/////////////////////////
BOOL CWndResizer::SetMinimumSize( LPCTSTR panelName, CSize & size)
{
	CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  
	{
		return FALSE;
	}
	return pPanel->SetMinSize( size );
}
BOOL CWndResizer::SetMinimumSize(UINT uID, CSize & size)
{
	CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL) 
	{
		if ((pPanel = CreateUIPanel(uID)) == NULL)
		{
			return FALSE;
		}
	}

	return pPanel->SetMinSize( size );
}

BOOL CWndResizer::GetMinimumSize(LPCTSTR panelName, CSize & size) 
{
	CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL) 
	{
		return FALSE;
	}

	size = pPanel->MinSize;
	return TRUE;
}

BOOL CWndResizer::GetMinimumSize(UINT uID, CSize & size) 
{
	const CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL) 
	{
		return FALSE;
	}

	size = pPanel->MinSize;
	return TRUE;
}

/////////////////////////////
BOOL CWndResizer::SetMaximumSize(UINT uID, CSize & size)
{
	CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)  
	{
		return FALSE;
	}
	
	return pPanel->SetMaxSize(size);
}

BOOL CWndResizer::SetMaximumSize( LPCTSTR panelName, CSize & size)
{
	CPanel * pPanel = NULL;

	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL) 
	{
		return FALSE;
	}

	return pPanel->SetMaxSize(size);

}

BOOL CWndResizer::GetMaximumSize(  LPCTSTR panelName, CSize & size) 
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, panelName)) == NULL)  
	{
		return FALSE;
	}

	size = pPanel->MaxSize;
	return TRUE;
}

BOOL CWndResizer::GetMaximumSize(  UINT uID, CSize & size) 
{
	CPanel * pPanel = NULL;
	if ( (pPanel = FindPanelByName(&root, IdToName(uID))) == NULL)  
	{
		return FALSE;
	}

	size = pPanel->MaxSize;
	return TRUE;
}

/////////////////////////////
BOOL CWndResizer::SetFixedPanel(LPCTSTR splitContainerName, short panel)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *>( pContainer );
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}

	pSplitContainer->SetFixedPanel(panel);
	return TRUE;
}

BOOL CWndResizer::GetFixedPanel(LPCTSTR splitContainerName, short & panel)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *> (pContainer);
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}
	panel = pSplitContainer->GetFixedPanel();
	return TRUE;

}

BOOL CWndResizer::SetIsSplitterFixed(LPCTSTR splitContainerName, BOOL fixed)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *> (pContainer);
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}
	pSplitContainer->SetIsSplitterFixed( fixed );
	return TRUE;
}

BOOL CWndResizer::GetIsSplitterFixed(LPCTSTR splitContainerName, BOOL &fixed)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *> (pContainer);
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}
	fixed = pSplitContainer->GetIsSplitterFixed();
	return TRUE;
}



BOOL CWndResizer::SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *> (pContainer);
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}
	pSplitContainer->SetShowSplitterGrip( bShow );
	return TRUE;
}

BOOL CWndResizer::GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL &bShow)
{
	CPanel * pContainer = FindPanelByName(&root, splitContainerName);
	if (pContainer == NULL)
	{
		return FALSE;
	}
	CSplitContainer * pSplitContainer =  dynamic_cast<CSplitContainer *> (pContainer);
	if (pSplitContainer == NULL)
	{
		return FALSE;
	}
	bShow = pSplitContainer->GetShowSplitterGrip();
	return TRUE;
}

void CWndResizer::SetShowResizeGrip(CWndResizer::ResizeGrip show)
{  
	CGripperPanel * pPanel = (CGripperPanel *)FindPanelByName(&root, ResizeGripperPanelName);
	ASSERT(pPanel != NULL);
	if ( pPanel->m_bVisible != show )
	{
		pPanel->m_bVisible = show;
		m_pHookedWnd->Invalidate(TRUE);
	}
}

void CWndResizer::SetShowResizeGrip(BOOL show)
{
	SetShowResizeGrip( show ? RESIZE_GRIP_VISIBLE_ACTIVE_TRIANGLE : RESIZE_GRIP_HIDDEN );
}

CWndResizer::ResizeGrip CWndResizer::GetShowResizeGrip()
{
	CGripperPanel * pPanel = (CGripperPanel *)FindPanelByName(&root, ResizeGripperPanelName);
	ASSERT(pPanel != NULL);
	return (CWndResizer::ResizeGrip)pPanel->m_bVisible;
}

////////////////////
BOOL CWndResizer::SetFlowDirection(LPCTSTR flowPanelName, short direction)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
	{
		return FALSE;
	}
	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
	{
		return FALSE;
	}
	pFlowLayout->SetFlowDirection( direction == 1 ? CWndResizer::LEFT_TO_RIGHT : CWndResizer::TOP_TO_BOTTOM);
	return TRUE;
}
BOOL CWndResizer::GetFlowDirection(LPCTSTR flowPanelName, short &direction)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
	{
		return FALSE;
	}
	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
	{
		return FALSE;
	}
	direction = (pFlowLayout->GetFlowDirection() == CWndResizer::LEFT_TO_RIGHT ? 1 : 2);
	return TRUE;
}

BOOL CWndResizer::SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpacing)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
	{
		return FALSE;
	}
	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
	{
		return FALSE;
	}
	pFlowLayout->SetItemSpacingX(nSpacing);
	return TRUE;
}
BOOL CWndResizer::GetFlowItemSpacingX(LPCTSTR flowPanelName, int &nSpacing)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
		return FALSE;

	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
		return FALSE;

	nSpacing = pFlowLayout->GetItemSpacingX();
	return TRUE;
}

BOOL CWndResizer::SetFlowItemSpacingY(LPCTSTR flowPanelName, int nSpacing)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
		return FALSE;

	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
		return FALSE;

	pFlowLayout->SetItemSpacingY(nSpacing);
	return TRUE;

}

BOOL CWndResizer::GetFlowItemSpacingY(LPCTSTR flowPanelName, int &nSpacing)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, flowPanelName)) == NULL)
		return FALSE;

	CFlowLayoutPanel * pFlowLayout = dynamic_cast<CFlowLayoutPanel *> ( pPanel);
	if (pFlowLayout == NULL)
		return FALSE;

	nSpacing = pFlowLayout->GetItemSpacingY();
	return TRUE;
}


CPanel * CWndResizer::FindPanelByName(CPanel * pRoot, LPCTSTR name)
{
	if (CString(name).GetLength() == 0)
		return NULL;

	if (pRoot == NULL )
		return NULL;

	if (pRoot->Name.CompareNoCase(name) == 0)
		return pRoot;

	POSITION pos = pRoot->Children.GetHeadPosition();
	while(pos != NULL)
	{
		CPanel * pChild = pRoot->Children.GetNext(pos);
		CPanel * pFound = FindPanelByName(pChild, name);
		if (pFound != NULL )
			return pFound;
	}
	
	return NULL;
}

void CWndResizer::GetUIPanels(CPanel * pRoot, CPanelList * pList, BOOL bOle)
{
	if (pRoot == NULL )
	{
		return ;
	}

	CUIPanel * pUIPanel = dynamic_cast<CUIPanel *> ( pRoot);

	if (pUIPanel != NULL && pUIPanel->m_bOle == bOle)
	{
		pList->AddTail( pRoot );
	}

	// try the childreen
	POSITION pos = pRoot->Children.GetHeadPosition();
	while(pos != NULL)
	{
		CPanel * pChild = pRoot->Children.GetNext(pos);
		GetUIPanels(pChild, pList, bOle);

	}
	
}

void CWndResizer::GetVisualPanels(CPanel * pRoot, CPanelList * pList)
{
	if (pRoot == NULL )
	{
		return ;
	}

	CVisualPanel * pUIPanel = dynamic_cast<CVisualPanel *> ( pRoot);

	if (pUIPanel != NULL)
	{
		pList->AddTail( pRoot );
	}

	// try the childreen
	POSITION pos = pRoot->Children.GetHeadPosition();
	while(pos != NULL)
	{
		CPanel * pChild = pRoot->Children.GetNext(pos);
		GetVisualPanels(pChild, pList);
	}  
}


CPanel * CWndResizer::FindSplitterFromPoint(CPanel * pRoot, CPoint point)
{  
	if (pRoot == NULL )
	{
		return NULL;
	}

	CSpitterPanel * pSpitterPanel = dynamic_cast<CSpitterPanel *>(pRoot);

	if (pSpitterPanel != NULL && pRoot->PtInRect(point) == TRUE )
	{
		CSplitContainer * pContainer = (CSplitContainer *)pRoot->Parent;
		if (!pContainer->GetIsSplitterFixed())
		{
			CPoint ptScreen = point;
			m_pHookedWnd->ClientToScreen(&ptScreen);
			HWND hWndFromPoint = ::WindowFromPoint(ptScreen);
			if (m_pHookedWnd->m_hWnd == hWndFromPoint)
			{
				return pRoot;
			}
		}
	}

	// try the childreen
	POSITION pos = pRoot->Children.GetHeadPosition();
	while(pos != NULL)
	{
		CPanel * pChild = pRoot->Children.GetNext(pos);
		CPanel * pFound = FindSplitterFromPoint(pChild, point);
		if (pFound != NULL )
		{
			return pFound;
		}
	}
	
	
	return NULL;
}

CString CWndResizer::IdToName(UINT uID)
{
	CString sName;
	sName.Format(_T("%d"), uID);
	return sName;
}

CUIPanel * CWndResizer::CreateUIPanel(UINT uID)
{
	ASSERT(m_pHookedWnd != NULL);
	CWnd * pWnd = m_pHookedWnd->GetDlgItem(uID);

	if ( pWnd == NULL )
	{
		return NULL;
	}
	CRect rc(0, 0, 0, 0);
	pWnd->GetWindowRect( &rc );  
	m_pHookedWnd->ScreenToClient( &rc );

	CUIPanel * pPanel = new CUIPanel(&rc, uID);
	pPanel->Name = IdToName(uID);
	return pPanel ;
}

CUIPanel * CWndResizer::GetUIPanel(UINT uID)
{
	CUIPanel * pPanel = NULL;
	if ((pPanel = (CUIPanel *)FindPanelByName(&root, IdToName(uID))) == NULL)  
	{
		pPanel = CreateUIPanel(uID);
		if (pPanel != NULL)
		{
			root.AddChild( pPanel );
		}
	}
	return pPanel;
}

BOOL CWndResizer::SetSplitterPosition(LPCTSTR splitContainerName, UINT position)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, splitContainerName)) == NULL)
		return FALSE;

	CSplitContainer * pContainer = dynamic_cast<CSplitContainer *> ( pPanel);
	if (pContainer == NULL)
		return FALSE;

	pContainer->SetSplitterPosition( (int) position);
	ResizeUI( pContainer );
	return TRUE;
}

BOOL CWndResizer::GetSplitterPosition(LPCTSTR splitContainerName, UINT & position)
{
	ASSERT(m_pHookedWnd != NULL);
	CPanel * pPanel = NULL;
	if ((pPanel = FindPanelByName(&root, splitContainerName)) == NULL)
		return FALSE;

	CSplitContainer * pContainer = dynamic_cast<CSplitContainer *> ( pPanel);
	if (pContainer == NULL)
		return FALSE;

	position = (UINT) pContainer->GetSplitterPosition();
	return TRUE;
}


BOOL CWndResizer::InvokeOnResized()
{
	ASSERT(m_pHookedWnd != NULL);

	OnSize(0, 0, 0);
	return TRUE;
}

CString CWndResizer::GetDebugInfo()
{
	ASSERT(m_pHookedWnd != NULL);

	CString sInfo;
	CString sIndent;

	GetDebugInfo(&root, sInfo, sIndent);

	return sInfo;
}

void CWndResizer::GetDebugInfo(CPanel * pRoot, CString & info, CString indent)
{
	if (pRoot == NULL )
		return ;

	info.Append(_T("\n"));
	info.Append(indent);
	info.Append(pRoot->ToString() );

	indent.Append(_T("  "));
	for(int i = 0; i < pRoot->Children.GetCount(); i++)
	{
		CPanel * pChild = pRoot->Children.GetAt(pRoot->Children.FindIndex(i));
		GetDebugInfo(pChild, info, indent);
	}
}

void CWndResizer::ResizeUI(CPanel * pRoot)
{
	CPanelList panels;
	GetUIPanels(pRoot, &panels, FALSE);

	POSITION pos = NULL;

	if (panels.GetCount() > 0)
	{
		HDWP hDWP = ::BeginDeferWindowPos((int)panels.GetCount());
		ASSERT( hDWP != NULL);
		pos = panels.GetHeadPosition();
		while (pos != NULL)
		{
			CUIPanel * pPanel = (CUIPanel *) panels.GetNext(pos);

			::DeferWindowPos(hDWP, m_pHookedWnd->GetDlgItem(pPanel->m_uID)->m_hWnd, NULL,
										pPanel->left , pPanel->top , pPanel->Width(), pPanel->Height(),
										SWP_NOACTIVATE | SWP_NOZORDER );

		}
		BOOL bOk = ::EndDeferWindowPos(hDWP);
		 ASSERT( bOk );
		m_pHookedWnd->InvalidateRect(pRoot, FALSE);
	}

	panels.RemoveAll();
	GetUIPanels(pRoot, &panels, TRUE);
	pos = panels.GetHeadPosition();
	while (pos != NULL)
	{
		CUIPanel * pPanel = (CUIPanel *) panels.GetNext(pos);
		m_pHookedWnd->GetDlgItem(pPanel->m_uID)->MoveWindow(pPanel);
	}
}


void CWndResizer::OnLButtonDown(UINT nFlags, CPoint point)
{
	UpdateSplitterOffset(point);
}

void CWndResizer::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_pCaptured != NULL )
	{
		if ((nFlags & MK_LBUTTON) <= 0)
		{
			CPoint ptScreen = point;
			m_pHookedWnd->ClientToScreen(&ptScreen);
			HWND hWndFromPoint = ::WindowFromPoint(ptScreen);
			if (m_pCaptured->PtInRect( point ) == FALSE || hWndFromPoint != m_pHookedWnd->m_hWnd)
			{
				::ReleaseCapture();      
				m_pCaptured = NULL;
				HCURSOR hCur = ::SetCursor(m_hOldCursor);
				::DestroyCursor( hCur );
				m_hOldCursor = NULL;
			}
		}
	}

	if (m_pCaptured == NULL )
	{
		m_pCaptured = FindSplitterFromPoint(&root, point);
		if (m_pCaptured != NULL)
		{
			m_pHookedWnd->SetCapture();
			LPCTSTR cursor = NULL;
			CSplitContainer * pSplitContainer = (CSplitContainer *)m_pCaptured->Parent;
			if (pSplitContainer->m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
			{
				cursor = IDC_SIZEWE;
			}
			else
			{
				cursor = IDC_SIZENS;
			}
			HCURSOR hCur = AfxGetApp()->LoadStandardCursor(cursor);
			HCURSOR m_hOldCursor = ::SetCursor(hCur);
		}
	}

	if (m_pCaptured != NULL && (nFlags & MK_LBUTTON) > 0)
	{
		CSplitContainer * pSplitterContainer = (CSplitContainer *) m_pCaptured->Parent;
		
		if (pSplitterContainer->m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
		{
			pSplitterContainer->SetSplitterPosition( point.x - m_splitterOffset);
		}
		else
		{
			pSplitterContainer->SetSplitterPosition( point.y - m_splitterOffset);
		}
		UpdateSplitterOffset(point);

		ResizeUI(pSplitterContainer);

	}
}

void CWndResizer::OnLButtonUp(UINT nFlags, CPoint point)
{
	OnMouseMove(nFlags, point);
}
void CWndResizer::GetTrueClientRect(CWnd * pWnd, CRect * prc)
{
	int nMin = 0;
	int nMax = 0;
	int nCur = 0;

	pWnd->GetClientRect(prc);

	if ( (pWnd->GetStyle() & WS_HSCROLL) > 0)
	{
		pWnd->GetScrollRange(SB_HORZ, &nMin, &nMax);
		nCur = pWnd->GetScrollPos(SB_HORZ);
		prc->right = prc->left + nMax;
		prc->OffsetRect( -nCur , 0);
	}

	if ( (pWnd->GetStyle() & WS_VSCROLL) > 0)
	{
		pWnd->GetScrollRange(SB_VERT, &nMin, &nMax);
		nCur = pWnd->GetScrollPos(SB_VERT);
		prc->bottom = prc->top + nMax;
		prc->OffsetRect(0, -nCur);
	}  
}

void CWndResizer::EnsureRootMinMax()
{
	if (root.Width() < root.MinSize.cx)
		root.right = root.left + root.MinSize.cx;
	if (root.Width() > root.MaxSize.cx)
		 root.right = root.left + root.MaxSize.cx;

	if (root.Height() < root.MinSize.cy)
		 root.bottom = root.top + root.MinSize.cy;
	if (root.Height() > root.MaxSize.cy)
		 root.bottom = root.top + root.MaxSize.cy;
}

void CWndResizer::UpdateSplitterOffset(CPoint ptCurr)
{
	if (m_pCaptured == NULL )
		return;

	if (((CSplitContainer *)m_pCaptured->Parent)->m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
	{
		if ( ptCurr.x  < m_pCaptured->left)
		{
			m_splitterOffset = 0;
		}
		else if ( ptCurr.x > m_pCaptured->right)
		{
			m_splitterOffset = m_pCaptured->Width();
		}
		else
		{
			m_splitterOffset = ptCurr.x - m_pCaptured->left;
		}
	}
	else
	{
		if ( ptCurr.y  < m_pCaptured->top)
		{
			m_splitterOffset = 0;
		}
		else if ( ptCurr.y > m_pCaptured->bottom)
		{
			m_splitterOffset = m_pCaptured->Height();
		}
		else
		{
			m_splitterOffset = ptCurr.y - m_pCaptured->top;
		}
	}
}

void CWndResizer::OnSizing(UINT fwSide, LPRECT pRect)
{

	CRect * prc = (CRect *) pRect;

	CRect rcMin(0, 0, root.MinSize.cx, root.MinSize.cy);
	CRect rcMax(0, 0, root.MaxSize.cx, root.MaxSize.cy);

	LONG_PTR style = GetWindowLongPtr(m_pHookedWnd->m_hWnd , GWL_STYLE);
	LONG_PTR styleEx = GetWindowLongPtr(m_pHookedWnd->m_hWnd, GWL_EXSTYLE);

	::AdjustWindowRectEx(&rcMin, (DWORD)style, (m_pHookedWnd->GetMenu() != NULL), (DWORD)styleEx);
	::AdjustWindowRectEx(&rcMax, (DWORD)style, (m_pHookedWnd->GetMenu() != NULL), (DWORD)styleEx);

	switch (fwSide)
	{
	case WMSZ_BOTTOM:
		if (prc->Height() < rcMin.Height() )
		{
			prc->bottom = prc->top + rcMin.Height();
		}
		if (prc->Height() > rcMax.Height() )
		{
			prc->bottom = prc->top + rcMax.Height();
		}    
		break;
	case WMSZ_BOTTOMLEFT:
		if (prc->Height() < rcMin.Height() )
		{
			prc->bottom = prc->top + rcMin.Height();
		}
		if (prc->Width() < rcMin.Width() )
		{
			prc->left = prc->right - rcMin.Width();
		}

		if (prc->Height() > rcMax.Height() )
		{
			prc->bottom = prc->top + rcMax.Height();
		}
		if (prc->Width() > rcMax.Width() )
		{
			prc->left = prc->right - rcMax.Width();
		}
		break;
	case WMSZ_BOTTOMRIGHT:
		if (prc->Height() < rcMin.Height() )
		{
			prc->bottom = prc->top + rcMin.Height();
		}
		if (prc->Width() < rcMin.Width() )
		{
			prc->right = prc->left + rcMin.Width();
		}

		if (prc->Height() > rcMax.Height() )
		{
			prc->bottom = prc->top + rcMax.Height();
		}
		if (prc->Width() > rcMax.Width() )
		{
			prc->right = prc->left + rcMax.Width();
		}

		break;
	case WMSZ_LEFT:
		if (prc->Width() < rcMin.Width() )
		{
			prc->left = prc->right - rcMin.Width();
		}

		if (prc->Width() > rcMax.Width() )
		{
			prc->left = prc->right - rcMax.Width();
		}
		break;
	case WMSZ_RIGHT:
		if (prc->Width() < rcMin.Width() )
		{
			prc->right = prc->left + rcMin.Width();
		}

		if (prc->Width() > rcMax.Width() )
		{
			prc->right = prc->left + rcMax.Width();
		}
		break;
	case WMSZ_TOP:
		if (prc->Height() < rcMin.Height() )
		{
			prc->top = prc->bottom - rcMin.Height();
		}

		if (prc->Height() > rcMax.Height() )
		{
			prc->top = prc->bottom - rcMax.Height();
		}
		break;
	case WMSZ_TOPLEFT:
		if (prc->Height() < rcMin.Height() )
		{
			prc->top = prc->bottom - rcMin.Height();
		}
		if (prc->Width() < rcMin.Width() )
		{
			prc->left = prc->right - rcMin.Width();
		}


		if (prc->Height() > rcMax.Height() )
		{
			prc->top = prc->bottom - rcMax.Height();
		}
		if (prc->Width() > rcMax.Width() )
		{
			prc->left = prc->right - rcMax.Width();
		}
		break;
	case WMSZ_TOPRIGHT:
		if (prc->Height() < rcMin.Height() )
		{
			prc->top = prc->bottom - rcMin.Height();
		}
		if (prc->Width() < rcMin.Width() )
		{
			prc->right = prc->left + rcMin.Width();
		}

		if (prc->Height() > rcMax.Height() )
		{
			prc->top = prc->bottom - rcMax.Height();
		}
		if (prc->Width() > rcMax.Width() )
		{
			prc->right = prc->left + rcMax.Width();
		}
		break;
	}

}

void CWndResizer::OnPaint()
{
	if (m_pHookedWnd == NULL)
	{
		return;
	}
	CDC* pDC = m_pHookedWnd->GetDC(); // device context for painting

	CPanelList panelList;
	GetVisualPanels(&root, &panelList);
	POSITION pos = panelList.GetHeadPosition();
	while (pos != NULL)
	{
		CVisualPanel * pPanel = (CVisualPanel *) panelList.GetNext(pos);
		if (pPanel->m_bVisible)
		{
			pPanel->Draw(pDC);
		}
	}
}

void CWndResizer::OnThemeChanged()
{
	if (m_pHookedWnd == NULL)
		return;

	CGripperPanel * pPanel = (CGripperPanel *)FindPanelByName(&root, ResizeGripperPanelName);
	ASSERT(pPanel != NULL);
	if( pPanel->m_bVisible != RESIZE_GRIP_HIDDEN )
	{
		GetTrueClientRect(m_pHookedWnd, &root);
		EnsureRootMinMax();
		root.OnResized();
		ResizeUI(&root);
	}

	//	m_pHookedWnd->InvalidateRect( pPanel, FALSE );
}

LRESULT CWndResizer::OnNcHitTest( CPoint point )
{
	CGripperPanel * pPanel = (CGripperPanel *)FindPanelByName(&root, ResizeGripperPanelName);
	if( !pPanel || !pPanel->m_bVisible )
		return HTNOWHERE;

	ASSERT( m_pHookedWnd != NULL );
	m_pHookedWnd->ScreenToClient( &point );

	// use triangle, not rectangle, it looks more natural :-)
	if( pPanel->m_bVisible == RESIZE_GRIP_VISIBLE_ACTIVE_TRIANGLE )
	{
		if( pPanel->PtInTriangle(point) )
			return HTBOTTOMRIGHT;
	}
	else if( pPanel->m_bVisible == RESIZE_GRIP_VISIBLE_ACTIVE_RECT )
	{
		if( pPanel->PtInRect( point ) )
			return HTBOTTOMRIGHT;
	}

	return HTNOWHERE;
}


void CWndResizer::OnSize(UINT nType, int cx, int cy)
{
	GetTrueClientRect(m_pHookedWnd, &root);
	EnsureRootMinMax();
	root.OnResized();
	ResizeUI(&root);
}

void CWndResizer::OnScroll()
{
	GetTrueClientRect(m_pHookedWnd, &root);
	EnsureRootMinMax();
	root.OnResized();
	ResizeUI(&root);
}

void CWndResizer::OnDestroy()
{
	if (m_pHookedWnd != NULL)
	{
		Unhook();
	}
}


LRESULT CALLBACK CWndResizer::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	CWndResizer * pResizer = NULL;
	WndResizerData.Lookup(hWnd, pResizer);
	ASSERT( pResizer != NULL);
	LRESULT lResult;

	switch (uMsg)
	{
		case WM_SIZE:
		{
			int cx = LOWORD(lParam);
			int cy = HIWORD(lParam);
			pResizer->OnSize((UINT) wParam, cx, cy);
			break;
		}

		case WM_SIZING:
			pResizer->OnSizing((UINT) wParam, (LPRECT)lParam);      
			break;

		case WM_DESTROY:
			pResizer->OnDestroy();
			break;

		case WM_MOUSEMOVE:
			pResizer->OnMouseMove((UINT) wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
			break;

		case WM_LBUTTONDOWN:
			pResizer->OnLButtonDown((UINT) wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
			break;

		case WM_LBUTTONUP:
			pResizer->OnLButtonUp((UINT) wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
			break;

		case WM_PAINT:
			pResizer->OnPaint();
			break;

		case WM_HSCROLL:
		case WM_VSCROLL:
			pResizer->OnScroll();
			break;

		case WM_NCHITTEST:
			lResult = pResizer->OnNcHitTest( CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)) );
			if( lResult != HTNOWHERE )
				return lResult;
			break;

		//case WM_ERASEBKGND:
		//  return FALSE;
		//  break;
		default:
			break;
	}

	lResult = ::CallWindowProc(pResizer->m_pfnWndProc, hWnd, uMsg, wParam, lParam);

	switch( uMsg )
	{
		case WM_SYSCOLORCHANGE:
		case WM_THEMECHANGED:
			pResizer->OnThemeChanged();
			break;
	}

	return lResult;
}

/////////////////////////////  CPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CPanel::CPanel() : CRect(0, 0, 0, 0)
{
	Init();
}

CPanel::CPanel(const CRect * prc) : CRect(prc)
{
	Init();
}

void CPanel::Init()
{
	Parent = NULL;
	LeftOffset = 0;
	TopOffset = 0;
	RightOffset = 0;
	BottomOffset = 0; 
	MinSize.SetSize(10, 10);
	MaxSize.SetSize(100000, 100000);
	Anchor = (ANCHOR_LEFT | ANCHOR_TOP);  
	Dock = DOCK_NONE;
	m_bVisible = TRUE;
}

CPanel::~CPanel()
{
	while(Children.GetCount() > 0 )
	{
		delete Children.RemoveHead();
	}
}

void CPanel::OnResized()
{
	BOOL bOk = FALSE;
	CRect rcEmpty(this);  // available area for docking.  ininitally it is the entire area

	POSITION pos = Children.GetHeadPosition();
	while(pos != NULL)
	{
		CPanel * pChild = Children.GetNext(pos);    
		
		if (pChild->Dock != DOCK_NONE)
		{
			switch(pChild->Dock)
			{
			case DOCK_LEFT:
				pChild->SetRect(rcEmpty.left, rcEmpty.top, rcEmpty.left + pChild->Width(), rcEmpty.bottom);
				bOk = rcEmpty.SubtractRect(&rcEmpty, pChild);
				// ASSERT( bOk );
				break;
			case DOCK_TOP:
				pChild->SetRect(rcEmpty.left, rcEmpty.top, rcEmpty.right, rcEmpty.top + pChild->Height());
				bOk = rcEmpty.SubtractRect(&rcEmpty, pChild);
				// ASSERT( bOk );
				break;
			case DOCK_RIGHT:
				pChild->SetRect(rcEmpty.right - pChild->Width(), rcEmpty.top, rcEmpty.right, rcEmpty.bottom);
				bOk = rcEmpty.SubtractRect(&rcEmpty, pChild);
				// ASSERT( bOk );
				break;
			case DOCK_BOTTOM:
				pChild->SetRect(rcEmpty.left, rcEmpty.bottom - pChild->Height(), rcEmpty.right, rcEmpty.bottom);
				bOk = rcEmpty.SubtractRect(&rcEmpty, pChild);
				// ASSERT( bOk );
				break;
			case DOCK_FILL:
				pChild->SetRect(rcEmpty.left, rcEmpty.top, rcEmpty.right, rcEmpty.bottom);
				break;
			}
			pChild->OnResized();

			// if docking is in action, then we igonre anchor, therefore we continue
			continue;
		}

		CRect rc(0, 0, 0, 0);
		if ((pChild->Anchor & ANCHOR_HORIZONTALLY_CENTERED) ==  ANCHOR_HORIZONTALLY_CENTERED )
		{
			rc.left = this->left + ((int)( (this->Width() - pChild->Width()) /  2));
			rc.right = rc.left + pChild->Width();

			BOOL bReposition = FALSE;
			if (pChild->MinSize.cx > rc.Width() )
			{
				bReposition = TRUE;
				rc.right = rc.left + pChild->MinSize.cx;
			}
			if (pChild->MaxSize.cx < rc.Width() )
			{
				bReposition = TRUE;
				rc.right = rc.left + pChild->MaxSize.cx;
			}

			if (bReposition)
			{
				int nWidth = rc.Width();
				rc.left = (int)( (this->Width() - nWidth) /  2) ;
				rc.right = rc.left + nWidth;
			}
		}
		else if ((pChild->Anchor & ANCHOR_HORIZONTALLY) ==  ANCHOR_HORIZONTALLY )
		{
			rc.left = this->left + pChild->LeftOffset;
			rc.right = this->right - pChild->RightOffset;

			// we will be left anchor if minsize or maxsize does not match
			// (giving ANCHOR_LEFT priority over ANCHOR_RIGHT)
			if ((pChild->Anchor & ANCHOR_PRIORITY_RIGHT) == ANCHOR_PRIORITY_RIGHT)
			{
				if (pChild->MinSize.cx > rc.Width() )
				{
					rc.left = rc.right - pChild->MinSize.cx;
				}
				if (pChild->MaxSize.cx < rc.Width() )
				{
					rc.left = rc.right - pChild->MaxSize.cx;
				}

			}
			else
			{
				if (pChild->MinSize.cx > rc.Width() )
				{
					rc.right = rc.left + pChild->MinSize.cx;
				}
				if (pChild->MaxSize.cx < rc.Width() )
				{
					rc.right = rc.left + pChild->MaxSize.cx;
				}
			}
		}
		else if ((pChild->Anchor & ANCHOR_RIGHT) ==  ANCHOR_RIGHT )
		{
			rc.right = this->right - pChild->RightOffset;     
			rc.left = rc.right - pChild->Width();

			if (pChild->MinSize.cx > rc.Width() )
			{
				rc.left = rc.right - pChild->MinSize.cx;
			}
			if (pChild->MaxSize.cx < rc.Width() )
			{
				rc.left = rc.right - pChild->MaxSize.cx;
			}
		}
		else if ((pChild->Anchor & ANCHOR_LEFT) == ANCHOR_LEFT )
		{
			rc.left = this->left + pChild->LeftOffset;
			rc.right = rc.left + pChild->Width();   

			if (pChild->MinSize.cx > rc.Width() )
			{
				rc.right = rc.left + pChild->MinSize.cx;
			}
			if (pChild->MaxSize.cx < rc.Width() )
			{
				rc.right = rc.left + pChild->MaxSize.cx;
			}
		}
		else
		{
			// it should never be here
			ASSERT( FALSE );
		}


		if ((pChild->Anchor & ANCHOR_VERTICALLY_CENTERED) ==  ANCHOR_VERTICALLY_CENTERED )
		{
			rc.top = this->top + ((int)( (this->Height() - pChild->Height()) /  2));
			rc.bottom = rc.top + pChild->Height();

			BOOL bReposition = FALSE;
			if (pChild->MinSize.cy > rc.Height() )
			{
				bReposition = TRUE;
				rc.bottom = rc.top + pChild->MinSize.cy;
			}
			if (pChild->MaxSize.cy < rc.Height() )
			{
				bReposition = TRUE;
				rc.bottom = rc.top + pChild->MaxSize.cy;
			}

			if (bReposition)
			{
				int nHeight = rc.Height();
				rc.top = (int)( (this->Height() - nHeight) /  2);
				rc.bottom = rc.top + nHeight;
			}
		}
		else if ((pChild->Anchor & ANCHOR_VERTICALLY ) == ANCHOR_VERTICALLY )
		{
			rc.top = this->top + pChild->TopOffset;
			rc.bottom = this->bottom - pChild->BottomOffset;

			if ((pChild->Anchor & ANCHOR_PRIORITY_BOTTOM) == ANCHOR_PRIORITY_BOTTOM)
			{
				if (pChild->MinSize.cy > rc.Height() )
				{
					rc.top = rc.bottom - pChild->MinSize.cy;
				}
				if (pChild->MaxSize.cy < rc.Height() )
				{
					rc.top = rc.bottom - pChild->MaxSize.cy;
				}
			}
			else
			{
				if (pChild->MinSize.cy > rc.Height() )
				{
					rc.bottom = rc.top + pChild->MinSize.cy;
				}
				if (pChild->MaxSize.cy < rc.Height() )
				{
					rc.bottom = rc.top + pChild->MaxSize.cy;
				}
			}
		}
		else if ((pChild->Anchor & ANCHOR_BOTTOM) == ANCHOR_BOTTOM )
		{
			rc.bottom = this->bottom - pChild->BottomOffset;     
			rc.top = rc.bottom - pChild->Height();

			if (pChild->MinSize.cy > rc.Height() )
			{
				rc.top = rc.bottom - pChild->MinSize.cy;
			}
			if (pChild->MaxSize.cy < rc.Height() )
			{
				rc.top = rc.bottom - pChild->MaxSize.cy;
			}
		}
		else if ((pChild->Anchor & ANCHOR_TOP) == ANCHOR_TOP )
		{
			rc.top = this->top + pChild->TopOffset;
			rc.bottom = rc.top + pChild->Height();   

			if (pChild->MinSize.cy > rc.Height() )
			{
				rc.bottom = rc.top + pChild->MinSize.cy;
			}
			if (pChild->MaxSize.cy < rc.Height() )
			{
				rc.bottom = rc.top + pChild->MaxSize.cy;
			}
		}
		else
		{
			// it should never be here
			ASSERT( FALSE );
		}
		pChild->SetRect(rc.TopLeft(), rc.BottomRight());

		pChild->OnResized();

	}
}
BOOL CPanel::AddChild(CPanel * pChild)
{
	if (pChild->Parent != NULL)
	{
		BOOL bOk = pChild->Parent->RemoveChild(pChild);
		if (bOk == FALSE)
		{
			return FALSE;
		}
	}
	pChild->LeftOffset = pChild->left - this->left;
	pChild->TopOffset = pChild->top - this->top;
	pChild->RightOffset = this->right - pChild->right;
	pChild->BottomOffset = this->bottom - pChild->bottom;

	pChild->Parent = this;

	Children.AddTail( pChild );
	return TRUE;
}

BOOL CPanel::RemoveChild(CPanel * pChild)
{
	POSITION pos = Children.Find(pChild);
	if (pos == NULL)
	{
		return FALSE;
	}
	Children.RemoveAt(pos);
	return TRUE;
	
}

BOOL CPanel::SetAnchor(UINT anchor)
{
	if ((anchor & ANCHOR_VERTICALLY_CENTERED) <= 0 )
	{
		if ((anchor & ANCHOR_TOP) <= 0 )
		{
			if ((anchor & ANCHOR_BOTTOM) <= 0 )
			{
				anchor |= ANCHOR_TOP;  // default
			}
		}
	}

	if ((anchor & ANCHOR_HORIZONTALLY_CENTERED) <= 0 )
	{
		if ((anchor & ANCHOR_LEFT) <= 0 )
		{
			if ((anchor & ANCHOR_RIGHT) <= 0 )
			{
				anchor |= ANCHOR_LEFT; // default
			}
		}
	}
	Anchor = anchor;

	return TRUE;
}

BOOL CPanel::SetMinSize(CSize & size)
{  
	if (MaxSize.cx < size.cx)  
	{
		return FALSE;
	}
	if (MaxSize.cy < size.cy)
	{
		return FALSE;
	}

	MinSize = size;
	return TRUE;
}

BOOL CPanel::SetMaxSize(CSize & size)
{
	if (MinSize.cx > size.cx)  
	{
		return FALSE;
	}
	if (MinSize.cy > size.cy)
	{
		return FALSE;
	}

	MaxSize = size;
	return TRUE;
}

BOOL CPanel::SetOffset(UINT anchor, int offset)
{
	BOOL bRes = FALSE;

	if (anchor & ANCHOR_TOP)
	{
		TopOffset = offset;
		bRes = TRUE;
	}
	
	if (anchor & ANCHOR_LEFT)
	{
		LeftOffset = offset;
		bRes = TRUE;
	}
	
	if (anchor & ANCHOR_BOTTOM)
	{
		BottomOffset = offset;
		bRes = TRUE;
	}

	if (anchor & ANCHOR_RIGHT)
	{
		RightOffset = offset;
		bRes = TRUE;
	}

	return bRes;
}

BOOL CPanel::SetVisible(BOOL bVisible)
{
	m_bVisible = bVisible;

	POSITION pos = Children.GetHeadPosition();
	while( pos )
	{
		CPanel *pPanel = Children.GetNext( pos );
		pPanel->SetVisible( bVisible );
	}

	return TRUE;
}

BOOL CPanel::GetVisible(BOOL &bVisible)
{
	bVisible = m_bVisible;
	return TRUE;
}

BOOL CPanel::IsVisible()
{
	return m_bVisible;
}


CString CPanel::ToString()
{
	CString sFormat(_T("Name(%s), Type(%s), Anchor(%d), Size(w:%d, h:%d), Area(l:%d, t:%d, r:%d, b:%d), MinSize(w:%d, h:%d), MaxSize(w:%d, h:%d), Parent(%s), ChildrenCount(%d)"));

	CString sTo;
	sTo.Format(sFormat, Name, GetTypeName(), Anchor, Width(), Height(), left, top, right, bottom, MinSize.cx, MinSize.cy, MaxSize.cx, MaxSize.cy, (Parent == NULL? _T("NULL") : Parent->Name), Children.GetCount());
	return sTo;
}

CString CPanel::GetTypeName()
{
	return _T("CPanel");
}

CWnd * CPanel::GetHookedWnd()
{
	if (Parent != NULL)
		return Parent->GetHookedWnd();

	return NULL;
}

BOOL CPanel::PtInTriangle( CPoint point, UINT nEdge ) const
{
	switch( nEdge )
	{
		case ANCHOR_TOPLEFT:
			return PtInTriangle( point, CPoint(left,top), CPoint(right,top), CPoint(left,bottom) );

		case ANCHOR_TOPRIGHT:
			return PtInTriangle( point, CPoint(left,top), CPoint(right,top), CPoint(right,bottom) );

		case ANCHOR_BOTTOMLEFT:
			return PtInTriangle( point, CPoint(left,top), CPoint(left,bottom), CPoint(right,bottom) );

		case ANCHOR_BOTTOMRIGHT:
			return PtInTriangle( point, CPoint(left,bottom), CPoint(right,top), CPoint(right,bottom) );

		default:
			return FALSE;
	}
}

/// \note Works only for not big differences between points (max ~10000) :-)
static int GetSign( const CPoint &a, const CPoint &b, const CPoint &c )
{
	return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
}

BOOL CPanel::PtInTriangle( CPoint point, CPoint v1, CPoint v2, CPoint v3 )
{
	bool b1 = GetSign(point, v1, v2) < 0;
	bool b2 = GetSign(point, v2, v3) < 0;
	bool b3 = GetSign(point, v3, v1) < 0;

	return ((b1 == b2) && (b2 == b3));
}


/////////////////////////////  CRootPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CRootPanel::CRootPanel() : CPanel()
{
	m_pHookWnd = NULL;
}

CRootPanel::~CRootPanel()
{
}

CWnd * CRootPanel::GetHookedWnd()
{
	return m_pHookWnd;
}

CString CRootPanel::GetTypeName()
{
	return _T("CRootPanel");
}

/////////////////////////////  CUIPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CUIPanel::CUIPanel(const CRect * prc, UINT uID) : CPanel(prc)
{
	m_uID = uID;
	m_bOle = FALSE;
}

CUIPanel::~CUIPanel()
{

}

CString CUIPanel::GetTypeName()
{
	return _T("CUIPanel");
}

BOOL CUIPanel::SetVisible(BOOL bVisible)
{
	BOOL bRes = FALSE;
	
	CPanel::SetVisible( bVisible );

	CWnd *wnd = GetHookedWnd();
	if( wnd )
	{
		CWnd *ctl = wnd->GetDlgItem( m_uID );
		if( ctl )
			bRes = ctl->ShowWindow( bVisible ? SW_SHOW : SW_HIDE );
	}

	return bRes;
}

BOOL CUIPanel::GetVisible(BOOL &bVisible)
{
	bVisible = m_bVisible;
	return TRUE;
}

BOOL CUIPanel::IsVisible()
{
	return m_bVisible;
}


/////////////////////////////  CVisualPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVisualPanel::CVisualPanel() : CPanel()
{
	m_bVisible = FALSE;
	m_rcPrev.SetRect(0, 0, 0, 0);
}

CVisualPanel::CVisualPanel(const CRect * prc) : CPanel(prc)
{
	m_bVisible = FALSE;
	m_rcPrev.SetRect(this->left, this->top, this->right, this->bottom);
}

CVisualPanel::~CVisualPanel()
{


}

void CVisualPanel::Draw(CDC * pDC)
{

}

CString CVisualPanel::GetTypeName()
{
	return _T("CVisualPanel");
}

void CVisualPanel::OnResized()
{
	CWnd * pWnd = NULL;
	if ((pWnd = GetHookedWnd()) != NULL)
	{
		pWnd->InvalidateRect( &m_rcPrev, FALSE );
		pWnd->InvalidateRect( this, FALSE );
	}
	m_rcPrev.SetRect(this->left, this->top, this->right, this->bottom);
}

/////////////////////////////  CGripperPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CGripperPanel::CGripperPanel() : CVisualPanel()
{
	m_hTheme = NULL;
}

CGripperPanel::CGripperPanel(const CRect * prc) : CVisualPanel(prc)
{
	m_hTheme = NULL;
}

CGripperPanel::~CGripperPanel()
{
	if (m_hTheme != NULL)
	{
		(void)::CloseThemeData(m_hTheme);
		m_hTheme = NULL;
	}
}
void CGripperPanel::Draw(CDC * pDC)
{
	int iPartId = SBP_SIZEBOX;
	int iStateId = 5; //SZB_HALFBOTTOMRIGHTALIGN;
	LPCWSTR wClassName = L"SCROLLBAR";
	HWND hwnd = NULL;

	if (m_hTheme == NULL)
	{
		hwnd = GetHookedWnd()->GetSafeHwnd();
		if (hwnd == NULL)
			return;

		m_hTheme = ::OpenThemeData(hwnd, wClassName);
	}

	BOOL bDrawStd = TRUE;

	if (m_hTheme)
	{
		HRESULT lres = ::DrawThemeBackground(m_hTheme, pDC->m_hDC, iPartId, iStateId, this, this);
		if (!SUCCEEDED(lres))
		{
			// could't draw - possibly theme changed, need reopen theme
			(void)::CloseThemeData(m_hTheme);
			m_hTheme = NULL;

			if( hwnd )
			{
				m_hTheme = ::OpenThemeData(hwnd, wClassName);
				if( m_hTheme )
					lres = ::DrawThemeBackground(m_hTheme, pDC->m_hDC, iPartId, iStateId, this, this);
			}
		}
		
		if (SUCCEEDED(lres))
			bDrawStd = FALSE;
	}

	if (bDrawStd)
		(void)pDC->DrawFrameControl(this, DFC_SCROLL, DFCS_SCROLLSIZEGRIP );
}

CString CGripperPanel::GetTypeName()
{
	return _T("CGripperPanel");
}

/////////////////////////////  CSplitterGripperPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CSplitterGripperPanel::CSplitterGripperPanel(CWndResizer::SplitterOrientation type) : CVisualPanel()
{
	m_OrienType = type;
}
CSplitterGripperPanel::~CSplitterGripperPanel()
{

}
void CSplitterGripperPanel::Draw(CDC * pDC)
{
	CPen penDark(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
	CPen penWhite(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)); //RGB(255, 255, 255));

	if (m_OrienType == CWndResizer::SPLIT_CONTAINER_H )
	{
		CPen * pOrigPen = pDC->SelectObject(&penWhite);

		CRect rc(0, 0, 0, 0);
		rc.SetRect(left + 1, top + 1, left + 3, top + 3); 
		while(rc.bottom <= bottom + 1)
		{
			pDC->Rectangle(&rc);
			rc.OffsetRect(0, 4);
		}

		pDC->SelectObject(&penDark);
		rc.SetRect(left, top, left + 2, top + 2);
		while(rc.bottom <= bottom)
		{
			pDC->Rectangle(&rc);
			rc.OffsetRect(0, 4);
		}

		pDC->SelectObject(pOrigPen);
	}
	else
	{
		CPen * pOrigPen = pDC->SelectObject(&penWhite);

		CRect rc(0, 0, 0, 0);
		rc.SetRect(left + 1, top + 1, left + 3, top + 3); 
		while(rc.right <= right + 1)
		{
			pDC->Rectangle(&rc);
			rc.OffsetRect(4, 0);
		}

		pDC->SelectObject(&penDark);
		rc.SetRect(left, top, left + 2, top + 2);
		while(rc.right <= right)
		{
			pDC->Rectangle(&rc);
			rc.OffsetRect(4, 0);
		}

		pDC->SelectObject(pOrigPen);
	} 
}

CString CSplitterGripperPanel::GetTypeName()
{
	return _T("CSplitterGripperPanel");
}


/////////////////////////////  CSpitterPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CSpitterPanel::CSpitterPanel(const CRect * prc, CWndResizer::SplitterOrientation type) : CPanel(prc)
{
	m_OrienType = type;
	m_pGrippePanel = NULL;

	if (m_pGrippePanel == NULL)
	{
		m_pGrippePanel = new CSplitterGripperPanel(type);
		if (m_OrienType == CWndResizer::SPLIT_CONTAINER_H )
		{
			m_pGrippePanel->SetRect(0, 0, 3, 12);
		}
		else
		{
			m_pGrippePanel->SetRect(0, 0, 12, 3);
		}
		m_pGrippePanel->SetAnchor ( ANCHOR_HORIZONTALLY_CENTERED | ANCHOR_VERTICALLY_CENTERED );
		m_pGrippePanel->SetMinSize(CSize(2,2));
		BOOL bOk = AddChild(m_pGrippePanel);
		ASSERT( bOk);
	}
}
CSpitterPanel::CSpitterPanel(CWndResizer::SplitterOrientation type) : CPanel()
{
	m_OrienType = type;
	m_pGrippePanel = NULL;

}

CSpitterPanel::~CSpitterPanel()
{

}


CString CSpitterPanel::GetTypeName()
{
	return _T("CSpitterPanel");
}

#ifdef USE_SPLIT_PANEL
/////////////////////////////  CSpitPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CSplitPanel::CSplitPanel(CPanel * pPanel) : CPanel(pPanel)
{
	pPanel->LeftOffset = 0;
	pPanel->TopOffset = 0;
	pPanel->RightOffset = 0;
	pPanel->BottomOffset = 0;
	pPanel->Anchor = ANCHOR_ALL;
	Name = pPanel->Name;
	pPanel->Name = _T("");
	m_pOriginalPanel = pPanel;
	MaxSize = pPanel->MaxSize;
	MinSize = pPanel->MinSize;
	Children.AddTail( pPanel );
}

CSplitPanel::~CSplitPanel()
{
}

BOOL CSplitPanel::SetAnchor(UINT anchor)
{
	return FALSE;
}

BOOL CSplitPanel::AddChild(CPanel * prc)
{
	return m_pOriginalPanel->AddChild(prc);
}
BOOL CSplitPanel::RemoveChild(CPanel * prc)
{
	return m_pOriginalPanel->RemoveChild(prc);
}


CString CSplitPanel::GetTypeName()
{
	return _T("CSplitPanel");
}

BOOL CSplitPanel::SetMinSize(CSize & size)
{
	if( !CPanel::SetMinSize( size ) )
		return FALSE;

	if( m_pOriginalPanel )
		return m_pOriginalPanel->SetMinSize( size );

	return TRUE;
}

BOOL CSplitPanel::SetMaxSize(CSize & size)
{
	if( !CPanel::SetMaxSize( size ) )
		return FALSE;

	if( m_pOriginalPanel )
		return m_pOriginalPanel->SetMaxSize( size );

	return TRUE;
}
#endif

/////////////////////////////  CSplitContainer
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CSplitContainer::CSplitContainer(CSplitPanel * pPanelA, CSplitPanel * pPanelB, CWndResizer::SplitterOrientation type) : CPanel()
{
	m_IsSplitterFixed = FALSE;
	m_FixedPanel = 0;

	m_pPanelA = NULL;
	m_pPanelB = NULL;
	m_pSplitter = NULL;

	m_Orientation = type;
	m_pPanelA = pPanelA;
	m_pPanelB = pPanelB;
	UnionRect(m_pPanelA, m_pPanelB);  

	CRect rc(0, 0, 0, 0);  
	GetSplitArea(&rc);
	m_pSplitter = new CSpitterPanel(&rc, type);
	m_pSplitter->m_pGrippePanel->m_bVisible = FALSE;


	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
	{
		m_pPanelA->Anchor = (ANCHOR_LEFT | ANCHOR_TOP | ANCHOR_BOTTOM);
		m_pPanelB->Anchor = (ANCHOR_RIGHT | ANCHOR_TOP | ANCHOR_BOTTOM);
	}
	else
	{
		m_pPanelA->Anchor = (ANCHOR_LEFT | ANCHOR_TOP | ANCHOR_RIGHT);
		m_pPanelB->Anchor = (ANCHOR_LEFT | ANCHOR_BOTTOM | ANCHOR_RIGHT);
	}

	m_nSplitterSize = GetSplitterSize(m_pPanelA, m_pPanelB);

	BOOL bOk = AddChild(m_pPanelA);
	ASSERT( bOk);

	bOk = AddChild(m_pSplitter);
	ASSERT( bOk);

	bOk = AddChild(m_pPanelB);
	ASSERT( bOk);

	UpdateRatio();
}

CSplitContainer::~CSplitContainer()
{
}

void CSplitContainer::OnResized()
{
	CPanel::OnResized();

	BOOL bShowA = TRUE;
	BOOL bShowB = TRUE;
	BOOL bShowS = TRUE;
 
	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
	{
		if (Width() < MinSize.cx)
			return;

		if (m_FixedPanel == CWndResizer::FIXED_LEFT ) // left panel is fixed
		{
			m_pPanelB->left = m_pPanelA->right + m_nSplitterSize;

			if (m_pPanelB->MinSize.cx > m_pPanelB->Width() )
			{
				m_pPanelB->left = m_pPanelB->right - m_pPanelB->MinSize.cx;
				m_pPanelA->right = m_pPanelB->left - m_nSplitterSize;
			}
		}
		else if (m_FixedPanel == CWndResizer::FIXED_RIGHT )  // right panel is fixed
		{
			m_pPanelA->right = m_pPanelB->left - m_nSplitterSize;

			if (m_pPanelA->MinSize.cx > m_pPanelA->Width() )
			{
				m_pPanelA->right = m_pPanelA->left + m_pPanelA->MinSize.cx;
				m_pPanelB->left = m_pPanelA->right + m_nSplitterSize;
			}
		}
		else if (m_FixedPanel == CWndResizer::FIXED_LEFT_HIDDEN ) // left panel is fixed and hidden
		{
			m_pPanelB->left  = m_pPanelA->left;
			m_pPanelA->right = m_pPanelA->left;
			bShowS = FALSE;
		}
		else if (m_FixedPanel == CWndResizer::FIXED_RIGHT_HIDDEN )  // right panel is hidden
		{
			m_pPanelA->right = m_pPanelB->right;
			m_pPanelB->left  = m_pPanelB->right;
			bShowS = FALSE;
		}
		else
		{
			m_pPanelA->right = (LONG) ((double)m_pPanelA->left + ((double)this->Width() * m_nRatio));

			if (m_pPanelA->MinSize.cx > m_pPanelA->Width() )
			{
				m_pPanelA->right = m_pPanelA->left + m_pPanelA->MinSize.cx;
			}

			m_pPanelB->left = m_pPanelA->right + m_nSplitterSize;

			if (m_pPanelB->MinSize.cx > m_pPanelB->Width() )
			{
				m_pPanelB->left = m_pPanelB->right - m_pPanelB->MinSize.cx;
				m_pPanelA->right = m_pPanelB->left - m_nSplitterSize;
			}
		}

		if (m_pPanelA->Width() <= 2)
			bShowA = FALSE;
		if (m_pPanelB->Width() <= 2)
			bShowB = FALSE;
	}
	else /*if (m_Orientation == CWndResizer::SPLIT_CONTAINER_V)*/
	{
		if (Height() < MinSize.cy)
		{
			return;
		}

		if (m_FixedPanel == CWndResizer::FIXED_TOP ) // top panel is fixed
		{
			m_pPanelB->top = m_pPanelA->bottom + m_nSplitterSize;

			if (m_pPanelB->MinSize.cy > m_pPanelB->Height() )
			{
				m_pPanelB->top = m_pPanelB->bottom - m_pPanelB->MinSize.cy;
				m_pPanelA->bottom = m_pPanelB->top - m_nSplitterSize;
			}
		}
		else if (m_FixedPanel == CWndResizer::FIXED_BOTTOM )  // bottom panel is fixed
		{
			m_pPanelA->bottom = m_pPanelB->top - m_nSplitterSize;

			if (m_pPanelA->MinSize.cy > m_pPanelA->Height() )
			{
				m_pPanelA->bottom = m_pPanelA->top + m_pPanelA->MinSize.cy;
				m_pPanelB->top = m_pPanelA->bottom + m_nSplitterSize;
			}
		}
		else if (m_FixedPanel == CWndResizer::FIXED_TOP_HIDDEN ) // top panel is fixed and hidden
		{
			m_pPanelB->top = m_pPanelA->top;
			bShowA = FALSE;
			bShowS = FALSE;
		}
		else if (m_FixedPanel == CWndResizer::FIXED_BOTTOM_HIDDEN )	// bottom panel is fixed and hidden
		{
			m_pPanelA->bottom = m_pPanelB->bottom;
			bShowS = FALSE;
			bShowB = FALSE;
		}
		else
		{
			m_pPanelA->bottom = (LONG) ((double)m_pPanelA->top + ((double)this->Height() * m_nRatio));

			if (m_pPanelA->MinSize.cy > m_pPanelA->Height() )
			{
				m_pPanelA->bottom = m_pPanelA->top + m_pPanelA->MinSize.cy;
			}

			m_pPanelB->top = m_pPanelA->bottom + m_nSplitterSize;

			if (m_pPanelB->MinSize.cy > m_pPanelB->Height() )
			{
				m_pPanelB->top = m_pPanelB->bottom - m_pPanelB->MinSize.cy;
				m_pPanelA->bottom = m_pPanelB->top - m_nSplitterSize;
			}
		}

		if (m_pPanelA->Height() <= 2)
			bShowA = FALSE;
		if (m_pPanelB->Height() <= 2)
			bShowB = FALSE;
	}

#if 0
	GetSplitArea(m_pSplitter);
	m_pPanelA->OnResized();
	m_pPanelB->OnResized();
	m_pSplitter->OnResized();
#else
	m_pPanelA->SetVisible( bShowA );
	if( bShowA )
		m_pPanelA->OnResized();

	m_pPanelB->SetVisible( bShowB );
	if( bShowB )
		m_pPanelB->OnResized();

	if( bShowS )
	{
		if( !bShowA )
			m_nRatio = 0;
		else if( !bShowB )
			m_nRatio = 100;

		GetSplitArea(m_pSplitter);

		m_pSplitter->SetVisible( !m_IsSplitterFixed );
		m_pSplitter->OnResized();
	}
	else
		m_pSplitter->SetVisible( FALSE );

#endif
}

void CSplitContainer::SetSplitterPosition(int leftOfSplitter)
{ 
	short nFixedPanel = m_FixedPanel;
	m_FixedPanel = 0;
	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H )
	{    
		m_pPanelA->right = leftOfSplitter;
		m_pPanelB->left = m_pPanelA->right + m_nSplitterSize;
	}
	else
	{
		m_pPanelA->bottom = leftOfSplitter;
		m_pPanelB->top = m_pPanelA->bottom + m_nSplitterSize;
	}
	UpdateRatio();

	OnResized();

	UpdateRatio();
	m_FixedPanel = nFixedPanel;
}

int CSplitContainer::GetSplitterPosition()
{ 
	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H )
		return m_pPanelA->right;

	return m_pPanelA->bottom;
}

BOOL CSplitContainer::AddChild(CPanel * prc)
{
	if (Children.GetCount() == 3)
		return FALSE;

	return CPanel::AddChild(prc);
}

BOOL CSplitContainer::RemoveChild(CPanel * prc)
{
	return FALSE; // cannot remove child from split container
}

void CSplitContainer::GetSplitArea(CRect * pSplitterPanel)
{

	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
	{
		pSplitterPanel->left = m_pPanelA->right;
		pSplitterPanel->top = this->top;
		pSplitterPanel->right = m_pPanelB->left;
		pSplitterPanel->bottom = this->bottom;
	}
	else // vertical
	{
		pSplitterPanel->left = this->left;
		pSplitterPanel->top = m_pPanelA->bottom;
		pSplitterPanel->right = this->right;
		pSplitterPanel->bottom = m_pPanelB->top;
	}  
}

int CSplitContainer::GetSplitterSize(CPanel * m_pPanelA, CPanel * m_pPanelB)
{
	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H)
	{
		int nWidth = m_pPanelB->left - m_pPanelA->right;
		return nWidth;
	}
	else // vertical
	{
		int nHeight = m_pPanelB->top - m_pPanelA->bottom;
		return nHeight;
	}  
}




void CSplitContainer::UpdateRatio()
{

	if (m_Orientation == CWndResizer::SPLIT_CONTAINER_H )
	{    
		m_nRatio = (double)m_pPanelA->Width() / (double)this->Width();
	}
	else
	{
		m_nRatio = (double)m_pPanelA->Height() / (double)this->Height();
	}

}

CSplitContainer *  CSplitContainer::Create(CPanel * pPanelA, CPanel * pPanelB)
{
#ifdef USE_SPLIT_PANEL
	CSplitPanel * pSplitPanelA =  dynamic_cast<CSplitPanel *>( pPanelA );
	if (pSplitPanelA != NULL)
	{
		return NULL;  // already a part of a CSplitContainer
	}

	CSplitPanel * pSplitPanelB =  dynamic_cast<CSplitPanel *>( pPanelB );
	if (pSplitPanelB != NULL)
	{
		return NULL; // already a part of a CSplitContainer
	}
#endif

	CRect rcDest(0, 0, 0, 0);
	CWndResizer::SplitterOrientation orien = CWndResizer::SPLIT_CONTAINER_H;

	if (::IntersectRect(&rcDest, pPanelA, pPanelB) == TRUE) // invalid spliter container, a spliter continer's two panel cannot intersect each other
	{
		return NULL;
	} 


	if (pPanelA->right < pPanelB->left)
	{
			orien = CWndResizer::SPLIT_CONTAINER_H;    
	}
	else if (pPanelA->bottom < pPanelB->top)
	{
			orien =  CWndResizer::SPLIT_CONTAINER_V;    
	}
	else
	{
		return NULL;
	}
#ifdef USE_SPLIT_PANEL
	if (pPanelA->Parent != NULL)
	{
		if (pPanelA->Parent->RemoveChild( pPanelA ) == FALSE)
		{
			return NULL;
		}
	}
	if (pPanelB->Parent != NULL)
	{
		if (pPanelB->Parent->RemoveChild( pPanelB ) == FALSE)
		{
			return NULL;
		}
	}
#endif

//	pSplitPanelA = new CSplitPanel( pPanelA );  
//	pSplitPanelB = new CSplitPanel( pPanelB );  


//	CSplitContainer * pSpliter = new CSplitContainer(pSplitPanelA, pSplitPanelB, orien);
	CSplitContainer * pSpliter = new CSplitContainer(pPanelA, pPanelB, orien);
	return pSpliter;
}

CString CSplitContainer::GetTypeName()
{
	return _T("CSplitContainer");
}

void CSplitContainer::SetFixedPanel(short nFixedPanel /* 1=left/top; 2=right/bototm; other=no fixed panel */)
{
	m_FixedPanel = nFixedPanel;
}

short CSplitContainer::GetFixedPanel()
{
	return m_FixedPanel;
}

void CSplitContainer::SetIsSplitterFixed(BOOL bFixed)
{
	m_IsSplitterFixed = bFixed;
}

BOOL CSplitContainer::GetIsSplitterFixed()
{
	return m_IsSplitterFixed;
}

void CSplitContainer::SetShowSplitterGrip(BOOL bShow)
{
	m_pSplitter->m_pGrippePanel->m_bVisible = bShow;
}

BOOL CSplitContainer::GetShowSplitterGrip()
{
	return m_pSplitter->m_pGrippePanel->m_bVisible;
}


/////////////////////////////  CFlowLayoutPanel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CFlowLayoutPanel::CFlowLayoutPanel() : CPanel()
{
	m_nItemSpacingX = 0;
	m_nItemSpacingY = 0;
	m_nFlowDirection = CWndResizer::LEFT_TO_RIGHT;
}
CFlowLayoutPanel::CFlowLayoutPanel(const CRect * prc) : CPanel(prc)
{
	m_nItemSpacingX = 0;
	m_nItemSpacingY = 0;
	m_nFlowDirection = CWndResizer::LEFT_TO_RIGHT;
}
CFlowLayoutPanel::~CFlowLayoutPanel()
{

}
void CFlowLayoutPanel::OnResized()
{
	int max = 0; // maximimum height of a item in the row in case of left-to-right, otherwise maximum width of a item
	int x = left;
	int y = top;
	POSITION pos = Children.GetHeadPosition();

	// first one will be at the top-left corner, no matter what
	if (pos != NULL)
	{
		CPanel * pPanel = Children.GetNext(pos);
		pPanel->MoveToXY(x, y);
		pPanel->OnResized();

		if (m_nFlowDirection == CWndResizer::LEFT_TO_RIGHT)
		{
			x += pPanel->Width() + m_nItemSpacingX;
			max = (pPanel->Height() > max ? pPanel->Height() : max);
		}
		else
		{
			y += pPanel->Height() + m_nItemSpacingY;
			max = (pPanel->Width() > max ? pPanel->Width() : max);
		}
	}


	if (m_nFlowDirection == CWndResizer::LEFT_TO_RIGHT)
	{
		while(pos != NULL)
		{
			CPanel * pPanel = Children.GetNext(pos);
			// check to see if it is to wrap
			if (x + pPanel->Width() > right)
			{
				x = left;
				y += (max + m_nItemSpacingY);
				max = 0;
			}

			pPanel->MoveToXY(x, y);
			pPanel->OnResized();

			x += pPanel->Width() + m_nItemSpacingX;
			max = (pPanel->Height() > max ? pPanel->Height() : max);
		}
	}
	else
	{
		while(pos != NULL)
		{
			CPanel * pPanel = Children.GetNext(pos);
			// check to see if it is to wrap
			if (y + pPanel->Height() > bottom)
			{
				x += (max + m_nItemSpacingX);
				y = top;
				max = 0;
			}

			pPanel->MoveToXY(x, y);
			pPanel->OnResized();

			y += pPanel->Height() + m_nItemSpacingY;
			max = (pPanel->Width() > max ? pPanel->Width() : max);
		}

	}
}
CString CFlowLayoutPanel::GetTypeName()
{
	return _T("CFlowLayoutPanel");

}
void CFlowLayoutPanel::SetFlowDirection(CWndResizer::FlowDirection direction)
{
	m_nFlowDirection = direction;
}
CWndResizer::FlowDirection CFlowLayoutPanel::GetFlowDirection()
{
	return m_nFlowDirection;
}
void CFlowLayoutPanel::SetItemSpacingX(int nSpace)
{
	m_nItemSpacingX = nSpace;
}
int CFlowLayoutPanel::GetItemSpacingX()
{
	return m_nItemSpacingX;
}
void CFlowLayoutPanel::SetItemSpacingY(int nSpace)
{
	m_nItemSpacingY = nSpace;
}
int CFlowLayoutPanel::GetItemSpacingY()
{
	return m_nItemSpacingY;
}

GeneralRe: Support for hiding/showing controls Pin
mskalski4-Jun-12 4:25
mskalski4-Jun-12 4:25 
GeneralRe: Support for hiding/showing controls Pin
Mizan Rahman5-Jun-12 4:25
Mizan Rahman5-Jun-12 4:25 
QuestionPossible flicker fix Pin
Jacob Skjoet11-Apr-12 8:57
Jacob Skjoet11-Apr-12 8:57 
Questionhow I implement this class in another project? Pin
manomano9-Apr-12 10:04
manomano9-Apr-12 10:04 
AnswerRe: how I implement this class in another project? Pin
Mizan Rahman10-Apr-12 1:59
Mizan Rahman10-Apr-12 1:59 
Questionproportional spacing? Nice work! Pin
Bill Heitler10-Mar-12 5:38
Bill Heitler10-Mar-12 5:38 
AnswerRe: proportional spacing? Nice work! Pin
Mizan Rahman12-Mar-12 3:59
Mizan Rahman12-Mar-12 3:59 
QuestionGroupbox - overlap problem Pin
nnhattan10-Feb-12 11:04
nnhattan10-Feb-12 11:04 
AnswerRe: Groupbox - overlap problem Pin
nnhattan14-Feb-12 4:43
nnhattan14-Feb-12 4:43 
GeneralRe: Groupbox - overlap problem Pin
John W Beatty22-Feb-12 1:51
John W Beatty22-Feb-12 1:51 
QuestionFor OCX control ! Pin
nnhattan2-Feb-12 5:59
nnhattan2-Feb-12 5:59 
AnswerRe: For OCX control ! Pin
nnhattan2-Feb-12 6:55
nnhattan2-Feb-12 6:55 
QuestionGreat! Pin
Igor Okulist5-Jan-12 6:28
Igor Okulist5-Jan-12 6:28 
Questionnice one and well presented. Pin
revram11-Dec-11 1:14
revram11-Dec-11 1:14 
GeneralMy vote of 5 Pin
Franc Morales8-Dec-11 20:41
Franc Morales8-Dec-11 20:41 
Questionflicker Pin
Dave Calkins7-Dec-11 13:20
Dave Calkins7-Dec-11 13:20 
AnswerRe: flicker Pin
Mizan Rahman7-Dec-11 21:55
Mizan Rahman7-Dec-11 21:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.