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 MergeEditFrm.cpp
10 * @brief Implementation file for CMergeEditFrame
15 #include "MergeEditFrm.h"
19 #include "MergeEditView.h"
20 #include "LocationView.h"
21 #include "DiffViewBar.h"
22 #include "OptionsDef.h"
23 #include "OptionsMgr.h"
29 #define SWAPPARAMS_IF(c, a, b) (c ? a : b), (c ? b : a)
31 /////////////////////////////////////////////////////////////////////////////
34 IMPLEMENT_DYNCREATE(CMergeEditFrame, CMergeFrameCommon)
36 BEGIN_MESSAGE_MAP(CMergeEditFrame, CMergeFrameCommon)
37 //{{AFX_MSG_MAP(CMergeEditFrame)
42 ON_UPDATE_COMMAND_UI(ID_VIEW_DETAIL_BAR, OnUpdateControlBarMenu)
43 ON_COMMAND_EX(ID_VIEW_DETAIL_BAR, OnBarCheck)
44 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
45 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
46 ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
47 ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
48 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
50 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
54 #define IDT_SAVEPOSITION 2
56 /////////////////////////////////////////////////////////////////////////////
57 // CMergeEditFrame construction/destruction
62 CMergeEditFrame::CMergeEditFrame()
63 : CMergeFrameCommon(IDI_EQUALTEXTFILE, IDI_NOTEQUALTEXTFILE)
64 , m_pwndDetailMergeEditSplitterView(nullptr)
72 CMergeEditFrame::~CMergeEditFrame()
76 BOOL CMergeEditFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
77 CCreateContext* pContext)
79 m_wndSplitter.HideBorders(true);
80 m_wndSplitter.Create(this, 2, 1, CSize(1, 1), pContext, WS_CHILD | WS_VISIBLE | 1/*SPLS_DYNAMIC_SPLIT*/);
82 // Merge frame has also a dockable bar at the very left
83 // This is not the client area, but we create it now because we want
84 // to use the CCreateContext
85 String sCaption = _("Location Pane");
86 if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
88 TRACE0("Failed to create LocationBar\n");
92 CLocationView *pLocationView = new CLocationView;
93 DWORD dwStyle = AFX_WS_DEFAULT_VIEW & ~WS_BORDER;
94 pLocationView->Create(nullptr, nullptr, dwStyle, CRect(0,0,40,100), &m_wndLocationBar, 152, pContext);
96 // Merge frame has also a dockable bar at the very bottom
97 // This is not the client area, but we create it now because we want
98 // to use the CCreateContext
99 sCaption = _("Diff Pane");
100 if (!m_wndDetailBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_DETAIL_BAR))
102 TRACE0("Failed to create DiffViewBar\n");
106 m_pwndDetailMergeEditSplitterView = new CMergeEditSplitterView();
107 m_pwndDetailMergeEditSplitterView->m_bDetailView = true;
108 m_pwndDetailMergeEditSplitterView->Create(nullptr, nullptr, dwStyle, CRect(0,0,1,1), &m_wndDetailBar, ID_VIEW_DETAIL_BAR+1, pContext);
110 // tell merge doc about these views
111 m_pMergeDoc = dynamic_cast<CMergeDoc *>(pContext->m_pCurrentDoc);
112 m_pMergeDoc->ForEachView([&](auto& pView) {
113 pView->SetStatusInterface(m_wndStatusBar.GetIMergeEditStatus(pView->m_nThisPane));
115 m_pMergeDoc->SetLocationView(pLocationView);
117 m_wndFilePathBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
118 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
119 m_pMergeDoc->GetView(0, pane)->SetActivePane();
121 m_wndStatusBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
123 // Set frame window handles so we can post stage changes back
124 pLocationView->SetFrameHwnd(GetSafeHwnd());
125 m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
126 m_wndDetailBar.SetFrameHwnd(GetSafeHwnd());
131 /////////////////////////////////////////////////////////////////////////////
132 // CMergeEditFrame message handlers
135 * @brief Create the child frame, the splitter, the filename bar, the status bar,
136 * the diff dockable bar, and the four views
138 * @note the panels layout is
159 int CMergeEditFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
161 if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
164 EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM|CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT);
168 // Merge frame has a header bar at top
169 if (!m_wndFilePathBar.Create(this))
171 TRACE0("Failed to create dialog bar\n");
172 return -1; // fail to create
175 // Set filename bars inactive so colors get initialized
176 m_wndFilePathBar.SetActive(0, false);
177 m_wndFilePathBar.SetActive(1, false);
178 m_wndFilePathBar.SetActive(2, false);
180 // Merge frame also has a dockable bar at the very left
181 // created in OnCreateClient
182 m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
183 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
184 m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
185 CRect rc{ 0, 0, 0, 0 };
186 DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT, &rc);
188 // Merge frame also has a dockable bar at the very bottom
189 // created in OnCreateClient
190 m_wndDetailBar.SetBarStyle(m_wndDetailBar.GetBarStyle() |
191 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_TOP);
192 m_wndDetailBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
193 DockControlBar(&m_wndDetailBar, AFX_IDW_DOCKBAR_BOTTOM, &rc);
195 // Merge frame also has a status bar at bottom,
196 // m_wndDetailBar is below, so we create this bar after m_wndDetailBar
197 if (!m_wndStatusBar.Create(this))
199 TRACE0("Failed to create status bar\n");
200 return -1; // fail to create
203 // load docking positions and sizes
204 CDockState pDockState;
205 pDockState.LoadState(_T("Settings"));
206 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
207 SetDockState(pDockState);
208 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
209 m_wndLocationBar.LoadState(_T("Settings"));
210 m_wndDetailBar.LoadState(_T("Settings"));
216 * @brief We must use this function before a call to SetDockState
218 * @note Without this, SetDockState will assert or crash if a bar from the
219 * CDockState is missing in the current CMergeEditFrame.
220 * The bars are identified with their ID. This means the missing bar bug is triggered
221 * when we run WinMerge after changing the ID of a bar.
223 bool CMergeEditFrame::EnsureValidDockState(CDockState& state)
225 for (int i = (int) state.m_arrBarInfo.GetSize()-1 ; i >= 0; i--)
227 bool barIsCorrect = true;
228 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
229 if (pInfo == nullptr)
230 barIsCorrect = false;
233 if (! pInfo->m_bFloating)
235 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
236 if (pInfo->m_pBar == nullptr)
237 barIsCorrect = false; //toolbar id's probably changed
242 state.m_arrBarInfo.RemoveAt(i);
247 void CMergeEditFrame::ActivateFrame(int nCmdShow)
249 CMergeFrameCommon::ActivateFrame(nCmdShow);
252 BOOL CMergeEditFrame::DestroyWindow()
257 return CMergeFrameCommon::DestroyWindow();
261 * @brief Save coordinates of the frame, splitters, and bars
263 * @note Do not save the maximized/restored state here. We are interested
264 * in the state of the active frame, and maybe this frame is not active
266 void CMergeEditFrame::SavePosition()
268 // save the bars layout
269 // save docking positions and sizes
270 CDockState m_pDockState;
271 GetDockState(m_pDockState);
272 m_pDockState.SaveState(_T("Settings"));
273 // for the dimensions of the diff pane, use the CSizingControlBar save
274 m_wndLocationBar.SaveState(_T("Settings"));
275 m_wndDetailBar.SaveState(_T("Settings"));
278 void CMergeEditFrame::SaveActivePane()
280 for (int iRowParent = 0; iRowParent < m_wndSplitter.GetRowCount(); ++iRowParent)
283 auto& splitterWnd = static_cast<CMergeEditSplitterView*>(m_wndSplitter.GetPane(iRowParent, 0))->m_wndSplitter;
284 splitterWnd.GetActivePane(&iRow, &iCol);
285 if (iRow >= 0 || iCol >= 0)
286 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, max(iRow, iCol));
290 void CMergeEditFrame::OnClose()
292 // clean up pointers.
293 CMergeFrameCommon::OnClose();
295 GetMainFrame()->ClearStatusbarItemCount();
298 /// update splitting position for panels 1/2 and headerbar and statusbar
299 void CMergeEditFrame::UpdateHeaderSizes()
301 if(!::IsWindow(m_wndFilePathBar.m_hWnd) || !::IsWindow(m_wndSplitter.m_hWnd))
306 CMergeDoc * pDoc = GetMergeDoc();
307 auto& wndSplitter = GetMergeEditSplitterWnd(0);
308 if (wndSplitter.GetColumnCount() > 1)
310 for (pane = 0; pane < wndSplitter.GetColumnCount(); pane++)
313 wndSplitter.GetColumnInfo(pane, w[pane], wmin);
314 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
320 wndSplitter.GetColumnInfo(0, w2, wmin);
321 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
322 w[pane] = (w2 - 4 * pDoc->m_nBuffers) / pDoc->m_nBuffers;
325 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + pDoc->m_nBuffers - 1, w))
327 std::copy_n(w, pDoc->m_nBuffers - 1, m_nLastSplitPos);
329 // resize controls in header dialog bar
330 m_wndFilePathBar.Resize(w);
332 m_wndStatusBar.Resize(w);
336 IHeaderBar * CMergeEditFrame::GetHeaderInterface()
338 return &m_wndFilePathBar;
341 void CMergeEditFrame::UpdateAutoPaneResize()
343 auto& wndSplitter = GetMergeEditSplitterWnd(0);
344 wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
347 void CMergeEditFrame::UpdateSplitter()
349 for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
350 GetMergeEditSplitterWnd(iRow).RecalcLayout();
351 m_wndSplitter.RecalcLayout();
352 m_pwndDetailMergeEditSplitterView->m_wndSplitter.RecalcLayout();
356 * @brief Synchronize control with splitter position,
358 void CMergeEditFrame::OnIdleUpdateCmdUI()
361 CMergeFrameCommon::OnIdleUpdateCmdUI();
364 void CMergeEditFrame::OnTimer(UINT_PTR nIDEvent)
366 if (nIDEvent == IDT_SAVEPOSITION)
369 KillTimer(IDT_SAVEPOSITION);
375 CMergeFrameCommon::OnTimer(nIDEvent);
378 void CMergeEditFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
380 CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
382 CMergeDoc *pDoc = GetMergeDoc();
383 if (bActivate && pDoc != nullptr)
384 this->GetParentFrame()->PostMessage(WM_USER+1);
389 * @brief Split panes vertically
391 void CMergeEditFrame::OnViewSplitVertically()
393 auto& wndSplitter = GetMergeEditSplitterWnd(0);
394 bool bSplitVertically = (wndSplitter.GetColumnCount() != 1);
395 bSplitVertically = !bSplitVertically; // toggle
396 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
397 for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
398 GetMergeEditSplitterWnd(iRow).FlipSplit();
399 m_pwndDetailMergeEditSplitterView->m_wndSplitter.FlipSplit();
403 * @brief Update "Split Vertically" UI items
405 void CMergeEditFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
407 auto& wndSplitter = GetMergeEditSplitterWnd(0);
408 pCmdUI->Enable(TRUE);
409 pCmdUI->SetCheck((wndSplitter.GetColumnCount() != 1));
412 /// Document commanding us to close
413 void CMergeEditFrame::CloseNow()
415 SavePosition(); // Save settings before closing!
422 * @brief Update any resources necessary after a GUI language change
424 void CMergeEditFrame::UpdateResources()
426 m_wndStatusBar.UpdateResources();
427 m_wndLocationBar.UpdateResources();
428 m_wndDetailBar.UpdateResources();
432 * @brief Save pane sizes and positions when one of panes requests it.
434 LRESULT CMergeEditFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
436 KillTimer(IDT_SAVEPOSITION);
437 SetTimer(IDT_SAVEPOSITION, 300, nullptr);
441 void CMergeEditFrame::OnSize(UINT nType, int cx, int cy)
443 CMDIChildWnd::OnSize(nType, cx, cy);