OSDN Git Service

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