diff --git a/Src/BasicFlatStatusBar.cpp b/Src/BasicFlatStatusBar.cpp index 608ca719134..45f24d3db37 100644 --- a/Src/BasicFlatStatusBar.cpp +++ b/Src/BasicFlatStatusBar.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2024 Takashi Sawanaka +// SPDX-License-Identifier: BSL-1.0 /** * @file BasicFlatStatusBar.cpp * diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp index 366e8dc6224..6d0c801970c 100644 --- a/Src/MainFrm.cpp +++ b/Src/MainFrm.cpp @@ -220,6 +220,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) ON_MESSAGE(WM_COPYDATA, OnCopyData) ON_MESSAGE(WM_USER+1, OnUser1) ON_WM_ACTIVATEAPP() + ON_UPDATE_COMMAND_UI_RANGE(CMenuBar::FIRST_MENUID, CMenuBar::FIRST_MENUID + 10, OnUpdateMenuBarMenuItem) // [File] menu ON_COMMAND(ID_FILE_NEW, (OnFileNew<2, ID_MERGE_COMPARE_TEXT>)) ON_COMMAND(ID_FILE_NEW_TABLE, (OnFileNew<2, ID_MERGE_COMPARE_TABLE>)) @@ -288,6 +289,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) ON_COMMAND(ID_LASTFILE, OnLastFile) ON_UPDATE_COMMAND_UI(ID_LASTFILE, OnUpdateLastFile) // Tool bar drop-down menu + ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_MENUBAR, OnMenubarButtonDropDown) ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnToolbarButtonDropDown) ON_COMMAND_RANGE(ID_DIFF_OPTIONS_WHITESPACE_COMPARE, ID_DIFF_OPTIONS_WHITESPACE_IGNOREALL, OnDiffWhitespace) ON_UPDATE_COMMAND_UI_RANGE(ID_DIFF_OPTIONS_WHITESPACE_COMPARE, ID_DIFF_OPTIONS_WHITESPACE_IGNOREALL, OnUpdateDiffWhitespace) @@ -446,6 +448,11 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) return 0; } +void CMainFrame::OnUpdateMenuBarMenuItem(CCmdUI* pCmdUI) +{ + m_wndMenuBar.OnUpdateMenuBarMenuItem(pCmdUI); +} + void CMainFrame::OnTimer(UINT_PTR nIDEvent) { __super::OnTimer(nIDEvent); @@ -1682,6 +1689,8 @@ void CMainFrame::OnClose() theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom); theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE)); + GetOptionsMgr()->SaveOption(OPT_REBAR_STATE, m_wndReBar.GenerateStateString()); + for (auto pFrame: GetAllImgMergeFrames()) { if (!pFrame->CloseNow()) @@ -2167,6 +2176,8 @@ void CMainFrame::SelectFilter() */ BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { + if (m_wndMenuBar.PreTranslateMessage(pMsg)) + return TRUE; // Check if we got 'ESC pressed' -message if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE)) { @@ -2470,26 +2481,30 @@ void CMainFrame::OnActivateApp(BOOL bActive, DWORD dwThreadID) BOOL CMainFrame::CreateToolbar() { - if (!m_wndToolBar.CreateEx(this) || + if (!m_wndMenuBar.Create(this)) + { + return FALSE; + } + + if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { return FALSE; } - if (!m_wndReBar.Create(this, RBS_BANDBORDERS, + if (!m_wndReBar.Create(this, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_ALIGN_TOP)) { return FALSE; } - VERIFY(m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT)); - // Remove this if you don't want tool tips or a resizable toolbar m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); - m_wndReBar.AddBar(&m_wndToolBar); + m_wndReBar.AddBar(&m_wndMenuBar); + m_wndReBar.AddBar(&m_wndToolBar, nullptr, nullptr, RBBS_GRIPPERALWAYS | RBBS_FIXEDBMP | RBBS_BREAK); LoadToolbarImages(); @@ -2508,6 +2523,10 @@ BOOL CMainFrame::CreateToolbar() __super::ShowControlBar(&m_wndToolBar, false, 0); } + __super::ShowControlBar(&m_wndMenuBar, true, 0); + + m_wndReBar.LoadStateFromString(GetOptionsMgr()->GetString(OPT_REBAR_STATE).c_str()); + return TRUE; } @@ -2542,7 +2561,7 @@ void CMainFrame::LoadToolbarImages() REBARBANDINFO rbbi = { sizeof REBARBANDINFO }; rbbi.fMask = RBBIM_CHILDSIZE; rbbi.cyMinChild = sizeButton.cy; - m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi); + m_wndReBar.GetReBarCtrl().SetBandInfo(1, &rbbi); } @@ -2916,6 +2935,12 @@ void CMainFrame::OnPluginsList() dlg.DoModal(); } +void CMainFrame::OnMenubarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_wndMenuBar.OnMenuBarMenuItem(reinterpret_cast(pNMHDR)->iItem); + *pResult = 0; +} + void CMainFrame::OnToolbarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTOOLBAR pToolBar = reinterpret_cast(pNMHDR); diff --git a/Src/MainFrm.h b/Src/MainFrm.h index a638ea6a05a..c1b4046959a 100644 --- a/Src/MainFrm.h +++ b/Src/MainFrm.h @@ -14,6 +14,8 @@ #include #include #include +#include "MyReBar.h" +#include "MenuBar.h" #include "MDITabBar.h" #include "BasicFlatStatusBar.h" #include "PathContext.h" @@ -222,6 +224,7 @@ class CMainFrame : public CMDIFrameWnd DirWatcher* GetDirWatcher() { return m_pDirWatcher.get(); } void WatchDocuments(IMergeDoc* pMergeDoc); void UnwatchDocuments(IMergeDoc* pMergeDoc); + CMenuBar* GetMenuBar() { return &m_wndMenuBar; } CToolBar* GetToolbar() { return &m_wndToolBar; } static void WaitAndDoMessageLoop(bool& completed, int ms); @@ -248,7 +251,8 @@ class CMainFrame : public CMDIFrameWnd protected: // control bar embedded members CBasicFlatStatusBar m_wndStatusBar; - CReBar m_wndReBar; + CMyReBar m_wndReBar; + CMenuBar m_wndMenuBar; CToolBar m_wndToolBar; CMDITabBar m_wndTabBar; CTypedPtrArray m_arrChild; @@ -274,6 +278,11 @@ class CMainFrame : public CMDIFrameWnd } break; } + case WM_MDISETMENU: + GetMainFrame()->SetMenuBarState(AFX_MBS_HIDDEN); + GetMainFrame()->GetMenuBar()->AttachMenu(CMenu::FromHandle(reinterpret_cast(wParam))); + return TRUE; + break; case WM_TIMER: if (wParam == m_nRedrawTimer) { @@ -383,6 +392,7 @@ class CMainFrame : public CMDIFrameWnd afx_msg void OnUpdatePluginName(CCmdUI* pCmdUI); afx_msg void OnUpdateStatusNum(CCmdUI* pCmdUI); afx_msg void OnToolbarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnMenubarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnDiffWhitespace(UINT nID); afx_msg void OnUpdateDiffWhitespace(CCmdUI* pCmdUI); afx_msg void OnDiffIgnoreBlankLines(); @@ -418,6 +428,7 @@ class CMainFrame : public CMDIFrameWnd afx_msg LRESULT OnChildFrameRemoved(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnChildFrameActivate(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnChildFrameActivated(WPARAM wParam, LPARAM lParam); + afx_msg void OnUpdateMenuBarMenuItem(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() diff --git a/Src/MenuBar.cpp b/Src/MenuBar.cpp new file mode 100644 index 00000000000..1f2b9b3b97b --- /dev/null +++ b/Src/MenuBar.cpp @@ -0,0 +1,426 @@ +// Copyright (c) 2024 Takashi Sawanaka +// SPDX-License-Identifier: BSL-1.0 +/** + * @file MenuBar.cpp + * + * @brief Implementation of the CMenuBar class + */ + +#include "StdAfx.h" +#include "MenuBar.h" + +static const UINT UWM_SHOWPOPUPMENU = WM_APP + 1; +HHOOK CMenuBar::m_hHook = nullptr; +CMenuBar* CMenuBar::m_pThis = nullptr; + +#ifndef TBCDRF_USECDCOLORS +#define TBCDRF_USECDCOLORS 0x00800000 +#endif + +IMPLEMENT_DYNAMIC(CMenuBar, CToolBar) + +BEGIN_MESSAGE_MAP(CMenuBar, CToolBar) + ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_MOUSEMOVE() + ON_WM_MOUSELEAVE() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_MESSAGE(UWM_SHOWPOPUPMENU, OnShowPopupMenu) +END_MESSAGE_MAP() + +CMenuBar::CMenuBar() + : m_hMenu(nullptr) + , m_bActive(false) + , m_bMouseTracking(false) + , m_nMDIButtonDown(-1) + , m_nMDIButtonHot(-1) + , m_hwndOldFocus(nullptr) + , m_nCurrentMenuItemFlags(0) + , m_nCurrentHotItem(-1) + , m_hCurrentPopupMenu(nullptr) + , m_bShowKeyboardCues(false) +{ + m_pThis = this; +} + +static TBBUTTON makeTBButton(int id, const TCHAR* str) +{ + TBBUTTON btn{ I_IMAGENONE, id, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_DROPDOWN | BTNS_AUTOSIZE }; + btn.iString = reinterpret_cast(str); + return btn; +} + +BOOL CMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) +{ + bool ret = __super::CreateEx(pParentWnd, TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, + dwStyle, CRect(0, 0, 0, 0), nID); + + CToolBarCtrl& toolbar = GetToolBarCtrl(); + TEXTMETRIC tm; + CClientDC dc(this); + CFont* pOldFont = dc.SelectObject(toolbar.GetFont()); + dc.GetTextMetrics(&tm); + const int lpx = dc.GetDeviceCaps(LOGPIXELSX); + auto pointToPixel = [lpx](float point) -> int { return static_cast(point * lpx / 72.0f); }; + const int cy = pointToPixel(3); + dc.SelectObject(pOldFont); + + TBBUTTON btn = makeTBButton(FIRST_MENUID, _T("")); + toolbar.SetButtonStructSize(sizeof(TBBUTTON)); + SendMessage(WM_UPDATEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL|DT_HIDEPREFIX), 0); + toolbar.InsertButton(0, &btn); + toolbar.SetButtonSize(CSize(tm.tmHeight + cy * 2, tm.tmHeight + cy * 2)); + + return ret; +} + +bool CMenuBar::AttachMenu(CMenu* pMenu) +{ + CToolBarCtrl& toolbar = GetToolBarCtrl(); + + toolbar.SetRedraw(false); + + const int nCount = toolbar.GetButtonCount(); + for (int i = 0; i < nCount; i++) + toolbar.DeleteButton(0); + + if (pMenu && pMenu->m_hMenu) + { + m_hMenu = pMenu->m_hMenu; + + const int nItems = pMenu->GetMenuItemCount(); + std::vector btns(nItems); + std::vector> menuStrings(nItems); + + for (int i = 0; i < nItems; i++) + { + TCHAR szString[256]{}; + MENUITEMINFO mii{ sizeof MENUITEMINFO, MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU, MFT_STRING, 0, 0, nullptr, nullptr, nullptr, 0, szString, sizeof(szString) / sizeof(szString[0]) }; + pMenu->GetMenuItemInfo(i, &mii, TRUE); + menuStrings[i] = std::basic_string(L" ") + szString + L" "; + + btns[i] = makeTBButton(FIRST_MENUID + i, menuStrings[i].c_str()); + } + + toolbar.SendMessage(TB_ADDBUTTONSW, nItems, (LPARAM)btns.data()); + } + + toolbar.SetRedraw(true); + + return true; +} + +void CMenuBar::DrawMDIButtons(HDC hDC) +{ + int nTypes[3] = { DFCS_CAPTIONMIN | DFCS_FLAT, DFCS_CAPTIONRESTORE | DFCS_FLAT, DFCS_CAPTIONCLOSE | DFCS_FLAT }; + CRect rcButtons = GetMDIButtonsRect(); + const int bw = GetSystemMetrics(SM_CXSMICON); + const int w = bw + GetSystemMetrics(SM_CXBORDER) * 2; + const int h = rcButtons.Height(); + CRect rc{ rcButtons.left, rcButtons.top + (h - bw) / 2, rcButtons.left + bw, rcButtons.top + (h + bw) / 2}; + for (int i = 0; i < 3; ++i) + { + if (m_nMDIButtonDown == i) + nTypes[i] |= DFCS_PUSHED; + if (m_nMDIButtonHot == i) + nTypes[i] |= DFCS_HOT; + ::DrawFrameControl(hDC, rc, DFC_CAPTION, nTypes[i]); + rc.left += w; + rc.right += w; + } +} + +int CMenuBar::GetMDIButtonIndexFromPoint(CPoint pt) const +{ + CRect rcButtons = GetMDIButtonsRect(); + if (!rcButtons.PtInRect(pt)) + return -1; + for (int i = 0; i < 3; ++i) + { + if (GetMDIButtonRect(i).PtInRect(pt)) + return i; + } + return -1; +} + +CRect CMenuBar::GetMDIButtonsRect() const +{ + CRect rcClient; + GetClientRect(&rcClient); + const int w = GetSystemMetrics(SM_CXSMICON) + GetSystemMetrics(SM_CXBORDER) * 2; + return { rcClient.right - w * 3, rcClient.top, rcClient.right, rcClient.bottom }; +} + +CRect CMenuBar::GetMDIButtonRect(int nItem) const +{ + CRect rcButtons = GetMDIButtonsRect(); + const int w = GetSystemMetrics(SM_CXSMICON) + GetSystemMetrics(SM_CXBORDER) * 2; + return { rcButtons.left + w * nItem, rcButtons.top, rcButtons.left + w * nItem + w, rcButtons.bottom }; +} + +static bool IsMDIChildMaximized() +{ + CMDIFrameWnd* pMDIFrameWnd = DYNAMIC_DOWNCAST(CMDIFrameWnd, AfxGetMainWnd()); + if (!pMDIFrameWnd) + return false; + BOOL bMaximized = FALSE; + pMDIFrameWnd->MDIGetActive(&bMaximized); + return bMaximized; +} + +void CMenuBar::ShowKeyboardCues(bool show) +{ + m_bShowKeyboardCues = show; + SendMessage(WM_UPDATEUISTATE, MAKEWPARAM(show ? UIS_CLEAR : UIS_SET, UISF_HIDEACCEL|UISF_HIDEFOCUS), 0); +} + +void CMenuBar::LoseFocus() +{ + m_bActive = false; + m_hwndOldFocus = nullptr; + ShowKeyboardCues(false); + GetToolBarCtrl().SetHotItem(-1); +} + +void CMenuBar::OnSetFocus(CWnd* pOldWnd) +{ + m_bActive = true; + m_hwndOldFocus = pOldWnd->m_hWnd; + __super::OnSetFocus(pOldWnd); +} + +void CMenuBar::OnKillFocus(CWnd* pNewWnd) +{ + LoseFocus(); + __super::OnKillFocus(pNewWnd); +} + +void CMenuBar::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMTBCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); + DWORD dwDrawState = pNMCD->nmcd.dwDrawStage; + if (dwDrawState == CDDS_PREPAINT) + { + *pResult = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT; + return; + } + else if (dwDrawState == CDDS_ITEMPREPAINT) + { + pNMCD->clrHighlightHotTrack = GetSysColor(COLOR_3DFACE); + pNMCD->clrText = GetSysColor(COLOR_MENUTEXT); + *pResult = CDRF_DODEFAULT | TBCDRF_USECDCOLORS | TBCDRF_HILITEHOTTRACK; + return; + } + else if (dwDrawState == CDDS_POSTPAINT && + (m_bMouseTracking && IsMDIChildMaximized())) + { + DrawMDIButtons(pNMCD->nmcd.hdc); + } + *pResult = 0; +} + +void CMenuBar::OnMouseMove(UINT nFlags, CPoint point) +{ + CRect rcMDIButtons = GetMDIButtonsRect(); + InvalidateRect(&rcMDIButtons); + m_nMDIButtonHot = rcMDIButtons.PtInRect(point) ? GetMDIButtonIndexFromPoint(point) : -1; + if (!m_bMouseTracking) + { + TRACKMOUSEEVENT tme = { sizeof TRACKMOUSEEVENT, TME_LEAVE, m_hWnd }; + TrackMouseEvent(&tme); + m_bMouseTracking = true; + } + __super::OnMouseMove(nFlags, point); +} + +void CMenuBar::OnMouseLeave() +{ + TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE | TME_CANCEL, m_hWnd }; + TrackMouseEvent(&tme); + m_bMouseTracking = false; + CRect rcButtons = GetMDIButtonsRect(); + InvalidateRect(&rcButtons); + m_nMDIButtonDown = -1; + m_nMDIButtonHot = -1; + __super::OnMouseLeave(); +} + +void CMenuBar::OnLButtonDown(UINT nFlags, CPoint point) +{ + CRect rcButtons = GetMDIButtonsRect(); + if (rcButtons.PtInRect(point)) + { + m_nMDIButtonDown = GetMDIButtonIndexFromPoint(point); + InvalidateRect(rcButtons); + return; + } + __super::OnLButtonDown(nFlags, point); +} + +void CMenuBar::OnLButtonUp(UINT nFlags, CPoint point) +{ + CRect rcButtons = GetMDIButtonsRect(); + if (m_nMDIButtonDown != -1 && rcButtons.PtInRect(point)) + { + CMDIFrameWnd* pMDIFrameWnd = DYNAMIC_DOWNCAST(CMDIFrameWnd, AfxGetMainWnd()); + if (pMDIFrameWnd) + { + CFrameWnd* pFrameWnd = pMDIFrameWnd->GetActiveFrame(); + if (pFrameWnd) + { + switch (GetMDIButtonIndexFromPoint(point)) + { + case 0: + pFrameWnd->ShowWindow(SW_MINIMIZE); + break; + case 1: + ::SendMessage(pMDIFrameWnd->m_hWndMDIClient, WM_MDIRESTORE, (WPARAM)(pFrameWnd->m_hWnd), 0); + break; + case 2: + pFrameWnd->PostMessage(WM_CLOSE); + break; + } + } + } + } + InvalidateRect(rcButtons); + m_nMDIButtonDown = -1; + __super::OnLButtonUp(nFlags, point); +} + +void CMenuBar::OnMenuBarMenuItem(UINT nID) +{ + const int nHotItem = nID - FIRST_MENUID; + CMenu* pPopup = CMenu::FromHandle(::GetSubMenu(m_hMenu, nHotItem)); + if (!pPopup) + return; + + CToolBarCtrl& toolbar = GetToolBarCtrl(); + toolbar.SetHotItem(nHotItem); + m_nCurrentMenuItemFlags = 0; + m_nCurrentHotItem = nHotItem; + m_hCurrentPopupMenu = pPopup->m_hMenu; + m_ptCurrentCursor = {}; + m_pThis = this; + + if (m_bShowKeyboardCues) + { + AfxGetMainWnd()->PostMessage(WM_KEYDOWN, VK_DOWN, 0); + AfxGetMainWnd()->PostMessage(WM_KEYUP, VK_DOWN, 0); + } + + m_hHook = SetWindowsHookEx(WH_MSGFILTER, MsgFilterProc, nullptr, GetCurrentThreadId()); + + CRect rc; + toolbar.GetItemRect(nHotItem, &rc); + ClientToScreen(&rc); + pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, rc.left, rc.bottom, AfxGetMainWnd()); + + UnhookWindowsHookEx(m_hHook); +} + +void CMenuBar::OnUpdateMenuBarMenuItem(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(); +} + +LRESULT CMenuBar::OnShowPopupMenu(WPARAM wParam, LPARAM lParam) +{ + OnMenuBarMenuItem(static_cast(wParam)); + return 0; +} + +BOOL CMenuBar::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_SYSKEYDOWN || pMsg->message == WM_SYSKEYUP) + { + if (pMsg->wParam == VK_F10 || pMsg->wParam == VK_MENU) + { + if (pMsg->message == WM_SYSKEYDOWN) + { + ShowKeyboardCues(true); + } + else if (pMsg->message == WM_SYSKEYUP && m_bShowKeyboardCues) + { + if (!m_bActive) + SetFocus(); + else if (m_hwndOldFocus != nullptr && IsWindow(m_hwndOldFocus)) + ::SetFocus(m_hwndOldFocus); + } + return TRUE; + } + UINT uId = 0; + if ((pMsg->message == WM_SYSKEYDOWN) && GetToolBarCtrl().MapAccelerator(static_cast(pMsg->wParam), &uId) != 0) + { + ShowKeyboardCues(true); + OnMenuBarMenuItem(uId); + return TRUE; + } + } + else if (pMsg->message == WM_KEYDOWN && m_bActive && (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)) + { + if (pMsg->wParam == VK_ESCAPE) + { + if (m_hwndOldFocus != nullptr && IsWindow(m_hwndOldFocus)) + ::SetFocus(m_hwndOldFocus); + } + else if (pMsg->wParam == VK_RETURN) + { + OnMenuBarMenuItem(FIRST_MENUID + GetToolBarCtrl().GetHotItem()); + } + return TRUE; + } + return FALSE; +} + +LRESULT CALLBACK CMenuBar::MsgFilterProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode != MSGF_MENU) + return CallNextHookEx(m_hHook, nCode, wParam, lParam); + MSG* pMsg = reinterpret_cast(lParam); + if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_LEFT || pMsg->wParam == VK_RIGHT)) + { + if ((pMsg->wParam == VK_LEFT && ::GetSubMenu(m_pThis->m_hMenu, m_pThis->m_nCurrentHotItem) == m_pThis->m_hCurrentPopupMenu) || + (pMsg->wParam == VK_RIGHT && (m_pThis->m_nCurrentMenuItemFlags & MF_POPUP) == 0)) + { + AfxGetMainWnd()->PostMessage(WM_CANCELMODE); + CToolBarCtrl& toolbar = m_pThis->GetToolBarCtrl(); + const int nCount = toolbar.GetButtonCount(); + int nHotItem = toolbar.GetHotItem(); + nHotItem = (nHotItem + nCount + ((pMsg->wParam == VK_RIGHT) ? 1 : -1)) % nCount; + m_pThis->PostMessage(UWM_SHOWPOPUPMENU, FIRST_MENUID + nHotItem); + return 0; + } + } + else if (pMsg->message == WM_MOUSEMOVE) + { + CRect rc; + m_pThis->GetWindowRect(&rc); + CPoint ptPrev = m_pThis->m_ptCurrentCursor; + m_pThis->m_ptCurrentCursor = { GET_X_LPARAM(pMsg->lParam) - rc.left, GET_Y_LPARAM(pMsg->lParam) - rc.top }; + if (ptPrev == CPoint{} || ptPrev == m_pThis->m_ptCurrentCursor) + return 0; + const int nItem = m_pThis->GetToolBarCtrl().HitTest(&m_pThis->m_ptCurrentCursor); + if (nItem >= 0 && m_pThis->m_nCurrentHotItem != nItem) + { + AfxGetMainWnd()->PostMessage(WM_CANCELMODE); + m_pThis->PostMessage(UWM_SHOWPOPUPMENU, FIRST_MENUID + nItem); + return 0; + } + } + else if (pMsg->message == WM_MENUSELECT) + { + m_pThis->m_nCurrentMenuItemFlags = HIWORD(pMsg->wParam); + m_pThis->m_hCurrentPopupMenu = reinterpret_cast(pMsg->lParam); + if (m_pThis->m_nCurrentMenuItemFlags == 0xffff && m_pThis->m_hCurrentPopupMenu == nullptr) // Menu closing + { + if (m_pThis->m_hwndOldFocus && IsWindow(m_pThis->m_hwndOldFocus)) + ::SetFocus(m_pThis->m_hwndOldFocus); + else + m_pThis->LoseFocus(); + } + } + return CallNextHookEx(m_hHook, nCode, wParam, lParam); +} diff --git a/Src/MenuBar.h b/Src/MenuBar.h new file mode 100644 index 00000000000..8ee299059d5 --- /dev/null +++ b/Src/MenuBar.h @@ -0,0 +1,61 @@ +/** + * @file MenuBar.h + * + * @brief Declaration file for CMenuBar + * + */ +#pragma once + +#include +#include +#include + +class CMenuBar : public CToolBar +{ + DECLARE_DYNAMIC(CMenuBar) +public: + constexpr static int FIRST_MENUID = 10000; + + CMenuBar(); + + virtual BOOL Create(CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP, UINT nID = AFX_IDW_MENUBAR); + bool AttachMenu(CMenu* pMenu); + void OnMenuBarMenuItem(UINT nID); + void OnUpdateMenuBarMenuItem(CCmdUI* pCmdUI); + BOOL PreTranslateMessage(MSG* pMsg); + +protected: + //{{AFX_MSG(CMenuBar) + afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnMouseLeave(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg LRESULT OnShowPopupMenu(WPARAM wParam, LPARAM lParam); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + static LRESULT CALLBACK MsgFilterProc(int code, WPARAM wp, LPARAM lp); + void DrawMDIButtons(HDC hDC); + int GetMDIButtonIndexFromPoint(CPoint pt) const; + CRect GetMDIButtonsRect() const; + CRect GetMDIButtonRect(int nItem) const; + void ShowKeyboardCues(bool show); + void LoseFocus(); + + HMENU m_hMenu; + bool m_bActive; + bool m_bMouseTracking; + bool m_bShowKeyboardCues; + int m_nMDIButtonDown; + int m_nMDIButtonHot; + HWND m_hwndOldFocus; + int m_nCurrentHotItem; + UINT m_nCurrentMenuItemFlags; + HMENU m_hCurrentPopupMenu; + CPoint m_ptCurrentCursor; + static HHOOK m_hHook; + static CMenuBar* m_pThis; +}; \ No newline at end of file diff --git a/Src/Merge.vcxproj b/Src/Merge.vcxproj index 646eebf143d..17dc1c6a1bb 100644 --- a/Src/Merge.vcxproj +++ b/Src/Merge.vcxproj @@ -946,6 +946,7 @@ $(IntDir)$(TargetName)2.pch + @@ -1445,6 +1446,7 @@ + @@ -1464,6 +1466,7 @@ + @@ -1657,6 +1660,7 @@ + diff --git a/Src/Merge.vcxproj.filters b/Src/Merge.vcxproj.filters index 40704962b1b..7d185baf24c 100644 --- a/Src/Merge.vcxproj.filters +++ b/Src/Merge.vcxproj.filters @@ -729,6 +729,12 @@ MFCGui\Common\Source Files + + MFCGui\Common\Source Files + + + MFCGui\Common\Source Files + @@ -1394,6 +1400,12 @@ MFCGui\Common\Header Files + + MFCGui\Common\Header Files + + + MFCGui\Common\Header Files + diff --git a/Src/MyReBar.cpp b/Src/MyReBar.cpp new file mode 100644 index 00000000000..eb55cec3e0c --- /dev/null +++ b/Src/MyReBar.cpp @@ -0,0 +1,72 @@ +#include "StdAfx.h" +#include "MyReBar.h" + +BEGIN_MESSAGE_MAP(CMyReBar, CReBar) + ON_WM_ERASEBKGND() +END_MESSAGE_MAP() + +CMyReBar::CMyReBar() +{ +} + +BOOL CMyReBar::OnEraseBkgnd(CDC* pDC) +{ + CRect rect; + GetClientRect(&rect); + pDC->FillSolidRect(&rect, GetSysColor(COLOR_3DHIGHLIGHT)); + return TRUE; +} + +void CMyReBar::LoadStateFromString(const CString& state) +{ + constexpr int BARCOUNT_MAX = 4; + unsigned count = 0; + unsigned id[BARCOUNT_MAX]{}; + int cx[BARCOUNT_MAX]{-1, -1}; + _stscanf_s(state, _T("%u,%u,%d,%u,%d,%u,%d,%u,%d"), &count, &id[0], &cx[0], &id[1], &cx[1], &id[2], &cx[2], &id[3], &cx[3]); + count = (count > BARCOUNT_MAX) ? BARCOUNT_MAX : count; + CReBarCtrl& rebar = GetReBarCtrl(); + const unsigned nBarCount = rebar.GetBandCount(); + for (unsigned i = 0; i < count; ++i) + { + for (unsigned j = i; j < nBarCount; ++j) + { + REBARBANDINFO rbi{ sizeof(REBARBANDINFO), RBBIM_CHILD | RBBIM_SIZE | RBBIM_STYLE }; + rebar.GetBandInfo(j, &rbi); + if (id[i] != 0 && id[i] == static_cast(GetWindowLong(rbi.hwndChild, GWL_ID))) + { + if (j != i) + rebar.MoveBand(j, i); + if (cx[i] < 0) + { + rbi.fStyle |= RBBS_BREAK; + rbi.cx = -cx[i]; + } + else + { + rbi.fStyle &= ~RBBS_BREAK; + rbi.cx = cx[i]; + } + rbi.fMask &= ~RBBIM_CHILD; + rebar.SetBandInfo(i, &rbi); + } + } + } +} + +CString CMyReBar::GenerateStateString() +{ + CReBarCtrl& rebar = GetReBarCtrl(); + const unsigned nCount = rebar.GetBandCount(); + CString state; + state.AppendFormat(_T("%d"), nCount); + for (unsigned i = 0; i < nCount; ++i) + { + REBARBANDINFO rbi{ sizeof(REBARBANDINFO), RBBIM_CHILD | RBBIM_SIZE | RBBIM_STYLE }; + rebar.GetBandInfo(i, &rbi); + state.AppendFormat(_T(",%u,%d"), + GetWindowLong(rbi.hwndChild, GWL_ID), + ((rbi.fStyle & RBBS_BREAK) != 0) ? -static_cast(rbi.cx) : rbi.cx); + } + return state; +} \ No newline at end of file diff --git a/Src/MyReBar.h b/Src/MyReBar.h new file mode 100644 index 00000000000..3b3462d50ef --- /dev/null +++ b/Src/MyReBar.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class CMyReBar : public CReBar +{ +public: + CMyReBar(); + void LoadStateFromString(const CString& state); + CString GenerateStateString(); + +protected: + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + DECLARE_MESSAGE_MAP() +}; \ No newline at end of file diff --git a/Src/OptionsDef.h b/Src/OptionsDef.h index 2ab3fbb5901..397157726bb 100644 --- a/Src/OptionsDef.h +++ b/Src/OptionsDef.h @@ -30,6 +30,7 @@ inline const String OPT_TREE_MODE {_T("Settings/TreeMode"s)}; inline const String OPT_SHOW_TOOLBAR {_T("Settings/ShowToolbar"s)}; inline const String OPT_SHOW_STATUSBAR {_T("Settings/ShowStatusbar"s)}; inline const String OPT_SHOW_TABBAR {_T("Settings/ShowTabbar"s)}; +inline const String OPT_REBAR_STATE {_T("Settings/ReBarState"s)}; inline const String OPT_TOOLBAR_SIZE {_T("Settings/ToolbarSize"s)}; inline const String OPT_RESIZE_PANES {_T("Settings/AutoResizePanes"s)}; diff --git a/Src/OptionsInit.cpp b/Src/OptionsInit.cpp index 63b605e1618..c3c10defca4 100644 --- a/Src/OptionsInit.cpp +++ b/Src/OptionsInit.cpp @@ -64,6 +64,7 @@ void Init(COptionsMgr *pOptions) pOptions->InitOption(OPT_SHOW_TOOLBAR, true); pOptions->InitOption(OPT_SHOW_STATUSBAR, true); pOptions->InitOption(OPT_SHOW_TABBAR, true); + pOptions->InitOption(OPT_REBAR_STATE, _T("")); pOptions->InitOption(OPT_TOOLBAR_SIZE, 0, 0, 2); pOptions->InitOption(OPT_RESIZE_PANES, false);