OSDN Git Service

f9df628ae7435abef1dfa562088e26bc1f7dd55d
[winmerge-jp/winmerge-jp.git] / Src / MainFrm.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  MainFrm.cpp
23  *
24  * @brief Implementation of the CMainFrame class
25  */
26
27 #include "StdAfx.h"
28 #include "MainFrm.h"
29 #include <vector>
30 #include <afxinet.h>
31 #include <boost/range/mfc.hpp>
32 #include "Constants.h"
33 #include "Merge.h"
34 #include "FileFilterHelper.h"
35 #include "UnicodeString.h"
36 #include "BCMenu.h"
37 #include "OpenFrm.h"
38 #include "DirFrame.h"           // Include type information
39 #include "ChildFrm.h"
40 #include "HexMergeFrm.h"
41 #include "DirView.h"
42 #include "DirDoc.h"
43 #include "OpenDoc.h"
44 #include "MergeDoc.h"
45 #include "MergeEditView.h"
46 #include "HexMergeDoc.h"
47 #include "HexMergeView.h"
48 #include "ImgMergeFrm.h"
49 #include "LineFiltersList.h"
50 #include "ConflictFileParser.h"
51 #include "LineFiltersDlg.h"
52 #include "paths.h"
53 #include "Environment.h"
54 #include "PatchTool.h"
55 #include "Plugins.h"
56 #include "ConfigLog.h"
57 #include "7zCommon.h"
58 #include "Merge7zFormatMergePluginImpl.h"
59 #include "FileFiltersDlg.h"
60 #include "OptionsMgr.h"
61 #include "OptionsDef.h"
62 #include "codepage_detect.h"
63 #include "unicoder.h"
64 #include "PreferencesDlg.h"
65 #include "FileOrFolderSelect.h"
66 #include "PluginsListDlg.h"
67 #include "stringdiffs.h"
68 #include "MergeCmdLineInfo.h"
69 #include "OptionsFont.h"
70 #include "JumpList.h"
71 #include "DropHandler.h"
72 #include "LanguageSelect.h"
73 #include "VersionInfo.h"
74 #include "Bitmap.h"
75 #include "CCrystalTextMarkers.h"
76
77 using std::vector;
78 using boost::begin;
79 using boost::end;
80
81 #ifdef _DEBUG
82 #define new DEBUG_NEW
83 #endif
84
85 static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale, CImageList& ImgList);
86 static CPtrList &GetDocList(CMultiDocTemplate *pTemplate);
87 template<class DocClass>
88 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles);
89
90 /**
91  * @brief A table associating menuitem id, icon and menus to apply.
92  */
93 const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
94         { ID_FILE_OPENCONFLICT,                 IDB_FILE_OPENCONFLICT,                  CMainFrame::MENU_ALL },
95         { ID_FILE_NEW3,                 IDB_FILE_NEW3,                  CMainFrame::MENU_ALL },
96         { ID_EDIT_COPY,                                 IDB_EDIT_COPY,                                  CMainFrame::MENU_ALL },
97         { ID_EDIT_CUT,                                  IDB_EDIT_CUT,                                   CMainFrame::MENU_ALL },
98         { ID_EDIT_PASTE,                                IDB_EDIT_PASTE,                                 CMainFrame::MENU_ALL },
99         { ID_EDIT_FIND,                                 IDB_EDIT_SEARCH,                                CMainFrame::MENU_ALL },
100         { ID_WINDOW_CASCADE,                    IDB_WINDOW_CASCADE,                             CMainFrame::MENU_ALL },
101         { ID_WINDOW_TILE_HORZ,                  IDB_WINDOW_HORIZONTAL,                  CMainFrame::MENU_ALL },
102         { ID_WINDOW_TILE_VERT,                  IDB_WINDOW_VERTICAL,                    CMainFrame::MENU_ALL },
103         { ID_FILE_CLOSE,                                IDB_WINDOW_CLOSE,                               CMainFrame::MENU_ALL },
104         { ID_WINDOW_CHANGE_PANE,                IDB_WINDOW_CHANGEPANE,                  CMainFrame::MENU_ALL },
105         { ID_EDIT_WMGOTO,                               IDB_EDIT_GOTO,                                  CMainFrame::MENU_ALL },
106         { ID_EDIT_REPLACE,                              IDB_EDIT_REPLACE,                               CMainFrame::MENU_ALL },
107         { ID_VIEW_SELECTFONT,                   IDB_VIEW_SELECTFONT,                    CMainFrame::MENU_ALL },
108         { ID_APP_EXIT,                                  IDB_FILE_EXIT,                                  CMainFrame::MENU_ALL },
109         { ID_HELP_CONTENTS,                             IDB_HELP_CONTENTS,                              CMainFrame::MENU_ALL },
110         { ID_EDIT_SELECT_ALL,                   IDB_EDIT_SELECTALL,                             CMainFrame::MENU_ALL },
111         { ID_TOOLS_FILTERS,                             IDB_TOOLS_FILTERS,                              CMainFrame::MENU_ALL },
112         { ID_TOOLS_CUSTOMIZECOLUMNS,    IDB_TOOLS_COLUMNS,                              CMainFrame::MENU_ALL },
113         { ID_TOOLS_GENERATEPATCH,               IDB_TOOLS_GENERATEPATCH,                CMainFrame::MENU_ALL },
114         { ID_PLUGINS_LIST,                              IDB_PLUGINS_LIST,                               CMainFrame::MENU_ALL },
115         { ID_COPY_FROM_LEFT,                    IDB_COPY_FROM_LEFT,                             CMainFrame::MENU_ALL },
116         { ID_COPY_FROM_RIGHT,                   IDB_COPY_FROM_RIGHT,                    CMainFrame::MENU_ALL },
117         { ID_FILE_PRINT,                                IDB_FILE_PRINT,                                 CMainFrame::MENU_FILECMP },
118         { ID_TOOLS_GENERATEREPORT,              IDB_TOOLS_GENERATEREPORT,               CMainFrame::MENU_FILECMP },
119         { ID_EDIT_TOGGLE_BOOKMARK,              IDB_EDIT_TOGGLE_BOOKMARK,               CMainFrame::MENU_FILECMP },
120         { ID_EDIT_GOTO_NEXT_BOOKMARK,   IDB_EDIT_GOTO_NEXT_BOOKMARK,    CMainFrame::MENU_FILECMP },
121         { ID_EDIT_GOTO_PREV_BOOKMARK,   IDB_EDIT_GOTO_PREV_BOOKMARK,    CMainFrame::MENU_FILECMP },
122         { ID_EDIT_CLEAR_ALL_BOOKMARKS,  IDB_EDIT_CLEAR_ALL_BOOKMARKS,   CMainFrame::MENU_FILECMP },
123         { ID_VIEW_ZOOMIN,                               IDB_VIEW_ZOOMIN,                                CMainFrame::MENU_FILECMP },
124         { ID_VIEW_ZOOMOUT,                              IDB_VIEW_ZOOMOUT,                               CMainFrame::MENU_FILECMP },
125         { ID_MERGE_COMPARE,                             IDB_MERGE_COMPARE,                              CMainFrame::MENU_FOLDERCMP },
126         { ID_MERGE_COMPARE_LEFT1_LEFT2,         IDB_MERGE_COMPARE_LEFT1_LEFT2,  CMainFrame::MENU_FOLDERCMP },
127         { ID_MERGE_COMPARE_RIGHT1_RIGHT2,       IDB_MERGE_COMPARE_RIGHT1_RIGHT2,CMainFrame::MENU_FOLDERCMP },
128         { ID_MERGE_COMPARE_LEFT1_RIGHT2,        IDB_MERGE_COMPARE_LEFT1_RIGHT2, CMainFrame::MENU_FOLDERCMP },
129         { ID_MERGE_COMPARE_LEFT2_RIGHT1,        IDB_MERGE_COMPARE_LEFT2_RIGHT1, CMainFrame::MENU_FOLDERCMP },
130         { ID_MERGE_DELETE,                              IDB_MERGE_DELETE,                               CMainFrame::MENU_FOLDERCMP },
131         { ID_TOOLS_GENERATEREPORT,              IDB_TOOLS_GENERATEREPORT,               CMainFrame::MENU_FOLDERCMP },
132         { ID_DIR_COPY_LEFT_TO_RIGHT,    IDB_LEFT_TO_RIGHT,                              CMainFrame::MENU_FOLDERCMP },
133         { ID_DIR_COPY_LEFT_TO_MIDDLE,   IDB_LEFT_TO_MIDDLE,                             CMainFrame::MENU_FOLDERCMP },
134         { ID_DIR_COPY_RIGHT_TO_LEFT,    IDB_RIGHT_TO_LEFT,                              CMainFrame::MENU_FOLDERCMP },
135         { ID_DIR_COPY_RIGHT_TO_MIDDLE,  IDB_RIGHT_TO_MIDDLE,                    CMainFrame::MENU_FOLDERCMP },
136         { ID_DIR_COPY_MIDDLE_TO_LEFT,   IDB_MIDDLE_TO_LEFT,                             CMainFrame::MENU_FOLDERCMP },
137         { ID_DIR_COPY_MIDDLE_TO_RIGHT,  IDB_MIDDLE_TO_RIGHT,                    CMainFrame::MENU_FOLDERCMP },
138         { ID_DIR_COPY_LEFT_TO_BROWSE,   IDB_LEFT_TO_BROWSE,                             CMainFrame::MENU_FOLDERCMP },
139         { ID_DIR_COPY_MIDDLE_TO_BROWSE, IDB_MIDDLE_TO_BROWSE,                   CMainFrame::MENU_FOLDERCMP },
140         { ID_DIR_COPY_RIGHT_TO_BROWSE,  IDB_RIGHT_TO_BROWSE,                    CMainFrame::MENU_FOLDERCMP },
141         { ID_DIR_MOVE_LEFT_TO_BROWSE,   IDB_MOVE_LEFT_TO_BROWSE,                CMainFrame::MENU_FOLDERCMP },
142         { ID_DIR_MOVE_MIDDLE_TO_BROWSE, IDB_MOVE_MIDDLE_TO_BROWSE,              CMainFrame::MENU_FOLDERCMP },
143         { ID_DIR_MOVE_RIGHT_TO_BROWSE,  IDB_MOVE_RIGHT_TO_BROWSE,               CMainFrame::MENU_FOLDERCMP },
144         { ID_DIR_DEL_LEFT,                              IDB_LEFT,                                               CMainFrame::MENU_FOLDERCMP },
145         { ID_DIR_DEL_MIDDLE,                    IDB_MIDDLE,                                             CMainFrame::MENU_FOLDERCMP },
146         { ID_DIR_DEL_RIGHT,                             IDB_RIGHT,                                              CMainFrame::MENU_FOLDERCMP },
147         { ID_DIR_DEL_BOTH,                              IDB_BOTH,                                               CMainFrame::MENU_FOLDERCMP },
148         { ID_DIR_DEL_ALL,                               IDB_ALL,                                                CMainFrame::MENU_FOLDERCMP },
149         { ID_DIR_COPY_PATHNAMES_LEFT,   IDB_LEFT,                                               CMainFrame::MENU_FOLDERCMP },
150         { ID_DIR_COPY_PATHNAMES_MIDDLE, IDB_MIDDLE,                                             CMainFrame::MENU_FOLDERCMP },
151         { ID_DIR_COPY_PATHNAMES_RIGHT,  IDB_RIGHT,                                              CMainFrame::MENU_FOLDERCMP },
152         { ID_DIR_COPY_PATHNAMES_BOTH,   IDB_BOTH,                                               CMainFrame::MENU_FOLDERCMP },
153         { ID_DIR_COPY_PATHNAMES_ALL,    IDB_ALL,                                                CMainFrame::MENU_FOLDERCMP },
154         { ID_DIR_COPY_LEFT_TO_CLIPBOARD, IDB_LEFT,                                              CMainFrame::MENU_FOLDERCMP },
155         { ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, IDB_MIDDLE,                                  CMainFrame::MENU_FOLDERCMP },
156         { ID_DIR_COPY_RIGHT_TO_CLIPBOARD, IDB_RIGHT,                                    CMainFrame::MENU_FOLDERCMP },
157         { ID_DIR_COPY_BOTH_TO_CLIPBOARD, IDB_BOTH,                                              CMainFrame::MENU_FOLDERCMP },
158         { ID_DIR_COPY_ALL_TO_CLIPBOARD, IDB_ALL,                                                CMainFrame::MENU_FOLDERCMP },
159         { ID_DIR_ZIP_LEFT,                              IDB_LEFT,                                               CMainFrame::MENU_FOLDERCMP },
160         { ID_DIR_ZIP_MIDDLE,                    IDB_MIDDLE,                                             CMainFrame::MENU_FOLDERCMP },
161         { ID_DIR_ZIP_RIGHT,                             IDB_RIGHT,                                              CMainFrame::MENU_FOLDERCMP },
162         { ID_DIR_ZIP_BOTH,                              IDB_BOTH,                                               CMainFrame::MENU_FOLDERCMP },
163         { ID_DIR_ZIP_ALL,                               IDB_ALL,                                                CMainFrame::MENU_FOLDERCMP }
164 };
165
166
167 /////////////////////////////////////////////////////////////////////////////
168 // CMainFrame
169
170 IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
171
172 BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
173         //{{AFX_MSG_MAP(CMainFrame)
174         ON_WM_MENUCHAR()
175         ON_WM_MEASUREITEM()
176         ON_WM_INITMENUPOPUP()
177         ON_WM_INITMENU()
178         ON_WM_CREATE()
179         ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
180         ON_COMMAND(ID_HELP_GNULICENSE, OnHelpGnulicense)
181         ON_COMMAND(ID_OPTIONS, OnOptions)
182         ON_COMMAND(ID_VIEW_SELECTFONT, OnViewSelectfont)
183         ON_COMMAND(ID_VIEW_USEDEFAULTFONT, OnViewUsedefaultfont)
184         ON_COMMAND(ID_HELP_CONTENTS, OnHelpContents)
185         ON_WM_CLOSE()
186         ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
187         ON_WM_DESTROY()
188         ON_COMMAND_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnPluginUnpackMode)
189         ON_UPDATE_COMMAND_UI_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnUpdatePluginUnpackMode)
190         ON_COMMAND_RANGE(ID_PREDIFFER_MANUAL, ID_PREDIFFER_AUTO, OnPluginPrediffMode)
191         ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFER_MANUAL, ID_PREDIFFER_AUTO, OnUpdatePluginPrediffMode)
192         ON_UPDATE_COMMAND_UI(ID_RELOAD_PLUGINS, OnUpdateReloadPlugins)
193         ON_COMMAND(ID_RELOAD_PLUGINS, OnReloadPlugins)
194         ON_COMMAND(ID_HELP_GETCONFIG, OnSaveConfigData)
195         ON_COMMAND(ID_FILE_NEW, OnFileNew)
196         ON_COMMAND(ID_FILE_NEW3, OnFileNew3)
197         ON_COMMAND(ID_TOOLS_FILTERS, OnToolsFilters)
198         ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
199         ON_UPDATE_COMMAND_UI(ID_VIEW_TAB_BAR, OnUpdateViewTabBar)
200         ON_COMMAND(ID_VIEW_TAB_BAR, OnViewTabBar)
201         ON_UPDATE_COMMAND_UI(ID_VIEW_RESIZE_PANES, OnUpdateResizePanes)
202         ON_COMMAND(ID_VIEW_RESIZE_PANES, OnResizePanes)
203         ON_COMMAND(ID_FILE_OPENPROJECT, OnFileOpenProject)
204         ON_MESSAGE(WM_COPYDATA, OnCopyData)
205         ON_MESSAGE(WM_USER+1, OnUser1)
206         ON_COMMAND(ID_WINDOW_CLOSEALL, OnWindowCloseAll)
207         ON_UPDATE_COMMAND_UI(ID_WINDOW_CLOSEALL, OnUpdateWindowCloseAll)
208         ON_COMMAND(ID_FILE_SAVEPROJECT, OnSaveProject)
209         ON_WM_ACTIVATEAPP()
210         ON_COMMAND_RANGE(ID_TOOLBAR_NONE, ID_TOOLBAR_HUGE, OnToolbarSize)
211         ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLBAR_NONE, ID_TOOLBAR_HUGE, OnUpdateToolbarSize)
212         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
213         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
214         ON_COMMAND(ID_HELP_RELEASENOTES, OnHelpReleasenotes)
215         ON_COMMAND(ID_HELP_TRANSLATIONS, OnHelpTranslations)
216         ON_COMMAND(ID_FILE_OPENCONFLICT, OnFileOpenConflict)
217         ON_COMMAND(ID_PLUGINS_LIST, OnPluginsList)
218         ON_UPDATE_COMMAND_UI(ID_STATUS_PLUGIN, OnUpdatePluginName)
219         ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDiffOptionsDropDown)
220         ON_COMMAND_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnDiffWhitespace)
221         ON_UPDATE_COMMAND_UI_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnUpdateDiffWhitespace)
222         ON_COMMAND(IDC_DIFF_CASESENSITIVE, OnDiffCaseSensitive)
223         ON_UPDATE_COMMAND_UI(IDC_DIFF_CASESENSITIVE, OnUpdateDiffCaseSensitive)
224         ON_COMMAND(IDC_DIFF_IGNOREEOL, OnDiffIgnoreEOL)
225         ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNOREEOL, OnUpdateDiffIgnoreEOL)
226         ON_COMMAND(IDC_RECURS_CHECK, OnIncludeSubfolders)
227         ON_UPDATE_COMMAND_UI(IDC_RECURS_CHECK, OnUpdateIncludeSubfolders)
228         ON_COMMAND_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnCompareMethod)
229         ON_UPDATE_COMMAND_UI_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnUpdateCompareMethod)
230         ON_COMMAND_RANGE(ID_MRU_FIRST, ID_MRU_LAST, OnMRUs)
231         ON_UPDATE_COMMAND_UI(ID_MRU_FIRST, OnUpdateNoMRUs)
232         ON_UPDATE_COMMAND_UI(ID_NO_MRU, OnUpdateNoMRUs)
233         //}}AFX_MSG_MAP
234 END_MESSAGE_MAP()
235
236 /**
237  * @brief MainFrame statusbar panels/indicators
238  */
239 static UINT StatusbarIndicators[] =
240 {
241         ID_SEPARATOR,           // Plugin name
242         ID_SEPARATOR,           // status line indicator
243         ID_SEPARATOR,           // Merge mode
244         ID_SEPARATOR,           // Diff number
245         ID_INDICATOR_CAPS,      // Caps Lock
246         ID_INDICATOR_NUM,       // Num Lock
247         ID_INDICATOR_OVR,       // Insert
248 };
249
250 /**
251   * @brief Return a const reference to a CMultiDocTemplate's list of documents.
252   */
253 static CPtrList &GetDocList(CMultiDocTemplate *pTemplate)
254 {
255         struct Template : public CMultiDocTemplate
256         {
257         public:
258                 using CMultiDocTemplate::m_docList;
259         };
260         return static_cast<struct Template *>(pTemplate)->m_docList;
261 }
262
263 /////////////////////////////////////////////////////////////////////////////
264 // CMainFrame construction/destruction
265
266 /**
267  * @brief MainFrame constructor. Loads settings from registry.
268  * @todo Preference for logging?
269  */
270 CMainFrame::CMainFrame()
271 : m_bFirstTime(true)
272 , m_pDropHandler(NULL)
273 , m_bShowErrors(false)
274 {
275 }
276
277 CMainFrame::~CMainFrame()
278 {
279         GetOptionsMgr()->SaveOption(OPT_TABBAR_AUTO_MAXWIDTH, m_wndTabBar.GetAutoMaxWidth());
280         strdiff::Close();
281 }
282
283 #ifdef _UNICODE
284 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassW");
285 #else
286 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassA");
287 #endif
288 /**
289  * @brief Change MainFrame window class name
290  *        see http://support.microsoft.com/kb/403825/ja
291  */
292 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
293 {
294         WNDCLASS wndcls;
295         BOOL bRes = CMDIFrameWnd::PreCreateWindow(cs);
296         HINSTANCE hInst = AfxGetInstanceHandle();
297         // see if the class already exists
298         if (!::GetClassInfo(hInst, szClassName, &wndcls))
299         {
300                 // get default stuff
301                 ::GetClassInfo(hInst, cs.lpszClass, &wndcls);
302                 // register a new class
303                 wndcls.lpszClassName = szClassName;
304                 wndcls.hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(IDR_MAINFRAME));
305                 ::RegisterClass(&wndcls);
306         }
307         cs.lpszClass = szClassName;
308         return bRes;
309 }
310
311 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
312 {
313         if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
314                 return -1;
315
316         m_lfDiff = Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP);
317         m_lfDir = Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP);
318         
319         if (!CreateToolbar())
320         {
321                 TRACE0("Failed to create toolbar\n");
322                 return -1;      // fail to create
323         }
324         
325         if (!m_wndTabBar.Create(this))
326         {
327                 TRACE0("Failed to create tab bar\n");
328                 return -1;      // fail to create
329         }
330         m_wndTabBar.SetAutoMaxWidth(GetOptionsMgr()->GetBool(OPT_TABBAR_AUTO_MAXWIDTH));
331
332         if (!GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR))
333                 CMDIFrameWnd::ShowControlBar(&m_wndTabBar, false, 0);
334
335         if (!m_wndStatusBar.Create(this))
336         {
337                 TRACE0("Failed to create status bar\n");
338                 return -1;      // fail to create
339         }
340         theApp.SetIndicators(m_wndStatusBar, StatusbarIndicators,
341                         countof(StatusbarIndicators));
342
343         const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
344         auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
345         m_wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH | SBPS_NOBORDERS, 0);
346         m_wndStatusBar.SetPaneInfo(1, ID_STATUS_PLUGIN, 0, pointToPixel(225));
347         m_wndStatusBar.SetPaneInfo(2, ID_STATUS_MERGINGMODE, 0, pointToPixel(75)); 
348         m_wndStatusBar.SetPaneInfo(3, ID_STATUS_DIFFNUM, 0, pointToPixel(112)); 
349
350         if (!GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR))
351                 CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, false, 0);
352
353         m_pDropHandler = new DropHandler(std::bind(&CMainFrame::OnDropFiles, this, std::placeholders::_1));
354         RegisterDragDrop(m_hWnd, m_pDropHandler);
355
356         return 0;
357 }
358
359 void CMainFrame::OnDestroy(void)
360 {
361         if (m_pDropHandler)
362                 RevokeDragDrop(m_hWnd);
363 }
364
365 static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
366 {
367         int i;
368         for (i = 0 ; i < ::GetMenuItemCount(mainMenu) ; i++)
369                 if (::GetMenuItemID(::GetSubMenu(mainMenu, i), 0) == nIDFirstMenuItem)
370                         break;
371         HMENU menu = ::GetSubMenu(mainMenu, i);
372
373         if (!bFirstSubmenu)
374         {
375                 // look for last submenu
376                 for (i = ::GetMenuItemCount(menu) ; i >= 0  ; i--)
377                         if (::GetSubMenu(menu, i) != NULL)
378                                 return ::GetSubMenu(menu, i);
379         }
380         else
381         {
382                 // look for first submenu
383                 for (i = 0 ; i < ::GetMenuItemCount(menu) ; i++)
384                         if (::GetSubMenu(menu, i) != NULL)
385                                 return ::GetSubMenu(menu, i);
386         }
387
388         // error, submenu not found
389         return NULL;
390 }
391
392 /** 
393  * @brief Find the scripts submenu from the main menu
394  * As now this is the first submenu in "Edit" menu
395  * We find the "Edit" menu by looking for a menu 
396  *  starting with ID_EDIT_UNDO.
397  */
398 HMENU CMainFrame::GetScriptsSubmenu(HMENU mainMenu)
399 {
400         return GetSubmenu(mainMenu, ID_PLUGINS_LIST, false);
401 }
402
403 /**
404  * @brief Find the scripts submenu from the main menu
405  * As now this is the first submenu in "Plugins" menu
406  * We find the "Plugins" menu by looking for a menu 
407  *  starting with ID_UNPACK_MANUAL.
408  */
409 HMENU CMainFrame::GetPrediffersSubmenu(HMENU mainMenu)
410 {
411         return GetSubmenu(mainMenu, ID_PLUGINS_LIST, true);
412 }
413
414 /**
415  * @brief Create a new menu for the view..
416  * @param [in] view Menu view either MENU_DEFAULT, MENU_MERGEVIEW or MENU_DIRVIEW.
417  * @param [in] ID Menu's resource ID.
418  * @return Menu for the view.
419  */
420 HMENU CMainFrame::NewMenu(int view, int ID)
421 {
422         int menu_view, index;
423         if (m_pMenus[view] == NULL)
424         {
425                 m_pMenus[view].reset(new BCMenu());
426                 if (m_pMenus[view] == NULL)
427                         return NULL;
428         }
429
430         switch (view)
431         {
432         case MENU_MERGEVIEW:
433                 menu_view = MENU_FILECMP;
434                 break;
435         case MENU_DIRVIEW:
436                 menu_view = MENU_FOLDERCMP;
437                 break;
438         case MENU_DEFAULT:
439         default:
440                 menu_view = MENU_MAINFRM;
441                 break;
442         };
443
444         if (!m_pMenus[view]->LoadMenu(ID))
445         {
446                 ASSERT(false);
447                 return NULL;
448         }
449
450         if (view == MENU_IMGMERGEVIEW)
451         {
452                 BCMenu *pMenu = new BCMenu;
453                 pMenu->LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
454                 m_pMenus[view]->InsertMenu(4, MF_BYPOSITION | MF_POPUP, (UINT_PTR)pMenu->GetSubMenu(0)->m_hMenu, const_cast<TCHAR *>(LoadResString(IDS_IMAGE_MENU).c_str())); 
455         }
456
457         // Load bitmaps to menuitems
458         for (index = 0; index < countof(m_MenuIcons); index ++)
459         {
460                 if (menu_view == (m_MenuIcons[index].menusToApply & menu_view))
461                 {
462                         m_pMenus[view]->ModifyODMenu(NULL, m_MenuIcons[index].menuitemID, m_MenuIcons[index].iconResID);
463                 }
464         }
465
466         m_pMenus[view]->LoadToolbar(IDR_MAINFRAME);
467
468         theApp.TranslateMenu(m_pMenus[view]->m_hMenu);
469
470         return (m_pMenus[view]->Detach());
471
472 }
473 /** 
474 * @brief Create new default (CMainFrame) menu.
475 */
476 HMENU CMainFrame::NewDefaultMenu(int ID /*=0*/)
477 {
478         if (ID == 0)
479                 ID = IDR_MAINFRAME;
480         return NewMenu( MENU_DEFAULT, ID );
481 }
482
483 /**
484  * @brief Create new File compare (CMergeEditView) menu.
485  */
486 HMENU CMainFrame::NewMergeViewMenu()
487 {
488         return NewMenu( MENU_MERGEVIEW, IDR_MERGEDOCTYPE);
489 }
490
491 /**
492  * @brief Create new Dir compare (CDirView) menu
493  */
494 HMENU CMainFrame::NewDirViewMenu()
495 {
496         return NewMenu(MENU_DIRVIEW, IDR_DIRDOCTYPE );
497 }
498
499 /**
500  * @brief Create new File compare (CHexMergeView) menu.
501  */
502 HMENU CMainFrame::NewHexMergeViewMenu()
503 {
504         return NewMenu( MENU_HEXMERGEVIEW, IDR_MERGEDOCTYPE);
505 }
506
507 /**
508  * @brief Create new Image compare (CImgMergeView) menu.
509  */
510 HMENU CMainFrame::NewImgMergeViewMenu()
511 {
512         return NewMenu( MENU_IMGMERGEVIEW, IDR_MERGEDOCTYPE);
513 }
514
515 /**
516  * @brief Create new File compare (COpenView) menu.
517  */
518 HMENU CMainFrame::NewOpenViewMenu()
519 {
520         return NewMenu( MENU_OPENVIEW, IDR_MAINFRAME);
521 }
522
523 /**
524  * @brief This handler ensures that the popup menu items are drawn correctly.
525  */
526 void CMainFrame::OnMeasureItem(int nIDCtl,
527         LPMEASUREITEMSTRUCT lpMeasureItemStruct)
528 {
529         bool setflag = false;
530         if (lpMeasureItemStruct->CtlType == ODT_MENU)
531         {
532                 if (IsMenu(reinterpret_cast<HMENU>(static_cast<uintptr_t>(lpMeasureItemStruct->itemID))))
533                 {
534                         CMenu* cmenu =
535                                 CMenu::FromHandle(reinterpret_cast<HMENU>(static_cast<uintptr_t>(lpMeasureItemStruct->itemID)));
536
537                         if (m_pMenus[MENU_DEFAULT]->IsMenu(cmenu))
538                         {
539                                 m_pMenus[MENU_DEFAULT]->MeasureItem(lpMeasureItemStruct);
540                                 setflag = true;
541                         }
542                 }
543         }
544
545         if (!setflag)
546                 CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
547 }
548
549 /**
550  * @brief This handler ensures that keyboard shortcuts work.
551  */
552 LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags, 
553         CMenu* pMenu) 
554 {
555         LRESULT lresult;
556         if(m_pMenus[MENU_DEFAULT]->IsMenu(pMenu))
557                 lresult=BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
558         else
559                 lresult=CMDIFrameWnd::OnMenuChar(nChar, nFlags, pMenu);
560         return(lresult);
561 }
562
563 /**
564  * @brief This handler updates the menus from time to time.
565  */
566 void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
567 {
568         CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
569         
570         if (!bSysMenu)
571         {
572                 if (BCMenu::IsMenu(pPopupMenu))
573                 {
574                         BCMenu::UpdateMenu(pPopupMenu);
575                 }
576         }
577 }
578
579 /////////////////////////////////////////////////////////////////////////////
580 // CMainFrame message handlers
581
582 void CMainFrame::OnFileOpen() 
583 {
584         DoFileOpen();
585 }
586
587 /**
588  * @brief Check for BOM, and also, if bGuessEncoding, try to deduce codepage
589  *
590  * Unpacks info from FileLocation & delegates all work to codepage_detect module
591  */
592 static void
593 FileLocationGuessEncodings(FileLocation & fileloc, int iGuessEncoding)
594 {
595         fileloc.encoding = GuessCodepageEncoding(fileloc.filepath, iGuessEncoding);
596 }
597
598 bool CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
599         int nFiles, const FileLocation ifileloc[],
600         const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
601         const PackingInfo * infoUnpacker /*= NULL*/)
602 {
603         int pane;
604         FileFilterHelper filterImg, filterBin;
605         filterImg.UseMask(true);
606         filterImg.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
607         filterBin.UseMask(true);
608         filterBin.SetMask(GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS));
609         for (pane = 0; pane < nFiles; ++pane)
610         {
611                 if (filterImg.includeFile(ifileloc[pane].filepath) && CImgMergeFrame::IsLoadable())
612                         return ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
613                 else if (filterBin.includeFile(ifileloc[pane].filepath) && CHexMergeView::IsLoadable())
614                         return ShowHexMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
615         }
616         return ShowMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
617 }
618
619 std::array<bool, 3> GetROFromFlags(int nFiles, const DWORD dwFlags[])
620 {
621         std::array<bool, 3> bRO = { false, false, false };
622         for (int pane = 0; pane < nFiles; pane++)
623         {
624                 if (dwFlags)
625                         bRO[pane] = ((dwFlags[pane] & FFILEOPEN_READONLY) > 0);
626         }
627         return bRO;
628 }
629
630 int GetActivePaneFromFlags(int nFiles, const DWORD dwFlags[])
631 {
632         int nActivePane = -1;
633         for (int pane = 0; pane < nFiles; ++pane)
634         {
635                 if (dwFlags && (dwFlags[pane] & FFILEOPEN_SETFOCUS))
636                         nActivePane = pane;
637         }
638         return nActivePane;
639 }
640
641 /**
642  * @brief Creates new MergeDoc instance and shows documents.
643  * @param [in] pDirDoc Dir compare document to create a new Merge document for.
644  * @param [in] ifilelocLeft Left side file location info.
645  * @param [in] ifilelocRight Right side file location info.
646  * @param [in] dwLeftFlags Left side flags.
647  * @param [in] dwRightFlags Right side flags.
648  * @param [in] infoUnpacker Plugin info.
649  * @return success/failure
650  */
651 bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
652         int nFiles, const FileLocation ifileloc[],
653         const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
654         const PackingInfo * infoUnpacker /*= NULL*/)
655 {
656         if (!m_pMenus[MENU_MERGEVIEW])
657                 theApp.m_pDiffTemplate->m_hMenuShared = NewMergeViewMenu();
658         CMergeDoc * pMergeDoc = GetMergeDocForDiff<CMergeDoc>(theApp.m_pDiffTemplate, pDirDoc, nFiles);
659
660         // Make local copies, so we can change encoding if we guess it below
661         FileLocation fileloc[3];
662         std::copy_n(ifileloc, nFiles, fileloc);
663
664         ASSERT(pMergeDoc);              // must ASSERT to get an answer to the question below ;-)
665         if (!pMergeDoc)
666                 return false; // when does this happen ?
667
668         // if an unpacker is selected, it must be used during LoadFromFile
669         // MergeDoc must memorize it for SaveToFile
670         // Warning : this unpacker may differ from the pDirDoc one
671         // (through menu : "Plugins"->"Open with unpacker")
672         pMergeDoc->SetUnpacker(infoUnpacker);
673
674         // detect codepage
675         int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
676         for (int pane = 0; pane < nFiles; pane++)
677         {
678                 if (fileloc[pane].encoding.m_unicoding == -1)
679                         fileloc[pane].encoding.m_unicoding = ucr::NONE;
680                 if (fileloc[pane].encoding.m_unicoding == ucr::NONE && fileloc[pane].encoding.m_codepage == -1)
681                 {
682                         FileLocationGuessEncodings(fileloc[pane], iGuessEncodingType);
683                 }
684
685                 // TODO (Perry, 2005-12-04)
686                 // Should we do any unification if unicodings are different?
687
688
689 #ifndef _UNICODE
690                 // In ANSI (8-bit) build, character loss can occur in merging
691                 // if the two buffers use different encodings
692                 if (pane > 0 && fileloc[pane - 1].encoding.m_codepage != fileloc[pane].encoding.m_codepage)
693                 {
694                         CString msg;
695                         msg.Format(theApp.LoadString(IDS_SUGGEST_IGNORECODEPAGE).c_str(), fileloc[pane - 1].encoding.m_codepage,fileloc[pane].encoding.m_codepage);
696                         int msgflags = MB_YESNO | MB_ICONQUESTION | MB_DONT_ASK_AGAIN;
697                         // Two files with different codepages
698                         // Warn and propose to use the default codepage for both
699                         int userChoice = AfxMessageBox(msg, msgflags);
700                         if (userChoice == IDYES)
701                         {
702                                 fileloc[pane - 1].encoding.SetCodepage(ucr::getDefaultCodepage());
703                                 fileloc[pane - 1].encoding.m_bom = false;
704                                 fileloc[pane].encoding.SetCodepage(ucr::getDefaultCodepage());
705                                 fileloc[pane].encoding.m_bom = false;
706                         }
707                 }
708 #endif
709         }
710
711         // Note that OpenDocs() takes care of closing compare window when needed.
712         if (!pMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
713                 return false;
714
715         for (int pane = 0; pane < nFiles; pane++)
716         {
717                 if (dwFlags)
718                 {
719                         bool bModified = (dwFlags[pane] & FFILEOPEN_MODIFIED) > 0;
720                         if (bModified)
721                         {
722                                 pMergeDoc->m_ptBuf[pane]->SetModified(TRUE);
723                                 pMergeDoc->UpdateHeaderPath(pane);
724                         }
725                         if (dwFlags[pane] & FFILEOPEN_AUTOMERGE)
726                         {
727                                 pMergeDoc->DoAutoMerge(pane);
728                         }
729                 }
730         }
731
732         if (!sReportFile.empty())
733                 pMergeDoc->GenerateReport(sReportFile);
734
735         return true;
736 }
737
738 bool CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
739         const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
740         const PackingInfo * infoUnpacker /*= NULL*/)
741 {
742         if (!m_pMenus[MENU_HEXMERGEVIEW])
743                 theApp.m_pHexMergeTemplate->m_hMenuShared = NewHexMergeViewMenu();
744         CHexMergeDoc *pHexMergeDoc = GetMergeDocForDiff<CHexMergeDoc>(theApp.m_pHexMergeTemplate, pDirDoc, nFiles);
745         if (!pHexMergeDoc)
746                 return false;
747
748         if (!pHexMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
749                 return false;
750         
751         if (!sReportFile.empty())
752                 pHexMergeDoc->GenerateReport(sReportFile);
753
754         return true;
755 }
756
757 bool CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
758         const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
759         const PackingInfo * infoUnpacker/* = NULL*/)
760 {
761         CImgMergeFrame *pImgMergeFrame = new CImgMergeFrame();
762         if (!CImgMergeFrame::menu.m_hMenu)
763                 CImgMergeFrame::menu.m_hMenu = NewImgMergeViewMenu();
764         pImgMergeFrame->SetSharedMenu(CImgMergeFrame::menu.m_hMenu);
765
766         pImgMergeFrame->SetDirDoc(pDirDoc);
767         pDirDoc->AddMergeDoc(pImgMergeFrame);
768                 
769         if (!pImgMergeFrame->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags), this))
770                 return ShowMergeDoc(pDirDoc, nFiles, fileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
771
772         for (int pane = 0; pane < nFiles; pane++)
773         {
774                 if (dwFlags && (dwFlags[pane] & FFILEOPEN_AUTOMERGE))
775                         pImgMergeFrame->DoAutoMerge(pane);
776         }
777
778         if (!sReportFile.empty())
779                 pImgMergeFrame->GenerateReport(sReportFile);
780
781         return true;
782 }
783
784 /**
785  * @brief Show GNU licence information in notepad (local file) or in Web Browser
786  */
787 void CMainFrame::OnHelpGnulicense() 
788 {
789         const String spath = paths::ConcatPath(env::GetProgPath(), LicenseFile);
790         theApp.OpenFileOrUrl(spath.c_str(), LicenceUrl);
791 }
792
793 /**
794  * @brief Opens Options-dialog and saves changed options
795  */
796 void CMainFrame::OnOptions() 
797 {
798         // Using singleton shared syntax colors
799         CPreferencesDlg dlg(GetOptionsMgr(), theApp.GetMainSyntaxColors());
800         INT_PTR rv = dlg.DoModal();
801
802         if (rv == IDOK)
803         {
804                 LANGID lang = static_cast<LANGID>(GetOptionsMgr()->GetInt(OPT_SELECTED_LANGUAGE));
805                 if (lang != theApp.m_pLangDlg->GetLangId())
806                 {
807                         theApp.m_pLangDlg->SetLanguage(lang, TRUE);
808         
809                         // Update status bar inicator texts
810                         theApp.SetIndicators(m_wndStatusBar, 0, 0);
811         
812                         // Update the current menu
813                         ReloadMenu();
814         
815                         // update the title text of the document
816                         UpdateDocTitle();
817
818                         UpdateResources();
819                 }
820
821                 // Set new temporary path
822                 theApp.SetupTempPath();
823
824                 // Set new filterpath
825                 String filterPath = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
826                 theApp.m_pGlobalFileFilter->SetUserFilterPath(filterPath);
827
828                 theApp.UpdateCodepageModule();
829
830                 strdiff::SetBreakChars(GetOptionsMgr()->GetString(OPT_BREAK_SEPARATORS).c_str());
831
832                 // make an attempt at rescanning any open diff sessions
833                 ApplyDiffOptions();
834
835                 // Update all dirdoc settings
836                 for (auto pDirDoc : GetAllDirDocs())
837                         pDirDoc->RefreshOptions();
838                 for (auto pOpenDoc : GetAllOpenDocs())
839                         pOpenDoc->RefreshOptions();
840                 for (auto pMergeDoc : GetAllHexMergeDocs())
841                         pMergeDoc->RefreshOptions();
842         }
843 }
844
845 static bool AddToRecentDocs(const PathContext& paths, const unsigned flags[], bool recurse, const String& filter)
846 {
847         String params, title;
848         for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
849         {
850                 if (flags && (flags[nIndex] & FFILEOPEN_READONLY))
851                 {
852                         switch (nIndex)
853                         {
854                         case 0: params += _T("/wl "); break;
855                         case 1: params += paths.GetSize() == 2 ? _T("/wr ") : _T("/wm "); break;
856                         case 2: params += _T("/wr "); break;
857                         }
858                 }
859                 params += _T("\"") + paths[nIndex] + _T("\" ");
860
861                 String path = paths[nIndex];
862                 paths::normalize(path);
863                 title += paths::FindFileName(path);
864                 if (nIndex < paths.GetSize() - 1)
865                         title += _T(" - ");
866         }
867         if (recurse)
868                 params += _T("/r ");
869         if (!filter.empty())
870                 params += _T("/f \"") + filter + _T("\" ");
871         return JumpList::AddToRecentDocs(_T(""), params, title, params, 0);
872 }
873
874 /**
875  * @brief Begin a diff: open dirdoc if it is directories, else open a mergedoc for editing.
876  * @param [in] pszLeft Left-side path.
877  * @param [in] pszRight Right-side path.
878  * @param [in] dwLeftFlags Left-side flags.
879  * @param [in] dwRightFlags Right-side flags.
880  * @param [in] bRecurse Do we run recursive (folder) compare?
881  * @param [in] pDirDoc Dir compare document to use.
882  * @param [in] prediffer Prediffer plugin name.
883  * @return `true` if opening files and compare succeeded, `false` otherwise.
884  */
885 bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
886         const DWORD dwFlags[] /*=NULL*/, const String strDesc[] /*=NULL*/, const String& sReportFile /*=T("")*/, bool bRecurse /*= false*/, CDirDoc *pDirDoc/*=NULL*/,
887         String prediffer /*=_T("")*/, const PackingInfo *infoUnpacker/*=NULL*/)
888 {
889         if (pDirDoc && !pDirDoc->CloseMergeDocs())
890                 return FALSE;
891
892         FileTransform::g_bUnpackerMode = theApp.GetProfileInt(_T("Settings"), _T("UnpackerMode"), PLUGIN_MANUAL);
893         FileTransform::g_bPredifferMode = theApp.GetProfileInt(_T("Settings"), _T("PredifferMode"), PLUGIN_MANUAL);
894
895         Merge7zFormatMergePluginScope scope(infoUnpacker);
896
897         PathContext tFiles;
898         if (pFiles)
899                 tFiles = *pFiles;
900         bool bRO[3] = {0};
901         if (dwFlags)
902         {
903                 bRO[0] = (dwFlags[0] & FFILEOPEN_READONLY) != 0;
904                 bRO[1] = (dwFlags[1] & FFILEOPEN_READONLY) != 0;
905                 bRO[2] = (dwFlags[2] & FFILEOPEN_READONLY) != 0;
906         };
907
908         // pop up dialog unless arguments exist (and are compatible)
909         paths::PATH_EXISTENCE pathsType = paths::GetPairComparability(tFiles, IsArchiveFile);
910         if (pathsType == paths::DOES_NOT_EXIST)
911         {
912                 if (!m_pMenus[MENU_OPENVIEW])
913                         theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
914                 COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
915                 if (dwFlags)
916                 {
917                         pOpenDoc->m_dwFlags[0] = dwFlags[0];
918                         pOpenDoc->m_dwFlags[1] = dwFlags[1];
919                         pOpenDoc->m_dwFlags[2] = dwFlags[2];
920                 }
921                 pOpenDoc->m_files = tFiles;
922                 pOpenDoc->m_bRecurse = bRecurse;
923                 if (infoUnpacker)
924                         pOpenDoc->m_infoHandler = *infoUnpacker;
925                 CFrameWnd *pFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
926                 theApp.m_pOpenTemplate->InitialUpdateFrame(pFrame, pOpenDoc);
927                 return TRUE;
928         }
929         else
930         {
931                 // Add trailing '\' for directories if its missing
932                 if (pathsType == paths::IS_EXISTING_DIR)
933                 {
934                         if (!paths::EndsWithSlash(tFiles[0]) && !IsArchiveFile(tFiles[0]))
935                                 tFiles[0] = paths::AddTrailingSlash(tFiles[0]);
936                         if (!paths::EndsWithSlash(tFiles[1]) && !IsArchiveFile(tFiles[1]))
937                                 tFiles[1] = paths::AddTrailingSlash(tFiles[1]);
938                         if (tFiles.GetSize() == 3 && !paths::EndsWithSlash(tFiles[2]) && !IsArchiveFile(tFiles[1]))
939                                 tFiles[2] = paths::AddTrailingSlash(tFiles[2]);
940                 }
941
942                 //save the MRU left and right files.
943                 if (dwFlags)
944                 {
945                         if (!(dwFlags[0] & FFILEOPEN_NOMRU))
946                                 addToMru(tFiles[0].c_str(), _T("Files\\Left"));
947                         if (!(dwFlags[1] & FFILEOPEN_NOMRU))
948                                 addToMru(tFiles[1].c_str(), _T("Files\\Right"));
949                         if (tFiles.GetSize() == 3 && !(dwFlags[2] & FFILEOPEN_NOMRU))
950                                 addToMru(tFiles[2].c_str(), _T("Files\\Option"));
951                 }
952         }
953
954         CTempPathContext *pTempPathContext = NULL;
955         if (pathsType == paths::IS_EXISTING_DIR)
956         {
957                 DecompressResult res= DecompressArchive(m_hWnd, tFiles);
958                 if (res.pTempPathContext)
959                 {
960                         pathsType = res.pathsType;
961                         tFiles = res.files;
962                         pTempPathContext = res.pTempPathContext;
963                 }
964         }
965
966         // Determine if we want a new dirview open, now that we know if it was
967         // an archive. Don't open a new dirview if we are comparing files.
968         if (!pDirDoc)
969         {
970                 if (pathsType == paths::IS_EXISTING_DIR)
971                 {
972                         CDirDoc::m_nDirsTemp = tFiles.GetSize();
973                         if (!m_pMenus[MENU_DIRVIEW])
974                                 theApp.m_pDirTemplate->m_hMenuShared = NewDirViewMenu();
975                         pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->OpenDocumentFile(NULL));
976                 }
977                 else
978                 {
979                         pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
980                 }
981         }
982
983         // open the diff
984         if (pathsType == paths::IS_EXISTING_DIR)
985         {
986                 if (pDirDoc)
987                 {
988                         // Anything that can go wrong inside InitCompare() will yield an
989                         // exception. There is no point in checking return value.
990                         pDirDoc->InitCompare(tFiles, bRecurse, pTempPathContext);
991
992                         pDirDoc->SetReportFile(sReportFile);
993                         pDirDoc->SetDescriptions(strDesc);
994                         pDirDoc->SetTitle(NULL);
995                         for (int nIndex = 0; nIndex < tFiles.GetSize(); nIndex++)
996                                 pDirDoc->SetReadOnly(nIndex, bRO[nIndex]);
997
998                         pDirDoc->Rescan();
999                 }
1000         }
1001         else
1002         {               
1003                 FileLocation fileloc[3];
1004
1005                 for (int nPane = 0; nPane < tFiles.GetSize(); nPane++)
1006                         fileloc[nPane].setPath(tFiles[nPane]);
1007
1008                 if (!prediffer.empty())
1009                 {
1010                         String strBothFilenames = strutils::join(tFiles.begin(), tFiles.end(), _T("|"));
1011                         pDirDoc->GetPluginManager().SetPrediffer(strBothFilenames, prediffer);
1012                 }
1013
1014                 ShowAutoMergeDoc(pDirDoc, tFiles.GetSize(), fileloc, dwFlags, strDesc, sReportFile,
1015                                 infoUnpacker);
1016         }
1017
1018         if (pFiles && (!dwFlags || !(dwFlags[0] & FFILEOPEN_NOMRU)))
1019         {
1020                 String filter = GetOptionsMgr()->GetString(OPT_FILEFILTER_CURRENT);
1021                 AddToRecentDocs(*pFiles, (unsigned *)dwFlags, bRecurse, filter);
1022         }
1023
1024         return TRUE;
1025 }
1026
1027 void CMainFrame::UpdateFont(FRAMETYPE frame)
1028 {
1029         if (frame == FRAME_FOLDER)
1030         {
1031                 for (auto pDoc : GetAllDirDocs())
1032                 {
1033                         if (pDoc)
1034                         {
1035                                 CDirView *pView = pDoc->GetMainView();
1036                                 if (pView)
1037                                         pView->SetFont(m_lfDir);
1038                         }
1039                 }
1040         }
1041         else
1042         {
1043                 for (auto pDoc : GetAllMergeDocs())
1044                 {
1045                         CMergeDoc *pMergeDoc = dynamic_cast<CMergeDoc *>(pDoc);
1046                         if (pMergeDoc)
1047                                 for (auto& pView: pMergeDoc->GetViewList())
1048                                         pView->SetFont(m_lfDiff);
1049                 }
1050         }
1051 }
1052
1053 /**
1054  * @brief Select font for Merge/Dir view
1055  * 
1056  * Shows font selection dialog to user, sets current font and saves
1057  * selected font properties to registry. Selects fon type to active
1058  * view (Merge/Dir compare). If there is no open views, then font
1059  * is selected for Merge view (for example user may want to change to
1060  * unicode font before comparing files).
1061  */
1062 void CMainFrame::OnViewSelectfont() 
1063 {
1064         FRAMETYPE frame = GetFrameType(GetActiveFrame());
1065         CHOOSEFONT cf = { sizeof CHOOSEFONT };
1066         LOGFONT *lf = NULL;
1067         cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCREENFONTS;
1068         if (frame == FRAME_FILE)
1069                 cf.Flags |= CF_FIXEDPITCHONLY; // Only fixed-width fonts for merge view
1070
1071         // CF_FIXEDPITCHONLY = 0x00004000L
1072         // in case you are a developer and want to disable it to test with, eg, a Chinese capable font
1073         if (frame == FRAME_FOLDER)
1074                 lf = &m_lfDir;
1075         else
1076                 lf = &m_lfDiff;
1077
1078         cf.lpLogFont = lf;
1079
1080         if (ChooseFont(&cf))
1081         {
1082                 Options::Font::Save(GetOptionsMgr(), frame == FRAME_FOLDER ? OPT_FONT_DIRCMP : OPT_FONT_FILECMP, lf, true);
1083                 UpdateFont(frame);
1084         }
1085 }
1086
1087 /**
1088  * @brief Use default font for active view type
1089  *
1090  * Disable user-selected font for active view type (Merge/Dir compare).
1091  * If there is no open views, then Merge view font is changed.
1092  */
1093 void CMainFrame::OnViewUsedefaultfont() 
1094 {
1095         FRAMETYPE frame = GetFrameType(GetActiveFrame());
1096
1097         if (frame == FRAME_FOLDER)
1098         {
1099                 Options::Font::Reset(GetOptionsMgr(), OPT_FONT_DIRCMP);
1100                 m_lfDir = Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP);
1101                 Options::Font::Save(GetOptionsMgr(), OPT_FONT_DIRCMP, &m_lfDir, false);
1102         }
1103         else
1104         {
1105                 Options::Font::Reset(GetOptionsMgr(), OPT_FONT_FILECMP);
1106                 m_lfDiff = Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP);
1107                 Options::Font::Save(GetOptionsMgr(), OPT_FONT_FILECMP, &m_lfDiff, false);
1108         }
1109
1110         UpdateFont(frame);
1111 }
1112
1113 /**
1114  * @brief Update any resources necessary after a GUI language change
1115  */
1116 void CMainFrame::UpdateResources()
1117 {
1118         m_wndStatusBar.SetPaneText(0, theApp.LoadString(AFX_IDS_IDLEMESSAGE).c_str());
1119
1120         for (auto pDoc : GetAllDirDocs())
1121                 pDoc->UpdateResources();
1122         for (auto pDoc : GetAllMergeDocs())
1123                 pDoc->UpdateResources();
1124         for (auto pDoc : GetAllOpenDocs())
1125                 pDoc->UpdateResources();
1126 }
1127
1128 /**
1129  * @brief Open WinMerge help.
1130  *
1131  * If local HTMLhelp file is found, open it, otherwise open HTML page from web.
1132  */
1133 void CMainFrame::OnHelpContents()
1134 {
1135         theApp.ShowHelp();
1136 }
1137
1138 /**
1139  * @brief Handle translation of default messages on the status bar
1140  */
1141 void CMainFrame::GetMessageString(UINT nID, CString& rMessage) const
1142 {
1143         // load appropriate string
1144         const String s = theApp.LoadString(nID);
1145         if (s.length() > 0)
1146                 AfxExtractSubString(rMessage, s.c_str(), 0);
1147 }
1148
1149 void CMainFrame::ActivateFrame(int nCmdShow) 
1150 {
1151         if (!m_bFirstTime)
1152         {
1153                 CMDIFrameWnd::ActivateFrame(nCmdShow);
1154                 return;
1155         }
1156
1157         m_bFirstTime = false;
1158
1159         WINDOWPLACEMENT wp;
1160         wp.length = sizeof(WINDOWPLACEMENT);
1161         GetWindowPlacement(&wp);
1162         wp.rcNormalPosition.left=theApp.GetProfileInt(_T("Settings"), _T("MainLeft"),0);
1163         wp.rcNormalPosition.top=theApp.GetProfileInt(_T("Settings"), _T("MainTop"),0);
1164         wp.rcNormalPosition.right=theApp.GetProfileInt(_T("Settings"), _T("MainRight"),0);
1165         wp.rcNormalPosition.bottom=theApp.GetProfileInt(_T("Settings"), _T("MainBottom"),0);
1166         if (nCmdShow != SW_MINIMIZE && theApp.GetProfileInt(_T("Settings"), _T("MainMax"), FALSE))
1167                 wp.showCmd = SW_MAXIMIZE;
1168         else
1169                 wp.showCmd = nCmdShow;
1170
1171         CRect dsk_rc,rc(wp.rcNormalPosition);
1172
1173         dsk_rc.left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
1174         dsk_rc.top = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
1175         dsk_rc.right = dsk_rc.left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
1176         dsk_rc.bottom = dsk_rc.top + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
1177         if (rc.Width() != 0 && rc.Height() != 0)
1178         {
1179                 // Ensure top-left corner is on visible area,
1180                 // 20 points margin is added to prevent "lost" window
1181                 CPoint ptTopLeft(rc.TopLeft());
1182                 ptTopLeft += CPoint(20, 20);
1183
1184                 if (dsk_rc.PtInRect(ptTopLeft))
1185                         SetWindowPlacement(&wp);
1186                 else
1187                         CMDIFrameWnd::ActivateFrame(nCmdShow);
1188         }
1189         else
1190                 CMDIFrameWnd::ActivateFrame(nCmdShow);
1191 }
1192
1193 /**
1194  * @brief Called when mainframe is about to be closed.
1195  * This function is called when mainframe is to be closed (not for
1196  * file/compare windows.
1197  */
1198 void CMainFrame::OnClose()
1199 {
1200         if (theApp.GetActiveOperations())
1201                 return;
1202
1203         // Check if there are multiple windows open and ask for closing them
1204         bool bAskClosing = GetOptionsMgr()->GetBool(OPT_ASK_MULTIWINDOW_CLOSE);
1205         if (bAskClosing)
1206         {
1207                 bool quit = AskCloseConfirmation();
1208                 if (!quit)
1209                         return;
1210         }
1211
1212         // Save last selected filter
1213         String filter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1214         GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
1215
1216         // save main window position
1217         WINDOWPLACEMENT wp;
1218         wp.length = sizeof(WINDOWPLACEMENT);
1219         GetWindowPlacement(&wp);
1220         theApp.WriteProfileInt(_T("Settings"), _T("MainLeft"),wp.rcNormalPosition.left);
1221         theApp.WriteProfileInt(_T("Settings"), _T("MainTop"),wp.rcNormalPosition.top);
1222         theApp.WriteProfileInt(_T("Settings"), _T("MainRight"),wp.rcNormalPosition.right);
1223         theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom);
1224         theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE));
1225
1226         // Close Non-Document/View frame with confirmation
1227         CMDIChildWnd *pChild = static_cast<CMDIChildWnd *>(CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD));
1228         while (pChild)
1229         {
1230                 CMDIChildWnd *pNextChild = static_cast<CMDIChildWnd *>(pChild->GetWindow(GW_HWNDNEXT));
1231                 if (GetFrameType(pChild) == FRAME_IMGFILE)
1232                 {
1233                         if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
1234                                 return;
1235                 }
1236                 pChild = pNextChild;
1237         }
1238
1239         CMDIFrameWnd::OnClose();
1240 }
1241
1242 /**
1243  * @brief Utility function to update CSuperComboBox format MRU
1244  */
1245 void CMainFrame::addToMru(LPCTSTR szItem, LPCTSTR szRegSubKey, UINT nMaxItems)
1246 {
1247         std::vector<CString> list;
1248         CString s;
1249         UINT cnt = AfxGetApp()->GetProfileInt(szRegSubKey, _T("Count"), 0);
1250         list.push_back(szItem);
1251         for (UINT i=0 ; i<cnt; ++i)
1252         {
1253                 s = AfxGetApp()->GetProfileString(szRegSubKey, strutils::format(_T("Item_%d"), i).c_str());
1254                 if (s != szItem)
1255                         list.push_back(s);
1256         }
1257         cnt = list.size() > nMaxItems ? nMaxItems : static_cast<UINT>(list.size());
1258         for (UINT i=0 ; i<cnt; ++i)
1259                 AfxGetApp()->WriteProfileString(szRegSubKey, strutils::format(_T("Item_%d"), i).c_str(), list[i]);
1260         // update count
1261         AfxGetApp()->WriteProfileInt(szRegSubKey, _T("Count"), cnt);
1262 }
1263
1264 void CMainFrame::ApplyDiffOptions() 
1265 {
1266         for (auto pMergeDoc : GetAllMergeDocs())
1267         {
1268                 // Re-read MergeDoc settings (also updates view settings)
1269                 // and rescan using new options
1270                 pMergeDoc->RefreshOptions();
1271                 pMergeDoc->FlushAndRescan(TRUE);
1272         }
1273 }
1274
1275 /// Get list of OpenDocs (documents underlying edit sessions)
1276 OpenDocList &CMainFrame::GetAllOpenDocs()
1277 {
1278         return static_cast<OpenDocList &>(GetDocList(theApp.m_pOpenTemplate));
1279 }
1280
1281 /// Get list of MergeDocs (documents underlying edit sessions)
1282 MergeDocList &CMainFrame::GetAllMergeDocs()
1283 {
1284         return static_cast<MergeDocList &>(GetDocList(theApp.m_pDiffTemplate));
1285 }
1286
1287 /// Get list of DirDocs (documents underlying a scan)
1288 DirDocList &CMainFrame::GetAllDirDocs()
1289 {
1290         return static_cast<DirDocList &>(GetDocList(theApp.m_pDirTemplate));
1291 }
1292
1293 /// Get list of HexMergeDocs (documents underlying edit sessions)
1294 HexMergeDocList &CMainFrame::GetAllHexMergeDocs()
1295 {
1296         return static_cast<HexMergeDocList &>(GetDocList(theApp.m_pHexMergeTemplate));
1297 }
1298
1299 /**
1300  * @brief Obtain a merge doc to display a difference in files.
1301  * @return Pointer to CMergeDoc to use. 
1302  */
1303 template<class DocClass>
1304 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles)
1305 {
1306         // Create a new merge doc
1307         DocClass::m_nBuffersTemp = nFiles;
1308         DocClass *pMergeDoc = static_cast<DocClass*>(pTemplate->OpenDocumentFile(NULL));
1309         if (pMergeDoc)
1310         {
1311                 pDirDoc->AddMergeDoc(pMergeDoc);
1312                 pMergeDoc->SetDirDoc(pDirDoc);
1313         }
1314         return pMergeDoc;
1315 }
1316
1317 // Clear the item count in the main status pane
1318 void CMainFrame::ClearStatusbarItemCount()
1319 {
1320         m_wndStatusBar.SetPaneText(2, _T(""));
1321 }
1322
1323 /**
1324  * @brief Generate patch from files selected.
1325  *
1326  * Creates a patch from selected files in active directory compare, or
1327  * active file compare. Files in file compare must be saved before
1328  * creating a patch.
1329  */
1330 void CMainFrame::OnToolsGeneratePatch()
1331 {
1332         CPatchTool patcher;
1333         patcher.CreatePatch();
1334 }
1335
1336 void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
1337 {
1338         PathContext tFiles(dropped_files);
1339         const size_t fileCount = tFiles.GetSize();
1340
1341         // If Ctrl pressed, do recursive compare
1342         bool recurse = !!::GetAsyncKeyState(VK_CONTROL) || GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
1343
1344         // If user has <Shift> pressed with one file selected,
1345         // assume it is an archive and set filenames to same
1346         if (::GetAsyncKeyState(VK_SHIFT) < 0 && fileCount == 1)
1347         {
1348                 tFiles.SetRight(tFiles[0]);
1349         }
1350
1351         // Check if they dropped a project file
1352         DWORD dwFlags[3] = {FFILEOPEN_NONE, FFILEOPEN_NONE, FFILEOPEN_NONE};
1353         if (fileCount == 1)
1354         {
1355                 if (theApp.IsProjectFile(tFiles[0]))
1356                 {
1357                         theApp.LoadAndOpenProjectFile(tFiles[0]);
1358                         return;
1359                 }
1360                 if (IsConflictFile(tFiles[0]))
1361                 {
1362                         DoOpenConflict(tFiles[0], nullptr, true);
1363                         return;
1364                 }
1365         }
1366
1367         DoFileOpen(&tFiles, dwFlags, NULL, _T(""), recurse);
1368 }
1369
1370 void CMainFrame::OnPluginUnpackMode(UINT nID )
1371 {
1372         switch (nID)
1373         {
1374         case ID_UNPACK_MANUAL:
1375                 FileTransform::g_bUnpackerMode = PLUGIN_MANUAL;
1376                 break;
1377         case ID_UNPACK_AUTO:
1378                 FileTransform::g_bUnpackerMode = PLUGIN_AUTO;
1379                 break;
1380         }
1381         theApp.WriteProfileInt(_T("Settings"), _T("UnpackerMode"), FileTransform::g_bUnpackerMode);
1382 }
1383
1384 void CMainFrame::OnUpdatePluginUnpackMode(CCmdUI* pCmdUI) 
1385 {
1386         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1387
1388         if (pCmdUI->m_nID == ID_UNPACK_MANUAL)
1389                 pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bUnpackerMode);
1390         if (pCmdUI->m_nID == ID_UNPACK_AUTO)
1391                 pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bUnpackerMode);
1392 }
1393 void CMainFrame::OnPluginPrediffMode(UINT nID )
1394 {
1395         switch (nID)
1396         {
1397         case ID_PREDIFFER_MANUAL:
1398                 FileTransform::g_bPredifferMode = PLUGIN_MANUAL;
1399                 break;
1400         case ID_PREDIFFER_AUTO:
1401                 FileTransform::g_bPredifferMode = PLUGIN_AUTO;
1402                 break;
1403         }
1404         PrediffingInfo infoPrediffer;
1405         for (auto pMergeDoc : GetAllMergeDocs())
1406                 pMergeDoc->SetPrediffer(&infoPrediffer);
1407         for (auto pDirDoc : GetAllDirDocs())
1408                 pDirDoc->GetPluginManager().SetPrediffSettingAll(FileTransform::g_bPredifferMode);
1409         theApp.WriteProfileInt(_T("Settings"), _T("PredifferMode"), FileTransform::g_bPredifferMode);
1410 }
1411
1412 void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI) 
1413 {
1414         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1415
1416         if (pCmdUI->m_nID == ID_PREDIFFER_MANUAL)
1417                 pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bPredifferMode);
1418         if (pCmdUI->m_nID == ID_PREDIFFER_AUTO)
1419                 pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bPredifferMode);
1420 }
1421 /**
1422  * @brief Called when "Reload Plugins" item is updated
1423  */
1424 void CMainFrame::OnUpdateReloadPlugins(CCmdUI* pCmdUI)
1425 {
1426         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1427 }
1428
1429 void CMainFrame::OnReloadPlugins()
1430 {
1431         // delete all script interfaces
1432         // (interfaces will be created again automatically when WinMerge needs them)
1433         CAllThreadsScripts::GetActiveSet()->FreeAllScripts();
1434
1435         // update the editor scripts submenu
1436         HMENU scriptsSubmenu = GetScriptsSubmenu(m_hMenuDefault);
1437         if (scriptsSubmenu != NULL)
1438                 CMergeEditView::createScriptsSubmenu(scriptsSubmenu);
1439         UpdatePrediffersMenu();
1440 }
1441
1442 /** @brief Return active merge edit view, if can figure it out/is available */
1443 CMergeEditView * CMainFrame::GetActiveMergeEditView()
1444 {
1445         // NB: GetActiveDocument does not return the Merge Doc 
1446         //     even when the merge edit view is in front
1447         // NB: CChildFrame::GetActiveView returns NULL when location view active
1448         // So we have this rather complicated logic to try to get a merge edit view
1449         // We look at the front child window, which should be a frame
1450         // and we can get a MergeEditView from it, if it is a CChildFrame
1451         // (DirViews use a different frame type)
1452         CChildFrame * pFrame = dynamic_cast<CChildFrame *>(GetActiveFrame());
1453         if (!pFrame) return 0;
1454         // Try to get the active MergeEditView (ie, left or right)
1455         if (pFrame->GetActiveView() && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
1456         {
1457                 return dynamic_cast<CMergeEditView *>(pFrame->GetActiveView());
1458         }
1459         return pFrame->GetMergeDoc()->GetActiveMergeView();
1460 }
1461
1462 void CMainFrame::UpdatePrediffersMenu()
1463 {
1464         CMenu* menu = GetMenu();
1465         if (menu == NULL)
1466         {
1467                 return;
1468         }
1469
1470         HMENU hMainMenu = menu->m_hMenu;
1471         HMENU prediffersSubmenu = GetPrediffersSubmenu(hMainMenu);
1472         if (prediffersSubmenu != NULL)
1473         {
1474                 CMergeEditView * pEditView = GetActiveMergeEditView();
1475                 if (pEditView)
1476                         pEditView->createPrediffersSubmenu(prediffersSubmenu);
1477                 else
1478                 {
1479                         // no view or dir view : display an empty submenu
1480                         int i = GetMenuItemCount(prediffersSubmenu);
1481                         while (i --)
1482                                 ::DeleteMenu(prediffersSubmenu, 0, MF_BYPOSITION);
1483                         ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, NULL);
1484                 }
1485         }
1486 }
1487
1488 /**
1489  * @brief Save WinMerge configuration and info to file
1490  */
1491 void CMainFrame::OnSaveConfigData()
1492 {
1493         CConfigLog configLog;
1494         String sError;
1495
1496         if (configLog.WriteLogFile(sError))
1497         {
1498                 String sFileName = configLog.GetFileName();
1499                 theApp.OpenFileToExternalEditor(sFileName);
1500         }
1501         else
1502         {
1503                 String sFileName = configLog.GetFileName();
1504                 String msg = strutils::format_string2(_("Cannot open file\n%1\n\n%2"), sFileName, sError);
1505                 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1506         }
1507 }
1508
1509 /**
1510  * @brief Open two new empty docs, 'Scratchpads'
1511  * 
1512  * Allows user to open two empty docs, to paste text to
1513  * compare from clipboard.
1514  * @note File filenames are set emptys and filedescriptors
1515  * are loaded from resource.
1516  * @sa CMergeDoc::OpenDocs()
1517  * @sa CMergeDoc::TrySaveAs()
1518  */
1519 void CMainFrame::FileNew(int nPanes) 
1520 {
1521         CDirDoc *pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
1522         
1523         // Load emptyfile descriptors and open empty docs
1524         // Use default codepage
1525         DWORD dwFlags[3] = {0, 0, 0};
1526         String strDesc[3];
1527         FileLocation fileloc[3];
1528         if (nPanes == 2)
1529         {
1530                 strDesc[0] = _("Untitled left");
1531                 strDesc[1] = _("Untitled right");
1532                 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1533                 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1534                 ShowMergeDoc(pDirDoc, 2, fileloc, dwFlags, strDesc);
1535         }
1536         else
1537         {
1538                 strDesc[0] = _("Untitled left");
1539                 strDesc[1] = _("Untitled middle");
1540                 strDesc[2] = _("Untitled right");
1541                 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1542                 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1543                 fileloc[2].encoding.SetCodepage(ucr::getDefaultCodepage());
1544                 ShowMergeDoc(pDirDoc, 3, fileloc, dwFlags, strDesc);
1545         }
1546 }
1547
1548 /**
1549  * @brief Open two new empty docs, 'Scratchpads'
1550  * 
1551  * Allows user to open two empty docs, to paste text to
1552  * compare from clipboard.
1553  * @note File filenames are set emptys and filedescriptors
1554  * are loaded from resource.
1555  * @sa CMergeDoc::OpenDocs()
1556  * @sa CMergeDoc::TrySaveAs()
1557  */
1558 void CMainFrame::OnFileNew() 
1559 {
1560         FileNew(2);
1561 }
1562
1563 void CMainFrame::OnFileNew3() 
1564 {
1565         FileNew(3);
1566 }
1567
1568 /**
1569  * @brief Open Filters dialog
1570  */
1571 void CMainFrame::OnToolsFilters()
1572 {
1573         String title = _("Filters");
1574         CPropertySheet sht(title.c_str());
1575         LineFiltersDlg lineFiltersDlg;
1576         FileFiltersDlg fileFiltersDlg;
1577         vector<FileFilterInfo> fileFilters;
1578         std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
1579         String selectedFilter;
1580         const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1581         sht.AddPage(&fileFiltersDlg);
1582         sht.AddPage(&lineFiltersDlg);
1583         sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
1584
1585         // Make sure all filters are up-to-date
1586         theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
1587
1588         theApp.m_pGlobalFileFilter->GetFileFilters(&fileFilters, selectedFilter);
1589         fileFiltersDlg.SetFilterArray(&fileFilters);
1590         fileFiltersDlg.SetSelected(selectedFilter);
1591         const bool lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
1592         lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig;
1593
1594         lineFilters->CloneFrom(theApp.m_pLineFilters.get());
1595         lineFiltersDlg.SetList(lineFilters.get());
1596
1597         if (sht.DoModal() == IDOK)
1598         {
1599                 String strNone = _("<None>");
1600                 String path = fileFiltersDlg.GetSelected();
1601                 if (path.find(strNone) != String::npos)
1602                 {
1603                         // Don't overwrite mask we already have
1604                         if (!theApp.m_pGlobalFileFilter->IsUsingMask())
1605                         {
1606                                 String sFilter(_T("*.*"));
1607                                 theApp.m_pGlobalFileFilter->SetFilter(sFilter);
1608                                 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1609                         }
1610                 }
1611                 else
1612                 {
1613                         theApp.m_pGlobalFileFilter->SetFileFilterPath(path);
1614                         theApp.m_pGlobalFileFilter->UseMask(false);
1615                         String sFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1616                         GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1617                 }
1618                 bool linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp;
1619                 GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled);
1620
1621                 // Check if compare documents need rescanning
1622                 bool bFileCompareRescan = false;
1623                 bool bFolderCompareRescan = false;
1624                 CFrameWnd * pFrame = GetActiveFrame();
1625                 FRAMETYPE frame = GetFrameType(pFrame);
1626                 if (frame == FRAME_FILE)
1627                 {
1628                         if (lineFiltersEnabledOrig != linefiltersEnabled ||
1629                                         !theApp.m_pLineFilters->Compare(lineFilters.get()))
1630                         {
1631                                 bFileCompareRescan = true;
1632                         }
1633                 }
1634                 else if (frame == FRAME_FOLDER)
1635                 {
1636                         const String newFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1637                         if (lineFiltersEnabledOrig != linefiltersEnabled || 
1638                                         !theApp.m_pLineFilters->Compare(lineFilters.get()) || origFilter != newFilter)
1639                         {
1640                                 int res = LangMessageBox(IDS_FILTERCHANGED, MB_ICONWARNING | MB_YESNO);
1641                                 if (res == IDYES)
1642                                         bFolderCompareRescan = true;
1643                         }
1644                 }
1645
1646                 // Save new filters before (possibly) rescanning
1647                 theApp.m_pLineFilters->CloneFrom(lineFilters.get());
1648                 theApp.m_pLineFilters->SaveFilters();
1649
1650                 if (bFileCompareRescan)
1651                 {
1652                         for (auto pMergeDoc : GetAllMergeDocs())
1653                                 pMergeDoc->FlushAndRescan(TRUE);
1654                 }
1655                 else if (bFolderCompareRescan)
1656                 {
1657                         for (auto pDirDoc : GetAllDirDocs())
1658                                 pDirDoc->Rescan();
1659                 }
1660         }
1661 }
1662
1663 /**
1664  * @brief Open Filters dialog.
1665  */
1666 void CMainFrame::SelectFilter()
1667 {
1668         OnToolsFilters();
1669 }
1670
1671 /**
1672  * @brief Closes application with ESC.
1673  *
1674  * Application is closed if:
1675  * - 'Close Windows with ESC' option is enabled and
1676  *    there is no open document windows
1677  * - '-e' commandline switch is given
1678  */
1679 BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
1680 {
1681         // Check if we got 'ESC pressed' -message
1682         if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
1683         {
1684                 if (theApp.m_bEscShutdown && m_wndTabBar.GetItemCount() <= 1)
1685                 {
1686                         AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1687                         return TRUE;
1688                 }
1689                 else if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC) && m_wndTabBar.GetItemCount() == 0)
1690                 {
1691                         AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1692                         return FALSE;
1693                 }
1694         }
1695         return CMDIFrameWnd::PreTranslateMessage(pMsg);
1696 }
1697
1698 /**
1699  * @brief Show/hide statusbar.
1700  */
1701 void CMainFrame::OnViewStatusBar()
1702 {
1703         bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR);
1704         GetOptionsMgr()->SaveOption(OPT_SHOW_STATUSBAR, bShow);
1705
1706         CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, bShow, 0);
1707 }
1708
1709 /**
1710  * @brief Updates "Show Tabbar" menuitem.
1711  */
1712 void CMainFrame::OnUpdateViewTabBar(CCmdUI* pCmdUI) 
1713 {
1714         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR));
1715 }
1716
1717 /**
1718  * @brief Show/hide tabbar.
1719  */
1720 void CMainFrame::OnViewTabBar()
1721 {
1722         bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR);
1723         GetOptionsMgr()->SaveOption(OPT_SHOW_TABBAR, bShow);
1724
1725         CMDIFrameWnd::ShowControlBar(&m_wndTabBar, bShow, 0);
1726 }
1727
1728 /**
1729  * @brief Updates "Automatically Resize Panes" menuitem.
1730  */
1731 void CMainFrame::OnUpdateResizePanes(CCmdUI* pCmdUI)
1732 {
1733         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
1734 }
1735
1736
1737 /**
1738  * @brief Enable/disable automatic pane resizing.
1739  */
1740 void CMainFrame::OnResizePanes()
1741 {
1742         bool bResize = !GetOptionsMgr()->GetBool(OPT_RESIZE_PANES);
1743         GetOptionsMgr()->SaveOption(OPT_RESIZE_PANES, bResize);
1744         // TODO: Introduce a common merge frame superclass?
1745         CFrameWnd *pActiveFrame = GetActiveFrame();
1746         if (CChildFrame *pFrame = DYNAMIC_DOWNCAST(CChildFrame, pActiveFrame))
1747         {
1748                 pFrame->UpdateAutoPaneResize();
1749                 if (bResize)
1750                         pFrame->UpdateSplitter();
1751         }
1752         else if (CHexMergeFrame *pFrame1 = DYNAMIC_DOWNCAST(CHexMergeFrame, pActiveFrame))
1753         {
1754                 pFrame1->UpdateAutoPaneResize();
1755                 if (bResize)
1756                         pFrame1->UpdateSplitter();
1757         }
1758 }
1759
1760 /**
1761  * @brief Open project-file.
1762  */
1763 void CMainFrame::OnFileOpenProject()
1764 {
1765         String sFilepath;
1766         
1767         // get the default projects path
1768         String strProjectPath = GetOptionsMgr()->GetString(OPT_PROJECTS_PATH);
1769         if (!SelectFile(GetSafeHwnd(), sFilepath, true, strProjectPath.c_str(), _T(""),
1770                         _("WinMerge Project Files (*.WinMerge)|*.WinMerge||")))
1771                 return;
1772         
1773         strProjectPath = paths::GetParentPath(sFilepath);
1774         // store this as the new project path
1775         GetOptionsMgr()->SaveOption(OPT_PROJECTS_PATH, strProjectPath);
1776
1777         theApp.LoadAndOpenProjectFile(sFilepath.c_str());
1778 }
1779
1780 /**
1781  * @brief Receive command line from another instance.
1782  *
1783  * This function receives command line when only single-instance
1784  * is allowed. New instance tried to start sends its command line
1785  * to here so we can open paths it was meant to.
1786  */
1787 LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
1788 {
1789         COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
1790         LPCTSTR pchData = (LPCTSTR)pCopyData->lpData;
1791         // Bail out if data isn't zero-terminated
1792         DWORD cchData = pCopyData->cbData / sizeof(TCHAR);
1793         if (cchData == 0 || pchData[cchData - 1] != _T('\0'))
1794                 return FALSE;
1795         ReplyMessage(TRUE);
1796         MergeCmdLineInfo cmdInfo(pchData);
1797         theApp.ApplyCommandLineConfigOptions(cmdInfo);
1798         theApp.ParseArgsAndDoOpen(cmdInfo, this);
1799         return TRUE;
1800 }
1801
1802 LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
1803 {
1804         CFrameWnd * pFrame = GetActiveFrame();
1805         if (pFrame)
1806         {
1807                 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
1808                 if (!pMergeDoc)
1809                         pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
1810                 if (pMergeDoc)
1811                         pMergeDoc->CheckFileChanged();
1812         }
1813         return 0;
1814 }
1815
1816 /**
1817  * @brief Close all open windows.
1818  * 
1819  * Asks about saving unsaved files and then closes all open windows.
1820  */
1821 void CMainFrame::OnWindowCloseAll()
1822 {
1823         CMDIChildWnd *pChild = MDIGetActive();
1824         while (pChild)
1825         {
1826                 CDocument* pDoc;
1827                 if ((pDoc = pChild->GetActiveDocument()) != NULL)
1828                 {
1829                         if (!pDoc->SaveModified())
1830                                 return;
1831                         pDoc->OnCloseDocument();
1832                 }
1833                 else if (GetFrameType(pChild) == FRAME_IMGFILE)
1834                 {
1835                         if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
1836                                 return;
1837                 }
1838                 else
1839                 {
1840                         pChild->DestroyWindow();
1841                 }
1842                 pChild = MDIGetActive();
1843         }
1844         return;
1845 }
1846
1847 /**
1848  * @brief Enables Window/Close All item if there are open windows.
1849  */ 
1850 void CMainFrame::OnUpdateWindowCloseAll(CCmdUI* pCmdUI)
1851 {
1852         const MergeDocList &mergedocs = GetAllMergeDocs();
1853         if (!mergedocs.IsEmpty())
1854         {
1855                 pCmdUI->Enable(TRUE);
1856                 return;
1857         }
1858
1859         const DirDocList &dirdocs = GetAllDirDocs();
1860         pCmdUI->Enable(!dirdocs.IsEmpty());
1861 }
1862
1863 /**
1864  * @brief Access to the singleton main frame (where we have some globals)
1865  */
1866 CMainFrame * GetMainFrame()
1867 {
1868         CWnd * mainwnd = AfxGetMainWnd();
1869         ASSERT(mainwnd != nullptr);
1870         CMainFrame *pMainframe = dynamic_cast<CMainFrame*>(mainwnd);
1871         ASSERT(pMainframe != nullptr);
1872         return pMainframe;
1873 }
1874
1875 /**
1876  * @brief Opens dialog for user to Load, edit and save project files.
1877  * This dialog gets current compare paths and filter (+other properties
1878  * possible in project files) and initializes the dialog with them.
1879  */
1880 void CMainFrame::OnSaveProject()
1881 {
1882         if (!m_pMenus[MENU_OPENVIEW])
1883                 theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
1884         COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
1885
1886         PathContext paths;
1887         CFrameWnd * pFrame = GetActiveFrame();
1888         FRAMETYPE frame = GetFrameType(pFrame);
1889
1890         if (frame == FRAME_FILE)
1891         {
1892                 CMergeDoc * pMergeDoc = static_cast<CMergeDoc *>(pFrame->GetActiveDocument());
1893                 pOpenDoc->m_files = pMergeDoc->m_filePaths;
1894                 for (size_t pane = 0; pane < pOpenDoc->m_files.size(); ++pane)
1895                         pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pMergeDoc->m_ptBuf[pane]->GetReadOnly() ? FFILEOPEN_PROJECT : 0);
1896                 pOpenDoc->m_bRecurse = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
1897                 pOpenDoc->m_strExt = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1898         }
1899         else if (frame == FRAME_FOLDER)
1900         {
1901                 // Get paths currently in compare
1902                 const CDirDoc * pDoc = static_cast<const CDirDoc*>(pFrame->GetActiveDocument());
1903                 const CDiffContext& ctxt = pDoc->GetDiffContext();
1904
1905                 // Set-up the dialog
1906                 for (int pane = 0; pane < ctxt.GetCompareDirs(); ++pane)
1907                 {
1908                         pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pDoc->GetReadOnly(pane) ? FFILEOPEN_READONLY : 0);
1909                         pOpenDoc->m_files.SetPath(pane, paths::AddTrailingSlash(ctxt.GetNormalizedPath(pane)));
1910                 }
1911                 pOpenDoc->m_bRecurse = ctxt.m_bRecursive;
1912                 pOpenDoc->m_strExt = static_cast<FileFilterHelper *>(ctxt.m_piFilterGlobal)->GetFilterNameOrMask();
1913         }
1914
1915         CFrameWnd *pOpenFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
1916         theApp.m_pOpenTemplate->InitialUpdateFrame(pOpenFrame, pOpenDoc);
1917 }
1918
1919 /** 
1920  * @brief Start flashing window if window is inactive.
1921  */
1922 void CMainFrame::StartFlashing()
1923 {
1924         CWnd * activeWindow = GetActiveWindow();
1925         if (activeWindow != this)
1926                 FlashWindowEx(FLASHW_ALL | FLASHW_TIMERNOFG, 0, 0);
1927 }
1928
1929 #if _MFC_VER > 0x0600
1930 void CMainFrame::OnActivateApp(BOOL bActive, DWORD dwThreadID)
1931 #else
1932 void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
1933 #endif
1934 {
1935 #if _MFC_VER > 0x0600
1936         CMDIFrameWnd::OnActivateApp(bActive, dwThreadID);
1937 #else
1938         CMDIFrameWnd::OnActivateApp(bActive, hTask);
1939 #endif
1940
1941         CFrameWnd * pFrame = GetActiveFrame();
1942         if (pFrame)
1943         {
1944                 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
1945                 if (!pMergeDoc)
1946                         pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
1947                 if (pMergeDoc)
1948                         PostMessage(WM_USER+1);
1949         }
1950 }
1951
1952 BOOL CMainFrame::CreateToolbar()
1953 {
1954         if (!m_wndToolBar.CreateEx(this) ||
1955                 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
1956         {
1957                 return FALSE;
1958         }
1959
1960         if (!m_wndReBar.Create(this))
1961         {
1962                 return FALSE;
1963         }
1964
1965         VERIFY(m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT));
1966
1967         // Remove this if you don't want tool tips or a resizable toolbar
1968         m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
1969                 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
1970         m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
1971
1972         m_wndReBar.AddBar(&m_wndToolBar);
1973
1974         LoadToolbarImages();
1975
1976         UINT nID, nStyle;
1977         int iImage;
1978         int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(ID_OPTIONS);
1979         m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
1980         nStyle |= TBSTYLE_DROPDOWN;
1981         m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
1982
1983         if (!GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR))
1984         {
1985                 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
1986         }
1987
1988         return TRUE;
1989 }
1990
1991 /** @brief Load toolbar images from the resource. */
1992 void CMainFrame::LoadToolbarImages()
1993 {
1994         const int toolbarSize = 16 << GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
1995         CToolBarCtrl& BarCtrl = m_wndToolBar.GetToolBarCtrl();
1996
1997         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].DeleteImageList();
1998         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].DeleteImageList();
1999         CSize sizeButton(0, 0);
2000
2001         LoadToolbarImageList(toolbarSize,
2002                 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
2003                 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
2004                 false, m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2005         LoadToolbarImageList(toolbarSize,
2006                 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
2007                 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
2008                 true, m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2009         sizeButton = CSize(toolbarSize + 8, toolbarSize + 8);
2010
2011         BarCtrl.SetButtonSize(sizeButton);
2012         BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2013         BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2014         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
2015         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
2016
2017         // resize the rebar.
2018         REBARBANDINFO rbbi = { sizeof REBARBANDINFO };
2019         rbbi.fMask = RBBIM_CHILDSIZE;
2020         rbbi.cyMinChild = sizeButton.cy;
2021         m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);
2022 }
2023
2024
2025 /**
2026  * @brief Load a transparent 24-bit color image list.
2027  */
2028 static void LoadHiColImageList(UINT nIDResource, UINT nIDResourceMask, int nWidth, int nHeight, int nCount, bool bGrayscale, CImageList& ImgList)
2029 {
2030         CBitmap bm, bmMask;
2031         bm.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_DEFAULTCOLOR));
2032         bmMask.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResourceMask), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_MONOCHROME));
2033         if (bGrayscale)
2034                 GrayScale(&bm);
2035         VERIFY(ImgList.Create(nWidth, nHeight, ILC_COLORDDB|ILC_MASK, nCount, 0));
2036         int nIndex = ImgList.Add(&bm, &bmMask);
2037         ASSERT(-1 != nIndex);
2038 }
2039
2040 /**
2041  * @brief Load toolbar image list.
2042  */
2043 static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale,
2044                 CImageList& ImgList)
2045 {
2046         const int ImageCount = 22;
2047         const int imageHeight = imageWidth - 1;
2048         LoadHiColImageList(nIDResource, nIDResourceMask, imageWidth, imageHeight, ImageCount, bGrayscale, ImgList);
2049 }
2050
2051 /**
2052  * @brief Called when the document title is modified.
2053  */
2054 void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
2055 {
2056         CFrameWnd::OnUpdateFrameTitle(bAddToTitle);
2057         
2058         if (m_wndTabBar.m_hWnd)
2059                 m_wndTabBar.UpdateTabs();
2060 }
2061
2062 /** @brief Show none/small/big/huge toolbar. */
2063 void CMainFrame::OnToolbarSize(UINT id)
2064 {
2065         if (id == ID_TOOLBAR_NONE)
2066         {
2067                 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, false);
2068                 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
2069         }
2070         else
2071         {
2072                 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2073                 GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, id - ID_TOOLBAR_SMALL);
2074
2075                 LoadToolbarImages();
2076
2077                 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, true, 0);
2078         }
2079 }
2080
2081 /** @brief Show none/small/big/huge toolbar. */
2082 void CMainFrame::OnUpdateToolbarSize(CCmdUI *pCmdUI)
2083 {
2084         if (!GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR))
2085                 pCmdUI->SetRadio(pCmdUI->m_nID == ID_TOOLBAR_NONE);
2086         else
2087                 pCmdUI->SetRadio((pCmdUI->m_nID - ID_TOOLBAR_SMALL) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE)));
2088 }
2089
2090 /** @brief Lang aware version of CFrameWnd::OnToolTipText() */
2091 BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
2092 {
2093         ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
2094
2095         // need to handle both ANSI and UNICODE versions of the message
2096         TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2097         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2098         String strFullText;
2099         CString strTipText;
2100         UINT_PTR nID = pNMHDR->idFrom;
2101         if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
2102                 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2103         {
2104                 // idFrom is actually the HWND of the tool
2105                 nID = ::GetDlgCtrlID((HWND)nID);
2106         }
2107
2108         if (nID != 0) // will be zero on a separator
2109         {
2110                 strFullText = theApp.LoadString(static_cast<UINT>(nID));
2111                 // don't handle the message if no string resource found
2112                 if (strFullText.empty())
2113                         return FALSE;
2114
2115                 // this is the command id, not the button index
2116                 AfxExtractSubString(strTipText, strFullText.c_str(), 1, '\n');
2117         }
2118 #ifndef _UNICODE
2119         if (pNMHDR->code == TTN_NEEDTEXTA)
2120                 lstrcpyn(pTTTA->szText, strTipText, countof(pTTTA->szText));
2121         else
2122                 _mbstowcsz(pTTTW->szText, strTipText, countof(pTTTW->szText));
2123 #else
2124         if (pNMHDR->code == TTN_NEEDTEXTA)
2125                 _wcstombsz(pTTTA->szText, strTipText, countof(pTTTA->szText));
2126         else
2127                 lstrcpyn(pTTTW->szText, strTipText, countof(pTTTW->szText));
2128 #endif
2129         *pResult = 0;
2130
2131         // bring the tooltip window above other popup windows
2132         ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
2133                 SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
2134
2135         return TRUE;    // message was handled
2136 }
2137
2138 /**
2139  * @brief Ask user for close confirmation when closing the mainframe.
2140  * This function asks if user wants to close multiple open windows when user
2141  * selects (perhaps accidentally) to close WinMerge (application).
2142  * @return true if user agreeds to close all windows.
2143  */
2144 bool CMainFrame::AskCloseConfirmation()
2145 {
2146         const DirDocList &dirdocs = GetAllDirDocs();
2147         const MergeDocList &mergedocs = GetAllMergeDocs();
2148
2149         int ret = IDYES;
2150         const size_t count = dirdocs.GetCount() + mergedocs.GetCount();
2151         if (count > 1)
2152         {
2153                 // Check that we don't have one empty dirdoc + mergedoc situation.
2154                 // That happens since we open "hidden" dirdoc for every file compare.
2155                 if (dirdocs.GetCount() == 1)
2156                 {
2157                         CDirDoc *pDoc = dirdocs.GetHead();
2158                         if (!pDoc->HasDiffs())
2159                                 return true;
2160                 }
2161                 ret = LangMessageBox(IDS_CLOSEALL_WINDOWS, MB_YESNO | MB_ICONWARNING);
2162         }
2163         return (ret == IDYES);
2164 }
2165
2166 /**
2167  * @brief Shows the release notes for user.
2168  * This function opens release notes HTML document into browser.
2169  */
2170 void CMainFrame::OnHelpReleasenotes()
2171 {
2172         const String sPath = paths::ConcatPath(env::GetProgPath(), RelNotes);
2173         ShellExecute(NULL, _T("open"), sPath.c_str(), NULL, NULL, SW_SHOWNORMAL);
2174 }
2175
2176 /**
2177  * @brief Shows the translations page.
2178  * This function opens translations page URL into browser.
2179  */
2180 void CMainFrame::OnHelpTranslations()
2181 {
2182         ShellExecute(NULL, _T("open"), TranslationsUrl, NULL, NULL, SW_SHOWNORMAL);
2183 }
2184
2185 /**
2186  * @brief Called when user selects File/Open Conflict...
2187  */
2188 void CMainFrame::OnFileOpenConflict()
2189 {
2190         String conflictFile;
2191         if (SelectFile(GetSafeHwnd(), conflictFile))
2192         {
2193                 DoOpenConflict(conflictFile);
2194         }
2195 }
2196
2197 /**
2198  * @brief Select and open conflict file for resolving.
2199  * This function lets user to select conflict file to resolve.
2200  * Then we parse conflict file to two files to "merge" and
2201  * save resulting file over original file.
2202  *
2203  * Set left-side file read-only as it is the repository file which cannot
2204  * be modified anyway. Right-side file is user's file which is set as
2205  * modified by default so user can just save it and accept workspace
2206  * file as resolved file.
2207  * @param [in] conflictFile Full path to conflict file to open.
2208  * @param [in] checked If true, do not check if it really is project file.
2209  * @return `true` if conflict file was opened for resolving.
2210  */
2211 bool CMainFrame::DoOpenConflict(const String& conflictFile, const String strDesc[] /*= nullptr*/, bool checked /*= false*/)
2212 {
2213         bool conflictCompared = false;
2214
2215         if (!checked)
2216         {
2217                 bool confFile = IsConflictFile(conflictFile);
2218                 if (!confFile)
2219                 {
2220                         String message = strutils::format_string1(_("The file\n%1\nis not a conflict file."), conflictFile);
2221                         AfxMessageBox(message.c_str(), MB_ICONSTOP);
2222                         return false;
2223                 }
2224         }
2225
2226         // Create temp files and put them into the list,
2227         // from where they get deleted when MainFrame is deleted.
2228         String ext = paths::FindExtension(conflictFile);
2229         TempFilePtr wTemp(new TempFile());
2230         String workFile = wTemp->Create(_T("confw_"), ext);
2231         m_tempFiles.push_back(wTemp);
2232         TempFilePtr vTemp(new TempFile());
2233         String revFile = vTemp->Create(_T("confv_"), ext);
2234         m_tempFiles.push_back(vTemp);
2235         TempFilePtr bTemp(new TempFile());
2236         String baseFile = vTemp->Create(_T("confb_"), ext);
2237         m_tempFiles.push_back(bTemp);
2238
2239         // Parse conflict file into two files.
2240         bool inners, threeWay;
2241         int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
2242         bool success = ParseConflictFile(conflictFile, workFile, revFile, baseFile, iGuessEncodingType, inners, threeWay);
2243
2244         if (success)
2245         {
2246                 // Open two parsed files to WinMerge, telling WinMerge to
2247                 // save over original file (given as third filename).
2248                 theApp.m_strSaveAsPath = conflictFile;
2249                 if (!threeWay)
2250                 {
2251                         String strDesc2[2] = { 
2252                                 (strDesc && !strDesc[0].empty()) ? strDesc[0] : _("Theirs File"),
2253                                 (strDesc && !strDesc[2].empty()) ? strDesc[2] : _("Mine File") };
2254                         DWORD dwFlags[2] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2255                         PathContext tmpPathContext(revFile, workFile);
2256                         conflictCompared = DoFileOpen(&tmpPathContext, dwFlags, strDesc2);
2257                 }
2258                 else
2259                 {
2260                         String strDesc3[3] = {
2261                                 (strDesc && !strDesc[0].empty()) ? strDesc[0] : _("Base File"),
2262                                 (strDesc && !strDesc[1].empty()) ? strDesc[1] : _("Theirs File"),
2263                                 (strDesc && !strDesc[2].empty()) ? strDesc[2] : _("Mine File") };
2264                         PathContext tmpPathContext(baseFile, revFile, workFile);
2265                         DWORD dwFlags[3] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2266                         conflictCompared = DoFileOpen(&tmpPathContext, dwFlags, strDesc3);
2267                 }
2268         }
2269         else
2270         {
2271                 LangMessageBox(IDS_ERROR_CONF_RESOLVE, MB_ICONSTOP);
2272         }
2273         return conflictCompared;
2274 }
2275
2276 /**
2277  * @brief Get type of frame (File/Folder compare).
2278  * @param [in] pFrame Pointer to frame to check.
2279  * @return FRAMETYPE of the given frame.
2280 */
2281 CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame) const
2282 {
2283         bool bMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame));
2284         bool bHexMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame));
2285         bool bImgMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame));
2286         bool bDirFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame));
2287
2288         if (bMergeFrame)
2289                 return FRAME_FILE;
2290         else if (bHexMergeFrame)
2291                 return FRAME_HEXFILE;
2292         else if (bImgMergeFrame)
2293                 return FRAME_IMGFILE;
2294         else if (bDirFrame)
2295                 return FRAME_FOLDER;
2296         else
2297                 return FRAME_OTHER;
2298 }
2299
2300 /**
2301  * @brief Show the plugins list dialog.
2302  */
2303 void CMainFrame::OnPluginsList()
2304 {
2305         PluginsListDlg dlg;
2306         dlg.DoModal();
2307 }
2308
2309 void CMainFrame::OnDiffOptionsDropDown(NMHDR* pNMHDR, LRESULT* pResult)
2310 {
2311         LPNMTOOLBAR pToolBar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);
2312         ClientToScreen(&(pToolBar->rcButton));
2313         BCMenu menu;
2314         VERIFY(menu.LoadMenu(IDR_POPUP_DIFF_OPTIONS));
2315         theApp.TranslateMenu(menu.m_hMenu);
2316         CMenu* pPopup = menu.GetSubMenu(0);
2317         if (pPopup != nullptr)
2318         {
2319                 pPopup->TrackPopupMenu(TPM_RIGHTALIGN | TPM_RIGHTBUTTON, 
2320                         pToolBar->rcButton.right, pToolBar->rcButton.bottom, this);
2321         }
2322         *pResult = 0;
2323 }
2324 void CMainFrame::OnDiffWhitespace(UINT nID)
2325 {
2326         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_WHITESPACE, nID - IDC_DIFF_WHITESPACE_COMPARE);
2327         ApplyDiffOptions();
2328 }
2329
2330 void CMainFrame::OnUpdateDiffWhitespace(CCmdUI* pCmdUI)
2331 {
2332         pCmdUI->SetRadio((pCmdUI->m_nID - IDC_DIFF_WHITESPACE_COMPARE) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_CMP_IGNORE_WHITESPACE)));
2333         pCmdUI->Enable();
2334 }
2335
2336 void CMainFrame::OnDiffCaseSensitive()
2337 {
2338         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CASE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2339         ApplyDiffOptions();
2340 }
2341
2342 void CMainFrame::OnUpdateDiffCaseSensitive(CCmdUI* pCmdUI)
2343 {
2344         pCmdUI->SetCheck(!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2345         pCmdUI->Enable();
2346 }
2347
2348 void CMainFrame::OnDiffIgnoreEOL()
2349 {
2350         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_EOL, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2351         ApplyDiffOptions();
2352 }
2353
2354 void CMainFrame::OnUpdateDiffIgnoreEOL(CCmdUI* pCmdUI)
2355 {
2356         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2357         pCmdUI->Enable();
2358 }
2359
2360 void CMainFrame::OnIncludeSubfolders()
2361 {
2362         GetOptionsMgr()->SaveOption(OPT_CMP_INCLUDE_SUBDIRS, !GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS));
2363         // Update all dirdoc settings
2364         for (auto pDirDoc : GetAllDirDocs())
2365                 pDirDoc->RefreshOptions();
2366         for (auto pOpenDoc : GetAllOpenDocs())
2367                 pOpenDoc->RefreshOptions();
2368 }
2369
2370 void CMainFrame::OnUpdateIncludeSubfolders(CCmdUI* pCmdUI)
2371 {
2372         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS));
2373         pCmdUI->Enable();
2374 }
2375
2376 void CMainFrame::OnCompareMethod(UINT nID)
2377
2378         GetOptionsMgr()->SaveOption(OPT_CMP_METHOD, nID - ID_COMPMETHOD_FULL_CONTENTS);
2379 }
2380
2381 void CMainFrame::OnUpdateCompareMethod(CCmdUI* pCmdUI)
2382 {
2383         pCmdUI->SetRadio((pCmdUI->m_nID - ID_COMPMETHOD_FULL_CONTENTS) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_CMP_METHOD)));
2384         pCmdUI->Enable();
2385 }
2386
2387 void CMainFrame::OnMRUs(UINT nID)
2388 {
2389         std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(GetOptionsMgr()->GetInt(OPT_MRU_MAX));
2390         const size_t idx = nID - ID_MRU_FIRST;
2391         if (idx < mrus.size())
2392         {
2393                 MergeCmdLineInfo cmdInfo((_T("\"") + mrus[idx].path + _T("\" ") + mrus[idx].params).c_str());
2394                 theApp.ParseArgsAndDoOpen(cmdInfo, this);
2395         }
2396 }
2397
2398 void CMainFrame::OnUpdateNoMRUs(CCmdUI* pCmdUI)
2399 {
2400         // append the MRU submenu
2401         HMENU hMenu = GetSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu, ID_FILE_NEW, false);
2402         if (hMenu == NULL)
2403                 return;
2404         
2405         // empty the menu
2406         size_t i = ::GetMenuItemCount(hMenu);
2407         while (i --)
2408                 ::DeleteMenu(hMenu, 0, MF_BYPOSITION);
2409
2410         std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(GetOptionsMgr()->GetInt(OPT_MRU_MAX));
2411
2412         if (mrus.size() == 0)
2413         {
2414                 // no script : create a <empty> entry
2415                 ::AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, theApp.LoadString(IDS_NO_EDIT_SCRIPTS).c_str());
2416         }
2417         else
2418         {
2419                 // or fill in the submenu with the scripts names
2420                 int ID = ID_MRU_FIRST;  // first ID in menu
2421                 for (i = 0 ; i < mrus.size() ; i++, ID++)
2422                         ::AppendMenu(hMenu, MF_STRING, ID, 
2423                                 ((i < 9 ? strutils::format(_T("&%d "), i+1) : strutils::format(_T("&%c "), 'a' + i - 9)) 
2424                                         + mrus[i].title).c_str());
2425         }
2426
2427         pCmdUI->Enable(true);
2428 }
2429
2430 /**
2431  * @brief Update plugin name
2432  * @param [in] pCmdUI UI component to update.
2433  */
2434 void CMainFrame::OnUpdatePluginName(CCmdUI* pCmdUI)
2435 {
2436         pCmdUI->SetText(_T(""));
2437 }
2438
2439 void CMainFrame::ReloadMenu()
2440 {
2441         // set the menu of the main frame window
2442         UINT idMenu = IDR_MAINFRAME;
2443         CMergeApp *pApp = dynamic_cast<CMergeApp *> (AfxGetApp());
2444         CMainFrame * pMainFrame = dynamic_cast<CMainFrame *> ((CFrameWnd*)pApp->m_pMainWnd);
2445         HMENU hNewDefaultMenu = pMainFrame->NewDefaultMenu(idMenu);
2446         HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
2447         HMENU hNewImgMergeMenu = pMainFrame->NewImgMergeViewMenu();
2448         HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
2449         if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
2450         {
2451                 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
2452                 CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
2453                 CMenu * pNewMergeMenu = CMenu::FromHandle(hNewMergeMenu);
2454                 CMenu * pNewImgMergeMenu = CMenu::FromHandle(hNewImgMergeMenu);
2455                 CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
2456
2457                 CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
2458                 while (pFrame)
2459                 {
2460                         if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2461                                 static_cast<CChildFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2462                         if (pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2463                                 static_cast<CHexMergeFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2464                         if (pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2465                                 static_cast<CImgMergeFrame *>(pFrame)->SetSharedMenu(hNewImgMergeMenu);
2466                         else if (pFrame->IsKindOf(RUNTIME_CLASS(COpenFrame)))
2467                                 static_cast<COpenFrame *>(pFrame)->SetSharedMenu(hNewDefaultMenu);
2468                         else if (pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2469                                 static_cast<CDirFrame *>(pFrame)->SetSharedMenu(hNewDirMenu);
2470                         pFrame = pFrame->GetNextWindow();
2471                 }
2472
2473                 CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
2474                 if (pActiveFrame)
2475                 {
2476                         if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2477                                 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2478                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2479                                 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2480                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2481                                 pMainFrame->MDISetMenu(pNewImgMergeMenu, NULL);
2482                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2483                                 pMainFrame->MDISetMenu(pNewDirMenu, NULL);
2484                         else
2485                                 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2486                 }
2487                 else
2488                         pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2489
2490                 // Don't delete the old menu
2491                 // There is a bug in BCMenu or in Windows98 : the new menu does not
2492                 // appear correctly if we destroy the old one
2493                 //                      if (pOldDefaultMenu)
2494                 //                              pOldDefaultMenu->DestroyMenu();
2495                 //                      if (pOldMergeMenu)
2496                 //                              pOldMergeMenu->DestroyMenu();
2497                 //                      if (pOldDirMenu)
2498                 //                              pOldDirMenu->DestroyMenu();
2499
2500                 // m_hMenuDefault is used to redraw the main menu when we close a child frame
2501                 // if this child frame had a different menu
2502                 pMainFrame->m_hMenuDefault = hNewDefaultMenu;
2503                 pApp->m_pOpenTemplate->m_hMenuShared = hNewDefaultMenu;
2504                 pApp->m_pDiffTemplate->m_hMenuShared = hNewMergeMenu;
2505                 pApp->m_pDirTemplate->m_hMenuShared = hNewDirMenu;
2506
2507                 // force redrawing the menu bar
2508                 pMainFrame->DrawMenuBar();
2509         }
2510 }
2511
2512 void CMainFrame::UpdateDocTitle()
2513 {
2514         CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
2515         POSITION posTemplate = pDocManager->GetFirstDocTemplatePosition();
2516         ASSERT(posTemplate != nullptr);
2517
2518         while (posTemplate != nullptr)
2519         {
2520                 CDocTemplate* pTemplate = pDocManager->GetNextDocTemplate(posTemplate);
2521
2522                 ASSERT(pTemplate != nullptr);
2523
2524                 for (auto pDoc : GetDocList(static_cast<CMultiDocTemplate *>(pTemplate)))
2525                 {
2526                         static_cast<CDocument *>(const_cast<void *>(pDoc))->SetTitle(nullptr);
2527                         ((CFrameWnd*)AfxGetApp()->m_pMainWnd)->OnUpdateFrameTitle(TRUE);
2528                 }
2529         }
2530 }