OSDN Git Service

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