OSDN Git Service

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