OSDN Git Service

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