// Copyright (c) 2002 // Sergey Klimov (kidd@ukr.net) // WTL Docking windows // // This code is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // This code may be used in compiled form in any way you desire. This // file may be redistributed unmodified by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. If // the source code in this file is used in any commercial application // then a simple email woulod be nice. #ifndef __WTL_DW__WNDFRMPKG_H__ #define __WTL_DW__WNDFRMPKG_H__ #pragma once #ifndef __ATLMISC_H__ #error WndFrmPkg.h requires atlmisc.h to be included first #endif #include #include "ssec.h" #include "DDTracker.h" #ifdef USE_BOOST #include #endif namespace dockwins{ class CWndFrame { public: typedef long position; typedef long distance; class CCmp { public: CCmp(HWND hWnd) :m_hWnd(hWnd) { } bool operator ()(const CWndFrame& frame) const { return (frame.hwnd() == m_hWnd); } protected: HWND m_hWnd; }; CWndFrame(position pos=0,HWND hWnd=NULL) :m_hWnd(hWnd),m_pos(pos) { } HWND hwnd() const { return m_hWnd; } operator position() const { return (position)m_pos; } CWndFrame& operator += (position val) { m_pos+=val; return *this; } CWndFrame& operator -= (position val) { m_pos-=val; return *this; } CWndFrame& operator = (position pos) { m_pos=pos; return *this; } CWndFrame& operator = (double pos) { m_pos=pos; return *this; } double get_real() { return m_pos; } HDWP DeferFramePos(HDWP hdwp,long x1,long y1,long x2,long y2) const { return ::DeferWindowPos(hdwp,hwnd(), NULL, x1,y1, x2-x1,y2-y1, SWP_NOZORDER | SWP_NOACTIVATE); } void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const { ::SendMessage(m_hWnd,WM_GETMINMAXINFO,NULL,reinterpret_cast(pMinMaxInfo)); } distance MinDistance() const { DFMHDR dockHdr; dockHdr.code=DC_GETMINDIST; dockHdr.hWnd=m_hWnd; dockHdr.hBar=::GetParent(m_hWnd); assert(::IsWindow(dockHdr.hBar)); return (distance)::SendMessage(dockHdr.hBar,WMDF_DOCK,NULL,reinterpret_cast(&dockHdr)); } protected: double m_pos; mutable HWND m_hWnd; }; //template //struct CWndFrameTraits : ssec::spraits //{ // typedef ssec::spraits baseClass; // static distance min_distance(const T& x) // { // const distance dist=TMinDist; // return dist+x.MinDistance(); // } // //}; template< class TFrame = CWndFrame , class TTraits = CDockingFrameTraits> class CWndFramesPackageBase { typedef typename TFrame CFrame; typedef typename TTraits CTraits; typedef typename CTraits::CSplitterBar CSplitterBar; typedef CWndFramesPackageBase thisClass; protected: enum {splitterSize=CSplitterBar::sbThickness}; typedef typename CFrame::position position; #if _MSC_VER >= 1310 template #else template #endif struct CWndFrameTraits : ssec::spraits { typedef ssec::spraits baseClass; static distance min_distance(const T& x) { const distance dist=TMinDist; return dist+x.MinDistance(); } }; typedef CWndFrameTraits CFrameTraits; typedef ssec::ssection CFrames; typedef typename CFrames::iterator iterator; typedef typename CFrames::reverse_iterator reverse_iterator; typedef typename CFrames::const_iterator const_iterator; typedef typename CFrames::const_reverse_iterator const_reverse_iterator; typedef ssec::bounds_type CBounds; protected: struct CEmbeddedSplitterBar : public CSplitterBar { CEmbeddedSplitterBar(bool bHorizontal,position vertex,const CRect& rcClient) :CSplitterBar(bHorizontal) { if(IsHorizontal()) { top=vertex; bottom=top+GetThickness(); left=rcClient.left; right=rcClient.right; } else { left=vertex; right=left+GetThickness(); top=rcClient.top; bottom=rcClient.bottom; } } }; class CEmbeddedSplitterBarPainter { public: CEmbeddedSplitterBarPainter(CDC& dc,bool bHorizontal,const CRect& rc) :m_dc(dc),m_rc(rc),m_bHorizontal(bHorizontal) { } void operator()(const CFrame& ref) const { CEmbeddedSplitterBar splitter(m_bHorizontal,ref,m_rc); splitter.Draw(m_dc); } protected: const CRect& m_rc; bool m_bHorizontal; CDC& m_dc; }; class CSplitterMoveTrackerBase : public IDDTracker//CDDTrackerBaseT { protected: void SetPosition() { assert(m_bounds.bind(m_pos)==m_pos); m_frames.set_position(m_i,m_pos); m_owner.Arrange(m_rc); m_wnd.RedrawWindow(&m_rc,NULL,RDW_INVALIDATE | RDW_UPDATENOW); } public: CSplitterMoveTrackerBase(HWND hWnd,thisClass& owner,const CPoint& pt,const CRect& rc) :m_wnd(hWnd),m_owner(owner),m_frames(owner.m_frames),m_rc(rc) { position pos=owner.IsHorizontal() ? pt.x : pt.y; m_i=m_frames.locate(pos); if(m_i!=m_frames.end()) { m_pos=(*m_i); m_offset=pos-m_pos; m_frames.get_effective_bounds(m_i,m_bounds); } } operator iterator() const { return m_i; } void OnMove(long x, long y) { position pos = m_owner.IsHorizontal() ? x : y; pos=m_bounds.bind(pos-m_offset); if(pos!=m_pos) { m_pos=pos; Move(); } } virtual void Move()=0; protected: thisClass& m_owner; CFrames& m_frames; iterator m_i; CBounds m_bounds; position m_pos; position m_offset; const CRect& m_rc; CWindow m_wnd; }; friend class CSplitterMoveTrackerBase; class CSplitterMoveTrackerFull : public CSplitterMoveTrackerBase { public: CSplitterMoveTrackerFull(HWND hWnd,thisClass& owner,const CPoint& pt,const CRect& rc) :CSplitterMoveTrackerBase(hWnd,owner,pt,rc) { } void Move() { SetPosition(); } }; class CSplitterMoveTrackerGhost : public CSplitterMoveTrackerBase { typedef CEmbeddedSplitterBar CSplitterBar; typedef CSimpleSplitterBarSlider CSlider; public: CSplitterMoveTrackerGhost(HWND hWnd,thisClass& owner, const CPoint& pt,const CRect& rc) :CSplitterMoveTrackerBase(hWnd,owner,pt,rc), m_dc(NULL),m_splitter(!owner.IsHorizontal(),m_pos,rc),m_slider(m_splitter) { CPoint point(rc.TopLeft ()); ::ClientToScreen(hWnd,&point); CSize offset(point.x-rc.left,point.y-rc.top); m_splitter+=offset; m_ghOffset=owner.IsHorizontal() ?offset.cx :offset.cy; } void BeginDrag() { m_splitter.DrawGhostBar(m_dc); } void EndDrag(bool bCanceled) { m_splitter.CleanGhostBar(m_dc); if(!bCanceled) SetPosition(); } void Move() { m_splitter.CleanGhostBar(m_dc); m_slider=m_pos+m_ghOffset; m_splitter.DrawGhostBar(m_dc); } protected: position m_ghOffset; CSplitterBar m_splitter; CSlider m_slider; CWindowDC m_dc; }; template class CMinMaxInfoAccumulator { typedef LPMINMAXINFO (*CFunPtr)(MINMAXINFO& mmInfo,LPMINMAXINFO pMinMaxInfo); public: CMinMaxInfoAccumulator(bool bHorizontal) { m_pFun=bHorizontal ? &MinMaxInfoH : &MinMaxInfoV; } LPMINMAXINFO operator() (LPMINMAXINFO pMinMaxInfo,const CFrame& x) const { MINMAXINFO mmInfo; ZeroMemory(&mmInfo,sizeof(MINMAXINFO)); x.GetMinMaxInfo(&mmInfo); return (*m_pFun)(mmInfo,pMinMaxInfo); } protected: static LPMINMAXINFO MinMaxInfoH(MINMAXINFO& mmInfo,LPMINMAXINFO pMinMaxInfo) { if(pMinMaxInfo->ptMinTrackSize.yptMinTrackSize.y=mmInfo.ptMinTrackSize.y; pMinMaxInfo->ptMinTrackSize.x+=mmInfo.ptMinTrackSize.x+add; return pMinMaxInfo; } static LPMINMAXINFO MinMaxInfoV(MINMAXINFO& mmInfo,LPMINMAXINFO pMinMaxInfo) { if(pMinMaxInfo->ptMinTrackSize.xptMinTrackSize.x=mmInfo.ptMinTrackSize.x; pMinMaxInfo->ptMinTrackSize.y+=mmInfo.ptMinTrackSize.y+add; return pMinMaxInfo; } protected: CFunPtr m_pFun; }; protected: bool ArrangeH(const CRect& rc) { HDWP hdwp=reinterpret_cast(TRUE); const_iterator begin=m_frames.begin(); const_iterator end=m_frames.end(); if(begin!=end) { hdwp=BeginDeferWindowPos((int)m_frames.size()); const_iterator next=begin; while((hdwp!=NULL)&&(++next!=end)) { long x=(*begin)+splitterSize; hdwp=begin->DeferFramePos(hdwp, x, rc.top, (*next), rc.bottom); begin=next; } if(hdwp!=NULL) { long x=(*begin)+splitterSize; hdwp=begin->DeferFramePos(hdwp, x, rc.top, rc.right, rc.bottom); if(hdwp) ::EndDeferWindowPos(hdwp); } } return hdwp!=NULL; } bool ArrangeV(const CRect& rc) { HDWP hdwp=reinterpret_cast(TRUE); const_iterator begin=m_frames.begin(); const_iterator end=m_frames.end(); if(begin!=end) { hdwp=BeginDeferWindowPos((int)m_frames.size()); const_iterator next=begin; while((hdwp!=NULL)&&(++next!=end)) { long y=(*begin)+splitterSize; hdwp=begin->DeferFramePos(hdwp, rc.left, y, rc.right, (*next)); begin=next; } if(hdwp!=NULL) { long y=(*begin)+splitterSize; hdwp=begin->DeferFramePos(hdwp, rc.left, y, rc.right, rc.bottom); if(hdwp) ::EndDeferWindowPos(hdwp); } } return hdwp!=NULL; } bool Arrange(const CRect& rcClient) { bool bRes; if(IsHorizontal()) bRes=ArrangeH(rcClient); else bRes=ArrangeV(rcClient); return bRes; } CWndFramesPackageBase(bool bHorizontal) :m_bHorizontal(bHorizontal),m_frames(0,0) { } public: bool IsHorizontal() const { return m_bHorizontal; } void SetOrientation(bool bHorizontal) { m_bHorizontal=bHorizontal; } HCURSOR GetCursor(const CPoint& pt,const CRect& rc) const { HCURSOR hCursor=NULL; position pos=IsHorizontal() ?pt.x :pt.y; const_iterator i=m_frames.locate(pos); if(i!=m_frames.end()) { CEmbeddedSplitterBar splitter(!IsHorizontal(),(*i),rc); hCursor=splitter.GetCursor(pt); } return hCursor; } /* bool StartSliding(HWND hWnd,const CPoint& pt,const CRect& rc,bool bGhostMove) { std::auto_ptr pTracker; if(bGhostMove) pTracker.reset(new CSplitterMoveTrackerGhost(hWnd,*this,pt,rc)); else pTracker.reset(new CSplitterMoveTrackerFull(hWnd,*this,pt,rc)); bool bRes=false; if(const_iterator(*pTracker)!=m_frames.end()) bRes=TrackDragAndDrop(*pTracker,hWnd); return bRes; } */ bool StartSliding(HWND hWnd,const CPoint& pt,const CRect& rc,bool bGhostMove) { bool bRes=false; position pos=IsHorizontal() ?pt.x :pt.y; const_iterator i=m_frames.locate(pos); if(i!=m_frames.end()) { CEmbeddedSplitterBar splitter(!IsHorizontal(),(*i),rc); if(splitter.IsPtIn(pt)) { std::auto_ptr pTracker; #if (_MSC_VER >= 1310) if(bGhostMove) pTracker.reset(new CSplitterMoveTrackerGhost(hWnd,*this,pt,rc)); else pTracker.reset(new CSplitterMoveTrackerFull(hWnd,*this,pt,rc)); #else // (_MSC_VER < 1310) if(bGhostMove) pTracker=std::auto_ptr( new CSplitterMoveTrackerGhost(hWnd,*this,pt,rc)); else pTracker=std::auto_ptr( new CSplitterMoveTrackerFull(hWnd,*this,pt,rc)); #endif if(const_iterator(*pTracker)!=m_frames.end()) bRes=TrackDragAndDrop(*pTracker,hWnd); } } return bRes; } bool UpdateLayout(const CRect& rc) { CBounds bounds; if(IsHorizontal()) { bounds.low=rc.left; bounds.hi=rc.right; } else { bounds.low=rc.top; bounds.hi=rc.bottom; } bounds.low-=splitterSize; CBounds::distance_t limit=m_frames.distance_limit(); if(bounds.distance()hWnd,WM_GETMINMAXINFO,NULL,reinterpret_cast(&mmInfo)); sz.cx=mmInfo.ptMinTrackSize.x; sz.cy=mmInfo.ptMinTrackSize.y; } LRESULT GetMinFrameDist(const DFMHDR* pHdr) const { CSize sz; GetMinFrameSize(pHdr,sz); return (IsHorizontal()) ? sz.cx : sz.cy; } void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const { if(m_frames.size()!=0) { pMinMaxInfo=std::accumulate(m_frames.begin(),m_frames.end(), pMinMaxInfo,CMinMaxInfoAccumulator(IsHorizontal())); if(IsHorizontal()) pMinMaxInfo->ptMinTrackSize.x-=splitterSize; else pMinMaxInfo->ptMinTrackSize.y-=splitterSize; } } void Draw(CDC& dc,const CRect& rc) const { if(m_frames.begin()!=m_frames.end()) std::for_each(++m_frames.begin(),m_frames.end(),CEmbeddedSplitterBarPainter(dc,!IsHorizontal(),rc)); } bool AcceptDock(DFDOCKRECT* pHdr,const CRect& rc) { bool bRes=false; CSize sz; GetMinFrameSize(&(pHdr->hdr),sz); if(rc.PtInRect(CPoint(pHdr->rect.left,pHdr->rect.top)) && (sz.cx<=rc.Width()) && (sz.cy<=rc.Height()) && ((( (IsHorizontal()) ? sz.cx : sz.cy)+m_frames.distance_limit())<(m_frames.hi()-m_frames.low()))) { if(IsHorizontal()) { pHdr->rect.top=rc.top; pHdr->rect.bottom=rc.bottom; } else { pHdr->rect.left=rc.left; pHdr->rect.right=rc.right; } bRes=true; } return bRes; } bool Dock(CFrame& frame,const DFDOCKRECT* pHdr,const CRect& rc) { position pos; CFrames::distance len; if(IsHorizontal()) { pos=pHdr->rect.left; len=pHdr->rect.right-pHdr->rect.left; } else { pos=pHdr->rect.top; len=pHdr->rect.bottom-pHdr->rect.top; } iterator i=m_frames.locate(pos); if(i!=m_frames.end()) { iterator inext=i; ++inext; CBounds fbounds((*i),(inext!=m_frames.end()) ? position(*inext) : m_frames.hi()); if((fbounds.low+(fbounds.hi-fbounds.low)/3)hWnd)); assert(i!=m_frames.end()); m_frames.erase(i); return Arrange(rc); } bool Replace(const DFDOCKREPLACE* pHdr,const CRect& rc) { iterator i=std::find_if(m_frames.begin(),m_frames.end(),CFrame::CCmp(pHdr->hdr.hWnd)); assert(i!=m_frames.end()); m_frames.replace(i,CFrame(0,pHdr->hWnd)); return Arrange(rc); } protected: bool m_bHorizontal; CFrames m_frames; }; template< class TTraits = CDockingFrameTraits > class CWndFramesPackage : public CWndFramesPackageBase, public CThemeImpl < CWndFramesPackage > { typedef CWndFrame CFrame; typedef TTraits CTraits; typedef typename CTraits::CSplitterBar CSplitterBar; typedef CWndFramesPackage thisClass; typedef CWndFramesPackageBase baseClass; public: CWndFramesPackage(bool bHorizontal):baseClass(bHorizontal) { } void PrepareForDocking(CWindow wnd,HDOCKBAR bar) { wnd.ShowWindow(SW_HIDE); DWORD style = wnd.GetWindowLong(GWL_STYLE); DWORD newStyle = style&(~WS_POPUP)|WS_CHILD; if(IsThemingSupported()) ::SetWindowTheme(wnd.m_hWnd, NULL, L""); wnd.SetWindowLong( GWL_STYLE, newStyle); wnd.SetParent(bar); wnd.SendMessage(WM_NCACTIVATE,TRUE); wnd.SendMessage(WMDF_NDOCKSTATECHANGED, MAKEWPARAM(TRUE,IsHorizontal()), reinterpret_cast(bar)); } void PrepareForUndocking(CWindow wnd,HDOCKBAR bar) { wnd.ShowWindow(SW_HIDE); DWORD style = wnd.GetWindowLong(GWL_STYLE); DWORD newStyle = style&(~WS_CHILD)|WS_POPUP; if(IsThemingSupported()) ::SetWindowTheme(wnd.m_hWnd, NULL, NULL); wnd.SetWindowLong( GWL_STYLE, newStyle); wnd.SetParent(NULL); wnd.SendMessage(WMDF_NDOCKSTATECHANGED, FALSE, reinterpret_cast(bar)); } bool SetDockingPosition(const DFDOCKPOS* pHdr,const CRect& rc) { assert(::IsWindow(pHdr->hdr.hWnd)); CWindow wnd(pHdr->hdr.hWnd); PrepareForDocking(wnd,pHdr->hdr.hBar); position pos=m_frames.low()+position((m_frames.hi()-m_frames.low())*pHdr->fPctPos); m_frames.insert(CFrame(pos,pHdr->hdr.hWnd),pHdr->nHeight); // wnd.ShowWindow(SW_SHOWNA); bool bRes=Arrange(rc); wnd.SetWindowPos(NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); return bRes; } bool GetDockingPosition(DFDOCKPOS* pHdr,const CRect& /*rc*/) const { assert(::IsWindow(pHdr->hdr.hWnd)); const_iterator i=std::find_if(m_frames.begin(),m_frames.end(),CFrame::CCmp(pHdr->hdr.hWnd)); bool bRes=(i!=m_frames.end()); if(bRes) { position pos=*i-m_frames.low(); pHdr->fPctPos=float(pos)/(m_frames.hi()-m_frames.low()); pHdr->nHeight=m_frames.get_frame_size(i)-CSplitterBar::GetThickness(); // pHdr->nWidth=IsHorizontal() ? rc.Height() : rc.Width(); // pHdr->nWidth-=CSplitterBar::GetThickness(); if(m_frames.size()==1) pHdr->dwDockSide|=CDockingSide::sSingle; } return bRes; } bool AcceptDock(DFDOCKRECT* pHdr,const CRect& rc) { return (!((m_frames.size()==1) &&(m_frames.begin()->hwnd()==pHdr->hdr.hWnd))) &&baseClass::AcceptDock(pHdr,rc); } bool Dock(DFDOCKRECT* pHdr,const CRect& rc) { assert(::IsWindow(pHdr->hdr.hWnd)); CWindow wnd(pHdr->hdr.hWnd); PrepareForDocking(wnd,pHdr->hdr.hBar); CFrame frame(0,pHdr->hdr.hWnd); bool bRes=baseClass::Dock(frame,pHdr,rc); wnd.SetWindowPos(NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); // wnd.ShowWindow(SW_SHOWNA); return bRes; } bool Undock(DFMHDR* pHdr,const CRect& rc) { bool bRes=baseClass::Undock(pHdr,rc); assert(bRes); PrepareForUndocking(pHdr->hWnd,pHdr->hBar); if(m_frames.size()==0) { DFMHDR dockHdr; dockHdr.hWnd = pHdr->hBar; dockHdr.hBar = ::GetParent(pHdr->hBar); assert(::IsWindow(dockHdr.hBar)); dockHdr.code=DC_UNDOCK; ::SendMessage(dockHdr.hBar,WMDF_DOCK,NULL,reinterpret_cast(&dockHdr)); ::PostMessage(dockHdr.hWnd,WM_CLOSE,NULL,NULL); } return bRes; } bool Replace(const DFDOCKREPLACE* pHdr,const CRect& rc) { PrepareForUndocking(pHdr->hdr.hWnd,pHdr->hdr.hBar); PrepareForDocking(pHdr->hWnd,pHdr->hdr.hBar); bool bRes=baseClass::Replace(pHdr,rc); assert(bRes); ::SetWindowPos(pHdr->hWnd,NULL,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); // ::ShowWindow(pHdr->hWnd,SW_SHOWNA); return bRes; } }; template class CPtrFrame { typedef CPtrFrame thisClass; public: typedef typename T::position position; typedef typename T::distance distance; class CCmp { public: CCmp(HWND hWnd) :m_hWnd(hWnd) { } bool operator ()(const thisClass& frame) const { return (frame.hwnd() == m_hWnd); } protected: HWND m_hWnd; }; CPtrFrame(const CPtrFrame& x):m_pos(x.m_pos),m_ptr(x.m_ptr) { } CPtrFrame(position pos, T* ptr) :m_pos(pos),m_ptr(ptr) { } HWND hwnd() const { ATLASSERT(m_ptr.get() != NULL); return m_ptr->operator HWND(); } operator position() const { return (position)m_pos; } thisClass& operator += (position val) { m_pos+=val; return *this; } thisClass& operator -= (position val) { m_pos-=val; return *this; } thisClass& operator = (position pos) { m_pos=pos; return *this; } thisClass& operator = (double pos) { m_pos=pos; return *this; } double get_real() { return m_pos; } HDWP DeferFramePos(HDWP hdwp,long x1,long y1,long x2,long y2) const { ATLASSERT(m_ptr.get() != NULL); return m_ptr->DeferFramePos(hdwp,x1,y1,x2,y2); } T* operator ->() const { return m_ptr.get(); } void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const { ATLASSERT(m_ptr.get() != NULL); m_ptr->GetMinMaxInfo(pMinMaxInfo); } distance MinDistance() const { ATLASSERT(m_ptr.get() != NULL); return m_ptr->MinDistance(); } protected: double m_pos; #ifdef USE_BOOST mutable boost::shared_ptr m_ptr; #else mutable std::auto_ptr m_ptr; #endif }; struct IFrame { typedef long position; typedef long distance; virtual ~IFrame(){}; virtual HWND hwnd() const=0; operator HWND() const { return hwnd(); } virtual bool AcceptDock(DFDOCKRECT* pHdr) const=0; virtual distance MinDistance() const=0; virtual void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const=0; virtual HDWP DeferFramePos(HDWP hdwp,long x1,long y1,long x2,long y2) const { return ::DeferWindowPos(hdwp,hwnd(), NULL, x1,y1, x2-x1,y2-y1, SWP_NOZORDER | SWP_NOACTIVATE); } }; class CWindowPtrWrapper : public IFrame { public: CWindowPtrWrapper(HWND* phWnd):m_phWnd(phWnd) { } virtual HWND hwnd() const { return (*m_phWnd); } virtual bool AcceptDock(DFDOCKRECT* /*pHdr*/) const { return false; } virtual HDWP DeferFramePos(HDWP hdwp,long x1,long y1,long x2,long y2) const { if(*m_phWnd!=NULL) hdwp=::DeferWindowPos(hdwp,hwnd(), NULL, x1,y1, x2-x1,y2-y1, SWP_NOZORDER | SWP_NOACTIVATE); return hdwp; } void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const { if(*m_phWnd!=NULL) ::SendMessage(hwnd(),WM_GETMINMAXINFO,NULL,reinterpret_cast(pMinMaxInfo)); } virtual distance MinDistance() const { MINMAXINFO mmInfo; ZeroMemory(&mmInfo,sizeof(MINMAXINFO)); GetMinMaxInfo(&mmInfo); return mmInfo.ptMinTrackSize.x;; } protected: HWND* m_phWnd; }; //TImbeddedPackegeWnd template class CSubWndFramesPackage : public CRect, public CWndFramesPackageBase,TTraits > { typedef CPtrFrame