OSDN Git Service

Various refactorings involving NULL, ASSERT, TRUE and FALSE (2)
[winmerge-jp/winmerge-jp.git] / Src / HexMergeDoc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
6 //    This program is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    This program is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License
17 //    along with this program; if not, write to the Free Software
18 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /** 
22  * @file  HexMergeDoc.cpp
23  *
24  * @brief Implementation file for CHexMergeDoc
25  *
26  */
27
28 #include "stdafx.h"
29 #include "HexMergeDoc.h"
30 #include <afxinet.h>
31 #include "UnicodeString.h"
32 #include "HexMergeFrm.h"
33 #include "HexMergeView.h"
34 #include "DiffItem.h"
35 #include "FolderCmp.h"
36 #include "DiffContext.h"        // FILE_SAME
37 #include "DirDoc.h"
38 #include "DirActions.h"
39 #include "OptionsDef.h"
40 #include "DiffFileInfo.h"
41 #include "SaveClosingDlg.h"
42 #include "DiffList.h"
43 #include "paths.h"
44 #include "OptionsMgr.h"
45 #include "FileOrFolderSelect.h"
46 #include "DiffWrapper.h"
47 #include "SyntaxColors.h"
48 #include "Merge.h"
49 #include "Constants.h"
50 #include "MainFrm.h"
51
52 #ifdef _DEBUG
53 #define new DEBUG_NEW
54 #endif
55
56 int CHexMergeDoc::m_nBuffersTemp = 2;
57
58 static void UpdateDiffItem(int nBuffers, DIFFITEM &di, CDiffContext *pCtxt);
59 static int Try(HRESULT hr, UINT type = MB_OKCANCEL|MB_ICONSTOP);
60
61 /**
62  * @brief Update diff item
63  */
64 static void UpdateDiffItem(int nBuffers, DIFFITEM &di, CDiffContext *pCtxt)
65 {
66         di.diffcode.setSideNone();
67         for (int nBuffer = 0; nBuffer < nBuffers; nBuffer++)
68         {
69                 di.diffFileInfo[nBuffer].ClearPartial();
70                 if (pCtxt->UpdateInfoFromDiskHalf(di, nBuffer))
71                         di.diffcode.diffcode |= DIFFCODE::FIRST << nBuffer;
72         }
73         // Clear flags
74         di.diffcode.diffcode &= ~(DIFFCODE::TEXTFLAGS | DIFFCODE::COMPAREFLAGS | DIFFCODE::COMPAREFLAGS3WAY);
75         // Really compare
76         FolderCmp folderCmp;
77         di.diffcode.diffcode |= folderCmp.prepAndCompareFiles(pCtxt, di);
78 }
79
80 /**
81  * @brief Issue an error popup if passed in HRESULT is nonzero
82  */
83 static int Try(HRESULT hr, UINT type)
84 {
85         return hr ? CInternetException(hr).ReportError(type) : 0;
86 }
87
88 /////////////////////////////////////////////////////////////////////////////
89 // CHexMergeDoc
90
91 IMPLEMENT_DYNCREATE(CHexMergeDoc, CDocument)
92
93 BEGIN_MESSAGE_MAP(CHexMergeDoc, CDocument)
94         //{{AFX_MSG_MAP(CHexMergeDoc)
95         ON_COMMAND(ID_FILE_SAVE, OnFileSave)
96         ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
97         ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
98         ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
99         ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
100         ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
101         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
102         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
103         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
104         ON_COMMAND(ID_RESCAN, OnFileReload)
105         ON_COMMAND(ID_L2R, OnL2r)
106         ON_COMMAND(ID_R2L, OnR2l)
107         ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
108         ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
109         ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
110         ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
111         ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
112         ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
113         ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
114         ON_COMMAND(ID_REFRESH, OnRefresh)
115         ON_COMMAND(ID_MERGE_COMPARE_TEXT, OnFileRecompareAsText)
116         //}}AFX_MSG_MAP
117 END_MESSAGE_MAP()
118
119 /////////////////////////////////////////////////////////////////////////////
120 // CHexMergeDoc construction/destruction
121
122 /**
123  * @brief Constructor.
124  */
125 CHexMergeDoc::CHexMergeDoc()
126 : m_pDirDoc(nullptr)
127 {
128         m_nBuffers = m_nBuffersTemp;
129         m_filePaths.SetSize(m_nBuffers);
130         std::fill_n(m_pView, m_nBuffers, static_cast<CHexMergeView *>(nullptr));
131         std::fill_n(m_nBufferType, m_nBuffers, BUFFER_NORMAL);
132 }
133
134 /**
135  * @brief Destructor.
136  *
137  * Informs associated dirdoc that mergedoc is closing.
138  */
139 CHexMergeDoc::~CHexMergeDoc()
140 {       
141         if (m_pDirDoc)
142                 m_pDirDoc->MergeDocClosing(this);
143 }
144
145 /**
146  * @brief Return active merge edit view (or left one if neither active)
147  */
148 CHexMergeView * CHexMergeDoc::GetActiveMergeView() const
149 {
150         CView * pActiveView = GetParentFrame()->GetActiveView();
151         CHexMergeView * pHexMergeView = dynamic_cast<CHexMergeView *>(pActiveView);
152         if (!pHexMergeView)
153                 pHexMergeView = m_pView[0]; // default to left view (in case some location or detail view active)
154         return pHexMergeView;
155 }
156
157 /**
158  * @brief Update associated diff item
159  */
160 int CHexMergeDoc::UpdateDiffItem(CDirDoc *pDirDoc)
161 {
162         // If directory compare has results
163         if (pDirDoc && pDirDoc->HasDiffs())
164         {
165                 CDiffContext &ctxt = pDirDoc->GetDiffContext();
166                 if (UINT_PTR pos = FindItemFromPaths(ctxt, m_filePaths))
167                 {
168                         DIFFITEM &di = ctxt.GetDiffRefAt(pos);
169                         ::UpdateDiffItem(m_nBuffers, di, &ctxt);
170                 }
171         }
172         bool bDiff = false;
173         int lengthFirst = m_pView[0]->GetLength();
174         void *bufferFirst = m_pView[0]->GetBuffer(lengthFirst);
175         for (int nBuffer = 1; nBuffer < m_nBuffers; nBuffer++)
176         {
177                 int length = m_pView[nBuffer]->GetLength();
178                 if (lengthFirst != length)
179                         bDiff = true;
180                 else
181                 {
182                         void *buffer = m_pView[nBuffer]->GetBuffer(length);
183                         bDiff = (memcmp(bufferFirst, buffer, lengthFirst) != 0);
184                 }
185                 if (bDiff)
186                         break;
187         }
188         GetParentFrame()->SetLastCompareResult(bDiff);
189         return bDiff ? 1 : 0;
190 }
191
192 /**
193  * @brief Asks and then saves modified files
194  */
195 bool CHexMergeDoc::PromptAndSaveIfNeeded(bool bAllowCancel)
196 {
197         bool bLModified = false, bMModified = false, bRModified = false;
198
199         if (m_nBuffers == 3)
200         {
201                 bLModified = m_pView[0]->GetModified();
202                 bMModified = m_pView[1]->GetModified();
203                 bRModified = m_pView[2]->GetModified();
204         }
205         else
206         {
207                 bLModified = m_pView[0]->GetModified();
208                 bRModified = m_pView[1]->GetModified();
209         }
210         if (!bLModified && !bMModified && !bRModified)
211                  return true;
212
213         const String &pathLeft = m_filePaths.GetLeft();
214         const String &pathMiddle = m_filePaths.GetMiddle();
215         const String &pathRight = m_filePaths.GetRight();
216
217         bool result = true;
218         bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
219
220         SaveClosingDlg dlg;
221         dlg.DoAskFor(bLModified, bMModified, bRModified);
222         if (!bAllowCancel)
223                 dlg.m_bDisableCancel = true;
224         if (!pathLeft.empty())
225                 dlg.m_sLeftFile = pathLeft;
226         else
227                 dlg.m_sLeftFile = m_strDesc[0];
228         if (m_nBuffers == 3)
229         {
230                 if (!pathMiddle.empty())
231                         dlg.m_sMiddleFile = pathMiddle;
232                 else
233                         dlg.m_sMiddleFile = m_strDesc[1];
234         }
235         if (!pathRight.empty())
236                 dlg.m_sRightFile = pathRight;
237         else
238                 dlg.m_sRightFile = m_strDesc[1];
239
240         if (dlg.DoModal() == IDOK)
241         {
242                 if (bLModified)
243                 {
244                         if (dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
245                         {
246                                 switch (Try(m_pView[0]->SaveFile(pathLeft.c_str())))
247                                 {
248                                 case 0:
249                                         bLSaveSuccess = true;
250                                         break;
251                                 case IDCANCEL:
252                                         result = false;
253                                         break;
254                                 }
255                         }
256                         else
257                         {
258                                 m_pView[0]->SetSavePoint();
259                         }
260                 }
261                 if (bMModified)
262                 {
263                         if (dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
264                         {
265                                 switch (Try(m_pView[1]->SaveFile(pathMiddle.c_str())))
266                                 {
267                                 case 0:
268                                         bMSaveSuccess = true;
269                                         break;
270                                 case IDCANCEL:
271                                         result = false;
272                                         break;
273                                 }
274                         }
275                         else
276                         {
277                                 m_pView[1]->SetSavePoint();
278                         }
279                 }
280                 if (bRModified)
281                 {
282                         if (dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
283                         {
284                                 switch (Try(m_pView[m_nBuffers - 1]->SaveFile(pathRight.c_str())))
285                                 {
286                                 case 0:
287                                         bRSaveSuccess = true;
288                                         break;
289                                 case IDCANCEL:
290                                         result = false;
291                                         break;
292                                 }
293                         }
294                         else
295                         {
296                                 m_pView[m_nBuffers - 1]->SetSavePoint();
297                         }
298                 }
299         }
300         else
301         {       
302                 result = FALSE;
303         }
304
305         // If file were modified and saving was successfull,
306         // update status on dir view
307         if (bLSaveSuccess || bMSaveSuccess || bRSaveSuccess)
308         {
309                 UpdateDiffItem(m_pDirDoc);
310         }
311
312         return result;
313 }
314
315 /**
316  * @brief Save modified documents
317  */
318 BOOL CHexMergeDoc::SaveModified()
319 {
320         return PromptAndSaveIfNeeded(true);
321 }
322
323 /**
324  * @brief Saves both files
325  */
326 void CHexMergeDoc::OnFileSave() 
327 {
328         for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
329                 DoFileSave(nBuffer);
330 }
331
332 void CHexMergeDoc::DoFileSave(int nBuffer)
333 {
334         if (m_pView[nBuffer]->GetModified())
335         {
336                 if (m_nBufferType[nBuffer] == BUFFER_UNNAMED)
337                         DoFileSaveAs(nBuffer);
338                 else
339                 {
340                         const String &path = m_filePaths.GetPath(nBuffer);
341                         if (Try(m_pView[nBuffer]->SaveFile(path.c_str())) == IDCANCEL)
342                                 return;
343                 }
344                 UpdateDiffItem(m_pDirDoc);
345         }
346 }
347
348 void CHexMergeDoc::DoFileSaveAs(int nBuffer)
349 {
350         const String &path = m_filePaths.GetPath(nBuffer);
351         String strPath;
352         String title;
353         if (nBuffer == 0)
354                 title = _("Save Left File As");
355         else if (nBuffer == m_nBuffers - 1)
356                 title = _("Save Right File As");
357         else
358                 title = _("Save Middle File As");
359         if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
360         {
361                 if (Try(m_pView[nBuffer]->SaveFile(strPath.c_str())) == IDCANCEL)
362                         return;
363                 if (path.empty())
364                 {
365                         // We are saving scratchpad (unnamed file)
366                         m_nBufferType[nBuffer] = BUFFER_UNNAMED_SAVED;
367                         m_strDesc[nBuffer].erase();
368                 }
369
370                 m_filePaths.SetPath(nBuffer, strPath);
371                 UpdateDiffItem(m_pDirDoc);
372                 UpdateHeaderPath(nBuffer);
373         }
374 }
375
376 /**
377  * @brief Saves left-side file
378  */
379 void CHexMergeDoc::OnFileSaveLeft()
380 {
381         DoFileSave(0);
382 }
383
384 /**
385  * @brief Saves middle-side file
386  */
387 void CHexMergeDoc::OnFileSaveMiddle()
388 {
389         DoFileSave(1);
390 }
391
392 /**
393  * @brief Saves right-side file
394  */
395 void CHexMergeDoc::OnFileSaveRight()
396 {
397         DoFileSave(m_nBuffers - 1);
398 }
399
400 /**
401  * @brief Saves left-side file with name asked
402  */
403 void CHexMergeDoc::OnFileSaveAsLeft()
404 {
405         DoFileSaveAs(0);
406 }
407
408 /**
409  * @brief Saves right-side file with name asked
410  */
411 void CHexMergeDoc::OnFileSaveAsMiddle()
412 {
413         DoFileSaveAs(1);
414 }
415
416 /**
417  * @brief Saves right-side file with name asked
418  */
419 void CHexMergeDoc::OnFileSaveAsRight()
420 {
421         DoFileSaveAs(m_nBuffers - 1);
422 }
423
424 /**
425  * @brief Update diff-number pane text
426  */
427 void CHexMergeDoc::OnUpdateStatusNum(CCmdUI* pCmdUI) 
428 {
429         String s;
430         pCmdUI->SetText(s.c_str());
431 }
432
433 /**
434  * @brief DirDoc gives us its identity just after it creates us
435  */
436 void CHexMergeDoc::SetDirDoc(CDirDoc * pDirDoc)
437 {
438         ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
439         m_pDirDoc = pDirDoc;
440 }
441
442 /**
443  * @brief Return pointer to parent frame
444  */
445 CHexMergeFrame * CHexMergeDoc::GetParentFrame() const
446 {
447         return static_cast<CHexMergeFrame *>(m_pView[0]->GetParentFrame()); 
448 }
449
450 /**
451  * @brief DirDoc is closing
452  */
453 void CHexMergeDoc::DirDocClosing(CDirDoc * pDirDoc)
454 {
455         ASSERT(m_pDirDoc == pDirDoc);
456         m_pDirDoc = 0;
457 }
458
459 /**
460  * @brief DirDoc commanding us to close
461  */
462 bool CHexMergeDoc::CloseNow()
463 {
464         // Allow user to cancel closing
465         if (!PromptAndSaveIfNeeded(true))
466                 return false;
467
468         GetParentFrame()->CloseNow();
469         return true;
470 }
471
472 /**
473 * @brief Load one file
474 */
475 HRESULT CHexMergeDoc::LoadOneFile(int index, LPCTSTR filename, bool readOnly, const String& strDesc)
476 {
477         if (filename[0])
478         {
479                 if (Try(m_pView[index]->LoadFile(filename), MB_ICONSTOP) != 0)
480                         return E_FAIL;
481                 m_pView[index]->SetReadOnly(readOnly);
482                 m_filePaths.SetPath(index, filename);
483                 ASSERT(m_nBufferType[index] == BUFFER_NORMAL); // should have been initialized to BUFFER_NORMAL in constructor
484                 if (!strDesc.empty())
485                 {
486                         m_strDesc[index] = strDesc;
487                         m_nBufferType[index] = BUFFER_NORMAL_NAMED;
488                 }
489         }
490         else
491         {
492                 m_nBufferType[index] = BUFFER_UNNAMED;
493                 m_strDesc[index] = strDesc;
494         }
495         UpdateHeaderPath(index);
496         m_pView[index]->ResizeWindow();
497         return S_OK;
498 }
499
500 /**
501  * @brief Load files and initialize frame's compare result icon
502  */
503 bool CHexMergeDoc::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], int nPane)
504 {
505         CHexMergeFrame *pf = GetParentFrame();
506         ASSERT(pf != nullptr);
507         bool bSucceeded = true;
508         int nBuffer;
509         for (nBuffer = 0; nBuffer < nFiles; nBuffer++)
510         {
511                 if (FAILED(LoadOneFile(nBuffer, fileloc[nBuffer].filepath.c_str(), bRO[nBuffer], strDesc ? strDesc[nBuffer] : _T(""))))
512                 {
513                         bSucceeded = false;
514                         break;
515                 }
516         }
517         if (nBuffer == nFiles)
518         {
519                 // An extra ResizeWindow() on the left view aligns scroll ranges, and
520                 // also triggers initial diff coloring by invalidating the client area.
521                 m_pView[0]->ResizeWindow();
522
523                 OnRefresh();
524
525                 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
526                         m_pView[0]->SendMessage(WM_COMMAND, ID_FIRSTDIFF);
527         }
528         else
529         {
530                 // Use verify macro to trap possible error in debug.
531                 VERIFY(pf->DestroyWindow());
532         }
533         return bSucceeded;
534 }
535
536 void CHexMergeDoc::CheckFileChanged(void)
537 {
538         for (int pane = 0; pane < m_nBuffers; ++pane)
539         {
540                 if (m_pView[pane]->IsFileChangedOnDisk(m_filePaths[pane].c_str()))
541                 {
542                         String msg = strutils::format_string1(_("Another application has updated file\n%1\nsince WinMerge scanned it last time.\n\nDo you want to reload the file?"), m_filePaths[pane]);
543                         if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING) == IDYES)
544                         {
545                                 OnFileReload();
546                         }
547                         break;
548                 }
549         }
550 }
551
552 /**
553  * @brief Write path and filename to headerbar
554  * @note SetText() does not repaint unchanged text
555  */
556 void CHexMergeDoc::UpdateHeaderPath(int pane)
557 {
558         CHexMergeFrame *pf = GetParentFrame();
559         ASSERT(pf != nullptr);
560         String sText;
561
562         if (m_nBufferType[pane] == BUFFER_UNNAMED ||
563                 m_nBufferType[pane] == BUFFER_NORMAL_NAMED)
564         {
565                 sText = m_strDesc[pane];
566         }
567         else
568         {
569                 sText = m_filePaths.GetPath(pane);
570                 if (m_pDirDoc)
571                         m_pDirDoc->ApplyDisplayRoot(pane, sText);
572         }
573         if (m_pView[pane]->GetModified())
574                 sText.insert(0, _T("* "));
575         pf->GetHeaderInterface()->SetText(pane, sText);
576
577         SetTitle(nullptr);
578 }
579
580
581 /**
582  * @brief Customize a heksedit control's settings
583  */
584 static void Customize(IHexEditorWindow::Settings *settings)
585 {
586         settings->bSaveIni = false;
587         //settings->iAutomaticBPL = FALSE;
588         //settings->iBytesPerLine = 16;
589         //settings->iFontSize = 8;
590 }
591
592 /**
593  * @brief Customize a heksedit control's colors
594  */
595 static void Customize(IHexEditorWindow::Colors *colors)
596 {
597         COptionsMgr *pOptionsMgr = GetOptionsMgr();
598         colors->iSelBkColorValue = RGB(224, 224, 224);
599         colors->iDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF);
600         colors->iSelDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF);
601         colors->iDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF_TEXT);
602         if (colors->iDiffTextColorValue == 0xFFFFFFFF)
603                 colors->iDiffTextColorValue = 0;
604         colors->iSelDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF_TEXT);
605         if (colors->iSelDiffTextColorValue == 0xFFFFFFFF)
606                 colors->iSelDiffTextColorValue = 0;
607         SyntaxColors *pSyntaxColors = theApp.GetMainSyntaxColors();
608         colors->iTextColorValue = pSyntaxColors->GetColor(COLORINDEX_NORMALTEXT);
609         colors->iBkColorValue = pSyntaxColors->GetColor(COLORINDEX_BKGND);
610         colors->iSelTextColorValue = pSyntaxColors->GetColor(COLORINDEX_SELTEXT);
611         colors->iSelBkColorValue = pSyntaxColors->GetColor(COLORINDEX_SELBKGND);
612 }
613
614 /**
615  * @brief Customize a heksedit control's settings and colors
616  */
617 static void Customize(IHexEditorWindow *pif)
618 {
619         Customize(pif->get_settings());
620         Customize(pif->get_colors());
621         //LANGID wLangID = (LANGID)GetThreadLocale();
622         //pif->load_lang(wLangID);
623 }
624
625 void CHexMergeDoc::RefreshOptions()
626 {
627         for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
628         {
629                 IHexEditorWindow *pif = m_pView[nBuffer]->GetInterface();
630                 pif->read_ini_data();
631                 Customize(pif);
632                 pif->resize_window();
633         }
634 }
635
636 /**
637  * @brief Update document filenames to title
638  */
639 void CHexMergeDoc::SetTitle(LPCTSTR lpszTitle)
640 {
641         String sTitle;
642         String sFileName[3];
643
644         if (lpszTitle)
645                 sTitle = lpszTitle;
646         else
647         {
648                 for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); nBuffer++)
649                         sFileName[nBuffer] = !m_strDesc[nBuffer].empty() ? m_strDesc[nBuffer] : paths::FindFileName(m_filePaths[nBuffer]);
650                 if (std::count(&sFileName[0], &sFileName[0] + m_nBuffers, sFileName[0]) == m_nBuffers)
651                         sTitle = sFileName[0] + strutils::format(_T(" x %d"), m_nBuffers);
652                 else
653                         sTitle = strutils::join(&sFileName[0], &sFileName[0] + m_nBuffers, _T(" - "));
654         }
655         CDocument::SetTitle(sTitle.c_str());
656 }
657
658 /**
659  * @brief We have two child views (left & right), so we keep pointers directly
660  * at them (the MFC view list doesn't have them both)
661  */
662 void CHexMergeDoc::SetMergeViews(CHexMergeView *pView[])
663 {
664         for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
665         {
666                 ASSERT(pView[nBuffer] != nullptr && m_pView[nBuffer] == nullptr);
667                 m_pView[nBuffer] = pView[nBuffer];
668                 m_pView[nBuffer]->m_nThisPane = nBuffer;
669         }
670 }
671
672 /**
673  * @brief Called when "Save left" item is updated
674  */
675 void CHexMergeDoc::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
676 {
677         pCmdUI->Enable(m_pView[0]->GetModified());
678 }
679
680 /**
681  * @brief Called when "Save middle" item is updated
682  */
683 void CHexMergeDoc::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
684 {
685         pCmdUI->Enable(m_nBuffers == 3 && m_pView[1]->GetModified());
686 }
687
688 /**
689  * @brief Called when "Save right" item is updated
690  */
691 void CHexMergeDoc::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
692 {
693         pCmdUI->Enable(m_pView[m_nBuffers - 1]->GetModified());
694 }
695
696 /**
697  * @brief Called when "Save" item is updated
698  */
699 void CHexMergeDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
700 {
701         bool bModified = false;
702         for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
703                 bModified |= m_pView[nBuffer]->GetModified();
704         pCmdUI->Enable(bModified);
705 }
706
707 /**
708  * @brief Reloads the opened files
709  */
710 void CHexMergeDoc::OnFileReload()
711 {
712         if (!PromptAndSaveIfNeeded(true))
713                 return;
714         
715         FileLocation fileloc[3];
716         bool bRO[3];
717         for (int pane = 0; pane < m_nBuffers; pane++)
718         {
719                 fileloc[pane].setPath(m_filePaths[pane]);
720                 bRO[pane] = m_pView[pane]->GetReadOnly();
721         }
722         int nActivePane = GetActiveMergeView()->m_nThisPane;
723         OpenDocs(m_nBuffers, fileloc, bRO, m_strDesc, nActivePane);
724 }
725
726 /**
727  * @brief Copy selected bytes from left to right
728  */
729 void CHexMergeDoc::OnL2r()
730 {
731         int dstPane = (GetActiveMergeView()->m_nThisPane < m_nBuffers - 1) ? GetActiveMergeView()->m_nThisPane + 1 : m_nBuffers - 1;
732         int srcPane = dstPane - 1;
733         CHexMergeView::CopySel(m_pView[srcPane], m_pView[dstPane]);
734 }
735
736 /**
737  * @brief Copy selected bytes from right to left
738  */
739 void CHexMergeDoc::OnR2l()
740 {
741         int dstPane = (GetActiveMergeView()->m_nThisPane > 0) ? GetActiveMergeView()->m_nThisPane - 1 : 0;
742         int srcPane = dstPane + 1;
743         CHexMergeView::CopySel(m_pView[srcPane], m_pView[dstPane]);
744 }
745
746 /**
747  * @brief Copy selected bytes from left to active pane
748  */
749 void CHexMergeDoc::OnCopyFromLeft()
750 {
751         int dstPane = GetActiveMergeView()->m_nThisPane;
752         int srcPane = (dstPane - 1 < 0) ? 0 : dstPane - 1;
753         CHexMergeView::CopySel(m_pView[srcPane], m_pView[dstPane]);
754 }
755
756 /**
757  * @brief Copy selected bytes from right to active pane
758  */
759 void CHexMergeDoc::OnCopyFromRight()
760 {
761         int dstPane = GetActiveMergeView()->m_nThisPane;
762         int srcPane = (dstPane + 1 > m_nBuffers - 1) ? m_nBuffers - 1 : dstPane + 1;
763         CHexMergeView::CopySel(m_pView[srcPane], m_pView[dstPane]);
764 }
765
766 /**
767  * @brief Copy all bytes from left to right
768  */
769 void CHexMergeDoc::OnAllRight()
770 {
771         int dstPane = (GetActiveMergeView()->m_nThisPane < m_nBuffers - 1) ? GetActiveMergeView()->m_nThisPane + 1 : m_nBuffers - 1;
772         int srcPane = dstPane - 1;
773         CHexMergeView::CopyAll(m_pView[srcPane], m_pView[dstPane]);
774 }
775
776 /**
777  * @brief Copy all bytes from right to left
778  */
779 void CHexMergeDoc::OnAllLeft()
780 {
781         int dstPane = (GetActiveMergeView()->m_nThisPane > 0) ? GetActiveMergeView()->m_nThisPane - 1 : 0;
782         int srcPane = dstPane + 1;
783         CHexMergeView::CopyAll(m_pView[srcPane], m_pView[dstPane]);
784 }
785
786 /**
787  * @brief Called when user selects View/Zoom In from menu.
788  */
789 void CHexMergeDoc::OnViewZoomIn()
790 {
791         for (int pane = 0; pane < m_nBuffers; pane++)
792                 m_pView[pane]->ZoomText(1);
793 }
794
795 /**
796  * @brief Called when user selects View/Zoom Out from menu.
797  */
798 void CHexMergeDoc::OnViewZoomOut()
799 {
800         for (int pane = 0; pane < m_nBuffers; pane++)
801                 m_pView[pane]->ZoomText(-1);
802 }
803
804 /**
805  * @brief Called when user selects View/Zoom Normal from menu.
806  */
807 void CHexMergeDoc::OnViewZoomNormal()
808 {
809         for (int pane = 0; pane < m_nBuffers; pane++)
810                 m_pView[pane]->ZoomText(0);
811 }
812
813 void CHexMergeDoc::OnRefresh()
814 {
815         if (UpdateDiffItem(m_pDirDoc) == 0)
816                 LangMessageBox(IDS_FILESSAME, MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN);
817 }
818
819 void CHexMergeDoc::OnFileRecompareAsText()
820 {
821         FileLocation fileloc[3];
822         DWORD dwFlags[3];
823         String strDesc[3];
824         int nBuffers = m_nBuffers;
825         CDirDoc *pDirDoc = m_pDirDoc->GetMainView() ? m_pDirDoc : 
826                 static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
827         for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
828         {
829                 fileloc[nBuffer].setPath(m_filePaths[nBuffer]);
830                 dwFlags[nBuffer] = m_pView[nBuffer]->GetReadOnly() ? FFILEOPEN_READONLY : 0;
831                 strDesc[nBuffer] = m_strDesc[nBuffer];
832         }
833         CloseNow();
834         GetMainFrame()->ShowMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
835 }