OSDN Git Service

Improve plugin system (#797) (6)
[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         CFrameWnd* pParentFrame = GetParentFrame();
258         BOOL result = CMergeFrameCommon::DestroyWindow();
259         if (pParentFrame)
260                 pParentFrame->OnUpdateFrameTitle(FALSE);
261         return result;
262 }
263
264 /**
265  * @brief Save coordinates of the frame, splitters, and bars
266  *
267  * @note Do not save the maximized/restored state here. We are interested
268  * in the state of the active frame, and maybe this frame is not active
269  */
270 void CMergeEditFrame::SavePosition()
271 {
272         // save the bars layout
273         // save docking positions and sizes
274         CDockState m_pDockState;
275         GetDockState(m_pDockState);
276         m_pDockState.SaveState(_T("Settings"));
277         // for the dimensions of the diff pane, use the CSizingControlBar save
278         m_wndLocationBar.SaveState(_T("Settings"));
279         m_wndDetailBar.SaveState(_T("Settings"));
280 }
281
282 void CMergeEditFrame::SaveActivePane()
283 {
284         for (int iRowParent = 0; iRowParent < m_wndSplitter.GetRowCount(); ++iRowParent)
285         {
286                 int iRow, iCol;
287                 auto& splitterWnd = static_cast<CMergeEditSplitterView*>(m_wndSplitter.GetPane(iRowParent, 0))->m_wndSplitter;
288                 splitterWnd.GetActivePane(&iRow, &iCol);
289                 if (iRow >= 0 || iCol >= 0)
290                         GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, max(iRow, iCol));
291         }
292 }
293
294 void CMergeEditFrame::OnClose() 
295 {
296         // clean up pointers.
297         CMergeFrameCommon::OnClose();
298
299         GetMainFrame()->ClearStatusbarItemCount();
300 }
301
302 /// update splitting position for panels 1/2 and headerbar and statusbar 
303 void CMergeEditFrame::UpdateHeaderSizes()
304 {
305         if(!::IsWindow(m_wndFilePathBar.m_hWnd) || !::IsWindow(m_wndSplitter.m_hWnd))
306                 return;
307         
308         int w[3];
309         int pane;
310         CMergeDoc * pDoc = GetMergeDoc();
311         auto& wndSplitter = GetMergeEditSplitterWnd(0);
312         if (wndSplitter.GetColumnCount() > 1)
313         {
314                 for (pane = 0; pane < wndSplitter.GetColumnCount(); pane++)
315                 {
316                         int wmin;
317                         wndSplitter.GetColumnInfo(pane, w[pane], wmin);
318                         if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
319                 }
320         }
321         else
322         {
323                 int w2, wmin;
324                 wndSplitter.GetColumnInfo(0, w2, wmin);
325                 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
326                         w[pane] = (w2 - 4 * pDoc->m_nBuffers) / pDoc->m_nBuffers;
327         }
328
329         if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + pDoc->m_nBuffers - 1, w))
330         {
331                 std::copy_n(w, pDoc->m_nBuffers - 1, m_nLastSplitPos);
332
333                 // resize controls in header dialog bar
334                 m_wndFilePathBar.Resize(w);
335
336                 m_wndStatusBar.Resize(w);
337         }
338 }
339
340 IHeaderBar * CMergeEditFrame::GetHeaderInterface()
341 {
342         return &m_wndFilePathBar;
343 }
344
345 void CMergeEditFrame::UpdateAutoPaneResize()
346 {
347         auto& wndSplitter = GetMergeEditSplitterWnd(0);
348         wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
349 }
350
351 void CMergeEditFrame::UpdateSplitter()
352 {
353         for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
354                 GetMergeEditSplitterWnd(iRow).RecalcLayout();
355         m_wndSplitter.RecalcLayout();
356         m_pwndDetailMergeEditSplitterView->m_wndSplitter.RecalcLayout();
357 }
358
359 /**
360  * @brief Synchronize control with splitter position,
361  */
362 void CMergeEditFrame::OnIdleUpdateCmdUI()
363 {
364         UpdateHeaderSizes();
365         CMergeFrameCommon::OnIdleUpdateCmdUI();
366 }
367
368 void CMergeEditFrame::OnTimer(UINT_PTR nIDEvent) 
369 {
370         if (nIDEvent == IDT_SAVEPOSITION)
371         {
372                 SavePosition();
373                 KillTimer(IDT_SAVEPOSITION);
374         }
375         else
376         {
377                 UpdateHeaderSizes();
378         }
379         CMergeFrameCommon::OnTimer(nIDEvent);
380 }
381
382 void CMergeEditFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
383 {
384         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
385
386         CMergeDoc *pDoc = GetMergeDoc();
387         if (bActivate && pDoc != nullptr)
388                 this->GetParentFrame()->PostMessage(WM_USER+1);
389         return;
390 }
391
392 /**
393  * @brief Split panes vertically
394  */
395 void CMergeEditFrame::OnViewSplitVertically() 
396 {
397         auto& wndSplitter = GetMergeEditSplitterWnd(0);
398         bool bSplitVertically = (wndSplitter.GetColumnCount() != 1);
399         bSplitVertically = !bSplitVertically; // toggle
400         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
401         for (int iRow = 0; iRow < m_wndSplitter.GetRowCount(); ++iRow)
402                 GetMergeEditSplitterWnd(iRow).FlipSplit();
403         m_pwndDetailMergeEditSplitterView->m_wndSplitter.FlipSplit();
404 }
405
406 /**
407  * @brief Update "Split Vertically" UI items
408  */
409 void CMergeEditFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
410 {
411         auto& wndSplitter = GetMergeEditSplitterWnd(0);
412         pCmdUI->Enable(TRUE);
413         pCmdUI->SetCheck((wndSplitter.GetColumnCount() != 1));
414 }
415
416 /**
417  * @brief Update any resources necessary after a GUI language change
418  */
419 void CMergeEditFrame::UpdateResources()
420 {
421         m_wndStatusBar.UpdateResources();
422         m_wndLocationBar.UpdateResources();
423         m_wndDetailBar.UpdateResources();
424 }
425
426 /**
427  * @brief Save pane sizes and positions when one of panes requests it.
428  */
429 LRESULT CMergeEditFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
430 {
431         KillTimer(IDT_SAVEPOSITION);
432         SetTimer(IDT_SAVEPOSITION, 300, nullptr);
433         return 0;
434 }
435
436 void CMergeEditFrame::OnSize(UINT nType, int cx, int cy) 
437 {
438         CMDIChildWnd::OnSize(nType, cx, cy);
439         
440         UpdateHeaderSizes();
441 }