OSDN Git Service

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