OSDN Git Service

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