// Win32++ Version 7.2 // Released: 5th AUgust 2011 // // David Nash // email: dnash@bigpond.net.au // url: https://sourceforge.net/projects/win32-framework // // // Copyright (c) 2005-2011 David Nash // // Permission is hereby granted, free of charge, to // any person obtaining a copy of this software and // associated documentation files (the "Software"), // to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, // merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom // the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice // shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE // OR OTHER DEALINGS IN THE SOFTWARE. // //////////////////////////////////////////////////////// #ifndef _WIN32XX_REBAR_H_ #define _WIN32XX_REBAR_H_ #include "wincore.h" #include "gdi.h" namespace Win32xx { struct ReBarTheme { BOOL UseThemes; // TRUE if themes are used COLORREF clrBkgnd1; // Colour 1 for rebar background COLORREF clrBkgnd2; // Colour 2 for rebar background COLORREF clrBand1; // Colour 1 for rebar band background. Use NULL if not required COLORREF clrBand2; // Colour 2 for rebar band background. Use NULL if not required BOOL FlatStyle; // Bands are rendered with flat rather than raised style BOOL BandsLeft; // Position bands left on rearrange BOOL LockMenuBand; // Lock MenuBar's band in dedicated top row, without gripper BOOL RoundBorders; // Use rounded band borders BOOL ShortBands; // Allows bands to be shorter than maximum available width BOOL UseLines; // Displays horizontal lines between bands }; //////////////////////////////////// // Declaration of the CReBar class // class CReBar : public CWnd { public: CReBar(); virtual ~CReBar(); // Operations BOOL DeleteBand(const int nBand) const; int HitTest(RBHITTESTINFO& rbht); HWND HitTest(POINT pt); int IDToIndex(UINT uBandID) const; BOOL InsertBand(const int nBand, REBARBANDINFO& rbbi) const; BOOL IsBandVisible(int nBand) const; void MaximizeBand(UINT uBand, BOOL fIdeal = FALSE); void MinimizeBand(UINT uBand); BOOL MoveBand(UINT uFrom, UINT uTo); void MoveBandsLeft(); BOOL ResizeBand(const int nBand, const CSize& sz) const; BOOL ShowGripper(int nBand, BOOL fShow) const; BOOL ShowBand(int nBand, BOOL fShow) const; BOOL SizeToRect(CRect& rect) const; // Attributes int GetBand(const HWND hWnd) const; CRect GetBandBorders(int nBand) const; int GetBandCount() const; BOOL GetBandInfo(const int nBand, REBARBANDINFO& rbbi) const; CRect GetBandRect(int i) const; UINT GetBarHeight() const; BOOL GetBarInfo(REBARINFO& rbi) const; HWND GetMenuBar() {return m_hMenuBar;} ReBarTheme& GetReBarTheme() {return m_Theme;} UINT GetRowCount() const; int GetRowHeight(int nRow) const; UINT GetSizeofRBBI() const; HWND GetToolTips() const; BOOL SetBandBitmap(const int nBand, const CBitmap* pBackground) const; BOOL SetBandColor(const int nBand, const COLORREF clrFore, const COLORREF clrBack) const; BOOL SetBandInfo(const int nBand, REBARBANDINFO& rbbi) const; BOOL SetBarInfo(REBARINFO& rbi) const; void SetMenuBar(HWND hMenuBar) {m_hMenuBar = hMenuBar;} void SetReBarTheme(ReBarTheme& Theme); protected: //Overridables virtual BOOL OnEraseBkgnd(CDC* pDC); virtual void PreCreate(CREATESTRUCT& cs); virtual void PreRegisterClass(WNDCLASS &wc); virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); private: CReBar(const CReBar&); // Disable copy construction CReBar& operator = (const CReBar&); // Disable assignment operator ReBarTheme m_Theme; BOOL m_bIsDragging; HWND m_hMenuBar; LPARAM m_Orig_lParam; }; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ namespace Win32xx { /////////////////////////////////// // Definitions for the CReBar class // inline CReBar::CReBar() : m_bIsDragging(FALSE), m_hMenuBar(0), m_Orig_lParam(0L) { ZeroMemory(&m_Theme, sizeof(ReBarTheme)); } inline CReBar::~CReBar() { } inline BOOL CReBar::DeleteBand(int nBand) const // Deletes a band from a rebar control. { assert(::IsWindow(m_hWnd)); return (BOOL)SendMessage(RB_DELETEBAND, nBand, 0L); } inline int CReBar::GetBand(HWND hWnd) const // Returns the zero based band number for this window handle { assert(::IsWindow(m_hWnd)); int nResult = -1; if (NULL == hWnd) return nResult; for (int nBand = 0; nBand < GetBandCount(); ++nBand) { REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_CHILD; GetBandInfo(nBand, rbbi); if (rbbi.hwndChild == hWnd) nResult = nBand; } return nResult; } inline CRect CReBar::GetBandBorders(int nBand) const // Retrieves the borders of a band. { assert(::IsWindow(m_hWnd)); CRect rc; SendMessage(RB_GETBANDBORDERS, nBand, (LPARAM)&rc); return rc; } inline int CReBar::GetBandCount() const // Retrieves the count of bands currently in the rebar control. { assert(::IsWindow(m_hWnd)); return (int)SendMessage(RB_GETBANDCOUNT, 0L, 0L); } inline BOOL CReBar::GetBandInfo(int nBand, REBARBANDINFO& rbbi) const // Retrieves information about a specified band in a rebar control. { assert(::IsWindow(m_hWnd)); assert(nBand >= 0); // REBARBANDINFO describes individual BAND characteristics rbbi.cbSize = GetSizeofRBBI(); return (BOOL)SendMessage(RB_GETBANDINFO, nBand, (LPARAM)&rbbi); } inline CRect CReBar::GetBandRect(int i) const // Retrieves the bounding rectangle for a given band in a rebar control. { assert(::IsWindow(m_hWnd)); CRect rc; SendMessage(RB_GETRECT, i, (LPARAM)&rc); return rc; } inline UINT CReBar::GetBarHeight() const // Retrieves the height of the rebar control. { assert(::IsWindow(m_hWnd)); return (UINT)SendMessage(RB_GETBARHEIGHT, 0L, 0L); } inline BOOL CReBar::GetBarInfo(REBARINFO& rbi) const // Retrieves information about the rebar control and the image list it uses. { assert(::IsWindow(m_hWnd)); // REBARINFO describes overall rebar control characteristics rbi.cbSize = GetSizeofRBBI(); return (BOOL)SendMessage(RB_GETBARINFO, 0L, (LPARAM)&rbi); } inline UINT CReBar::GetRowCount() const // Retrieves the number of rows of bands in a rebar control. { assert(::IsWindow(m_hWnd)); return (UINT)SendMessage(RB_GETROWCOUNT, 0L, 0L); } inline int CReBar::GetRowHeight(int nRow) const // Retrieves the height of a specified row in a rebar control. { assert(::IsWindow(m_hWnd)); return (int)SendMessage(RB_GETROWHEIGHT, nRow, 0L); } inline UINT CReBar::GetSizeofRBBI() const // The size of the REBARBANDINFO struct changes according to _WIN32_WINNT // sizeof(REBARBANDINFO) can report an incorrect size for older Window versions, // or newer Window version without XP themes enabled. // Use this function to get a safe size for REBARBANDINFO. { assert(::IsWindow(m_hWnd)); UINT uSizeof = sizeof(REBARBANDINFO); #if defined REBARBANDINFO_V6_SIZE // only defined for VS2008 or higher #if !defined (_WIN32_WINNT) || _WIN32_WINNT >= 0x0600 if ((GetWinVersion() < 2600) || (GetComCtlVersion() < 610)) // Vista and Vista themes? uSizeof = REBARBANDINFO_V6_SIZE; #endif #endif return uSizeof; } inline HWND CReBar::GetToolTips() const // Retrieves the handle to any ToolTip control associated with the rebar control. { assert(::IsWindow(m_hWnd)); return (HWND)SendMessage(RB_GETTOOLTIPS, 0L, 0L); } inline int CReBar::HitTest(RBHITTESTINFO& rbht) // Determines which portion of a rebar band is at a given point on the screen, // if a rebar band exists at that point. { assert(::IsWindow(m_hWnd)); return (int)SendMessage(RB_HITTEST, 0L, (LPARAM)&rbht); } inline HWND CReBar::HitTest(POINT pt) // Return the child HWND at the given point { assert(::IsWindow(m_hWnd)); // Convert the point to client co-ordinates ScreenToClient(pt); // Get the rebar band with the point RBHITTESTINFO rbhti = {0}; rbhti.pt = pt; int iBand = HitTest(rbhti); if (iBand >= 0) { // Get the rebar band's hWnd REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_CHILD; GetBandInfo(iBand, rbbi); return rbbi.hwndChild; } else return NULL; } inline int CReBar::IDToIndex(UINT uBandID) const // Converts a band identifier to a band index in a rebar control. { assert(::IsWindow(m_hWnd)); return (int)SendMessage(RB_IDTOINDEX, (WPARAM)uBandID, 0L); } inline BOOL CReBar::InsertBand(int nBand, REBARBANDINFO& rbbi) const // Inserts a new band in a rebar control. { assert(::IsWindow(m_hWnd)); rbbi.cbSize = GetSizeofRBBI(); return (BOOL)SendMessage(RB_INSERTBAND, nBand, (LPARAM)&rbbi); } inline BOOL CReBar::IsBandVisible(int nBand) const // Returns true if the band is visible { assert(::IsWindow(m_hWnd)); REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_STYLE; GetBandInfo(nBand, rbbi); return !(rbbi.fStyle & RBBS_HIDDEN); } inline BOOL CReBar::OnEraseBkgnd(CDC* pDC) { BOOL Erase = TRUE; if (!m_Theme.UseThemes) Erase = FALSE; if (!m_Theme.clrBkgnd1 && !m_Theme.clrBkgnd2 && !m_Theme.clrBand1 && !m_Theme.clrBand2) Erase = FALSE; if (Erase) { CRect rcReBar = GetClientRect(); int BarWidth = rcReBar.Width(); int BarHeight = rcReBar.Height(); // Create and set up our memory DC CMemDC MemDC(pDC); MemDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); // Draw to ReBar background to the memory DC rcReBar.right = 600; MemDC.GradientFill(m_Theme.clrBkgnd1, m_Theme.clrBkgnd2, rcReBar, TRUE); if (BarWidth >= 600) { rcReBar.left = 600; rcReBar.right = BarWidth; MemDC.SolidFill(m_Theme.clrBkgnd2, rcReBar); } if (m_Theme.clrBand1 || m_Theme.clrBand2) { // Draw the individual band backgrounds for (int nBand = 0 ; nBand < GetBandCount(); ++nBand) { if (IsBandVisible(nBand)) { if (nBand != GetBand(m_hMenuBar)) { // Determine the size of this band CRect rcBand = GetBandRect(nBand); // Determine the size of the child window REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_CHILD ; GetBandInfo(nBand, rbbi); CRect rcChild; ::GetWindowRect(rbbi.hwndChild, &rcChild); int ChildWidth = rcChild.right - rcChild.left; // Determine our drawing rectangle CRect rcDraw = rcBand; rcDraw.bottom = rcDraw.top + (rcBand.bottom - rcBand.top)/2; int xPad = IsXPThemed()? 2: 0; rcDraw.left -= xPad; // Fill the Source CDC with the band's background CMemDC SourceDC(pDC); SourceDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); CRect rcBorder = GetBandBorders(nBand); rcDraw.right = rcBand.left + ChildWidth + rcBorder.left; SourceDC.SolidFill(m_Theme.clrBand1, rcDraw); rcDraw.top = rcDraw.bottom; rcDraw.bottom = rcBand.bottom; SourceDC.GradientFill(m_Theme.clrBand1, m_Theme.clrBand2, rcDraw, FALSE); // Set Curve amount for rounded edges int Curve = m_Theme.RoundBorders? 12 : 0; // Create our mask for rounded edges using RoundRect CMemDC MaskDC(pDC); MaskDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); rcDraw.top = rcBand.top; if (!m_Theme.FlatStyle) ::InflateRect(&rcDraw, 1, 1); int left = rcDraw.left; int right = rcDraw.right; int top = rcDraw.top; int bottom = rcDraw.bottom; int cx = rcDraw.right - rcBand.left + xPad; int cy = rcDraw.bottom - rcBand.top; if (m_Theme.FlatStyle) { MaskDC.SolidFill(RGB(0,0,0), rcDraw); MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT); MaskDC.RoundRect(left, top, right, bottom, Curve, Curve); } else { MaskDC.SolidFill(RGB(0,0,0), rcDraw); MaskDC.RoundRect(left, top, right, bottom, Curve, Curve); MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT); } // Copy Source DC to Memory DC using the RoundRect mask MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT); MemDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, SRCAND); MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT); // Extra drawing to prevent jagged edge while moving bands if (m_bIsDragging) { CClientDC ReBarDC(this); ReBarDC.BitBlt(rcDraw.right - ChildWidth, rcDraw.top, ChildWidth, cy, &MemDC, rcDraw.right - ChildWidth, rcDraw.top, SRCCOPY); } } } } } if (m_Theme.UseLines) { // Draw lines between bands for (int j = 0; j < GetBandCount()-1; ++j) { rcReBar = GetBandRect(j); rcReBar.left = MAX(0, rcReBar.left - 4); rcReBar.bottom +=2; MemDC.DrawEdge(rcReBar, EDGE_ETCHED, BF_BOTTOM | BF_ADJUST); } } // Copy the Memory DC to the window's DC pDC->BitBlt(0, 0, BarWidth, BarHeight, &MemDC, 0, 0, SRCCOPY); } return Erase; } inline void CReBar::PreCreate(CREATESTRUCT &cs) // Sets the CREATESTRUCT paramaters prior to window creation { cs.style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | RBS_VARHEIGHT | RBS_BANDBORDERS ; cs.cy = 100; } inline void CReBar::PreRegisterClass(WNDCLASS &wc) { // Set the Window Class wc.lpszClassName = REBARCLASSNAME; } inline void CReBar::MaximizeBand(UINT uBand, BOOL fIdeal /*= FALSE*/) // Resizes a band in a rebar control to either its ideal or largest size. { assert(::IsWindow(m_hWnd)); SendMessage(RB_MAXIMIZEBAND, (WPARAM)uBand, (LPARAM)fIdeal); } inline void CReBar::MinimizeBand(UINT uBand) // Resizes a band in a rebar control to its smallest size. { assert(::IsWindow(m_hWnd)); SendMessage(RB_MINIMIZEBAND, (WPARAM)uBand, 0L); } inline BOOL CReBar::MoveBand(UINT uFrom, UINT uTo) // Moves a band from one index to another. { assert(::IsWindow(m_hWnd)); return (BOOL)SendMessage(RB_MOVEBAND, (WPARAM)uFrom, (LPARAM)uTo); } inline void CReBar::MoveBandsLeft() // Repositions the bands so they are left justified { assert(::IsWindow(m_hWnd)); int OldrcTop = -1; for (int nBand = GetBandCount() -1; nBand >= 0; --nBand) { CRect rc = GetBandRect(nBand); if (rc.top != OldrcTop) { // Maximize the last band on each row if (IsBandVisible(nBand)) { ::SendMessage(GetHwnd(), RB_MAXIMIZEBAND, nBand, 0L); OldrcTop = rc.top; } } } } inline BOOL CReBar::ResizeBand(int nBand, const CSize& sz) const // Sets a band's size { assert(::IsWindow(m_hWnd)); REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_SIZE; GetBandInfo(nBand, rbbi); rbbi.cx = sz.cx + 2; rbbi.cxMinChild = sz.cx + 2; rbbi.cyMinChild = sz.cy; rbbi.cyMaxChild = sz.cy; return SetBandInfo(nBand, rbbi ); } inline BOOL CReBar::SetBandBitmap(int nBand, const CBitmap* pBackground) const // Sets the band's bitmaps { assert(::IsWindow(m_hWnd)); assert(pBackground); REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_STYLE; GetBandInfo(nBand, rbbi); rbbi.fMask |= RBBIM_BACKGROUND; rbbi.hbmBack = *pBackground; return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); } inline BOOL CReBar::SetBandColor(int nBand, COLORREF clrFore, COLORREF clrBack) const // Sets the band's color // Note: No effect with XP themes enabled // No effect if a bitmap has been set { assert(::IsWindow(m_hWnd)); REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_COLORS; rbbi.clrFore = clrFore; rbbi.clrBack = clrBack; return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); } inline BOOL CReBar::SetBandInfo(int nBand, REBARBANDINFO& rbbi) const // Sets the characteristics of a rebar control. { assert(::IsWindow(m_hWnd)); assert(nBand >= 0); // REBARBANDINFO describes individual BAND characteristics0 rbbi.cbSize = GetSizeofRBBI(); return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); } inline BOOL CReBar::SetBarInfo(REBARINFO& rbi) const // REBARINFO associates an image list with the rebar // A band will also need to set RBBIM_IMAGE { assert(::IsWindow(m_hWnd)); rbi.cbSize = GetSizeofRBBI(); return (BOOL)SendMessage(RB_SETBARINFO, 0L, (LPARAM)&rbi); } inline void CReBar::SetReBarTheme(ReBarTheme& Theme) { m_Theme.UseThemes = Theme.UseThemes; m_Theme.clrBkgnd1 = Theme.clrBkgnd1; m_Theme.clrBkgnd2 = Theme.clrBkgnd2; m_Theme.clrBand1 = Theme.clrBand1; m_Theme.clrBand2 = Theme.clrBand2; m_Theme.BandsLeft = Theme.BandsLeft; m_Theme.LockMenuBand = Theme.LockMenuBand; m_Theme.ShortBands = Theme.ShortBands; m_Theme.UseLines = Theme.UseLines; m_Theme.FlatStyle = Theme.FlatStyle; m_Theme.RoundBorders = Theme.RoundBorders; if (IsWindow()) { if (m_Theme.LockMenuBand) ShowGripper(GetBand(m_hMenuBar), FALSE); else ShowGripper(GetBand(m_hMenuBar), TRUE); Invalidate(); } } inline BOOL CReBar::ShowBand(int nBand, BOOL fShow) const // Show or hide a band { assert(::IsWindow(m_hWnd)); return (BOOL)SendMessage(RB_SHOWBAND, (WPARAM)nBand, (LPARAM)fShow); } inline BOOL CReBar::ShowGripper(int nBand, BOOL fShow) const // Show or hide the band's gripper { assert(::IsWindow(m_hWnd)); REBARBANDINFO rbbi = {0}; rbbi.cbSize = GetSizeofRBBI(); rbbi.fMask = RBBIM_STYLE; GetBandInfo(nBand, rbbi); if (fShow) { rbbi.fStyle |= RBBS_GRIPPERALWAYS; rbbi.fStyle &= ~RBBS_NOGRIPPER; } else { rbbi.fStyle &= ~RBBS_GRIPPERALWAYS; rbbi.fStyle |= RBBS_NOGRIPPER; } return SetBandInfo(nBand, rbbi); } inline BOOL CReBar::SizeToRect(CRect& rect) const // Attempts to find the best layout of the bands for the given rectangle. // The rebar bands will be arranged and wrapped as necessary to fit the rectangle. { assert(::IsWindow(m_hWnd)); return (BOOL)SendMessage(RB_SIZETORECT, 0, (LPARAM) (LPRECT)rect); } inline LRESULT CReBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_MOUSEMOVE: if (m_Theme.UseThemes && m_Theme.LockMenuBand) { // We want to lock the first row in place, but allow other bands to move! // Use move messages to limit the resizing of bands int y = GET_Y_LPARAM(lParam); if (y <= GetRowHeight(0)) return 0L; // throw this message away } break; case WM_LBUTTONDOWN: m_Orig_lParam = lParam; // Store the x,y position m_bIsDragging = TRUE; break; case WM_LBUTTONUP: if (m_Theme.UseThemes && m_Theme.LockMenuBand) { // Use move messages to limit the resizing of bands int y = GET_Y_LPARAM(lParam); if (y <= GetRowHeight(0)) { // Use x,y from WM_LBUTTONDOWN for WM_LBUTTONUP position lParam = m_Orig_lParam; } } m_bIsDragging = FALSE; break; case UWM_GETREBARTHEME: { ReBarTheme& rm = GetReBarTheme(); return (LRESULT)&rm; } case UWM_TOOLBAR_RESIZE: { HWND hToolBar = (HWND)wParam; LPSIZE pToolBarSize = (LPSIZE)lParam; ResizeBand(GetBand(hToolBar), *pToolBarSize); } break; } // pass unhandled messages on for default processing return CWnd::WndProcDefault(uMsg, wParam, lParam); } } // namespace Win32xx #endif // #ifndef _WIN32XX_REBAR_H_