OSDN Git Service

refactor
[winmerge-jp/winmerge-jp.git] / Src / HexMergeFrm.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  HexMergeFrm.cpp
9  *
10  * @brief Implementation file for CHexMergeFrame
11  *
12  */
13
14 #include "stdafx.h"
15 #include "HexMergeFrm.h"
16 #include "Merge.h"
17 #include "HexMergeDoc.h"
18 #include "HexMergeView.h"
19 #include "OptionsDef.h"
20 #include "OptionsMgr.h"
21
22 #ifdef _DEBUG
23 #define new DEBUG_NEW
24 #endif
25
26 #define SWAPPARAMS_IF(c, a, b) (c ? a : b), (c ? b : a)
27
28 /////////////////////////////////////////////////////////////////////////////
29 // CHexMergeFrame
30
31 IMPLEMENT_DYNCREATE(CHexMergeFrame, CMergeFrameCommon)
32
33 BEGIN_MESSAGE_MAP(CHexMergeFrame, CMergeFrameCommon)
34         //{{AFX_MSG_MAP(CHexMergeFrame)
35         ON_WM_CREATE()
36         ON_WM_CLOSE()
37         ON_WM_SIZE()
38         ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
39         ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
40         // [View] menu
41         ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
42         ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
43         //ON_UPDATE_COMMAND_UI(ID_VIEW_DETAIL_BAR, OnUpdateControlBarMenu)
44         //ON_COMMAND_EX(ID_VIEW_DETAIL_BAR, OnBarCheck)
45         //ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
46         //ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
47         //}}AFX_MSG_MAP
48 END_MESSAGE_MAP()
49
50 /**
51  * @brief Statusbar pane indexes
52  */
53 enum
54 {
55         PANE_LEFT_INFO = 0,
56         PANE_LEFT_RO,
57         PANE_LEFT_EOL,
58         PANE_RIGHT_INFO,
59         PANE_RIGHT_RO,
60         PANE_RIGHT_EOL,
61 };
62
63 /////////////////////////////////////////////////////////////////////////////
64 // CHexMergeFrame construction/destruction
65
66 CHexMergeFrame::CHexMergeFrame()
67         : CMergeFrameCommon(IDI_EQUALBINARY, IDI_BINARYDIFF)
68         , m_HScrollInfo{}, m_VScrollInfo{}
69 {
70         m_pMergeDoc = 0;
71 }
72
73 CHexMergeFrame::~CHexMergeFrame()
74 {
75 }
76
77 /**
78  * @brief Create a status bar to be associated with a heksedit control
79  */
80 void CHexMergeFrame::CreateHexWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
81 {
82         const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
83         auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
84         wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
85         wndStatusBar.SetIndicators(0, 3);
86         wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
87         wndStatusBar.SetPaneInfo(1, 0, 0, pointToPixel(60));
88         wndStatusBar.SetPaneInfo(2, 0, 0, pointToPixel(60));
89         wndStatusBar.SetParent(this);
90         wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
91 }
92
93 /**
94  * @brief Create the splitter, the filename bar, the status bar, and the two views
95  */
96 BOOL CHexMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
97         CCreateContext* pContext)
98 {
99         m_pMergeDoc = dynamic_cast<CHexMergeDoc *>(pContext->m_pCurrentDoc);
100         bool bSplitVert = !GetOptionsMgr()->GetBool(OPT_SPLIT_HORIZONTALLY);
101
102         // create a splitter with 1 row, 2 columns
103         if (!m_wndSplitter.CreateStatic(this, SWAPPARAMS_IF(bSplitVert, 1, m_pMergeDoc->m_nBuffers), WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL) )
104         {
105                 TRACE0("Failed to CreateStaticSplitter\n");
106                 return FALSE;
107         }
108
109         int nPane;
110         for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
111         {
112                 if (!m_wndSplitter.CreateView(SWAPPARAMS_IF(bSplitVert, 0, nPane),
113                         RUNTIME_CLASS(CHexMergeView), CSize(-1, 200), pContext))
114                 {
115                         TRACE0("Failed to create first pane\n");
116                         return FALSE;
117                 }
118         }
119
120         m_wndSplitter.ResizablePanes(true);
121         m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
122
123         // Merge frame has a header bar at top
124         if (!m_wndFilePathBar.Create(this))
125         {
126                 TRACE0("Failed to create dialog bar\n");
127                 return FALSE;      // fail to create
128         }
129
130         m_wndFilePathBar.SetPaneCount(m_pMergeDoc->m_nBuffers);
131         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
132                 SetActivePane(pane);
133         });
134         m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
135                 m_pMergeDoc->SetDescription(pane ,sText);
136                 m_pMergeDoc->UpdateHeaderPath(pane);
137         });
138         m_wndFilePathBar.SetOnFileSelectedCallback([&](int pane, const String& sFilepath) {
139                 m_pMergeDoc->ChangeFile(pane, sFilepath);
140         });
141
142         // Set filename bars inactive so colors get initialized
143         for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
144                 m_wndFilePathBar.SetActive(nPane, false);
145
146         CHexMergeView *pView[3];
147         for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
148                 pView[nPane] = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(SWAPPARAMS_IF(bSplitVert, 0, nPane)));
149
150         for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
151                 CreateHexWndStatusBar(m_wndStatusBar[nPane], pView[nPane]);
152         CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
153         m_rectBorder.bottom = size.cy;
154
155         // get the IHexEditorWindow interfaces
156         IHexEditorWindow *pif[3];
157         for (nPane = 0; nPane < m_pMergeDoc->m_nBuffers; nPane++)
158         {
159                 pif[nPane] = reinterpret_cast<IHexEditorWindow *>(
160                         ::GetWindowLongPtr(pView[nPane]->m_hWnd, GWLP_USERDATA));
161         }
162
163         // tell the heksedit controls about each other
164         if (m_pMergeDoc->m_nBuffers == 2)
165         {
166                 pif[0]->set_sibling(pif[1]);
167                 pif[1]->set_sibling(pif[0]);
168                 pif[1]->share_undorecords(pif[0]->share_undorecords(nullptr));
169         }
170         else if (m_pMergeDoc->m_nBuffers > 2)
171         {
172                 pif[0]->set_sibling(pif[1]);
173                 pif[1]->set_sibling2(pif[0], pif[2]);
174                 pif[2]->set_sibling(pif[1]);
175                 pif[2]->share_undorecords(
176                         pif[1]->share_undorecords(
177                                 pif[0]->share_undorecords(nullptr)));
178         }
179
180         // tell merge doc about these views
181         m_pMergeDoc = dynamic_cast<CHexMergeDoc *>(pContext->m_pCurrentDoc);
182         m_pMergeDoc->SetMergeViews(pView);
183         m_pMergeDoc->RefreshOptions();
184
185         return TRUE;
186 }
187
188 /////////////////////////////////////////////////////////////////////////////
189 // CHexMergeFrame message handlers
190
191 void CHexMergeFrame::ActivateFrame(int nCmdShow) 
192 {
193         CMergeFrameCommon::ActivateFrame(nCmdShow);
194 }
195
196 /**
197  * @brief Save the window's position, free related resources, and destroy the window
198  */
199 BOOL CHexMergeFrame::DestroyWindow() 
200 {
201         SavePosition();
202         SaveActivePane();
203         SaveWindowState();
204         CFrameWnd* pParentFrame = GetParentFrame();
205         BOOL result = CMergeFrameCommon::DestroyWindow();
206         if (pParentFrame)
207                 pParentFrame->OnUpdateFrameTitle(FALSE);
208         return result;
209 }
210
211 /**
212  * @brief Save coordinates of the frame, splitters, and bars
213  *
214  * @note Do not save the maximized/restored state here. We are interested
215  * in the state of the active frame, and maybe this frame is not active
216  */
217 void CHexMergeFrame::SavePosition()
218 {
219 }
220
221 void CHexMergeFrame::SaveActivePane()
222 {
223         if (CWnd* pLeft = m_wndSplitter.GetPane(0, 0))
224         {
225                 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, GetActivePane());
226         }
227 }
228
229 void CHexMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
230 {
231         CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
232
233         CHexMergeDoc *pDoc = GetMergeDoc();
234         if (bActivate && pDoc != nullptr)
235                 this->GetParentFrame()->PostMessage(WM_USER+1);
236         return;
237 }
238
239 void CHexMergeFrame::OnSize(UINT nType, int cx, int cy) 
240 {
241         CMDIChildWnd::OnSize(nType, cx, cy);
242         UpdateHeaderSizes();
243 }
244
245 /// update splitting position for panels 1/2 and headerbar and statusbar 
246 void CHexMergeFrame::UpdateHeaderSizes()
247 {
248         if (!m_wndSplitter.m_hWnd || !m_wndFilePathBar.m_hWnd)
249                 return;
250         CRect rcFrame;
251         GetClientRect(&rcFrame);
252         int w[3],wmin;
253         int nPaneCount = m_wndSplitter.GetColumnCount();
254         if (nPaneCount > 1)
255         {
256                 for (int pane = 0; pane < nPaneCount; pane++)
257                 {
258                         m_wndSplitter.GetColumnInfo(pane, w[pane], wmin);
259                         if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
260                 }
261         }
262         else
263         {
264                 nPaneCount = m_wndSplitter.GetRowCount();
265                 for (int pane = 0; pane < nPaneCount; pane++)
266                 {
267                         w[pane] = rcFrame.Width() / nPaneCount;
268                         if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
269                 }
270         }
271         
272         if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
273         {
274                 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
275
276                 // resize controls in header dialog bar
277                 m_wndFilePathBar.Resize(w);
278                 RECT rc = rcFrame;
279                 rc.top = rc.bottom - m_rectBorder.bottom;
280                 rc.right = 0;
281                 for (int pane = 0; pane < nPaneCount; pane++)
282                 {
283                         if (pane < nPaneCount - 1)
284                                 rc.right += w[pane] + 6;
285                         else
286                                 rc.right = rcFrame.right;
287                         m_wndStatusBar[pane].MoveWindow(&rc);
288                         rc.left = rc.right;
289                 }
290         }
291 }
292
293 void CHexMergeFrame::UpdateAutoPaneResize()
294 {
295         m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
296 }
297
298 void CHexMergeFrame::UpdateSplitter()
299 {
300         m_wndSplitter.RecalcLayout();
301 }
302
303 int CHexMergeFrame::GetActivePane()
304 {
305         int nPane;
306         if (m_wndSplitter.GetColumnCount() > 1)
307                 m_wndSplitter.GetActivePane(nullptr, &nPane);
308         else
309                 m_wndSplitter.GetActivePane(&nPane, nullptr);
310         return nPane;
311 }
312
313 void CHexMergeFrame::SetActivePane(int nPane)
314 {
315         if (m_wndSplitter.GetColumnCount() > 1)
316                 m_wndSplitter.SetActivePane(0, nPane);
317         else
318                 m_wndSplitter.SetActivePane(nPane, 0);
319 }
320
321 /**
322  * @brief Synchronize control and status bar placements with splitter position,
323  * update mod indicators, synchronize scrollbars
324  */
325 void CHexMergeFrame::OnIdleUpdateCmdUI()
326 {
327         if (IsWindowVisible())
328         {
329                 UpdateHeaderSizes();
330
331                 int pane;
332                 int nColumns = m_wndSplitter.GetColumnCount();
333                 CHexMergeView *pView[3] = {0};
334                 for (pane = 0; pane < nColumns; ++pane)
335                         pView[pane] = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(0, pane));
336
337                 // Update mod indicators
338                 tchar_t ind[2];
339
340                 for (pane = 0; pane < nColumns; ++pane)
341                 {
342                         if (m_wndFilePathBar.GetDlgItemText(IDC_STATIC_TITLE_PANE0 + pane, ind, 2))
343                                 if (pView[pane]->GetModified() ? ind[0] != _T('*') : ind[0] == _T('*'))
344                                         m_pMergeDoc->UpdateHeaderPath(pane);
345                 }
346
347                 // Synchronize scrollbars
348                 SCROLLINFO si, siView[3];
349                 // Synchronize horizontal scrollbars
350                 pView[0]->GetScrollInfo(SB_HORZ, &si, SIF_ALL | SIF_DISABLENOSCROLL);
351                 m_HScrollInfo[0] = si;
352                 for (pane = 1; pane < nColumns; ++pane)
353                 {
354                         SCROLLINFO siCur;
355                         pView[pane]->GetScrollInfo(SB_HORZ, &siCur, SIF_ALL | SIF_DISABLENOSCROLL);
356                         siView[pane] = siCur;
357                         if (si.nMin > siCur.nMin)
358                                 si.nMin = siCur.nMin;
359                         if (si.nPage < siCur.nPage)
360                                 si.nPage = siCur.nPage;
361                         if (si.nMax < siCur.nMax)
362                                 si.nMax = siCur.nMax;
363                         if (memcmp(&siCur, &m_HScrollInfo[pane], sizeof si))
364                         {
365                                 si.nPos = siCur.nPos;
366                                 si.nTrackPos = siCur.nTrackPos;
367                         }
368                         m_HScrollInfo[pane] = siCur;
369                 }
370                 for (pane = 0; pane < nColumns; ++pane)
371                 {
372                         if (memcmp(&si, &siView[pane], sizeof si))
373                         {
374                                 pView[pane]->SetScrollInfo(SB_HORZ, &si);
375                                 pView[pane]->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
376                         }
377                 }
378                 for (pane = 0; pane < nColumns; ++pane)
379                         m_wndSplitter.GetScrollBarCtrl(pView[pane], SB_HORZ)->SetScrollInfo(&si);
380
381                 // Synchronize vertical scrollbars
382                 pView[0]->GetScrollInfo(SB_VERT, &si, SIF_ALL | SIF_DISABLENOSCROLL);
383                 m_VScrollInfo[0] = si;
384                 for (pane = 1; pane < nColumns; ++pane)
385                 {
386                         SCROLLINFO siCur;
387                         pView[pane]->GetScrollInfo(SB_VERT, &siCur, SIF_ALL | SIF_DISABLENOSCROLL);
388                         siView[pane] = siCur;
389                         if (si.nMin > siCur.nMin)
390                                 si.nMin = siCur.nMin;
391                         if (si.nMax < siCur.nMax)
392                                 si.nMax = siCur.nMax;
393                         if (memcmp(&siCur, &m_VScrollInfo[pane], sizeof si))
394                         {
395                                 si.nPos = siCur.nPos;
396                                 si.nTrackPos = siCur.nTrackPos;
397                         }
398                         m_VScrollInfo[pane] = siCur;
399                 }
400                 for (pane = 0; pane < nColumns; ++pane)
401                 {
402                         if (memcmp(&si, &siView[pane], sizeof si))
403                         {
404                                 pView[pane]->SetScrollInfo(SB_VERT, &si);
405                                 pView[pane]->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
406                         }
407                 }
408                 if (nColumns > 0)
409                         m_wndSplitter.GetScrollBarCtrl(pView[nColumns - 1], SB_VERT)->SetScrollInfo(&si);
410         }
411         CMDIChildWnd::OnIdleUpdateCmdUI();
412 }
413
414 /**
415  * @brief Update any resources necessary after a GUI language change
416  */
417 void CHexMergeFrame::UpdateResources()
418 {
419 }
420
421 /**
422  * @brief Save pane sizes and positions when one of panes requests it.
423  */
424 LRESULT CHexMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
425 {
426         SavePosition();
427         return 0;
428 }
429
430 /**
431  * @brief Split panes vertically
432  */
433 void CHexMergeFrame::OnViewSplitVertically() 
434 {
435         bool bSplitVertically = (m_wndSplitter.GetColumnCount() != 1);
436         bSplitVertically = !bSplitVertically; // toggle
437         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
438         m_wndSplitter.FlipSplit();
439 }
440
441 /**
442  * @brief Update "Split Vertically" UI items
443  */
444 void CHexMergeFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
445 {
446         pCmdUI->Enable(TRUE);
447         pCmdUI->SetCheck((m_wndSplitter.GetColumnCount() != 1));
448 }
449