OSDN Git Service

c34c854c3d38e620df06d14051e8933762240324
[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_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)
49         ON_WM_SIZE()
50         ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
51         //}}AFX_MSG_MAP
52 END_MESSAGE_MAP()
53
54 #define IDT_SAVEPOSITION 2
55
56 /////////////////////////////////////////////////////////////////////////////
57 // CMergeEditFrame construction/destruction
58
59 /**
60  * @brief Constructor.
61  */
62 CMergeEditFrame::CMergeEditFrame()
63 : CMergeFrameCommon(IDI_EQUALTEXTFILE, IDI_NOTEQUALTEXTFILE)
64 , m_pwndDetailMergeEditSplitterView(nullptr)
65 {
66         m_pMergeDoc = 0;
67 }
68
69 /**
70  * Destructor.
71  */
72 CMergeEditFrame::~CMergeEditFrame()
73 {
74 }
75
76 BOOL CMergeEditFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
77         CCreateContext* pContext)
78 {
79         m_wndSplitter.HideBorders(true);
80         m_wndSplitter.Create(this, 2, 1, CSize(1, 1), pContext, WS_CHILD | WS_VISIBLE | 1/*SPLS_DYNAMIC_SPLIT*/);
81
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))
87         {
88                 TRACE0("Failed to create LocationBar\n");
89                 return FALSE;
90         }
91
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);
95
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))
101         {
102                 TRACE0("Failed to create DiffViewBar\n");
103                 return FALSE;
104         }
105
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);
109
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));
114         });
115         m_pMergeDoc->SetLocationView(pLocationView);
116
117         m_wndFilePathBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
118         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
119                 m_pMergeDoc->GetView(0, pane)->SetActivePane();
120         });
121         m_wndStatusBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
122         
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());
127
128         return TRUE;
129 }
130
131 /////////////////////////////////////////////////////////////////////////////
132 // CMergeEditFrame message handlers
133
134 /**
135 * @brief Create the child frame, the splitter, the filename bar, the status bar,
136 * the diff dockable bar, and the four views
137 *
138 * @note  the panels layout is 
139 * <ul>
140 *  <li>   -----------
141 *  <li>         !  header !
142 *  <li>         !.........!
143 *  <li>         !.   .   .!
144 *  <li>         !. 1 . 2 .!
145 *  <li>         !.   .   .!
146 *  <li>         !.........!
147 *  <li>         !.........!
148 *  <li>         !  status !
149 *  <li>         -----------
150 *  <li>         !.........!
151 *  <li>         !.   3   .!
152 *  <li>         !.dockable!
153 *  <li>         ! splitbar!
154 *  <li>         !.   4   .!
155 *  <li>         !.........!
156 *  <li>         -----------
157 * </ul>
158 */
159 int CMergeEditFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
160 {
161         if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
162                 return -1;
163
164         EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM|CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT);
165
166         RemoveBarBorder();
167
168         // Merge frame has a header bar at top
169         if (!m_wndFilePathBar.Create(this))
170         {
171                 TRACE0("Failed to create dialog bar\n");
172                 return -1;      // fail to create
173         }
174
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);
179
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);
187
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);
194
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))
198         {
199                 TRACE0("Failed to create status bar\n");
200                 return -1;      // fail to create
201         }       
202
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"));
211
212         return 0;
213 }
214
215 /**
216  * @brief We must use this function before a call to SetDockState
217  *
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. 
222  */
223 bool CMergeEditFrame::EnsureValidDockState(CDockState& state) 
224 {
225         for (int i = (int) state.m_arrBarInfo.GetSize()-1 ; i >= 0; i--) 
226         {
227                 bool barIsCorrect = true;
228                 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
229                 if (pInfo == nullptr) 
230                         barIsCorrect = false;
231                 else
232                 {
233                         if (! pInfo->m_bFloating) 
234                         {
235                                 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
236                                 if (pInfo->m_pBar == nullptr) 
237                                         barIsCorrect = false; //toolbar id's probably changed   
238                         }
239                 }
240
241                 if (! barIsCorrect)
242                         state.m_arrBarInfo.RemoveAt(i);
243         }
244         return true;
245 }
246
247 void CMergeEditFrame::ActivateFrame(int nCmdShow) 
248 {
249         CMergeFrameCommon::ActivateFrame(nCmdShow);
250 }
251
252 BOOL CMergeEditFrame::DestroyWindow() 
253 {
254         SavePosition();
255         SaveActivePane();
256         SaveWindowState();
257         return CMergeFrameCommon::DestroyWindow();
258 }
259
260 /**
261  * @brief Save coordinates of the frame, splitters, and bars
262  *
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
265  */
266 void CMergeEditFrame::SavePosition()
267 {
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"));
276 }
277
278 void CMergeEditFrame::SaveActivePane()
279 {
280         for (int iRowParent = 0; iRowParent < m_wndSplitter.GetRowCount(); ++iRowParent)
281         {
282                 int iRow, iCol;
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));
287         }
288 }
289
290 void CMergeEditFrame::OnClose() 
291 {
292         // clean up pointers.
293         CMergeFrameCommon::OnClose();
294
295         GetMainFrame()->ClearStatusbarItemCount();
296 }
297
298 /// update splitting position for panels 1/2 and headerbar and statusbar 
299 void CMergeEditFrame::UpdateHeaderSizes()
300 {
301         if(!::IsWindow(m_wndFilePathBar.m_hWnd) || !::IsWindow(m_wndSplitter.m_hWnd))
302                 return;
303         
304         int w[3];
305         int pane;
306         CMergeDoc * pDoc = GetMergeDoc();
307         auto& wndSplitter = GetMergeEditSplitterWnd(0);
308         if (wndSplitter.GetColumnCount() > 1)
309         {
310                 for (pane = 0; pane < wndSplitter.GetColumnCount(); pane++)
311                 {
312                         int wmin;
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)
315                 }
316         }
317         else
318         {
319                 int w2, wmin;
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;
323         }
324
325         if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + pDoc->m_nBuffers - 1, w))
326         {
327                 std::copy_n(w, pDoc->m_nBuffers - 1, m_nLastSplitPos);
328
329                 // resize controls in header dialog bar
330                 m_wndFilePathBar.Resize(w);
331
332                 m_wndStatusBar.Resize(w);
333         }
334 }
335
336 IHeaderBar * CMergeEditFrame::GetHeaderInterface()
337 {
338         return &m_wndFilePathBar;
339 }
340
341 void CMergeEditFrame::UpdateAutoPaneResize()
342 {
343         auto& wndSplitter = GetMergeEditSplitterWnd(0);
344         wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
345 }
346
347 void CMergeEditFrame::UpdateSplitter()
348 {
349         for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
350                 GetMergeEditSplitterWnd(iRow).RecalcLayout();
351         m_wndSplitter.RecalcLayout();
352         m_pwndDetailMergeEditSplitterView->m_wndSplitter.RecalcLayout();
353 }
354
355 /**
356  * @brief Synchronize control with splitter position,
357  */
358 void CMergeEditFrame::OnIdleUpdateCmdUI()
359 {
360         UpdateHeaderSizes();
361         CMergeFrameCommon::OnIdleUpdateCmdUI();
362 }
363
364 void CMergeEditFrame::OnTimer(UINT_PTR nIDEvent) 
365 {
366         if (nIDEvent == IDT_SAVEPOSITION)
367         {
368                 SavePosition();
369                 KillTimer(IDT_SAVEPOSITION);
370         }
371         else
372         {
373                 UpdateHeaderSizes();
374         }
375         CMergeFrameCommon::OnTimer(nIDEvent);
376 }
377
378 void CMergeEditFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
379 {
380         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
381
382         CMergeDoc *pDoc = GetMergeDoc();
383         if (bActivate && pDoc != nullptr)
384                 this->GetParentFrame()->PostMessage(WM_USER+1);
385         return;
386 }
387
388 /**
389  * @brief Split panes vertically
390  */
391 void CMergeEditFrame::OnViewSplitVertically() 
392 {
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();
400 }
401
402 /**
403  * @brief Update "Split Vertically" UI items
404  */
405 void CMergeEditFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
406 {
407         auto& wndSplitter = GetMergeEditSplitterWnd(0);
408         pCmdUI->Enable(TRUE);
409         pCmdUI->SetCheck((wndSplitter.GetColumnCount() != 1));
410 }
411
412 /// Document commanding us to close
413 void CMergeEditFrame::CloseNow()
414 {
415         SavePosition(); // Save settings before closing!
416         SaveActivePane();
417         MDIActivate();
418         MDIDestroy();
419 }
420
421 /**
422  * @brief Update any resources necessary after a GUI language change
423  */
424 void CMergeEditFrame::UpdateResources()
425 {
426         m_wndStatusBar.UpdateResources();
427         m_wndLocationBar.UpdateResources();
428         m_wndDetailBar.UpdateResources();
429 }
430
431 /**
432  * @brief Save pane sizes and positions when one of panes requests it.
433  */
434 LRESULT CMergeEditFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
435 {
436         KillTimer(IDT_SAVEPOSITION);
437         SetTimer(IDT_SAVEPOSITION, 300, nullptr);
438         return 0;
439 }
440
441 void CMergeEditFrame::OnSize(UINT nType, int cx, int cy) 
442 {
443         CMDIChildWnd::OnSize(nType, cx, cy);
444         
445         UpdateHeaderSizes();
446 }