OSDN Git Service

Use __super
[winmerge-jp/winmerge-jp.git] / Src / MergeEditFrm.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //    SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
7 /** 
8  * @file  MergeEditFrm.cpp
9  *
10  * @brief Implementation file for CMergeEditFrame
11  *
12  */
13
14 #include "stdafx.h"
15 #include "MergeEditFrm.h"
16 #include "Merge.h"
17 #include "MainFrm.h"
18 #include "MergeDoc.h"
19 #include "MergeEditView.h"
20 #include "LocationView.h"
21 #include "DiffViewBar.h"
22 #include "OptionsDef.h"
23 #include "OptionsMgr.h"
24
25 #ifdef _DEBUG
26 #define new DEBUG_NEW
27 #endif
28
29 #define SWAPPARAMS_IF(c, a, b) (c ? a : b), (c ? b : a)
30
31 /////////////////////////////////////////////////////////////////////////////
32 // CMergeEditFrame
33
34 IMPLEMENT_DYNCREATE(CMergeEditFrame, CMergeFrameCommon)
35
36 BEGIN_MESSAGE_MAP(CMergeEditFrame, CMergeFrameCommon)
37         //{{AFX_MSG_MAP(CMergeEditFrame)
38         ON_WM_CREATE()
39         ON_WM_CLOSE()
40         ON_WM_MDIACTIVATE()
41         ON_WM_TIMER()
42         ON_WM_SIZE()
43         ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
44         ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
45         // [View] menu
46         ON_UPDATE_COMMAND_UI(ID_VIEW_DETAIL_BAR, OnUpdateControlBarMenu)
47         ON_COMMAND_EX(ID_VIEW_DETAIL_BAR, OnBarCheck)
48         ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
49         ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
50         ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
51         ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
52         //}}AFX_MSG_MAP
53 END_MESSAGE_MAP()
54
55 #define IDT_SAVEPOSITION 2
56
57 /////////////////////////////////////////////////////////////////////////////
58 // CMergeEditFrame construction/destruction
59
60 /**
61  * @brief Constructor.
62  */
63 CMergeEditFrame::CMergeEditFrame()
64 : CMergeFrameCommon(IDI_EQUALTEXTFILE, IDI_NOTEQUALTEXTFILE)
65 , m_pwndDetailMergeEditSplitterView(nullptr)
66 {
67         m_pMergeDoc = 0;
68 }
69
70 /**
71  * Destructor.
72  */
73 CMergeEditFrame::~CMergeEditFrame()
74 {
75 }
76
77 BOOL CMergeEditFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
78         CCreateContext* pContext)
79 {
80         m_wndSplitter.HideBorders(true);
81         m_wndSplitter.Create(this, 2, 1, CSize(1, 1), pContext, WS_CHILD | WS_VISIBLE | 1/*SPLS_DYNAMIC_SPLIT*/);
82
83         // Merge frame has also a dockable bar at the very left
84         // This is not the client area, but we create it now because we want
85         // to use the CCreateContext
86         String sCaption = _("Location Pane");
87         if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
88         {
89                 TRACE0("Failed to create LocationBar\n");
90                 return FALSE;
91         }
92
93         CLocationView *pLocationView = new CLocationView;
94         DWORD dwStyle = AFX_WS_DEFAULT_VIEW & ~WS_BORDER;
95         pLocationView->Create(nullptr, nullptr, dwStyle, CRect(0,0,40,100), &m_wndLocationBar, 152, pContext);
96
97         // Merge frame has also a dockable bar at the very bottom
98         // This is not the client area, but we create it now because we want
99         // to use the CCreateContext
100         sCaption = _("Diff Pane");
101         if (!m_wndDetailBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_DETAIL_BAR))
102         {
103                 TRACE0("Failed to create DiffViewBar\n");
104                 return FALSE;
105         }
106
107         m_pwndDetailMergeEditSplitterView = new CMergeEditSplitterView();
108         m_pwndDetailMergeEditSplitterView->m_bDetailView = true;
109         m_pwndDetailMergeEditSplitterView->Create(nullptr, nullptr, dwStyle, CRect(0,0,1,1), &m_wndDetailBar, ID_VIEW_DETAIL_BAR+1, pContext);
110
111         // tell merge doc about these views
112         m_pMergeDoc = dynamic_cast<CMergeDoc *>(pContext->m_pCurrentDoc);
113         m_pMergeDoc->ForEachView([&](auto& pView) {
114                 pView->SetStatusInterface(m_wndStatusBar.GetIMergeEditStatus(pView->m_nThisPane));
115         });
116         m_pMergeDoc->SetLocationView(pLocationView);
117
118         m_wndFilePathBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
119         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
120                 m_pMergeDoc->GetView(0, pane)->SetActivePane();
121         });
122         m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
123                 m_pMergeDoc->SetDescription(pane, sText);
124                 m_pMergeDoc->UpdateHeaderPath(pane);
125         });
126         m_wndFilePathBar.SetOnFileSelectedCallback([&](int pane, const String& sFilepath) {
127                 m_pMergeDoc->ChangeFile(pane, sFilepath);
128         });
129         m_wndStatusBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
130         
131         // Set frame window handles so we can post stage changes back
132         pLocationView->SetFrameHwnd(GetSafeHwnd());
133         m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
134         m_wndDetailBar.SetFrameHwnd(GetSafeHwnd());
135
136         return TRUE;
137 }
138
139 /////////////////////////////////////////////////////////////////////////////
140 // CMergeEditFrame message handlers
141
142 /**
143 * @brief Create the child frame, the splitter, the filename bar, the status bar,
144 * the diff dockable bar, and the four views
145 *
146 * @note  the panels layout is 
147 * <ul>
148 *  <li>   -----------
149 *  <li>         !  header !
150 *  <li>         !.........!
151 *  <li>         !.   .   .!
152 *  <li>         !. 1 . 2 .!
153 *  <li>         !.   .   .!
154 *  <li>         !.........!
155 *  <li>         !.........!
156 *  <li>         !  status !
157 *  <li>         -----------
158 *  <li>         !.........!
159 *  <li>         !.   3   .!
160 *  <li>         !.dockable!
161 *  <li>         ! splitbar!
162 *  <li>         !.   4   .!
163 *  <li>         !.........!
164 *  <li>         -----------
165 * </ul>
166 */
167 int CMergeEditFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
168 {
169         if (__super::OnCreate(lpCreateStruct) == -1)
170                 return -1;
171
172         EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM|CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT);
173
174         RemoveBarBorder();
175
176         // Merge frame has a header bar at top
177         if (!m_wndFilePathBar.Create(this))
178         {
179                 TRACE0("Failed to create dialog bar\n");
180                 return -1;      // fail to create
181         }
182
183         // Set filename bars inactive so colors get initialized
184         m_wndFilePathBar.SetActive(0, false);
185         m_wndFilePathBar.SetActive(1, false);
186         m_wndFilePathBar.SetActive(2, false);
187
188         // Merge frame also has a dockable bar at the very left
189         // created in OnCreateClient 
190         m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
191                 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
192         m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
193         CRect rc{ 0, 0, 0, 0 };
194         DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT, &rc);
195
196         // Merge frame also has a dockable bar at the very bottom
197         // created in OnCreateClient 
198         m_wndDetailBar.SetBarStyle(m_wndDetailBar.GetBarStyle() |
199                 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_TOP);
200         m_wndDetailBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
201         DockControlBar(&m_wndDetailBar, AFX_IDW_DOCKBAR_BOTTOM, &rc);
202
203         // Merge frame also has a status bar at bottom, 
204         // m_wndDetailBar is below, so we create this bar after m_wndDetailBar
205         if (!m_wndStatusBar.Create(this))
206         {
207                 TRACE0("Failed to create status bar\n");
208                 return -1;      // fail to create
209         }       
210
211         // load docking positions and sizes
212         CDockState pDockState;
213         pDockState.LoadState(_T("Settings"));
214         if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
215                 SetDockState(pDockState);
216         // for the dimensions of the diff and location pane, use the CSizingControlBar loader
217         m_wndLocationBar.LoadState(_T("Settings"));
218         m_wndDetailBar.LoadState(_T("Settings"));
219
220         return 0;
221 }
222
223 BOOL CMergeEditFrame::OnBarCheck(UINT nID)
224 {
225         BOOL result = __super::OnBarCheck(nID);
226         // Fix for osdn.net #42862
227         if (nID == ID_VIEW_DETAIL_BAR && m_wndDetailBar.IsWindowVisible())
228         {
229                 int nDiff = m_pMergeDoc->GetCurrentDiff();
230                 m_pMergeDoc->ForEachView ([nDiff](auto& pView) { if (pView->m_bDetailView) pView->OnDisplayDiff(nDiff); });
231         }
232         return result;
233 }
234
235 /**
236  * @brief We must use this function before a call to SetDockState
237  *
238  * @note Without this, SetDockState will assert or crash if a bar from the 
239  * CDockState is missing in the current CMergeEditFrame.
240  * The bars are identified with their ID. This means the missing bar bug is triggered
241  * when we run WinMerge after changing the ID of a bar. 
242  */
243 bool CMergeEditFrame::EnsureValidDockState(CDockState& state) 
244 {
245         for (int i = (int) state.m_arrBarInfo.GetSize()-1 ; i >= 0; i--) 
246         {
247                 bool barIsCorrect = true;
248                 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
249                 if (pInfo == nullptr) 
250                         barIsCorrect = false;
251                 else
252                 {
253                         if (! pInfo->m_bFloating) 
254                         {
255                                 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
256                                 if (pInfo->m_pBar == nullptr) 
257                                         barIsCorrect = false; //toolbar id's probably changed   
258                         }
259                 }
260
261                 if (! barIsCorrect)
262                         state.m_arrBarInfo.RemoveAt(i);
263         }
264         return true;
265 }
266
267 void CMergeEditFrame::ActivateFrame(int nCmdShow) 
268 {
269         CMergeFrameCommon::ActivateFrame(nCmdShow);
270 }
271
272 BOOL CMergeEditFrame::DestroyWindow() 
273 {
274         SavePosition();
275         SaveActivePane();
276         SaveWindowState();
277         CFrameWnd* pParentFrame = GetParentFrame();
278         BOOL result = CMergeFrameCommon::DestroyWindow();
279         if (pParentFrame)
280                 pParentFrame->OnUpdateFrameTitle(FALSE);
281         return result;
282 }
283
284 /**
285  * @brief Save coordinates of the frame, splitters, and bars
286  *
287  * @note Do not save the maximized/restored state here. We are interested
288  * in the state of the active frame, and maybe this frame is not active
289  */
290 void CMergeEditFrame::SavePosition()
291 {
292         // save the bars layout
293         // save docking positions and sizes
294         CDockState m_pDockState;
295         GetDockState(m_pDockState);
296         m_pDockState.SaveState(_T("Settings"));
297         // for the dimensions of the diff pane, use the CSizingControlBar save
298         m_wndLocationBar.SaveState(_T("Settings"));
299         m_wndDetailBar.SaveState(_T("Settings"));
300 }
301
302 void CMergeEditFrame::SaveActivePane()
303 {
304         for (int iRowParent = 0; iRowParent < m_wndSplitter.GetRowCount(); ++iRowParent)
305         {
306                 int iRow, iCol;
307                 auto& splitterWnd = static_cast<CMergeEditSplitterView*>(m_wndSplitter.GetPane(iRowParent, 0))->m_wndSplitter;
308                 splitterWnd.GetActivePane(&iRow, &iCol);
309                 if (iRow >= 0 || iCol >= 0)
310                         GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, max(iRow, iCol));
311         }
312 }
313
314 void CMergeEditFrame::OnClose() 
315 {
316         // clean up pointers.
317         CMergeFrameCommon::OnClose();
318 }
319
320 /// update splitting position for panels 1/2 and headerbar and statusbar 
321 void CMergeEditFrame::UpdateHeaderSizes()
322 {
323         if(!::IsWindow(m_wndFilePathBar.m_hWnd) || !::IsWindow(m_wndSplitter.m_hWnd))
324                 return;
325         
326         int w[3];
327         int pane;
328         CMergeDoc * pDoc = GetMergeDoc();
329         auto& wndSplitter = GetMergeEditSplitterWnd(0);
330         if (wndSplitter.GetColumnCount() > 1)
331         {
332                 for (pane = 0; pane < wndSplitter.GetColumnCount(); pane++)
333                 {
334                         int wmin;
335                         wndSplitter.GetColumnInfo(pane, w[pane], wmin);
336                         if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
337                 }
338         }
339         else
340         {
341                 int w2, wmin;
342                 wndSplitter.GetColumnInfo(0, w2, wmin);
343                 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
344                         w[pane] = (w2 - 4 * pDoc->m_nBuffers) / pDoc->m_nBuffers;
345         }
346
347         if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + pDoc->m_nBuffers - 1, w))
348         {
349                 std::copy_n(w, pDoc->m_nBuffers - 1, m_nLastSplitPos);
350
351                 // resize controls in header dialog bar
352                 m_wndFilePathBar.Resize(w);
353
354                 m_wndStatusBar.Resize(w);
355         }
356 }
357
358 IHeaderBar * CMergeEditFrame::GetHeaderInterface()
359 {
360         return &m_wndFilePathBar;
361 }
362
363 void CMergeEditFrame::UpdateAutoPaneResize()
364 {
365         auto& wndSplitter = GetMergeEditSplitterWnd(0);
366         wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
367 }
368
369 void CMergeEditFrame::UpdateSplitter()
370 {
371         for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
372                 GetMergeEditSplitterWnd(iRow).RecalcLayout();
373         m_wndSplitter.RecalcLayout();
374         m_pwndDetailMergeEditSplitterView->m_wndSplitter.RecalcLayout();
375 }
376
377 /**
378  * @brief Synchronize control with splitter position,
379  */
380 void CMergeEditFrame::OnIdleUpdateCmdUI()
381 {
382         UpdateHeaderSizes();
383         CMergeFrameCommon::OnIdleUpdateCmdUI();
384 }
385
386 void CMergeEditFrame::OnTimer(UINT_PTR nIDEvent) 
387 {
388         if (nIDEvent == IDT_SAVEPOSITION)
389         {
390                 SavePosition();
391                 KillTimer(IDT_SAVEPOSITION);
392         }
393         else
394         {
395                 UpdateHeaderSizes();
396         }
397         CMergeFrameCommon::OnTimer(nIDEvent);
398 }
399
400 void CMergeEditFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
401 {
402         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
403
404         CMergeDoc *pDoc = GetMergeDoc();
405         if (bActivate && pDoc != nullptr)
406                 this->GetParentFrame()->PostMessage(WM_USER+1);
407         return;
408 }
409
410 /**
411  * @brief Split panes vertically
412  */
413 void CMergeEditFrame::OnViewSplitVertically() 
414 {
415         auto& wndSplitter = GetMergeEditSplitterWnd(0);
416         bool bSplitVertically = (wndSplitter.GetColumnCount() != 1);
417         bSplitVertically = !bSplitVertically; // toggle
418         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
419         for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
420                 GetMergeEditSplitterWnd(iRow).FlipSplit();
421         m_pwndDetailMergeEditSplitterView->m_wndSplitter.FlipSplit();
422 }
423
424 /**
425  * @brief Update "Split Vertically" UI items
426  */
427 void CMergeEditFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
428 {
429         auto& wndSplitter = GetMergeEditSplitterWnd(0);
430         pCmdUI->Enable(TRUE);
431         pCmdUI->SetCheck((wndSplitter.GetColumnCount() != 1));
432 }
433
434 /**
435  * @brief Update any resources necessary after a GUI language change
436  */
437 void CMergeEditFrame::UpdateResources()
438 {
439         m_wndStatusBar.UpdateResources();
440         m_wndLocationBar.UpdateResources();
441         m_wndDetailBar.UpdateResources();
442 }
443
444 /**
445  * @brief Save pane sizes and positions when one of panes requests it.
446  */
447 LRESULT CMergeEditFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
448 {
449         KillTimer(IDT_SAVEPOSITION);
450         SetTimer(IDT_SAVEPOSITION, 300, nullptr);
451         return 0;
452 }
453
454 void CMergeEditFrame::OnSize(UINT nType, int cx, int cy) 
455 {
456         __super::OnSize(nType, cx, cy);
457         
458         UpdateHeaderSizes();
459 }