1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file HexMergeFrm.cpp
10 * @brief Implementation file for CHexMergeFrame
15 #include "HexMergeFrm.h"
17 #include "HexMergeDoc.h"
18 #include "HexMergeView.h"
19 #include "OptionsDef.h"
20 #include "OptionsMgr.h"
26 /////////////////////////////////////////////////////////////////////////////
29 IMPLEMENT_DYNCREATE(CHexMergeFrame, CMergeFrameCommon)
31 BEGIN_MESSAGE_MAP(CHexMergeFrame, CMergeFrameCommon)
32 //{{AFX_MSG_MAP(CHexMergeFrame)
36 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
37 ON_UPDATE_COMMAND_UI(ID_VIEW_DETAIL_BAR, OnUpdateControlBarMenu)
38 ON_COMMAND_EX(ID_VIEW_DETAIL_BAR, OnBarCheck)
39 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
40 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
41 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
46 * @brief Statusbar pane indexes
58 /////////////////////////////////////////////////////////////////////////////
59 // CHexMergeFrame construction/destruction
61 CHexMergeFrame::CHexMergeFrame()
62 : CMergeFrameCommon(IDI_EQUALBINARY, IDI_BINARYDIFF)
63 , m_HScrollInfo{}, m_VScrollInfo{}
68 CHexMergeFrame::~CHexMergeFrame()
73 * @brief Create a status bar to be associated with a heksedit control
75 void CHexMergeFrame::CreateHexWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
77 const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
78 auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
79 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
80 wndStatusBar.SetIndicators(0, 3);
81 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
82 wndStatusBar.SetPaneInfo(1, 0, 0, pointToPixel(60));
83 wndStatusBar.SetPaneInfo(2, 0, 0, pointToPixel(60));
84 wndStatusBar.SetParent(this);
85 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
89 * @brief Create the splitter, the filename bar, the status bar, and the two views
91 BOOL CHexMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
92 CCreateContext* pContext)
94 m_pMergeDoc = dynamic_cast<CHexMergeDoc *>(pContext->m_pCurrentDoc);
96 // create a splitter with 1 row, 2 columns
97 if (!m_wndSplitter.CreateStatic(this, 1, m_pMergeDoc->m_nBuffers, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL) )
99 TRACE0("Failed to CreateStaticSplitter\n");
104 for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
106 if (!m_wndSplitter.CreateView(0, nPane,
107 RUNTIME_CLASS(CHexMergeView), CSize(-1, 200), pContext))
109 TRACE0("Failed to create first pane\n");
114 m_wndSplitter.ResizablePanes(true);
115 m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
117 // Merge frame has a header bar at top
118 if (!m_wndFilePathBar.Create(this))
120 TRACE0("Failed to create dialog bar\n");
121 return FALSE; // fail to create
124 m_wndFilePathBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
125 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
129 // Set filename bars inactive so colors get initialized
130 for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
131 m_wndFilePathBar.SetActive(nPane, false);
133 CHexMergeView *pView[3];
134 for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
135 pView[nPane] = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(0, nPane));
137 for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
138 CreateHexWndStatusBar(m_wndStatusBar[nPane], pView[nPane]);
139 CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
140 m_rectBorder.bottom = size.cy;
142 // get the IHexEditorWindow interfaces
143 IHexEditorWindow *pif[3];
144 for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
146 pif[nPane] = reinterpret_cast<IHexEditorWindow *>(
147 ::GetWindowLongPtr(pView[nPane]->m_hWnd, GWLP_USERDATA));
150 // tell the heksedit controls about each other
151 if (m_pMergeDoc->m_nBuffers == 2)
153 pif[0]->set_sibling(pif[1]);
154 pif[1]->set_sibling(pif[0]);
155 pif[1]->share_undorecords(pif[0]->share_undorecords(nullptr));
157 else if (m_pMergeDoc->m_nBuffers > 2)
159 pif[0]->set_sibling(pif[1]);
160 pif[1]->set_sibling2(pif[0], pif[2]);
161 pif[2]->set_sibling(pif[1]);
162 pif[2]->share_undorecords(
163 pif[1]->share_undorecords(
164 pif[0]->share_undorecords(nullptr)));
167 // tell merge doc about these views
168 m_pMergeDoc = dynamic_cast<CHexMergeDoc *>(pContext->m_pCurrentDoc);
169 m_pMergeDoc->SetMergeViews(pView);
170 m_pMergeDoc->RefreshOptions();
175 /////////////////////////////////////////////////////////////////////////////
176 // CHexMergeFrame message handlers
178 void CHexMergeFrame::ActivateFrame(int nCmdShow)
180 CMergeFrameCommon::ActivateFrame(nCmdShow);
184 * @brief Save the window's position, free related resources, and destroy the window
186 BOOL CHexMergeFrame::DestroyWindow()
191 return CMDIChildWnd::DestroyWindow();
195 * @brief Save coordinates of the frame, splitters, and bars
197 * @note Do not save the maximized/restored state here. We are interested
198 * in the state of the active frame, and maybe this frame is not active
200 void CHexMergeFrame::SavePosition()
204 void CHexMergeFrame::SaveActivePane()
206 if (CWnd* pLeft = m_wndSplitter.GetPane(0, 0))
208 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, GetActivePane());
212 void CHexMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
214 CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
216 CHexMergeDoc *pDoc = GetMergeDoc();
217 if (bActivate && pDoc != nullptr)
218 this->GetParentFrame()->PostMessage(WM_USER+1);
222 void CHexMergeFrame::OnSize(UINT nType, int cx, int cy)
224 CMDIChildWnd::OnSize(nType, cx, cy);
228 /// update splitting position for panels 1/2 and headerbar and statusbar
229 void CHexMergeFrame::UpdateHeaderSizes()
231 if (!m_wndSplitter.m_hWnd || !m_wndFilePathBar.m_hWnd)
234 int nPaneCount = m_wndSplitter.GetColumnCount();
235 for (int pane = 0; pane < nPaneCount; pane++)
237 m_wndSplitter.GetColumnInfo(pane, w[pane], wmin);
238 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
241 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
243 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
245 // resize controls in header dialog bar
246 m_wndFilePathBar.Resize(w);
248 GetClientRect(&rcFrame);
250 rc.top = rc.bottom - m_rectBorder.bottom;
252 for (int pane = 0; pane < nPaneCount; pane++)
254 if (pane < nPaneCount - 1)
255 rc.right += w[pane] + 6;
257 rc.right = rcFrame.right;
258 m_wndStatusBar[pane].MoveWindow(&rc);
264 void CHexMergeFrame::UpdateAutoPaneResize()
266 m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
269 void CHexMergeFrame::UpdateSplitter()
271 m_wndSplitter.RecalcLayout();
274 int CHexMergeFrame::GetActivePane()
277 if (m_wndSplitter.GetColumnCount() > 1)
278 m_wndSplitter.GetActivePane(nullptr, &nPane);
280 m_wndSplitter.GetActivePane(&nPane, nullptr);
284 void CHexMergeFrame::SetActivePane(int nPane)
286 if (m_wndSplitter.GetColumnCount() > 1)
287 m_wndSplitter.SetActivePane(0, nPane);
289 m_wndSplitter.SetActivePane(nPane, 0);
293 * @brief Synchronize control and status bar placements with splitter position,
294 * update mod indicators, synchronize scrollbars
296 void CHexMergeFrame::OnIdleUpdateCmdUI()
298 if (IsWindowVisible())
303 int nColumns = m_wndSplitter.GetColumnCount();
304 CHexMergeView *pView[3] = {0};
305 for (pane = 0; pane < nColumns; ++pane)
306 pView[pane] = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(0, pane));
308 // Update mod indicators
311 for (pane = 0; pane < nColumns; ++pane)
313 if (m_wndFilePathBar.GetDlgItemText(IDC_STATIC_TITLE_PANE0 + pane, ind, 2))
314 if (pView[pane]->GetModified() ? ind[0] != _T('*') : ind[0] == _T('*'))
315 m_pMergeDoc->UpdateHeaderPath(pane);
318 // Synchronize scrollbars
319 SCROLLINFO si, siView[3];
320 // Synchronize horizontal scrollbars
321 pView[0]->GetScrollInfo(SB_HORZ, &si, SIF_ALL | SIF_DISABLENOSCROLL);
322 m_HScrollInfo[0] = si;
323 for (pane = 1; pane < nColumns; ++pane)
326 pView[pane]->GetScrollInfo(SB_HORZ, &siCur, SIF_ALL | SIF_DISABLENOSCROLL);
327 siView[pane] = siCur;
328 if (si.nMin > siCur.nMin)
329 si.nMin = siCur.nMin;
330 if (si.nPage < siCur.nPage)
331 si.nPage = siCur.nPage;
332 if (si.nMax < siCur.nMax)
333 si.nMax = siCur.nMax;
334 if (memcmp(&siCur, &m_HScrollInfo[pane], sizeof si))
336 si.nPos = siCur.nPos;
337 si.nTrackPos = siCur.nTrackPos;
339 m_HScrollInfo[pane] = siCur;
341 for (pane = 0; pane < nColumns; ++pane)
343 if (memcmp(&si, &siView[pane], sizeof si))
345 pView[pane]->SetScrollInfo(SB_HORZ, &si);
346 pView[pane]->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
349 for (pane = 0; pane < nColumns; ++pane)
350 m_wndSplitter.GetScrollBarCtrl(pView[pane], SB_HORZ)->SetScrollInfo(&si);
352 // Synchronize vertical scrollbars
353 pView[0]->GetScrollInfo(SB_VERT, &si, SIF_ALL | SIF_DISABLENOSCROLL);
354 m_VScrollInfo[0] = si;
355 for (pane = 1; pane < nColumns; ++pane)
358 pView[pane]->GetScrollInfo(SB_VERT, &siCur, SIF_ALL | SIF_DISABLENOSCROLL);
359 siView[pane] = siCur;
360 if (si.nMin > siCur.nMin)
361 si.nMin = siCur.nMin;
362 if (si.nMax < siCur.nMax)
363 si.nMax = siCur.nMax;
364 if (memcmp(&siCur, &m_VScrollInfo[pane], sizeof si))
366 si.nPos = siCur.nPos;
367 si.nTrackPos = siCur.nTrackPos;
369 m_VScrollInfo[pane] = siCur;
371 for (pane = 0; pane < nColumns; ++pane)
373 if (memcmp(&si, &siView[pane], sizeof si))
375 pView[pane]->SetScrollInfo(SB_VERT, &si);
376 pView[pane]->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
380 m_wndSplitter.GetScrollBarCtrl(pView[nColumns - 1], SB_VERT)->SetScrollInfo(&si);
382 CMDIChildWnd::OnIdleUpdateCmdUI();
385 /// Document commanding us to close
386 void CHexMergeFrame::CloseNow()
388 SavePosition(); // Save settings before closing!
395 * @brief Update any resources necessary after a GUI language change
397 void CHexMergeFrame::UpdateResources()
402 * @brief Save pane sizes and positions when one of panes requests it.
404 LRESULT CHexMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)