OSDN Git Service

OptionsDef.*: Reduce executable file size
[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         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1554
1555         if (pCmdUI->m_nID == ID_UNPACK_MANUAL)
1556                 pCmdUI->SetRadio(PLUGIN_MANUAL == g_bUnpackerMode);
1557         if (pCmdUI->m_nID == ID_UNPACK_AUTO)
1558                 pCmdUI->SetRadio(PLUGIN_AUTO == g_bUnpackerMode);
1559 }
1560 void CMainFrame::OnPluginPrediffMode(UINT nID )
1561 {
1562         switch (nID)
1563         {
1564         case ID_PREDIFFER_MANUAL:
1565                 g_bPredifferMode = PLUGIN_MANUAL;
1566                 break;
1567         case ID_PREDIFFER_AUTO:
1568                 g_bPredifferMode = PLUGIN_AUTO;
1569                 break;
1570         }
1571         PrediffingInfo infoPrediffer;
1572         const MergeDocList &mergedocs = GetAllMergeDocs();
1573         POSITION pos = mergedocs.GetHeadPosition();
1574         while (pos)
1575                 mergedocs.GetNext(pos)->SetPrediffer(&infoPrediffer);
1576         const DirDocList &dirdocs = GetAllDirDocs();
1577         pos = dirdocs.GetHeadPosition();
1578         while (pos)
1579                 dirdocs.GetNext(pos)->SetPluginPrediffSettingAll(g_bPredifferMode);
1580         theApp.WriteProfileInt(_T("Settings"), _T("PredifferMode"), g_bPredifferMode);
1581 }
1582
1583 void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI) 
1584 {
1585         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1586
1587         if (pCmdUI->m_nID == ID_PREDIFFER_MANUAL)
1588                 pCmdUI->SetRadio(PLUGIN_MANUAL == g_bPredifferMode);
1589         if (pCmdUI->m_nID == ID_PREDIFFER_AUTO)
1590                 pCmdUI->SetRadio(PLUGIN_AUTO == g_bPredifferMode);
1591 }
1592 /**
1593  * @brief Called when "Reload Plugins" item is updated
1594  */
1595 void CMainFrame::OnUpdateReloadPlugins(CCmdUI* pCmdUI)
1596 {
1597         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1598 }
1599
1600 void CMainFrame::OnReloadPlugins()
1601 {
1602         // delete all script interfaces
1603         // (interfaces will be created again automatically when WinMerge needs them)
1604         CAllThreadsScripts::GetActiveSet()->FreeAllScripts();
1605
1606         // update the editor scripts submenu
1607         HMENU scriptsSubmenu = GetScriptsSubmenu(m_hMenuDefault);
1608         if (scriptsSubmenu != NULL)
1609                 CMergeEditView::createScriptsSubmenu(scriptsSubmenu);
1610         UpdatePrediffersMenu();
1611 }
1612
1613 /** @brief Return active merge edit view, if can figure it out/is available */
1614 CMergeEditView * CMainFrame::GetActiveMergeEditView()
1615 {
1616         // NB: GetActiveDocument does not return the Merge Doc 
1617         //     even when the merge edit view is in front
1618         // NB: CChildFrame::GetActiveView returns NULL when location view active
1619         // So we have this rather complicated logic to try to get a merge edit view
1620         // We look at the front child window, which should be a frame
1621         // and we can get a MergeEditView from it, if it is a CChildFrame
1622         // (DirViews use a different frame type)
1623         CChildFrame * pFrame = dynamic_cast<CChildFrame *>(GetActiveFrame());
1624         if (!pFrame) return 0;
1625         // Try to get the active MergeEditView (ie, left or right)
1626         if (pFrame->GetActiveView() && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
1627         {
1628                 return dynamic_cast<CMergeEditView *>(pFrame->GetActiveView());
1629         }
1630         return pFrame->GetMergeDoc()->GetActiveMergeView();
1631 }
1632
1633 void CMainFrame::UpdatePrediffersMenu()
1634 {
1635         CMenu* menu = GetMenu();
1636         if (menu == NULL)
1637         {
1638                 return;
1639         }
1640
1641         HMENU hMainMenu = menu->m_hMenu;
1642         HMENU prediffersSubmenu = GetPrediffersSubmenu(hMainMenu);
1643         if (prediffersSubmenu != NULL)
1644         {
1645                 CMergeEditView * pEditView = GetActiveMergeEditView();
1646                 if (pEditView)
1647                         pEditView->createPrediffersSubmenu(prediffersSubmenu);
1648                 else
1649                 {
1650                         // no view or dir view : display an empty submenu
1651                         int i = GetMenuItemCount(prediffersSubmenu);
1652                         while (i --)
1653                                 ::DeleteMenu(prediffersSubmenu, 0, MF_BYPOSITION);
1654                         ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, NULL);
1655                 }
1656         }
1657 }
1658
1659 /**
1660  * @brief Save WinMerge configuration and info to file
1661  */
1662 void CMainFrame::OnSaveConfigData()
1663 {
1664         CConfigLog configLog;
1665         String sError;
1666
1667         if (configLog.WriteLogFile(sError))
1668         {
1669                 String sFileName = configLog.GetFileName();
1670                 theApp.OpenFileToExternalEditor(sFileName);
1671         }
1672         else
1673         {
1674                 String sFileName = configLog.GetFileName();
1675                 String msg = LangFormatString2(IDS_ERROR_FILEOPEN, sFileName.c_str(), sError.c_str());
1676                 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1677         }
1678 }
1679
1680 /**
1681  * @brief Open two new empty docs, 'Scratchpads'
1682  * 
1683  * Allows user to open two empty docs, to paste text to
1684  * compare from clipboard.
1685  * @note File filenames are set emptys and filedescriptors
1686  * are loaded from resource.
1687  * @sa CMergeDoc::OpenDocs()
1688  * @sa CMergeDoc::TrySaveAs()
1689  */
1690 void CMainFrame::FileNew(int nPanes) 
1691 {
1692         CDirDoc *pDirDoc = (CDirDoc*)theApp.m_pDirTemplate->CreateNewDocument();
1693         
1694         // Load emptyfile descriptors and open empty docs
1695         // Use default codepage
1696         DWORD dwFlags[3] = {0, 0, 0};
1697         FileLocation fileloc[3];
1698         if (nPanes == 2)
1699         {
1700                 theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
1701                 theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
1702                 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1703                 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1704                 ShowMergeDoc(pDirDoc, 2, fileloc, dwFlags);
1705         }
1706         else
1707         {
1708                 theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
1709                 theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_MIDDLE_FILE);
1710                 theApp.m_strDescriptions[2] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
1711                 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1712                 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1713                 fileloc[2].encoding.SetCodepage(ucr::getDefaultCodepage());
1714                 ShowMergeDoc(pDirDoc, 3, fileloc, dwFlags);
1715         }
1716
1717         // Empty descriptors now that docs are open
1718         theApp.m_strDescriptions[0].erase();
1719         theApp.m_strDescriptions[1].erase();
1720         theApp.m_strDescriptions[2].erase();
1721 }
1722
1723 /**
1724  * @brief Open two new empty docs, 'Scratchpads'
1725  * 
1726  * Allows user to open two empty docs, to paste text to
1727  * compare from clipboard.
1728  * @note File filenames are set emptys and filedescriptors
1729  * are loaded from resource.
1730  * @sa CMergeDoc::OpenDocs()
1731  * @sa CMergeDoc::TrySaveAs()
1732  */
1733 void CMainFrame::OnFileNew() 
1734 {
1735         FileNew(2);
1736 }
1737
1738 void CMainFrame::OnFileNew3() 
1739 {
1740         FileNew(3);
1741 }
1742
1743 /**
1744  * @brief Open Filters dialog
1745  */
1746 void CMainFrame::OnToolsFilters()
1747 {
1748         String title = theApp.LoadString(IDS_FILTER_TITLE);
1749         CPropertySheet sht(title.c_str());
1750         LineFiltersDlg lineFiltersDlg;
1751         FileFiltersDlg fileFiltersDlg;
1752         vector<FileFilterInfo> fileFilters;
1753         std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
1754         String selectedFilter;
1755         const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1756         sht.AddPage(&fileFiltersDlg);
1757         sht.AddPage(&lineFiltersDlg);
1758         sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
1759
1760         // Make sure all filters are up-to-date
1761         theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
1762
1763         theApp.m_pGlobalFileFilter->GetFileFilters(&fileFilters, selectedFilter);
1764         fileFiltersDlg.SetFilterArray(&fileFilters);
1765         fileFiltersDlg.SetSelected(selectedFilter);
1766         const BOOL lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
1767         lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig;
1768
1769         lineFilters->CloneFrom(theApp.m_pLineFilters.get());
1770         lineFiltersDlg.SetList(lineFilters.get());
1771
1772         if (sht.DoModal() == IDOK)
1773         {
1774                 String strNone = theApp.LoadString(IDS_USERCHOICE_NONE);
1775                 String path = fileFiltersDlg.GetSelected();
1776                 if (path.find(strNone) != String::npos)
1777                 {
1778                         // Don't overwrite mask we already have
1779                         if (!theApp.m_pGlobalFileFilter->IsUsingMask())
1780                         {
1781                                 String sFilter(_T("*.*"));
1782                                 theApp.m_pGlobalFileFilter->SetFilter(sFilter);
1783                                 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1784                         }
1785                 }
1786                 else
1787                 {
1788                         theApp.m_pGlobalFileFilter->SetFileFilterPath(path);
1789                         theApp.m_pGlobalFileFilter->UseMask(FALSE);
1790                         String sFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1791                         GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1792                 }
1793                 BOOL linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp;
1794                 GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled == TRUE);
1795
1796                 // Check if compare documents need rescanning
1797                 BOOL bFileCompareRescan = FALSE;
1798                 BOOL bFolderCompareRescan = FALSE;
1799                 CFrameWnd * pFrame = GetActiveFrame();
1800                 FRAMETYPE frame = GetFrameType(pFrame);
1801                 if (frame == FRAME_FILE)
1802                 {
1803                         if (lineFiltersEnabledOrig != linefiltersEnabled ||
1804                                         !theApp.m_pLineFilters->Compare(lineFilters.get()))
1805                         {
1806                                 bFileCompareRescan = TRUE;
1807                         }
1808                 }
1809                 else if (frame == FRAME_FOLDER)
1810                 {
1811                         const String newFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1812                         if (lineFiltersEnabledOrig != linefiltersEnabled || 
1813                                         !theApp.m_pLineFilters->Compare(lineFilters.get()) || origFilter != newFilter)
1814                         {
1815                                 int res = LangMessageBox(IDS_FILTERCHANGED, MB_ICONWARNING | MB_YESNO);
1816                                 if (res == IDYES)
1817                                         bFolderCompareRescan = TRUE;
1818                         }
1819                 }
1820
1821                 // Save new filters before (possibly) rescanning
1822                 theApp.m_pLineFilters->CloneFrom(lineFilters.get());
1823                 theApp.m_pLineFilters->SaveFilters();
1824
1825                 if (bFileCompareRescan)
1826                 {
1827                         const MergeDocList &docs = GetAllMergeDocs();
1828                         POSITION pos = docs.GetHeadPosition();
1829                         while (pos)
1830                         {
1831                                 CMergeDoc * pMergeDoc = docs.GetNext(pos);
1832                                 pMergeDoc->FlushAndRescan(TRUE);
1833                         }
1834                 }
1835                 else if (bFolderCompareRescan)
1836                 {
1837                         const DirDocList &dirDocs = GetAllDirDocs();
1838                         POSITION pos = dirDocs.GetHeadPosition();
1839                         while (pos)
1840                         {
1841                                 CDirDoc * pDirDoc = dirDocs.GetNext(pos);
1842                                 pDirDoc->Rescan();
1843                         }
1844                 }
1845         }
1846 }
1847
1848 /**
1849  * @brief Open Filters dialog.
1850  */
1851 void CMainFrame::SelectFilter()
1852 {
1853         OnToolsFilters();
1854 }
1855
1856 /**
1857  * @brief Closes application with ESC.
1858  *
1859  * Application is closed if:
1860  * - 'Close Windows with ESC' option is enabled and
1861  *    there is no open document windows
1862  * - '-e' commandline switch is given
1863  */
1864 BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
1865 {
1866         // Check if we got 'ESC pressed' -message
1867         if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
1868         {
1869                 if (theApp.m_bEscShutdown && m_wndTabBar.GetItemCount() <= 1)
1870                 {
1871                         AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1872                         return TRUE;
1873                 }
1874                 else if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC) && m_wndTabBar.GetItemCount() == 0)
1875                 {
1876                         AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1877                         return FALSE;
1878                 }
1879         }
1880         return CMDIFrameWnd::PreTranslateMessage(pMsg);
1881 }
1882
1883 /**
1884  * @brief Show/hide statusbar.
1885  */
1886 void CMainFrame::OnViewStatusBar()
1887 {
1888         bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR);
1889         GetOptionsMgr()->SaveOption(OPT_SHOW_STATUSBAR, bShow);
1890
1891         CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, bShow, 0);
1892 }
1893
1894 /**
1895  * @brief Updates "Show Tabbar" menuitem.
1896  */
1897 void CMainFrame::OnUpdateViewTabBar(CCmdUI* pCmdUI) 
1898 {
1899         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR));
1900 }
1901
1902 /**
1903  * @brief Show/hide tabbar.
1904  */
1905 void CMainFrame::OnViewTabBar()
1906 {
1907         bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR);
1908         GetOptionsMgr()->SaveOption(OPT_SHOW_TABBAR, bShow);
1909
1910         CMDIFrameWnd::ShowControlBar(&m_wndTabBar, bShow, 0);
1911 }
1912
1913 /**
1914  * @brief Updates "Automatically Resize Panes" menuitem.
1915  */
1916 void CMainFrame::OnUpdateResizePanes(CCmdUI* pCmdUI)
1917 {
1918         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
1919 }
1920
1921
1922 /**
1923  * @brief Enable/disable automatic pane resizing.
1924  */
1925 void CMainFrame::OnResizePanes()
1926 {
1927         bool bResize = !GetOptionsMgr()->GetBool(OPT_RESIZE_PANES);
1928         GetOptionsMgr()->SaveOption(OPT_RESIZE_PANES, bResize);
1929         // TODO: Introduce a common merge frame superclass?
1930         CFrameWnd *pActiveFrame = GetActiveFrame();
1931         if (CChildFrame *pFrame = DYNAMIC_DOWNCAST(CChildFrame, pActiveFrame))
1932         {
1933                 pFrame->UpdateAutoPaneResize();
1934                 if (bResize)
1935                         pFrame->UpdateSplitter();
1936         }
1937         else if (CHexMergeFrame *pFrame = DYNAMIC_DOWNCAST(CHexMergeFrame, pActiveFrame))
1938         {
1939                 pFrame->UpdateAutoPaneResize();
1940                 if (bResize)
1941                         pFrame->UpdateSplitter();
1942         }
1943 }
1944
1945 /**
1946  * @brief Open project-file.
1947  */
1948 void CMainFrame::OnFileOpenproject()
1949 {
1950         String sFilepath;
1951         
1952         // get the default projects path
1953         String strProjectPath = GetOptionsMgr()->GetString(OPT_PROJECTS_PATH);
1954         if (!SelectFile(GetSafeHwnd(), sFilepath, strProjectPath.c_str(), IDS_OPEN_TITLE,
1955                         IDS_PROJECTFILES, TRUE))
1956                 return;
1957         
1958         strProjectPath = paths_GetParentPath(sFilepath);
1959         // store this as the new project path
1960         GetOptionsMgr()->SaveOption(OPT_PROJECTS_PATH, strProjectPath);
1961
1962         theApp.LoadAndOpenProjectFile(sFilepath.c_str());
1963 }
1964
1965 /**
1966  * @brief Receive command line from another instance.
1967  *
1968  * This function receives command line when only single-instance
1969  * is allowed. New instance tried to start sends its command line
1970  * to here so we can open paths it was meant to.
1971  */
1972 LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
1973 {
1974         COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
1975         LPCTSTR pchData = (LPCTSTR)pCopyData->lpData;
1976         // Bail out if data isn't zero-terminated
1977         DWORD cchData = pCopyData->cbData / sizeof(TCHAR);
1978         if (cchData == 0 || pchData[cchData - 1] != _T('\0'))
1979                 return FALSE;
1980         ReplyMessage(TRUE);
1981         MergeCmdLineInfo cmdInfo = pchData;
1982         theApp.ParseArgsAndDoOpen(cmdInfo, this);
1983         return TRUE;
1984 }
1985
1986 LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
1987 {
1988         CFrameWnd * pFrame = GetActiveFrame();
1989         if (pFrame)
1990         {
1991                 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
1992                 if (!pMergeDoc)
1993                         pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
1994                 if (pMergeDoc)
1995                         pMergeDoc->CheckFileChanged();
1996         }
1997         return 0;
1998 }
1999
2000 /**
2001  * @brief Handle timer events.
2002  * @param [in] nIDEvent Timer that timed out.
2003  */
2004 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
2005 {
2006         switch (nIDEvent)
2007         {
2008         case WM_NONINTERACTIVE:
2009                 KillTimer(WM_NONINTERACTIVE);
2010                 PostMessage(WM_CLOSE);
2011                 break;
2012         
2013         case ID_TIMER_FLASH:
2014                 // This timer keeps running until window is activated
2015                 // See OnActivate()
2016                 FlashWindow(TRUE);
2017                 break;
2018         }
2019         CMDIFrameWnd::OnTimer(nIDEvent);
2020 }
2021
2022 /**
2023  * @brief Close all open windows.
2024  * 
2025  * Asks about saving unsaved files and then closes all open windows.
2026  */
2027 void CMainFrame::OnWindowCloseAll()
2028 {
2029         CMDIChildWnd *pChild = MDIGetActive();
2030         CDocument* pDoc;
2031         while (pChild)
2032         {
2033                 if ((pDoc = pChild->GetActiveDocument()) != NULL)
2034                 {
2035                         if (!pDoc->SaveModified())
2036                                 return;
2037                         pDoc->OnCloseDocument();
2038                 }
2039                 else if (GetFrameType(pChild) == FRAME_IMGFILE)
2040                 {
2041                         if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
2042                                 return;
2043                 }
2044                 else
2045                 {
2046                         pChild->DestroyWindow();
2047                 }
2048                 pChild = MDIGetActive();
2049         }
2050         return;
2051 }
2052
2053 /**
2054  * @brief Enables Window/Close All item if there are open windows.
2055  */ 
2056 void CMainFrame::OnUpdateWindowCloseAll(CCmdUI* pCmdUI)
2057 {
2058         const MergeDocList &mergedocs = GetAllMergeDocs();
2059         if (!mergedocs.IsEmpty())
2060         {
2061                 pCmdUI->Enable(TRUE);
2062                 return;
2063         }
2064
2065         const DirDocList &dirdocs = GetAllDirDocs();
2066         pCmdUI->Enable(!dirdocs.IsEmpty());
2067 }
2068
2069 /**
2070  * @brief Access to the singleton main frame (where we have some globals)
2071  */
2072 CMainFrame * GetMainFrame()
2073 {
2074         CWnd * mainwnd = AfxGetMainWnd();
2075         ASSERT(mainwnd);
2076         CMainFrame *pMainframe = dynamic_cast<CMainFrame*>(mainwnd);
2077         ASSERT(pMainframe);
2078         return pMainframe;
2079 }
2080
2081 /** 
2082  * @brief Opens dialog for user to Load, edit and save project files.
2083  * This dialog gets current compare paths and filter (+other properties
2084  * possible in project files) and initializes the dialog with them.
2085  */
2086 void CMainFrame::OnSaveProject()
2087 {
2088         String title = theApp.LoadString(IDS_PROJFILEDLG_CAPTION);
2089         CPropertySheet sht(title.c_str());
2090         ProjectFilePathsDlg pathsDlg;
2091         sht.AddPage(&pathsDlg);
2092         sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
2093
2094         String left;
2095         String right;
2096         CFrameWnd * pFrame = GetActiveFrame();
2097         FRAMETYPE frame = GetFrameType(pFrame);
2098
2099         if (frame == FRAME_FILE)
2100         {
2101                 CMergeDoc * pMergeDoc = (CMergeDoc *) pFrame->GetActiveDocument();
2102                 left = pMergeDoc->m_filePaths.GetLeft();
2103                 right = pMergeDoc->m_filePaths.GetRight();
2104                 pathsDlg.SetPaths(left.c_str(), right.c_str());
2105                 pathsDlg.m_bLeftPathReadOnly = pMergeDoc->m_ptBuf[0]->GetReadOnly();
2106                 pathsDlg.m_bRightPathReadOnly = pMergeDoc->m_ptBuf[1]->GetReadOnly();
2107         }
2108         else if (frame == FRAME_FOLDER)
2109         {
2110                 // Get paths currently in compare
2111                 CDirDoc * pDoc = (CDirDoc*)pFrame->GetActiveDocument();
2112                 left = pDoc->GetLeftBasePath();
2113                 right = pDoc->GetRightBasePath();
2114                 if (!paths_EndsWithSlash(left))
2115                         left += _T("\\");
2116                 if (!paths_EndsWithSlash(right))
2117                         right += _T("\\");
2118                 
2119                 // Set-up the dialog
2120                 pathsDlg.SetPaths(left.c_str(), right.c_str());
2121                 pathsDlg.m_bIncludeSubfolders = pDoc->GetRecursive();
2122                 pathsDlg.m_bLeftPathReadOnly = pDoc->GetReadOnly(0);
2123                 pathsDlg.m_bRightPathReadOnly = pDoc->GetReadOnly(pDoc->m_nDirs - 1);
2124         }
2125
2126         String filterNameOrMask = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
2127         pathsDlg.m_sFilter = filterNameOrMask.c_str();
2128         sht.DoModal();
2129 }
2130
2131 /** 
2132  * @brief Start flashing window if window is inactive.
2133  */
2134 void CMainFrame::StartFlashing()
2135 {
2136         CWnd * activeWindow = GetActiveWindow();
2137         if (activeWindow != this)
2138         {
2139                 FlashWindow(TRUE);
2140                 m_bFlashing = TRUE;
2141                 SetTimer(ID_TIMER_FLASH, WINDOW_FLASH_TIMEOUT, NULL);
2142         }
2143 }
2144
2145 /** 
2146  * @brief Stop flashing window when window is activated.
2147  *
2148  * If WinMerge is inactive when compare finishes, we start flashing window
2149  * to alert user. When user activates WinMerge window we stop flashing.
2150  * @param [in] nState Tells if window is being activated or deactivated.
2151  * @param [in] pWndOther Pointer to window whose status is changing.
2152  * @param [in] Is window minimized?
2153  */
2154 void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
2155 {
2156         CMDIFrameWnd::OnActivate(nState, pWndOther, bMinimized);
2157
2158         if (nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
2159         {
2160                 if (m_bFlashing)
2161                 {
2162                         m_bFlashing = FALSE;
2163                         FlashWindow(FALSE);
2164                         KillTimer(ID_TIMER_FLASH);
2165                 }
2166         }
2167 }
2168
2169 #if _MFC_VER > 0x0600
2170 void CMainFrame::OnActivateApp(BOOL bActive, DWORD dwThreadID)
2171 #else
2172 void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
2173 #endif
2174 {
2175 #if _MFC_VER > 0x0600
2176         CMDIFrameWnd::OnActivateApp(bActive, dwThreadID);
2177 #else
2178         CMDIFrameWnd::OnActivateApp(bActive, hTask);
2179 #endif
2180
2181         CFrameWnd * pFrame = GetActiveFrame();
2182         if (pFrame)
2183         {
2184                 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
2185                 if (!pMergeDoc)
2186                         pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
2187                 if (pMergeDoc)
2188                         PostMessage(WM_USER+1);
2189         }
2190 }
2191
2192 BOOL CMainFrame::CreateToolbar()
2193 {
2194         if (!m_wndToolBar.CreateEx(this) ||
2195                 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
2196         {
2197                 return FALSE;
2198         }
2199
2200         if (!m_wndReBar.Create(this))
2201         {
2202                 return FALSE;
2203         }
2204
2205         VERIFY(m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT));
2206
2207         // Remove this if you don't want tool tips or a resizable toolbar
2208         m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
2209                 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
2210         m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
2211
2212         m_wndReBar.AddBar(&m_wndToolBar);
2213
2214         LoadToolbarImages();
2215
2216         UINT nID, nStyle;
2217         int iImage;
2218         int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(ID_OPTIONS);
2219         m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
2220         nStyle |= TBSTYLE_DROPDOWN;
2221         m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
2222
2223         if (GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR) == false)
2224         {
2225                 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
2226         }
2227
2228         return TRUE;
2229 }
2230
2231 /** @brief Load toolbar images from the resource. */
2232 void CMainFrame::LoadToolbarImages()
2233 {
2234         int toolbarSize = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2235         CToolBarCtrl& BarCtrl = m_wndToolBar.GetToolBarCtrl();
2236
2237         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].DeleteImageList();
2238         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].DeleteImageList();
2239         CSize sizeButton(0, 0);
2240
2241         if (toolbarSize == 0)
2242         {
2243                 LoadToolbarImageList(TOOLBAR_SIZE_16x16, IDB_TOOLBAR_ENABLED,
2244                         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2245                 LoadToolbarImageList(TOOLBAR_SIZE_16x16, IDB_TOOLBAR_DISABLED,
2246                         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2247                 sizeButton = CSize(24, 24);
2248         }
2249         else if (toolbarSize == 1)
2250         {
2251                 LoadToolbarImageList(TOOLBAR_SIZE_32x32, IDB_TOOLBAR_ENABLED32,
2252                         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2253                 LoadToolbarImageList(TOOLBAR_SIZE_32x32, IDB_TOOLBAR_DISABLED32,
2254                         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2255                 sizeButton = CSize(40, 40);
2256         }
2257
2258         BarCtrl.SetButtonSize(sizeButton);
2259         BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2260         BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2261         m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
2262         m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
2263
2264         // resize the rebar.
2265         REBARBANDINFO rbbi;
2266         rbbi.cbSize = sizeof(rbbi);
2267         rbbi.fMask = RBBIM_CHILDSIZE;
2268         rbbi.cyMinChild = sizeButton.cy;
2269         m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);
2270 }
2271
2272
2273 /**
2274  * @brief Load a transparent 24-bit color image list.
2275  */
2276 static void LoadHiColImageList(UINT nIDResource, int nWidth, int nHeight, int nCount, CImageList& ImgList, COLORREF crMask = RGB(255,0,255))
2277 {
2278         CBitmap bm;
2279         VERIFY(bm.LoadBitmap(nIDResource));
2280         VERIFY(ImgList.Create(nWidth, nHeight, ILC_COLORDDB|ILC_MASK, nCount, 0));
2281         int nIndex = ImgList.Add(&bm, crMask);
2282         ASSERT(-1 != nIndex);
2283 }
2284
2285 /**
2286  * @brief Load toolbar image list.
2287  */
2288 static void LoadToolbarImageList(CMainFrame::TOOLBAR_SIZE size, UINT nIDResource,
2289                 CImageList& ImgList)
2290 {
2291         const int ImageCount = 22;
2292         int imageWidth = 0;
2293         int imageHeight = 0;
2294
2295         switch (size)
2296         {
2297                 case CMainFrame::TOOLBAR_SIZE_16x16:
2298                         imageWidth = 16;
2299                         imageHeight = 15;
2300                         break;
2301                 case CMainFrame::TOOLBAR_SIZE_32x32:
2302                         imageWidth = 32;
2303                         imageHeight = 31;
2304                         break;
2305         }
2306
2307         LoadHiColImageList(nIDResource, imageWidth, imageHeight, ImageCount, ImgList);
2308 }
2309
2310 /**
2311  * @brief Called when the document title is modified.
2312  */
2313 void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
2314 {
2315         CFrameWnd::OnUpdateFrameTitle(bAddToTitle);
2316         
2317         if (m_wndTabBar.m_hWnd)
2318                 m_wndTabBar.UpdateTabs();
2319 }
2320
2321 /** @brief Hide the toolbar. */
2322 void CMainFrame::OnToolbarNone()
2323 {
2324         GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, false);
2325         CMDIFrameWnd::ShowControlBar(&m_wndToolBar, FALSE, 0);
2326 }
2327
2328 /** @brief Update menuitem for hidden toolbar. */
2329 void CMainFrame::OnUpdateToolbarNone(CCmdUI* pCmdUI)
2330 {
2331         bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2332         pCmdUI->SetRadio(!enabled);
2333 }
2334
2335 /** @brief Show small toolbar. */
2336 void CMainFrame::OnToolbarSmall()
2337 {
2338         GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2339         GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, 0);
2340
2341         LoadToolbarImages();
2342
2343         CMDIFrameWnd::ShowControlBar(&m_wndToolBar, TRUE, 0);
2344 }
2345
2346 /** @brief Update menuitem for small toolbar. */
2347 void CMainFrame::OnUpdateToolbarSmall(CCmdUI* pCmdUI)
2348 {
2349         bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2350         int toolbar = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2351         pCmdUI->SetRadio(enabled && toolbar == 0);
2352 }
2353
2354 /** @brief Show big toolbar. */
2355 void CMainFrame::OnToolbarBig()
2356 {
2357         GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2358         GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, 1);
2359
2360         LoadToolbarImages();
2361
2362         CMDIFrameWnd::ShowControlBar(&m_wndToolBar, TRUE, 0);
2363 }
2364
2365 /** @brief Update menuitem for big toolbar. */
2366 void CMainFrame::OnUpdateToolbarBig(CCmdUI* pCmdUI)
2367 {
2368         bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2369         int toolbar = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2370         pCmdUI->SetRadio(enabled && toolbar == 1);
2371 }
2372
2373 /** @brief Lang aware version of CFrameWnd::OnToolTipText() */
2374 BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
2375 {
2376         ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
2377
2378         // need to handle both ANSI and UNICODE versions of the message
2379         TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2380         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2381         String strFullText;
2382         CString strTipText;
2383         UINT_PTR nID = pNMHDR->idFrom;
2384         if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
2385                 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2386         {
2387                 // idFrom is actually the HWND of the tool
2388                 nID = ::GetDlgCtrlID((HWND)nID);
2389         }
2390
2391         if (nID != 0) // will be zero on a separator
2392         {
2393                 strFullText = theApp.LoadString(static_cast<UINT>(nID));
2394                 // don't handle the message if no string resource found
2395                 if (strFullText.empty())
2396                         return FALSE;
2397
2398                 // this is the command id, not the button index
2399                 AfxExtractSubString(strTipText, strFullText.c_str(), 1, '\n');
2400         }
2401 #ifndef _UNICODE
2402         if (pNMHDR->code == TTN_NEEDTEXTA)
2403                 lstrcpyn(pTTTA->szText, strTipText, countof(pTTTA->szText));
2404         else
2405                 _mbstowcsz(pTTTW->szText, strTipText, countof(pTTTW->szText));
2406 #else
2407         if (pNMHDR->code == TTN_NEEDTEXTA)
2408                 _wcstombsz(pTTTA->szText, strTipText, countof(pTTTA->szText));
2409         else
2410                 lstrcpyn(pTTTW->szText, strTipText, countof(pTTTW->szText));
2411 #endif
2412         *pResult = 0;
2413
2414         // bring the tooltip window above other popup windows
2415         ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
2416                 SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
2417
2418         return TRUE;    // message was handled
2419 }
2420
2421 /**
2422  * @brief Ask user for close confirmation when closing the mainframe.
2423  * This function asks if user wants to close multiple open windows when user
2424  * selects (perhaps accidentally) to close WinMerge (application).
2425  * @return true if user agreeds to close all windows.
2426  */
2427 bool CMainFrame::AskCloseConfirmation()
2428 {
2429         const DirDocList &dirdocs = GetAllDirDocs();
2430         const MergeDocList &mergedocs = GetAllMergeDocs();
2431
2432         int ret = IDYES;
2433         const size_t count = dirdocs.GetCount() + mergedocs.GetCount();
2434         if (count > 1)
2435         {
2436                 // Check that we don't have one empty dirdoc + mergedoc situation.
2437                 // That happens since we open "hidden" dirdoc for every file compare.
2438                 if (dirdocs.GetCount() == 1)
2439                 {
2440                         CDirDoc *pDoc = dirdocs.GetHead();
2441                         if (!pDoc->HasDiffs())
2442                                 return true;
2443                 }
2444                 ret = LangMessageBox(IDS_CLOSEALL_WINDOWS, MB_YESNO | MB_ICONWARNING);
2445         }
2446         return (ret == IDYES);
2447 }
2448
2449 void CMainFrame::OnHelpCheckForUpdates()
2450 {
2451         CVersionInfo version(AfxGetResourceHandle());
2452         CInternetSession session;
2453         try
2454         {
2455                 CHttpFile *file = (CHttpFile *)session.OpenURL(CurrentVersionURL);
2456                 if (!file)
2457                         return;
2458                 char buf[256] = { 0 };
2459                 file->Read(buf, sizeof(buf));
2460                 file->Close();
2461                 String current_version = ucr::toTString(buf);
2462                 string_replace(current_version, _T("\r\n"), _T(""));
2463                 delete file;
2464
2465                 int exe_vers[4] = { 0 }, cur_vers[4] = { 0 };
2466                 _stscanf(version.GetProductVersion().c_str(), _T("%d.%d.%d.%d"), &exe_vers[0], &exe_vers[1], &exe_vers[2], &exe_vers[3]);
2467                 _stscanf(current_version.c_str(),             _T("%d.%d.%d.%d"), &cur_vers[0], &cur_vers[1], &cur_vers[2], &cur_vers[3]);
2468                 String exe_version_hex = string_format(_T("%08x%08x%08x%08x"), exe_vers[0], exe_vers[1], exe_vers[2], exe_vers[3]);
2469                 String cur_version_hex = string_format(_T("%08x%08x%08x%08x"), cur_vers[0], cur_vers[1], cur_vers[2], cur_vers[3]);
2470
2471                 switch (exe_version_hex.compare(cur_version_hex))
2472                 {
2473                 case 1:
2474                         if (cur_version_hex == _T("00000000000000000000000000000000"))
2475                         {
2476                                 String msg = theApp.LoadString(IDS_CHECKFORUPDATES_FAILED);
2477                                 AfxMessageBox(msg.c_str(), MB_ICONERROR);
2478                                 break;
2479                         }
2480                         // pass through
2481                 case 0:
2482                 {
2483                         String msg = theApp.LoadString(IDS_CHECKFORUPDATES_UPTODATE);
2484                         AfxMessageBox(msg.c_str(), MB_ICONINFORMATION);
2485                         break;
2486                 }
2487                 case -1:
2488                 {
2489                         String msg = LangFormatString2(IDS_CHECKFORUPDATES_NEWVERSION, current_version.c_str(), version.GetProductVersion().c_str());
2490                         if (AfxMessageBox(msg.c_str(), MB_ICONINFORMATION | MB_YESNO) == IDYES)
2491                                 ShellExecute(NULL, _T("open"), DownloadUrl, NULL, NULL, SW_SHOWNORMAL);
2492                         break;
2493                 }
2494                 }
2495         }
2496         catch (CException& e)
2497         {
2498                 TCHAR msg[512];
2499                 e.GetErrorMessage(msg, sizeof(msg)/sizeof(msg[0]));
2500                 AfxMessageBox(msg, MB_ICONERROR);
2501         }
2502 }
2503
2504 /**
2505  * @brief Called when user selects File/Open Conflict...
2506  */
2507 void CMainFrame::OnFileOpenConflict()
2508 {
2509         String conflictFile;
2510         if (SelectFile(GetSafeHwnd(), conflictFile))
2511         {
2512                 DoOpenConflict(conflictFile);
2513         }
2514 }
2515
2516 /**
2517  * @brief Select and open conflict file for resolving.
2518  * This function lets user to select conflict file to resolve.
2519  * Then we parse conflict file to two files to "merge" and
2520  * save resulting file over original file.
2521  *
2522  * Set left-side file read-only as it is the repository file which cannot
2523  * be modified anyway. Right-side file is user's file which is set as
2524  * modified by default so user can just save it and accept workspace
2525  * file as resolved file.
2526  * @param [in] conflictFile Full path to conflict file to open.
2527  * @param [in] checked If true, do not check if it really is project file.
2528  * @return TRUE if conflict file was opened for resolving.
2529  */
2530 BOOL CMainFrame::DoOpenConflict(const String& conflictFile, bool checked)
2531 {
2532         BOOL conflictCompared = FALSE;
2533
2534         if (!checked)
2535         {
2536                 bool confFile = IsConflictFile(conflictFile);
2537                 if (!confFile)
2538                 {
2539                         String message = LangFormatString1(IDS_NOT_CONFLICT_FILE, conflictFile.c_str());
2540                         AfxMessageBox(message.c_str(), MB_ICONSTOP);
2541                         return FALSE;
2542                 }
2543         }
2544
2545         // Create temp files and put them into the list,
2546         // from where they get deleted when MainFrame is deleted.
2547         String ext = paths_FindExtension(conflictFile);
2548         TempFilePtr wTemp(new TempFile());
2549         String workFile = wTemp->Create(_T("confw_"), ext.c_str());
2550         m_tempFiles.push_back(wTemp);
2551         TempFilePtr vTemp(new TempFile());
2552         String revFile = vTemp->Create(_T("confv_"), ext.c_str());
2553         m_tempFiles.push_back(vTemp);
2554
2555         // Parse conflict file into two files.
2556         bool inners;
2557         int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
2558         bool success = ParseConflictFile(conflictFile, workFile, revFile, iGuessEncodingType, inners);
2559
2560         if (success)
2561         {
2562                 // Open two parsed files to WinMerge, telling WinMerge to
2563                 // save over original file (given as third filename).
2564                 theApp.m_strSaveAsPath = conflictFile.c_str();
2565                 String theirs = LoadResString(IDS_CONFLICT_THEIRS_FILE);
2566                 String my = LoadResString(IDS_CONFLICT_MINE_FILE);
2567                 theApp.m_strDescriptions[0] = theirs;
2568                 theApp.m_strDescriptions[1] = my;
2569
2570                 DWORD dwFlags[2] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2571                 conflictCompared = DoFileOpen(&PathContext(revFile, workFile), 
2572                                         dwFlags);
2573         }
2574         else
2575         {
2576                 LangMessageBox(IDS_ERROR_CONF_RESOLVE, MB_ICONSTOP);
2577         }
2578         return conflictCompared;
2579 }
2580
2581 /**
2582  * @brief Get type of frame (File/Folder compare).
2583  * @param [in] pFrame Pointer to frame to check.
2584  * @return FRAMETYPE of the given frame.
2585 */
2586 CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame) const
2587 {
2588         BOOL bMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame));
2589         BOOL bHexMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame));
2590         BOOL bImgMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame));
2591         BOOL bDirFrame = pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame));
2592
2593         if (bMergeFrame)
2594                 return FRAME_FILE;
2595         else if (bHexMergeFrame)
2596                 return FRAME_HEXFILE;
2597         else if (bImgMergeFrame)
2598                 return FRAME_IMGFILE;
2599         else if (bDirFrame)
2600                 return FRAME_FOLDER;
2601         else
2602                 return FRAME_OTHER;
2603 }
2604
2605 /**
2606  * @brief Show the plugins list dialog.
2607  */
2608 void CMainFrame::OnPluginsList()
2609 {
2610         PluginsListDlg dlg;
2611         dlg.DoModal();
2612 }
2613
2614 void CMainFrame::OnDiffOptionsDropDown(NMHDR* pNMHDR, LRESULT* pResult)
2615 {
2616         LPNMTOOLBAR pToolBar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);
2617         ClientToScreen(&(pToolBar->rcButton));
2618         CMenu menu;
2619         VERIFY(menu.LoadMenu(IDR_POPUP_DIFF_OPTIONS));
2620         theApp.TranslateMenu(menu.m_hMenu);
2621         CMenu* pPopup = menu.GetSubMenu(0);
2622         if (NULL != pPopup)
2623         {
2624                 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, 
2625                         pToolBar->rcButton.left, pToolBar->rcButton.bottom, this);
2626         }
2627         *pResult = 0;
2628 }
2629 void CMainFrame::OnDiffWhitespace(UINT nID)
2630 {
2631         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_WHITESPACE, nID - IDC_DIFF_WHITESPACE_COMPARE);
2632         ApplyDiffOptions();
2633 }
2634
2635 void CMainFrame::OnUpdateDiffWhitespace(CCmdUI* pCmdUI)
2636 {
2637         pCmdUI->SetRadio((pCmdUI->m_nID - IDC_DIFF_WHITESPACE_COMPARE) == GetOptionsMgr()->GetInt(OPT_CMP_IGNORE_WHITESPACE));
2638         pCmdUI->Enable();
2639 }
2640
2641 void CMainFrame::OnDiffCaseSensitive()
2642 {
2643         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CASE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2644         ApplyDiffOptions();
2645 }
2646
2647 void CMainFrame::OnUpdateDiffCaseSensitive(CCmdUI* pCmdUI)
2648 {
2649         pCmdUI->SetCheck(!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2650         pCmdUI->Enable();
2651 }
2652
2653 void CMainFrame::OnDiffIgnoreEOL()
2654 {
2655         GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_EOL, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2656         ApplyDiffOptions();
2657 }
2658
2659 void CMainFrame::OnUpdateDiffIgnoreEOL(CCmdUI* pCmdUI)
2660 {
2661         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2662         pCmdUI->Enable();
2663 }
2664
2665 void CMainFrame::OnCompareMethod(UINT nID)
2666
2667         GetOptionsMgr()->SaveOption(OPT_CMP_METHOD, nID - ID_COMPMETHOD_FULL_CONTENTS);
2668 }
2669
2670 void CMainFrame::OnUpdateCompareMethod(CCmdUI* pCmdUI)
2671 {
2672         pCmdUI->SetRadio((pCmdUI->m_nID - ID_COMPMETHOD_FULL_CONTENTS) == GetOptionsMgr()->GetInt(OPT_CMP_METHOD));
2673         pCmdUI->Enable();
2674 }
2675
2676 void CMainFrame::OnMRUs(UINT nID)
2677 {
2678         std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(9);
2679         const int idx = nID - ID_MRU_FIRST;
2680         if (idx < mrus.size())
2681         {
2682                 MergeCmdLineInfo cmdInfo((_T("\"") + mrus[idx].path + _T("\" ") + mrus[idx].params).c_str());
2683                 theApp.ParseArgsAndDoOpen(cmdInfo, this);
2684         }
2685 }
2686
2687 void CMainFrame::OnUpdateNoMRUs(CCmdUI* pCmdUI)
2688 {
2689         // append the MRU submenu
2690         HMENU hMenu = GetSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu, ID_FILE_NEW, false);
2691         if (hMenu == NULL)
2692                 return;
2693         
2694         // empty the menu
2695         int i = ::GetMenuItemCount(hMenu);
2696         while (i --)
2697                 ::DeleteMenu(hMenu, 0, MF_BYPOSITION);
2698
2699         std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(9);
2700
2701         if (mrus.size() == 0)
2702         {
2703                 // no script : create a <empty> entry
2704                 ::AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, theApp.LoadString(IDS_NO_EDIT_SCRIPTS).c_str());
2705         }
2706         else
2707         {
2708                 // or fill in the submenu with the scripts names
2709                 int ID = ID_MRU_FIRST;  // first ID in menu
2710                 for (i = 0 ; i < mrus.size() ; i++, ID++)
2711                         ::AppendMenu(hMenu, MF_STRING, ID, (string_format(_T("&%d "), i+1) + mrus[i].title).c_str());
2712         }
2713
2714         pCmdUI->Enable(true);
2715 }
2716
2717 /**
2718  * @brief Update plugin name
2719  * @param [in] pCmdUI UI component to update.
2720  */
2721 void CMainFrame::OnUpdatePluginName(CCmdUI* pCmdUI)
2722 {
2723         pCmdUI->SetText(_T(""));
2724 }
2725
2726 void CMainFrame::ReloadMenu()
2727 {
2728         // set the menu of the main frame window
2729         UINT idMenu = IDR_MAINFRAME;
2730         CMergeApp *pApp = dynamic_cast<CMergeApp *> (AfxGetApp());
2731         CMainFrame * pMainFrame = dynamic_cast<CMainFrame *> ((CFrameWnd*)pApp->m_pMainWnd);
2732         HMENU hNewDefaultMenu = pMainFrame->NewDefaultMenu(idMenu);
2733         HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
2734         HMENU hNewImgMergeMenu = pMainFrame->NewImgMergeViewMenu();
2735         HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
2736         if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
2737         {
2738                 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
2739                 CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
2740                 CMenu * pNewMergeMenu = CMenu::FromHandle(hNewMergeMenu);
2741                 CMenu * pNewImgMergeMenu = CMenu::FromHandle(hNewImgMergeMenu);
2742                 CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
2743
2744                 CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
2745                 while (pFrame)
2746                 {
2747                         if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2748                                 static_cast<CChildFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2749                         if (pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2750                                 static_cast<CHexMergeFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2751                         if (pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2752                                 static_cast<CImgMergeFrame *>(pFrame)->SetSharedMenu(hNewImgMergeMenu);
2753                         else if (pFrame->IsKindOf(RUNTIME_CLASS(COpenFrame)))
2754                                 static_cast<COpenFrame *>(pFrame)->SetSharedMenu(hNewDefaultMenu);
2755                         else if (pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2756                                 static_cast<CDirFrame *>(pFrame)->SetSharedMenu(hNewDirMenu);
2757                         pFrame = pFrame->GetNextWindow();
2758                 }
2759
2760                 CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
2761                 if (pActiveFrame)
2762                 {
2763                         if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2764                                 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2765                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2766                                 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2767                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2768                                 pMainFrame->MDISetMenu(pNewImgMergeMenu, NULL);
2769                         else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2770                                 pMainFrame->MDISetMenu(pNewDirMenu, NULL);
2771                         else
2772                                 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2773                 }
2774                 else
2775                         pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2776
2777                 // Don't delete the old menu
2778                 // There is a bug in BCMenu or in Windows98 : the new menu does not
2779                 // appear correctly if we destroy the old one
2780                 //                      if (pOldDefaultMenu)
2781                 //                              pOldDefaultMenu->DestroyMenu();
2782                 //                      if (pOldMergeMenu)
2783                 //                              pOldMergeMenu->DestroyMenu();
2784                 //                      if (pOldDirMenu)
2785                 //                              pOldDirMenu->DestroyMenu();
2786
2787                 // m_hMenuDefault is used to redraw the main menu when we close a child frame
2788                 // if this child frame had a different menu
2789                 pMainFrame->m_hMenuDefault = hNewDefaultMenu;
2790                 pApp->m_pOpenTemplate->m_hMenuShared = hNewDefaultMenu;
2791                 pApp->m_pDiffTemplate->m_hMenuShared = hNewMergeMenu;
2792                 pApp->m_pDirTemplate->m_hMenuShared = hNewDirMenu;
2793
2794                 // force redrawing the menu bar
2795                 pMainFrame->DrawMenuBar();
2796         }
2797 }
2798
2799 void CMainFrame::UpdateDocTitle()
2800 {
2801         CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
2802         POSITION posTemplate = pDocManager->GetFirstDocTemplatePosition();
2803         ASSERT(posTemplate != NULL);
2804
2805         while (posTemplate != NULL)
2806         {
2807                 CDocTemplate* pTemplate = pDocManager->GetNextDocTemplate(posTemplate);
2808
2809                 ASSERT(pTemplate != NULL);
2810
2811                 POSITION pos = pTemplate->GetFirstDocPosition();
2812                 CDocument* pDoc;
2813
2814                 while (pos != NULL)
2815                 {
2816                         pDoc = pTemplate->GetNextDoc(pos);
2817                         pDoc->SetTitle(NULL);
2818                         ((CFrameWnd*)AfxGetApp()->m_pMainWnd)->OnUpdateFrameTitle(TRUE);
2819                 }
2820         }
2821 }