1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
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.
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.
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.
20 /////////////////////////////////////////////////////////////////////////////
24 * @brief Implementation of the CMainFrame class
31 #include <boost/range/mfc.hpp>
32 #include "Constants.h"
34 #include "FileFilterHelper.h"
35 #include "UnicodeString.h"
38 #include "DirFrame.h" // Include type information
40 #include "HexMergeFrm.h"
45 #include "MergeEditView.h"
46 #include "HexMergeDoc.h"
47 #include "HexMergeView.h"
48 #include "ImgMergeFrm.h"
49 #include "LineFiltersList.h"
50 #include "ConflictFileParser.h"
51 #include "LineFiltersDlg.h"
53 #include "Environment.h"
54 #include "PatchTool.h"
56 #include "ConfigLog.h"
58 #include "Merge7zFormatMergePluginImpl.h"
59 #include "FileFiltersDlg.h"
60 #include "OptionsMgr.h"
61 #include "OptionsDef.h"
62 #include "codepage_detect.h"
64 #include "PreferencesDlg.h"
65 #include "FileOrFolderSelect.h"
66 #include "PluginsListDlg.h"
67 #include "stringdiffs.h"
68 #include "MergeCmdLineInfo.h"
69 #include "OptionsFont.h"
71 #include "DropHandler.h"
72 #include "LanguageSelect.h"
73 #include "VersionInfo.h"
75 #include "CCrystalTextMarkers.h"
85 static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale, CImageList& ImgList);
86 static CPtrList &GetDocList(CMultiDocTemplate *pTemplate);
87 template<class DocClass>
88 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles);
91 * @brief A table associating menuitem id, icon and menus to apply.
93 const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
94 { ID_FILE_OPENCONFLICT, IDB_FILE_OPENCONFLICT, CMainFrame::MENU_ALL },
95 { ID_EDIT_COPY, IDB_EDIT_COPY, CMainFrame::MENU_ALL },
96 { ID_EDIT_CUT, IDB_EDIT_CUT, CMainFrame::MENU_ALL },
97 { ID_EDIT_PASTE, IDB_EDIT_PASTE, CMainFrame::MENU_ALL },
98 { ID_EDIT_FIND, IDB_EDIT_SEARCH, CMainFrame::MENU_ALL },
99 { ID_WINDOW_CASCADE, IDB_WINDOW_CASCADE, CMainFrame::MENU_ALL },
100 { ID_WINDOW_TILE_HORZ, IDB_WINDOW_HORIZONTAL, CMainFrame::MENU_ALL },
101 { ID_WINDOW_TILE_VERT, IDB_WINDOW_VERTICAL, CMainFrame::MENU_ALL },
102 { ID_FILE_CLOSE, IDB_WINDOW_CLOSE, CMainFrame::MENU_ALL },
103 { ID_WINDOW_CHANGE_PANE, IDB_WINDOW_CHANGEPANE, CMainFrame::MENU_ALL },
104 { ID_EDIT_WMGOTO, IDB_EDIT_GOTO, CMainFrame::MENU_ALL },
105 { ID_EDIT_REPLACE, IDB_EDIT_REPLACE, CMainFrame::MENU_ALL },
106 { ID_VIEW_SELECTFONT, IDB_VIEW_SELECTFONT, CMainFrame::MENU_ALL },
107 { ID_APP_EXIT, IDB_FILE_EXIT, CMainFrame::MENU_ALL },
108 { ID_HELP_CONTENTS, IDB_HELP_CONTENTS, CMainFrame::MENU_ALL },
109 { ID_EDIT_SELECT_ALL, IDB_EDIT_SELECTALL, CMainFrame::MENU_ALL },
110 { ID_TOOLS_FILTERS, IDB_TOOLS_FILTERS, CMainFrame::MENU_ALL },
111 { ID_TOOLS_CUSTOMIZECOLUMNS, IDB_TOOLS_COLUMNS, CMainFrame::MENU_ALL },
112 { ID_TOOLS_GENERATEPATCH, IDB_TOOLS_GENERATEPATCH, CMainFrame::MENU_ALL },
113 { ID_PLUGINS_LIST, IDB_PLUGINS_LIST, CMainFrame::MENU_ALL },
114 { ID_COPY_FROM_LEFT, IDB_COPY_FROM_LEFT, CMainFrame::MENU_ALL },
115 { ID_COPY_FROM_RIGHT, IDB_COPY_FROM_RIGHT, CMainFrame::MENU_ALL },
116 { ID_FILE_PRINT, IDB_FILE_PRINT, CMainFrame::MENU_FILECMP },
117 { ID_TOOLS_GENERATEREPORT, IDB_TOOLS_GENERATEREPORT, CMainFrame::MENU_FILECMP },
118 { ID_EDIT_TOGGLE_BOOKMARK, IDB_EDIT_TOGGLE_BOOKMARK, CMainFrame::MENU_FILECMP },
119 { ID_EDIT_GOTO_NEXT_BOOKMARK, IDB_EDIT_GOTO_NEXT_BOOKMARK, CMainFrame::MENU_FILECMP },
120 { ID_EDIT_GOTO_PREV_BOOKMARK, IDB_EDIT_GOTO_PREV_BOOKMARK, CMainFrame::MENU_FILECMP },
121 { ID_EDIT_CLEAR_ALL_BOOKMARKS, IDB_EDIT_CLEAR_ALL_BOOKMARKS, CMainFrame::MENU_FILECMP },
122 { ID_VIEW_ZOOMIN, IDB_VIEW_ZOOMIN, CMainFrame::MENU_FILECMP },
123 { ID_VIEW_ZOOMOUT, IDB_VIEW_ZOOMOUT, CMainFrame::MENU_FILECMP },
124 { ID_MERGE_COMPARE, IDB_MERGE_COMPARE, CMainFrame::MENU_FOLDERCMP },
125 { ID_MERGE_COMPARE_LEFT1_LEFT2, IDB_MERGE_COMPARE_LEFT1_LEFT2, CMainFrame::MENU_FOLDERCMP },
126 { ID_MERGE_COMPARE_RIGHT1_RIGHT2, IDB_MERGE_COMPARE_RIGHT1_RIGHT2,CMainFrame::MENU_FOLDERCMP },
127 { ID_MERGE_COMPARE_LEFT1_RIGHT2, IDB_MERGE_COMPARE_LEFT1_RIGHT2, CMainFrame::MENU_FOLDERCMP },
128 { ID_MERGE_COMPARE_LEFT2_RIGHT1, IDB_MERGE_COMPARE_LEFT2_RIGHT1, CMainFrame::MENU_FOLDERCMP },
129 { ID_MERGE_DELETE, IDB_MERGE_DELETE, CMainFrame::MENU_FOLDERCMP },
130 { ID_TOOLS_GENERATEREPORT, IDB_TOOLS_GENERATEREPORT, CMainFrame::MENU_FOLDERCMP },
131 { ID_DIR_COPY_LEFT_TO_RIGHT, IDB_LEFT_TO_RIGHT, CMainFrame::MENU_FOLDERCMP },
132 { ID_DIR_COPY_LEFT_TO_MIDDLE, IDB_LEFT_TO_MIDDLE, CMainFrame::MENU_FOLDERCMP },
133 { ID_DIR_COPY_RIGHT_TO_LEFT, IDB_RIGHT_TO_LEFT, CMainFrame::MENU_FOLDERCMP },
134 { ID_DIR_COPY_RIGHT_TO_MIDDLE, IDB_RIGHT_TO_MIDDLE, CMainFrame::MENU_FOLDERCMP },
135 { ID_DIR_COPY_MIDDLE_TO_LEFT, IDB_MIDDLE_TO_LEFT, CMainFrame::MENU_FOLDERCMP },
136 { ID_DIR_COPY_MIDDLE_TO_RIGHT, IDB_MIDDLE_TO_RIGHT, CMainFrame::MENU_FOLDERCMP },
137 { ID_DIR_COPY_LEFT_TO_BROWSE, IDB_LEFT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
138 { ID_DIR_COPY_MIDDLE_TO_BROWSE, IDB_MIDDLE_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
139 { ID_DIR_COPY_RIGHT_TO_BROWSE, IDB_RIGHT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
140 { ID_DIR_MOVE_LEFT_TO_BROWSE, IDB_MOVE_LEFT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
141 { ID_DIR_MOVE_MIDDLE_TO_BROWSE, IDB_MOVE_MIDDLE_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
142 { ID_DIR_MOVE_RIGHT_TO_BROWSE, IDB_MOVE_RIGHT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
143 { ID_DIR_DEL_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
144 { ID_DIR_DEL_MIDDLE, IDB_MIDDLE, CMainFrame::MENU_FOLDERCMP },
145 { ID_DIR_DEL_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
146 { ID_DIR_DEL_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
147 { ID_DIR_DEL_ALL, IDB_ALL, CMainFrame::MENU_FOLDERCMP },
148 { ID_DIR_COPY_PATHNAMES_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
149 { ID_DIR_COPY_PATHNAMES_MIDDLE, IDB_MIDDLE, CMainFrame::MENU_FOLDERCMP },
150 { ID_DIR_COPY_PATHNAMES_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
151 { ID_DIR_COPY_PATHNAMES_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
152 { ID_DIR_COPY_PATHNAMES_ALL, IDB_ALL, CMainFrame::MENU_FOLDERCMP },
153 { ID_DIR_COPY_LEFT_TO_CLIPBOARD, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
154 { ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, IDB_MIDDLE, CMainFrame::MENU_FOLDERCMP },
155 { ID_DIR_COPY_RIGHT_TO_CLIPBOARD, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
156 { ID_DIR_COPY_BOTH_TO_CLIPBOARD, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
157 { ID_DIR_COPY_ALL_TO_CLIPBOARD, IDB_ALL, CMainFrame::MENU_FOLDERCMP },
158 { ID_DIR_ZIP_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
159 { ID_DIR_ZIP_MIDDLE, IDB_MIDDLE, CMainFrame::MENU_FOLDERCMP },
160 { ID_DIR_ZIP_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
161 { ID_DIR_ZIP_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
162 { ID_DIR_ZIP_ALL, IDB_ALL, CMainFrame::MENU_FOLDERCMP }
166 /////////////////////////////////////////////////////////////////////////////
169 IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
171 BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
172 //{{AFX_MSG_MAP(CMainFrame)
175 ON_WM_INITMENUPOPUP()
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)
188 ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
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)
212 ON_COMMAND_RANGE(ID_TOOLBAR_NONE, ID_TOOLBAR_HUGE, OnToolbarSize)
213 ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLBAR_NONE, ID_TOOLBAR_HUGE, OnUpdateToolbarSize)
214 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
215 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
216 ON_COMMAND(ID_HELP_RELEASENOTES, OnHelpReleasenotes)
217 ON_COMMAND(ID_HELP_TRANSLATIONS, OnHelpTranslations)
218 ON_COMMAND(ID_FILE_OPENCONFLICT, OnFileOpenConflict)
219 ON_COMMAND(ID_PLUGINS_LIST, OnPluginsList)
220 ON_UPDATE_COMMAND_UI(ID_STATUS_PLUGIN, OnUpdatePluginName)
221 ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDiffOptionsDropDown)
222 ON_COMMAND_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnDiffWhitespace)
223 ON_UPDATE_COMMAND_UI_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnUpdateDiffWhitespace)
224 ON_COMMAND(IDC_DIFF_CASESENSITIVE, OnDiffCaseSensitive)
225 ON_UPDATE_COMMAND_UI(IDC_DIFF_CASESENSITIVE, OnUpdateDiffCaseSensitive)
226 ON_COMMAND(IDC_DIFF_IGNOREEOL, OnDiffIgnoreEOL)
227 ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNOREEOL, OnUpdateDiffIgnoreEOL)
228 ON_COMMAND(IDC_RECURS_CHECK, OnIncludeSubfolders)
229 ON_UPDATE_COMMAND_UI(IDC_RECURS_CHECK, OnUpdateIncludeSubfolders)
230 ON_COMMAND_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnCompareMethod)
231 ON_UPDATE_COMMAND_UI_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnUpdateCompareMethod)
232 ON_COMMAND_RANGE(ID_MRU_FIRST, ID_MRU_LAST, OnMRUs)
233 ON_UPDATE_COMMAND_UI(ID_MRU_FIRST, OnUpdateNoMRUs)
234 ON_UPDATE_COMMAND_UI(ID_NO_MRU, OnUpdateNoMRUs)
239 * @brief MainFrame statusbar panels/indicators
241 static UINT StatusbarIndicators[] =
243 ID_SEPARATOR, // Plugin name
244 ID_SEPARATOR, // status line indicator
245 ID_SEPARATOR, // Merge mode
246 ID_SEPARATOR, // Diff number
247 ID_INDICATOR_CAPS, // Caps Lock
248 ID_INDICATOR_NUM, // Num Lock
249 ID_INDICATOR_OVR, // Insert
253 * @brief Return a const reference to a CMultiDocTemplate's list of documents.
255 static CPtrList &GetDocList(CMultiDocTemplate *pTemplate)
257 struct Template : public CMultiDocTemplate
260 using CMultiDocTemplate::m_docList;
262 return static_cast<struct Template *>(pTemplate)->m_docList;
265 /////////////////////////////////////////////////////////////////////////////
266 // CMainFrame construction/destruction
269 * @brief MainFrame constructor. Loads settings from registry.
270 * @todo Preference for logging?
272 CMainFrame::CMainFrame()
274 , m_pDropHandler(NULL)
275 , m_bShowErrors(false)
279 CMainFrame::~CMainFrame()
281 GetOptionsMgr()->SaveOption(OPT_TABBAR_AUTO_MAXWIDTH, m_wndTabBar.GetAutoMaxWidth());
286 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassW");
288 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassA");
291 * @brief Change MainFrame window class name
292 * see http://support.microsoft.com/kb/403825/ja
294 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
297 BOOL bRes = CMDIFrameWnd::PreCreateWindow(cs);
298 HINSTANCE hInst = AfxGetInstanceHandle();
299 // see if the class already exists
300 if (!::GetClassInfo(hInst, szClassName, &wndcls))
303 ::GetClassInfo(hInst, cs.lpszClass, &wndcls);
304 // register a new class
305 wndcls.lpszClassName = szClassName;
306 wndcls.hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(IDR_MAINFRAME));
307 ::RegisterClass(&wndcls);
309 cs.lpszClass = szClassName;
313 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
315 if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
318 m_lfDiff = Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP);
319 m_lfDir = Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP);
321 if (!CreateToolbar())
323 TRACE0("Failed to create toolbar\n");
324 return -1; // fail to create
327 if (!m_wndTabBar.Create(this))
329 TRACE0("Failed to create tab bar\n");
330 return -1; // fail to create
332 m_wndTabBar.SetAutoMaxWidth(GetOptionsMgr()->GetBool(OPT_TABBAR_AUTO_MAXWIDTH));
334 if (GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR) == false)
335 CMDIFrameWnd::ShowControlBar(&m_wndTabBar, false, 0);
337 if (!m_wndStatusBar.Create(this))
339 TRACE0("Failed to create status bar\n");
340 return -1; // fail to create
342 theApp.SetIndicators(m_wndStatusBar, StatusbarIndicators,
343 countof(StatusbarIndicators));
345 const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
346 auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
347 m_wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH | SBPS_NOBORDERS, 0);
348 m_wndStatusBar.SetPaneInfo(1, ID_STATUS_PLUGIN, 0, pointToPixel(225));
349 m_wndStatusBar.SetPaneInfo(2, ID_STATUS_MERGINGMODE, 0, pointToPixel(75));
350 m_wndStatusBar.SetPaneInfo(3, ID_STATUS_DIFFNUM, 0, pointToPixel(112));
352 if (GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR) == false)
353 CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, false, 0);
355 m_pDropHandler = new DropHandler(std::bind(&CMainFrame::OnDropFiles, this, std::placeholders::_1));
356 RegisterDragDrop(m_hWnd, m_pDropHandler);
361 void CMainFrame::OnDestroy(void)
364 RevokeDragDrop(m_hWnd);
367 static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
370 for (i = 0 ; i < ::GetMenuItemCount(mainMenu) ; i++)
371 if (::GetMenuItemID(::GetSubMenu(mainMenu, i), 0) == nIDFirstMenuItem)
373 HMENU menu = ::GetSubMenu(mainMenu, i);
377 // look for last submenu
378 for (i = ::GetMenuItemCount(menu) ; i >= 0 ; i--)
379 if (::GetSubMenu(menu, i) != NULL)
380 return ::GetSubMenu(menu, i);
384 // look for first submenu
385 for (i = 0 ; i < ::GetMenuItemCount(menu) ; i++)
386 if (::GetSubMenu(menu, i) != NULL)
387 return ::GetSubMenu(menu, i);
390 // error, submenu not found
395 * @brief Find the scripts submenu from the main menu
396 * As now this is the first submenu in "Edit" menu
397 * We find the "Edit" menu by looking for a menu
398 * starting with ID_EDIT_UNDO.
400 HMENU CMainFrame::GetScriptsSubmenu(HMENU mainMenu)
402 return GetSubmenu(mainMenu, ID_PLUGINS_LIST, false);
406 * @brief Find the scripts submenu from the main menu
407 * As now this is the first submenu in "Plugins" menu
408 * We find the "Plugins" menu by looking for a menu
409 * starting with ID_UNPACK_MANUAL.
411 HMENU CMainFrame::GetPrediffersSubmenu(HMENU mainMenu)
413 return GetSubmenu(mainMenu, ID_PLUGINS_LIST, true);
417 * @brief Create a new menu for the view..
418 * @param [in] view Menu view either MENU_DEFAULT, MENU_MERGEVIEW or MENU_DIRVIEW.
419 * @param [in] ID Menu's resource ID.
420 * @return Menu for the view.
422 HMENU CMainFrame::NewMenu(int view, int ID)
424 int menu_view, index;
425 if (m_pMenus[view] == NULL)
427 m_pMenus[view].reset(new BCMenu());
428 if (m_pMenus[view] == NULL)
435 menu_view = MENU_FILECMP;
438 menu_view = MENU_FOLDERCMP;
442 menu_view = MENU_MAINFRM;
446 if (!m_pMenus[view]->LoadMenu(ID))
452 if (view == MENU_IMGMERGEVIEW)
454 BCMenu *pMenu = new BCMenu;
455 pMenu->LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
456 m_pMenus[view]->InsertMenu(4, MF_BYPOSITION | MF_POPUP, (UINT_PTR)pMenu->GetSubMenu(0)->m_hMenu, const_cast<TCHAR *>(LoadResString(IDS_IMAGE_MENU).c_str()));
459 // Load bitmaps to menuitems
460 for (index = 0; index < countof(m_MenuIcons); index ++)
462 if (menu_view == (m_MenuIcons[index].menusToApply & menu_view))
464 m_pMenus[view]->ModifyODMenu(NULL, m_MenuIcons[index].menuitemID, m_MenuIcons[index].iconResID);
468 m_pMenus[view]->LoadToolbar(IDR_MAINFRAME);
470 theApp.TranslateMenu(m_pMenus[view]->m_hMenu);
472 return (m_pMenus[view]->Detach());
476 * @brief Create new default (CMainFrame) menu.
478 HMENU CMainFrame::NewDefaultMenu(int ID /*=0*/)
482 return NewMenu( MENU_DEFAULT, ID );
486 * @brief Create new File compare (CMergeEditView) menu.
488 HMENU CMainFrame::NewMergeViewMenu()
490 return NewMenu( MENU_MERGEVIEW, IDR_MERGEDOCTYPE);
494 * @brief Create new Dir compare (CDirView) menu
496 HMENU CMainFrame::NewDirViewMenu()
498 return NewMenu(MENU_DIRVIEW, IDR_DIRDOCTYPE );
502 * @brief Create new File compare (CHexMergeView) menu.
504 HMENU CMainFrame::NewHexMergeViewMenu()
506 return NewMenu( MENU_HEXMERGEVIEW, IDR_MERGEDOCTYPE);
510 * @brief Create new Image compare (CImgMergeView) menu.
512 HMENU CMainFrame::NewImgMergeViewMenu()
514 return NewMenu( MENU_IMGMERGEVIEW, IDR_MERGEDOCTYPE);
518 * @brief Create new File compare (COpenView) menu.
520 HMENU CMainFrame::NewOpenViewMenu()
522 return NewMenu( MENU_OPENVIEW, IDR_MAINFRAME);
526 * @brief This handler ensures that the popup menu items are drawn correctly.
528 void CMainFrame::OnMeasureItem(int nIDCtl,
529 LPMEASUREITEMSTRUCT lpMeasureItemStruct)
531 BOOL setflag = FALSE;
532 if (lpMeasureItemStruct->CtlType == ODT_MENU)
534 if (IsMenu(reinterpret_cast<HMENU>(static_cast<uintptr_t>(lpMeasureItemStruct->itemID))))
537 CMenu::FromHandle(reinterpret_cast<HMENU>(static_cast<uintptr_t>(lpMeasureItemStruct->itemID)));
539 if (m_pMenus[MENU_DEFAULT]->IsMenu(cmenu))
541 m_pMenus[MENU_DEFAULT]->MeasureItem(lpMeasureItemStruct);
548 CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
552 * @brief This handler ensures that keyboard shortcuts work.
554 LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags,
558 if(m_pMenus[MENU_DEFAULT]->IsMenu(pMenu))
559 lresult=BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
561 lresult=CMDIFrameWnd::OnMenuChar(nChar, nFlags, pMenu);
566 * @brief This handler updates the menus from time to time.
568 void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
570 CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
574 if (BCMenu::IsMenu(pPopupMenu))
576 BCMenu::UpdateMenu(pPopupMenu);
581 /////////////////////////////////////////////////////////////////////////////
582 // CMainFrame message handlers
584 void CMainFrame::OnFileOpen()
590 * @brief Check for BOM, and also, if bGuessEncoding, try to deduce codepage
592 * Unpacks info from FileLocation & delegates all work to codepage_detect module
595 FileLocationGuessEncodings(FileLocation & fileloc, int iGuessEncoding)
597 fileloc.encoding = GuessCodepageEncoding(fileloc.filepath, iGuessEncoding);
600 bool CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
601 int nFiles, const FileLocation ifileloc[],
602 const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
603 const PackingInfo * infoUnpacker /*= NULL*/)
606 FileFilterHelper filterImg, filterBin;
607 filterImg.UseMask(true);
608 filterImg.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
609 filterBin.UseMask(true);
610 filterBin.SetMask(GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS));
611 for (pane = 0; pane < nFiles; ++pane)
613 if (filterImg.includeFile(ifileloc[pane].filepath) && CImgMergeFrame::IsLoadable())
614 return ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
615 else if (filterBin.includeFile(ifileloc[pane].filepath) && CHexMergeView::IsLoadable())
616 return ShowHexMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
618 return ShowMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
621 std::array<bool, 3> GetROFromFlags(int nFiles, const DWORD dwFlags[])
623 std::array<bool, 3> bRO = { false, false, false };
624 for (int pane = 0; pane < nFiles; pane++)
627 bRO[pane] = ((dwFlags[pane] & FFILEOPEN_READONLY) > 0);
632 int GetActivePaneFromFlags(int nFiles, const DWORD dwFlags[])
634 int nActivePane = -1;
635 for (int pane = 0; pane < nFiles; ++pane)
637 if (dwFlags && (dwFlags[pane] & FFILEOPEN_SETFOCUS))
644 * @brief Creates new MergeDoc instance and shows documents.
645 * @param [in] pDirDoc Dir compare document to create a new Merge document for.
646 * @param [in] ifilelocLeft Left side file location info.
647 * @param [in] ifilelocRight Right side file location info.
648 * @param [in] dwLeftFlags Left side flags.
649 * @param [in] dwRightFlags Right side flags.
650 * @param [in] infoUnpacker Plugin info.
651 * @return success/failure
653 bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
654 int nFiles, const FileLocation ifileloc[],
655 const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
656 const PackingInfo * infoUnpacker /*= NULL*/)
658 if (!m_pMenus[MENU_MERGEVIEW])
659 theApp.m_pDiffTemplate->m_hMenuShared = NewMergeViewMenu();
660 CMergeDoc * pMergeDoc = GetMergeDocForDiff<CMergeDoc>(theApp.m_pDiffTemplate, pDirDoc, nFiles);
662 // Make local copies, so we can change encoding if we guess it below
663 FileLocation fileloc[3];
664 std::copy_n(ifileloc, nFiles, fileloc);
666 ASSERT(pMergeDoc); // must ASSERT to get an answer to the question below ;-)
668 return false; // when does this happen ?
670 // if an unpacker is selected, it must be used during LoadFromFile
671 // MergeDoc must memorize it for SaveToFile
672 // Warning : this unpacker may differ from the pDirDoc one
673 // (through menu : "Plugins"->"Open with unpacker")
674 pMergeDoc->SetUnpacker(infoUnpacker);
677 int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
678 for (int pane = 0; pane < nFiles; pane++)
680 if (fileloc[pane].encoding.m_unicoding == -1)
681 fileloc[pane].encoding.m_unicoding = ucr::NONE;
682 if (fileloc[pane].encoding.m_unicoding == ucr::NONE && fileloc[pane].encoding.m_codepage == -1)
684 FileLocationGuessEncodings(fileloc[pane], iGuessEncodingType);
687 // TODO (Perry, 2005-12-04)
688 // Should we do any unification if unicodings are different?
692 // In ANSI (8-bit) build, character loss can occur in merging
693 // if the two buffers use different encodings
694 if (pane > 0 && fileloc[pane - 1].encoding.m_codepage != fileloc[pane].encoding.m_codepage)
697 msg.Format(theApp.LoadString(IDS_SUGGEST_IGNORECODEPAGE).c_str(), fileloc[pane - 1].encoding.m_codepage,fileloc[pane].encoding.m_codepage);
698 int msgflags = MB_YESNO | MB_ICONQUESTION | MB_DONT_ASK_AGAIN;
699 // Two files with different codepages
700 // Warn and propose to use the default codepage for both
701 int userChoice = AfxMessageBox(msg, msgflags);
702 if (userChoice == IDYES)
704 fileloc[pane - 1].encoding.SetCodepage(ucr::getDefaultCodepage());
705 fileloc[pane - 1].encoding.m_bom = false;
706 fileloc[pane].encoding.SetCodepage(ucr::getDefaultCodepage());
707 fileloc[pane].encoding.m_bom = false;
713 // Note that OpenDocs() takes care of closing compare window when needed.
714 if (!pMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
717 for (int pane = 0; pane < nFiles; pane++)
721 BOOL bModified = (dwFlags[pane] & FFILEOPEN_MODIFIED) > 0;
724 pMergeDoc->m_ptBuf[pane]->SetModified(TRUE);
725 pMergeDoc->UpdateHeaderPath(pane);
727 if (dwFlags[pane] & FFILEOPEN_AUTOMERGE)
729 pMergeDoc->DoAutoMerge(pane);
734 if (!sReportFile.empty())
735 pMergeDoc->GenerateReport(sReportFile);
740 bool CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
741 const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
742 const PackingInfo * infoUnpacker /*= NULL*/)
744 if (!m_pMenus[MENU_HEXMERGEVIEW])
745 theApp.m_pHexMergeTemplate->m_hMenuShared = NewHexMergeViewMenu();
746 CHexMergeDoc *pHexMergeDoc = GetMergeDocForDiff<CHexMergeDoc>(theApp.m_pHexMergeTemplate, pDirDoc, nFiles);
750 if (!pHexMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
753 if (!sReportFile.empty())
754 pHexMergeDoc->GenerateReport(sReportFile);
759 bool CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
760 const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
761 const PackingInfo * infoUnpacker/* = NULL*/)
763 CImgMergeFrame *pImgMergeFrame = new CImgMergeFrame();
764 if (!CImgMergeFrame::menu.m_hMenu)
765 CImgMergeFrame::menu.m_hMenu = NewImgMergeViewMenu();
766 pImgMergeFrame->SetSharedMenu(CImgMergeFrame::menu.m_hMenu);
768 pImgMergeFrame->SetDirDoc(pDirDoc);
769 pDirDoc->AddMergeDoc(pImgMergeFrame);
771 if (!pImgMergeFrame->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags), this))
772 return ShowMergeDoc(pDirDoc, nFiles, fileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
774 for (int pane = 0; pane < nFiles; pane++)
776 if (dwFlags && (dwFlags[pane] & FFILEOPEN_AUTOMERGE))
777 pImgMergeFrame->DoAutoMerge(pane);
780 if (!sReportFile.empty())
781 pImgMergeFrame->GenerateReport(sReportFile);
787 * @brief Show GNU licence information in notepad (local file) or in Web Browser
789 void CMainFrame::OnHelpGnulicense()
791 const String spath = paths::ConcatPath(env::GetProgPath(), LicenseFile);
792 theApp.OpenFileOrUrl(spath.c_str(), LicenceUrl);
796 * @brief Opens Options-dialog and saves changed options
798 void CMainFrame::OnOptions()
800 // Using singleton shared syntax colors
801 CPreferencesDlg dlg(GetOptionsMgr(), theApp.GetMainSyntaxColors());
802 INT_PTR rv = dlg.DoModal();
806 LANGID lang = static_cast<LANGID>(GetOptionsMgr()->GetInt(OPT_SELECTED_LANGUAGE));
807 if (lang != theApp.m_pLangDlg->GetLangId())
809 theApp.m_pLangDlg->SetLanguage(lang, TRUE);
811 // Update status bar inicator texts
812 theApp.SetIndicators(m_wndStatusBar, 0, 0);
814 // Update the current menu
817 // update the title text of the document
823 // Set new temporary path
824 theApp.SetupTempPath();
826 // Set new filterpath
827 String filterPath = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
828 theApp.m_pGlobalFileFilter->SetUserFilterPath(filterPath);
830 theApp.UpdateCodepageModule();
832 strdiff::SetBreakChars(GetOptionsMgr()->GetString(OPT_BREAK_SEPARATORS).c_str());
834 // make an attempt at rescanning any open diff sessions
837 // Update all dirdoc settings
838 for (auto pDirDoc : GetAllDirDocs())
839 pDirDoc->RefreshOptions();
840 for (auto pOpenDoc : GetAllOpenDocs())
841 pOpenDoc->RefreshOptions();
842 for (auto pMergeDoc : GetAllHexMergeDocs())
843 pMergeDoc->RefreshOptions();
847 static bool AddToRecentDocs(const PathContext& paths, const unsigned flags[], bool recurse, const String& filter)
849 String params, title;
850 for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
852 if (flags && (flags[nIndex] & FFILEOPEN_READONLY))
856 case 0: params += _T("/wl "); break;
857 case 1: params += paths.GetSize() == 2 ? _T("/wr ") : _T("/wm "); break;
858 case 2: params += _T("/wr "); break;
861 params += _T("\"") + paths[nIndex] + _T("\" ");
863 String path = paths[nIndex];
864 paths::normalize(path);
865 title += paths::FindFileName(path);
866 if (nIndex < paths.GetSize() - 1)
872 params += _T("/f \"") + filter + _T("\" ");
873 return JumpList::AddToRecentDocs(_T(""), params, title, params, 0);
877 * @brief Begin a diff: open dirdoc if it is directories, else open a mergedoc for editing.
878 * @param [in] pszLeft Left-side path.
879 * @param [in] pszRight Right-side path.
880 * @param [in] dwLeftFlags Left-side flags.
881 * @param [in] dwRightFlags Right-side flags.
882 * @param [in] bRecurse Do we run recursive (folder) compare?
883 * @param [in] pDirDoc Dir compare document to use.
884 * @param [in] prediffer Prediffer plugin name.
885 * @return TRUE if opening files and compare succeeded, FALSE otherwise.
887 BOOL CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
888 const DWORD dwFlags[] /*=NULL*/, const String strDesc[] /*=NULL*/, const String& sReportFile /*=T("")*/, bool bRecurse /*=FALSE*/, CDirDoc *pDirDoc/*=NULL*/,
889 String prediffer /*=_T("")*/, const PackingInfo *infoUnpacker/*=NULL*/)
891 if (pDirDoc && !pDirDoc->CloseMergeDocs())
894 FileTransform::g_bUnpackerMode = theApp.GetProfileInt(_T("Settings"), _T("UnpackerMode"), PLUGIN_MANUAL);
895 FileTransform::g_bPredifferMode = theApp.GetProfileInt(_T("Settings"), _T("PredifferMode"), PLUGIN_MANUAL);
897 Merge7zFormatMergePluginScope scope(infoUnpacker);
905 bRO[0] = (dwFlags[0] & FFILEOPEN_READONLY) != 0;
906 bRO[1] = (dwFlags[1] & FFILEOPEN_READONLY) != 0;
907 bRO[2] = (dwFlags[2] & FFILEOPEN_READONLY) != 0;
910 // pop up dialog unless arguments exist (and are compatible)
911 paths::PATH_EXISTENCE pathsType = paths::GetPairComparability(tFiles, IsArchiveFile);
912 if (pathsType == paths::DOES_NOT_EXIST)
914 if (!m_pMenus[MENU_OPENVIEW])
915 theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
916 COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
919 pOpenDoc->m_dwFlags[0] = dwFlags[0];
920 pOpenDoc->m_dwFlags[1] = dwFlags[1];
921 pOpenDoc->m_dwFlags[2] = dwFlags[2];
923 pOpenDoc->m_files = tFiles;
924 pOpenDoc->m_bRecurse = bRecurse;
926 pOpenDoc->m_infoHandler = *infoUnpacker;
927 CFrameWnd *pFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
928 theApp.m_pOpenTemplate->InitialUpdateFrame(pFrame, pOpenDoc);
933 // Add trailing '\' for directories if its missing
934 if (pathsType == paths::IS_EXISTING_DIR)
936 if (!paths::EndsWithSlash(tFiles[0]) && !IsArchiveFile(tFiles[0]))
937 tFiles[0] = paths::AddTrailingSlash(tFiles[0]);
938 if (!paths::EndsWithSlash(tFiles[1]) && !IsArchiveFile(tFiles[1]))
939 tFiles[1] = paths::AddTrailingSlash(tFiles[1]);
940 if (tFiles.GetSize() == 3 && !paths::EndsWithSlash(tFiles[2]) && !IsArchiveFile(tFiles[1]))
941 tFiles[2] = paths::AddTrailingSlash(tFiles[2]);
944 //save the MRU left and right files.
947 if (!(dwFlags[0] & FFILEOPEN_NOMRU))
948 addToMru(tFiles[0].c_str(), _T("Files\\Left"));
949 if (!(dwFlags[1] & FFILEOPEN_NOMRU))
950 addToMru(tFiles[1].c_str(), _T("Files\\Right"));
951 if (tFiles.GetSize() == 3 && !(dwFlags[2] & FFILEOPEN_NOMRU))
952 addToMru(tFiles[2].c_str(), _T("Files\\Option"));
956 CTempPathContext *pTempPathContext = NULL;
957 if (pathsType == paths::IS_EXISTING_DIR)
959 DecompressResult res= DecompressArchive(m_hWnd, tFiles);
960 if (res.pTempPathContext)
962 pathsType = res.pathsType;
964 pTempPathContext = res.pTempPathContext;
968 // Determine if we want a new dirview open, now that we know if it was
969 // an archive. Don't open a new dirview if we are comparing files.
972 if (pathsType == paths::IS_EXISTING_DIR)
974 CDirDoc::m_nDirsTemp = tFiles.GetSize();
975 if (!m_pMenus[MENU_DIRVIEW])
976 theApp.m_pDirTemplate->m_hMenuShared = NewDirViewMenu();
977 pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->OpenDocumentFile(NULL));
981 pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
986 if (pathsType == paths::IS_EXISTING_DIR)
990 // Anything that can go wrong inside InitCompare() will yield an
991 // exception. There is no point in checking return value.
992 pDirDoc->InitCompare(tFiles, bRecurse, pTempPathContext);
994 pDirDoc->SetReportFile(sReportFile);
995 pDirDoc->SetDescriptions(strDesc);
996 pDirDoc->SetTitle(NULL);
997 for (int nIndex = 0; nIndex < tFiles.GetSize(); nIndex++)
998 pDirDoc->SetReadOnly(nIndex, bRO[nIndex]);
1005 FileLocation fileloc[3];
1007 for (int nPane = 0; nPane < tFiles.GetSize(); nPane++)
1008 fileloc[nPane].setPath(tFiles[nPane]);
1010 if (!prediffer.empty())
1012 String strBothFilenames = strutils::join(tFiles.begin(), tFiles.end(), _T("|"));
1013 pDirDoc->GetPluginManager().SetPrediffer(strBothFilenames, prediffer);
1016 ShowAutoMergeDoc(pDirDoc, tFiles.GetSize(), fileloc, dwFlags, strDesc, sReportFile,
1020 if (pFiles && (!dwFlags || !(dwFlags[0] & FFILEOPEN_NOMRU)))
1022 String filter = GetOptionsMgr()->GetString(OPT_FILEFILTER_CURRENT);
1023 AddToRecentDocs(*pFiles, (unsigned *)dwFlags, bRecurse, filter);
1029 void CMainFrame::UpdateFont(FRAMETYPE frame)
1031 if (frame == FRAME_FOLDER)
1033 for (auto pDoc : GetAllDirDocs())
1037 CDirView *pView = pDoc->GetMainView();
1039 pView->SetFont(m_lfDir);
1045 for (auto pDoc : GetAllMergeDocs())
1047 CMergeDoc *pMergeDoc = dynamic_cast<CMergeDoc *>(pDoc);
1049 for (auto& pView: pMergeDoc->GetViewList())
1050 pView->SetFont(m_lfDiff);
1056 * @brief Select font for Merge/Dir view
1058 * Shows font selection dialog to user, sets current font and saves
1059 * selected font properties to registry. Selects fon type to active
1060 * view (Merge/Dir compare). If there is no open views, then font
1061 * is selected for Merge view (for example user may want to change to
1062 * unicode font before comparing files).
1064 void CMainFrame::OnViewSelectfont()
1066 FRAMETYPE frame = GetFrameType(GetActiveFrame());
1067 CHOOSEFONT cf = { sizeof CHOOSEFONT };
1069 cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCREENFONTS;
1070 if (frame == FRAME_FILE)
1071 cf.Flags |= CF_FIXEDPITCHONLY; // Only fixed-width fonts for merge view
1073 // CF_FIXEDPITCHONLY = 0x00004000L
1074 // in case you are a developer and want to disable it to test with, eg, a Chinese capable font
1075 if (frame == FRAME_FOLDER)
1082 if (ChooseFont(&cf))
1084 Options::Font::Save(GetOptionsMgr(), frame == FRAME_FOLDER ? OPT_FONT_DIRCMP : OPT_FONT_FILECMP, lf, true);
1090 * @brief Enable 'Select font'.
1092 void CMainFrame::OnUpdateViewSelectfont(CCmdUI* pCmdUI)
1094 pCmdUI->Enable(TRUE);
1098 * @brief Use default font for active view type
1100 * Disable user-selected font for active view type (Merge/Dir compare).
1101 * If there is no open views, then Merge view font is changed.
1103 void CMainFrame::OnViewUsedefaultfont()
1105 FRAMETYPE frame = GetFrameType(GetActiveFrame());
1107 if (frame == FRAME_FOLDER)
1109 Options::Font::Reset(GetOptionsMgr(), OPT_FONT_DIRCMP);
1110 m_lfDir = Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP);
1111 Options::Font::Save(GetOptionsMgr(), OPT_FONT_DIRCMP, &m_lfDir, false);
1115 Options::Font::Reset(GetOptionsMgr(), OPT_FONT_FILECMP);
1116 m_lfDiff = Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP);
1117 Options::Font::Save(GetOptionsMgr(), OPT_FONT_FILECMP, &m_lfDiff, false);
1124 * @brief Enable 'Use Default font'.
1126 void CMainFrame::OnUpdateViewUsedefaultfont(CCmdUI* pCmdUI)
1128 pCmdUI->Enable(TRUE);
1132 * @brief Update any resources necessary after a GUI language change
1134 void CMainFrame::UpdateResources()
1136 m_wndStatusBar.SetPaneText(0, theApp.LoadString(AFX_IDS_IDLEMESSAGE).c_str());
1138 for (auto pDoc : GetAllDirDocs())
1139 pDoc->UpdateResources();
1140 for (auto pDoc : GetAllMergeDocs())
1141 pDoc->UpdateResources();
1142 for (auto pDoc : GetAllOpenDocs())
1143 pDoc->UpdateResources();
1147 * @brief Open WinMerge help.
1149 * If local HTMLhelp file is found, open it, otherwise open HTML page from web.
1151 void CMainFrame::OnHelpContents()
1157 * @brief Enable Open WinMerge help -menuitem.
1159 void CMainFrame::OnUpdateHelpContents(CCmdUI* pCmdUI)
1161 pCmdUI->Enable(TRUE);
1165 * @brief Handle translation of default messages on the status bar
1167 void CMainFrame::GetMessageString(UINT nID, CString& rMessage) const
1169 // load appropriate string
1170 const String s = theApp.LoadString(nID);
1172 AfxExtractSubString(rMessage, s.c_str(), 0);
1175 void CMainFrame::ActivateFrame(int nCmdShow)
1179 CMDIFrameWnd::ActivateFrame(nCmdShow);
1183 m_bFirstTime = FALSE;
1186 wp.length = sizeof(WINDOWPLACEMENT);
1187 GetWindowPlacement(&wp);
1188 wp.rcNormalPosition.left=theApp.GetProfileInt(_T("Settings"), _T("MainLeft"),0);
1189 wp.rcNormalPosition.top=theApp.GetProfileInt(_T("Settings"), _T("MainTop"),0);
1190 wp.rcNormalPosition.right=theApp.GetProfileInt(_T("Settings"), _T("MainRight"),0);
1191 wp.rcNormalPosition.bottom=theApp.GetProfileInt(_T("Settings"), _T("MainBottom"),0);
1192 if (nCmdShow != SW_MINIMIZE && theApp.GetProfileInt(_T("Settings"), _T("MainMax"), FALSE))
1193 wp.showCmd = SW_MAXIMIZE;
1195 wp.showCmd = nCmdShow;
1197 CRect dsk_rc,rc(wp.rcNormalPosition);
1199 dsk_rc.left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
1200 dsk_rc.top = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
1201 dsk_rc.right = dsk_rc.left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
1202 dsk_rc.bottom = dsk_rc.top + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
1203 if (rc.Width() != 0 && rc.Height() != 0)
1205 // Ensure top-left corner is on visible area,
1206 // 20 points margin is added to prevent "lost" window
1207 CPoint ptTopLeft(rc.TopLeft());
1208 ptTopLeft += CPoint(20, 20);
1210 if (dsk_rc.PtInRect(ptTopLeft))
1211 SetWindowPlacement(&wp);
1213 CMDIFrameWnd::ActivateFrame(nCmdShow);
1216 CMDIFrameWnd::ActivateFrame(nCmdShow);
1220 * @brief Called when mainframe is about to be closed.
1221 * This function is called when mainframe is to be closed (not for
1222 * file/compare windows.
1224 void CMainFrame::OnClose()
1226 if (theApp.GetActiveOperations())
1229 // Check if there are multiple windows open and ask for closing them
1230 BOOL bAskClosing = GetOptionsMgr()->GetBool(OPT_ASK_MULTIWINDOW_CLOSE);
1233 bool quit = AskCloseConfirmation();
1238 // Save last selected filter
1239 String filter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1240 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
1242 // save main window position
1244 wp.length = sizeof(WINDOWPLACEMENT);
1245 GetWindowPlacement(&wp);
1246 theApp.WriteProfileInt(_T("Settings"), _T("MainLeft"),wp.rcNormalPosition.left);
1247 theApp.WriteProfileInt(_T("Settings"), _T("MainTop"),wp.rcNormalPosition.top);
1248 theApp.WriteProfileInt(_T("Settings"), _T("MainRight"),wp.rcNormalPosition.right);
1249 theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom);
1250 theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE));
1252 // Close Non-Document/View frame with confirmation
1253 CMDIChildWnd *pChild = static_cast<CMDIChildWnd *>(CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD));
1256 CMDIChildWnd *pNextChild = static_cast<CMDIChildWnd *>(pChild->GetWindow(GW_HWNDNEXT));
1257 if (GetFrameType(pChild) == FRAME_IMGFILE)
1259 if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
1262 pChild = pNextChild;
1265 CMDIFrameWnd::OnClose();
1269 * @brief Utility function to update CSuperComboBox format MRU
1271 void CMainFrame::addToMru(LPCTSTR szItem, LPCTSTR szRegSubKey, UINT nMaxItems)
1273 std::vector<CString> list;
1275 UINT cnt = AfxGetApp()->GetProfileInt(szRegSubKey, _T("Count"), 0);
1276 list.push_back(szItem);
1277 for (UINT i=0 ; i<cnt; ++i)
1279 s = AfxGetApp()->GetProfileString(szRegSubKey, strutils::format(_T("Item_%d"), i).c_str());
1283 cnt = list.size() > nMaxItems ? nMaxItems : static_cast<UINT>(list.size());
1284 for (UINT i=0 ; i<cnt; ++i)
1285 AfxGetApp()->WriteProfileString(szRegSubKey, strutils::format(_T("Item_%d"), i).c_str(), list[i]);
1287 AfxGetApp()->WriteProfileInt(szRegSubKey, _T("Count"), cnt);
1290 void CMainFrame::ApplyDiffOptions()
1292 for (auto pMergeDoc : GetAllMergeDocs())
1294 // Re-read MergeDoc settings (also updates view settings)
1295 // and rescan using new options
1296 pMergeDoc->RefreshOptions();
1297 pMergeDoc->FlushAndRescan(TRUE);
1301 /// Get list of OpenDocs (documents underlying edit sessions)
1302 OpenDocList &CMainFrame::GetAllOpenDocs()
1304 return static_cast<OpenDocList &>(GetDocList(theApp.m_pOpenTemplate));
1307 /// Get list of MergeDocs (documents underlying edit sessions)
1308 MergeDocList &CMainFrame::GetAllMergeDocs()
1310 return static_cast<MergeDocList &>(GetDocList(theApp.m_pDiffTemplate));
1313 /// Get list of DirDocs (documents underlying a scan)
1314 DirDocList &CMainFrame::GetAllDirDocs()
1316 return static_cast<DirDocList &>(GetDocList(theApp.m_pDirTemplate));
1319 /// Get list of HexMergeDocs (documents underlying edit sessions)
1320 HexMergeDocList &CMainFrame::GetAllHexMergeDocs()
1322 return static_cast<HexMergeDocList &>(GetDocList(theApp.m_pHexMergeTemplate));
1326 * @brief Obtain a merge doc to display a difference in files.
1327 * @return Pointer to CMergeDoc to use.
1329 template<class DocClass>
1330 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles)
1332 // Create a new merge doc
1333 DocClass::m_nBuffersTemp = nFiles;
1334 DocClass *pMergeDoc = static_cast<DocClass*>(pTemplate->OpenDocumentFile(NULL));
1337 pDirDoc->AddMergeDoc(pMergeDoc);
1338 pMergeDoc->SetDirDoc(pDirDoc);
1343 // Clear the item count in the main status pane
1344 void CMainFrame::ClearStatusbarItemCount()
1346 m_wndStatusBar.SetPaneText(2, _T(""));
1350 * @brief Generate patch from files selected.
1352 * Creates a patch from selected files in active directory compare, or
1353 * active file compare. Files in file compare must be saved before
1356 void CMainFrame::OnToolsGeneratePatch()
1359 patcher.CreatePatch();
1362 void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
1364 PathContext tFiles(dropped_files);
1365 const size_t fileCount = tFiles.GetSize();
1367 // If Ctrl pressed, do recursive compare
1368 bool recurse = !!::GetAsyncKeyState(VK_CONTROL) || GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
1370 // If user has <Shift> pressed with one file selected,
1371 // assume it is an archive and set filenames to same
1372 if (::GetAsyncKeyState(VK_SHIFT) < 0 && fileCount == 1)
1374 tFiles.SetRight(tFiles[0]);
1377 // Check if they dropped a project file
1378 DWORD dwFlags[3] = {FFILEOPEN_NONE, FFILEOPEN_NONE, FFILEOPEN_NONE};
1381 if (theApp.IsProjectFile(tFiles[0]))
1383 theApp.LoadAndOpenProjectFile(tFiles[0]);
1386 if (IsConflictFile(tFiles[0]))
1388 DoOpenConflict(tFiles[0], nullptr, true);
1393 DoFileOpen(&tFiles, dwFlags, NULL, _T(""), recurse);
1396 void CMainFrame::OnPluginUnpackMode(UINT nID )
1400 case ID_UNPACK_MANUAL:
1401 FileTransform::g_bUnpackerMode = PLUGIN_MANUAL;
1403 case ID_UNPACK_AUTO:
1404 FileTransform::g_bUnpackerMode = PLUGIN_AUTO;
1407 theApp.WriteProfileInt(_T("Settings"), _T("UnpackerMode"), FileTransform::g_bUnpackerMode);
1410 void CMainFrame::OnUpdatePluginUnpackMode(CCmdUI* pCmdUI)
1412 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1414 if (pCmdUI->m_nID == ID_UNPACK_MANUAL)
1415 pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bUnpackerMode);
1416 if (pCmdUI->m_nID == ID_UNPACK_AUTO)
1417 pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bUnpackerMode);
1419 void CMainFrame::OnPluginPrediffMode(UINT nID )
1423 case ID_PREDIFFER_MANUAL:
1424 FileTransform::g_bPredifferMode = PLUGIN_MANUAL;
1426 case ID_PREDIFFER_AUTO:
1427 FileTransform::g_bPredifferMode = PLUGIN_AUTO;
1430 PrediffingInfo infoPrediffer;
1431 for (auto pMergeDoc : GetAllMergeDocs())
1432 pMergeDoc->SetPrediffer(&infoPrediffer);
1433 for (auto pDirDoc : GetAllDirDocs())
1434 pDirDoc->GetPluginManager().SetPrediffSettingAll(FileTransform::g_bPredifferMode);
1435 theApp.WriteProfileInt(_T("Settings"), _T("PredifferMode"), FileTransform::g_bPredifferMode);
1438 void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI)
1440 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1442 if (pCmdUI->m_nID == ID_PREDIFFER_MANUAL)
1443 pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bPredifferMode);
1444 if (pCmdUI->m_nID == ID_PREDIFFER_AUTO)
1445 pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bPredifferMode);
1448 * @brief Called when "Reload Plugins" item is updated
1450 void CMainFrame::OnUpdateReloadPlugins(CCmdUI* pCmdUI)
1452 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
1455 void CMainFrame::OnReloadPlugins()
1457 // delete all script interfaces
1458 // (interfaces will be created again automatically when WinMerge needs them)
1459 CAllThreadsScripts::GetActiveSet()->FreeAllScripts();
1461 // update the editor scripts submenu
1462 HMENU scriptsSubmenu = GetScriptsSubmenu(m_hMenuDefault);
1463 if (scriptsSubmenu != NULL)
1464 CMergeEditView::createScriptsSubmenu(scriptsSubmenu);
1465 UpdatePrediffersMenu();
1468 /** @brief Return active merge edit view, if can figure it out/is available */
1469 CMergeEditView * CMainFrame::GetActiveMergeEditView()
1471 // NB: GetActiveDocument does not return the Merge Doc
1472 // even when the merge edit view is in front
1473 // NB: CChildFrame::GetActiveView returns NULL when location view active
1474 // So we have this rather complicated logic to try to get a merge edit view
1475 // We look at the front child window, which should be a frame
1476 // and we can get a MergeEditView from it, if it is a CChildFrame
1477 // (DirViews use a different frame type)
1478 CChildFrame * pFrame = dynamic_cast<CChildFrame *>(GetActiveFrame());
1479 if (!pFrame) return 0;
1480 // Try to get the active MergeEditView (ie, left or right)
1481 if (pFrame->GetActiveView() && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
1483 return dynamic_cast<CMergeEditView *>(pFrame->GetActiveView());
1485 return pFrame->GetMergeDoc()->GetActiveMergeView();
1488 void CMainFrame::UpdatePrediffersMenu()
1490 CMenu* menu = GetMenu();
1496 HMENU hMainMenu = menu->m_hMenu;
1497 HMENU prediffersSubmenu = GetPrediffersSubmenu(hMainMenu);
1498 if (prediffersSubmenu != NULL)
1500 CMergeEditView * pEditView = GetActiveMergeEditView();
1502 pEditView->createPrediffersSubmenu(prediffersSubmenu);
1505 // no view or dir view : display an empty submenu
1506 int i = GetMenuItemCount(prediffersSubmenu);
1508 ::DeleteMenu(prediffersSubmenu, 0, MF_BYPOSITION);
1509 ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, NULL);
1515 * @brief Save WinMerge configuration and info to file
1517 void CMainFrame::OnSaveConfigData()
1519 CConfigLog configLog;
1522 if (configLog.WriteLogFile(sError))
1524 String sFileName = configLog.GetFileName();
1525 theApp.OpenFileToExternalEditor(sFileName);
1529 String sFileName = configLog.GetFileName();
1530 String msg = strutils::format_string2(_("Cannot open file\n%1\n\n%2"), sFileName, sError);
1531 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1536 * @brief Open two new empty docs, 'Scratchpads'
1538 * Allows user to open two empty docs, to paste text to
1539 * compare from clipboard.
1540 * @note File filenames are set emptys and filedescriptors
1541 * are loaded from resource.
1542 * @sa CMergeDoc::OpenDocs()
1543 * @sa CMergeDoc::TrySaveAs()
1545 void CMainFrame::FileNew(int nPanes)
1547 CDirDoc *pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
1549 // Load emptyfile descriptors and open empty docs
1550 // Use default codepage
1551 DWORD dwFlags[3] = {0, 0, 0};
1553 FileLocation fileloc[3];
1556 strDesc[0] = _("Untitled left");
1557 strDesc[1] = _("Untitled right");
1558 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1559 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1560 ShowMergeDoc(pDirDoc, 2, fileloc, dwFlags, strDesc);
1564 strDesc[0] = _("Untitled left");
1565 strDesc[1] = _("Untitled middle");
1566 strDesc[2] = _("Untitled right");
1567 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1568 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1569 fileloc[2].encoding.SetCodepage(ucr::getDefaultCodepage());
1570 ShowMergeDoc(pDirDoc, 3, fileloc, dwFlags, strDesc);
1575 * @brief Open two new empty docs, 'Scratchpads'
1577 * Allows user to open two empty docs, to paste text to
1578 * compare from clipboard.
1579 * @note File filenames are set emptys and filedescriptors
1580 * are loaded from resource.
1581 * @sa CMergeDoc::OpenDocs()
1582 * @sa CMergeDoc::TrySaveAs()
1584 void CMainFrame::OnFileNew()
1589 void CMainFrame::OnFileNew3()
1595 * @brief Open Filters dialog
1597 void CMainFrame::OnToolsFilters()
1599 String title = _("Filters");
1600 CPropertySheet sht(title.c_str());
1601 LineFiltersDlg lineFiltersDlg;
1602 FileFiltersDlg fileFiltersDlg;
1603 vector<FileFilterInfo> fileFilters;
1604 std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
1605 String selectedFilter;
1606 const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1607 sht.AddPage(&fileFiltersDlg);
1608 sht.AddPage(&lineFiltersDlg);
1609 sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
1611 // Make sure all filters are up-to-date
1612 theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
1614 theApp.m_pGlobalFileFilter->GetFileFilters(&fileFilters, selectedFilter);
1615 fileFiltersDlg.SetFilterArray(&fileFilters);
1616 fileFiltersDlg.SetSelected(selectedFilter);
1617 const bool lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
1618 lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig;
1620 lineFilters->CloneFrom(theApp.m_pLineFilters.get());
1621 lineFiltersDlg.SetList(lineFilters.get());
1623 if (sht.DoModal() == IDOK)
1625 String strNone = _("<None>");
1626 String path = fileFiltersDlg.GetSelected();
1627 if (path.find(strNone) != String::npos)
1629 // Don't overwrite mask we already have
1630 if (!theApp.m_pGlobalFileFilter->IsUsingMask())
1632 String sFilter(_T("*.*"));
1633 theApp.m_pGlobalFileFilter->SetFilter(sFilter);
1634 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1639 theApp.m_pGlobalFileFilter->SetFileFilterPath(path);
1640 theApp.m_pGlobalFileFilter->UseMask(FALSE);
1641 String sFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1642 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
1644 bool linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp;
1645 GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled);
1647 // Check if compare documents need rescanning
1648 BOOL bFileCompareRescan = FALSE;
1649 BOOL bFolderCompareRescan = FALSE;
1650 CFrameWnd * pFrame = GetActiveFrame();
1651 FRAMETYPE frame = GetFrameType(pFrame);
1652 if (frame == FRAME_FILE)
1654 if (lineFiltersEnabledOrig != linefiltersEnabled ||
1655 !theApp.m_pLineFilters->Compare(lineFilters.get()))
1657 bFileCompareRescan = TRUE;
1660 else if (frame == FRAME_FOLDER)
1662 const String newFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1663 if (lineFiltersEnabledOrig != linefiltersEnabled ||
1664 !theApp.m_pLineFilters->Compare(lineFilters.get()) || origFilter != newFilter)
1666 int res = LangMessageBox(IDS_FILTERCHANGED, MB_ICONWARNING | MB_YESNO);
1668 bFolderCompareRescan = TRUE;
1672 // Save new filters before (possibly) rescanning
1673 theApp.m_pLineFilters->CloneFrom(lineFilters.get());
1674 theApp.m_pLineFilters->SaveFilters();
1676 if (bFileCompareRescan)
1678 for (auto pMergeDoc : GetAllMergeDocs())
1679 pMergeDoc->FlushAndRescan(TRUE);
1681 else if (bFolderCompareRescan)
1683 for (auto pDirDoc : GetAllDirDocs())
1690 * @brief Open Filters dialog.
1692 void CMainFrame::SelectFilter()
1698 * @brief Closes application with ESC.
1700 * Application is closed if:
1701 * - 'Close Windows with ESC' option is enabled and
1702 * there is no open document windows
1703 * - '-e' commandline switch is given
1705 BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
1707 // Check if we got 'ESC pressed' -message
1708 if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
1710 if (theApp.m_bEscShutdown && m_wndTabBar.GetItemCount() <= 1)
1712 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1715 else if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC) && m_wndTabBar.GetItemCount() == 0)
1717 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
1721 return CMDIFrameWnd::PreTranslateMessage(pMsg);
1725 * @brief Show/hide statusbar.
1727 void CMainFrame::OnViewStatusBar()
1729 bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR);
1730 GetOptionsMgr()->SaveOption(OPT_SHOW_STATUSBAR, bShow);
1732 CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, bShow, 0);
1736 * @brief Updates "Show Tabbar" menuitem.
1738 void CMainFrame::OnUpdateViewTabBar(CCmdUI* pCmdUI)
1740 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR));
1744 * @brief Show/hide tabbar.
1746 void CMainFrame::OnViewTabBar()
1748 bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR);
1749 GetOptionsMgr()->SaveOption(OPT_SHOW_TABBAR, bShow);
1751 CMDIFrameWnd::ShowControlBar(&m_wndTabBar, bShow, 0);
1755 * @brief Updates "Automatically Resize Panes" menuitem.
1757 void CMainFrame::OnUpdateResizePanes(CCmdUI* pCmdUI)
1759 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
1764 * @brief Enable/disable automatic pane resizing.
1766 void CMainFrame::OnResizePanes()
1768 bool bResize = !GetOptionsMgr()->GetBool(OPT_RESIZE_PANES);
1769 GetOptionsMgr()->SaveOption(OPT_RESIZE_PANES, bResize);
1770 // TODO: Introduce a common merge frame superclass?
1771 CFrameWnd *pActiveFrame = GetActiveFrame();
1772 if (CChildFrame *pFrame = DYNAMIC_DOWNCAST(CChildFrame, pActiveFrame))
1774 pFrame->UpdateAutoPaneResize();
1776 pFrame->UpdateSplitter();
1778 else if (CHexMergeFrame *pFrame1 = DYNAMIC_DOWNCAST(CHexMergeFrame, pActiveFrame))
1780 pFrame1->UpdateAutoPaneResize();
1782 pFrame1->UpdateSplitter();
1787 * @brief Open project-file.
1789 void CMainFrame::OnFileOpenproject()
1793 // get the default projects path
1794 String strProjectPath = GetOptionsMgr()->GetString(OPT_PROJECTS_PATH);
1795 if (!SelectFile(GetSafeHwnd(), sFilepath, TRUE, strProjectPath.c_str(), _T(""),
1796 _("WinMerge Project Files (*.WinMerge)|*.WinMerge||")))
1799 strProjectPath = paths::GetParentPath(sFilepath);
1800 // store this as the new project path
1801 GetOptionsMgr()->SaveOption(OPT_PROJECTS_PATH, strProjectPath);
1803 theApp.LoadAndOpenProjectFile(sFilepath.c_str());
1807 * @brief Receive command line from another instance.
1809 * This function receives command line when only single-instance
1810 * is allowed. New instance tried to start sends its command line
1811 * to here so we can open paths it was meant to.
1813 LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
1815 COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
1816 LPCTSTR pchData = (LPCTSTR)pCopyData->lpData;
1817 // Bail out if data isn't zero-terminated
1818 DWORD cchData = pCopyData->cbData / sizeof(TCHAR);
1819 if (cchData == 0 || pchData[cchData - 1] != _T('\0'))
1822 MergeCmdLineInfo cmdInfo(pchData);
1823 theApp.ApplyCommandLineConfigOptions(cmdInfo);
1824 theApp.ParseArgsAndDoOpen(cmdInfo, this);
1828 LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
1830 CFrameWnd * pFrame = GetActiveFrame();
1833 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
1835 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
1837 pMergeDoc->CheckFileChanged();
1843 * @brief Close all open windows.
1845 * Asks about saving unsaved files and then closes all open windows.
1847 void CMainFrame::OnWindowCloseAll()
1849 CMDIChildWnd *pChild = MDIGetActive();
1853 if ((pDoc = pChild->GetActiveDocument()) != NULL)
1855 if (!pDoc->SaveModified())
1857 pDoc->OnCloseDocument();
1859 else if (GetFrameType(pChild) == FRAME_IMGFILE)
1861 if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
1866 pChild->DestroyWindow();
1868 pChild = MDIGetActive();
1874 * @brief Enables Window/Close All item if there are open windows.
1876 void CMainFrame::OnUpdateWindowCloseAll(CCmdUI* pCmdUI)
1878 const MergeDocList &mergedocs = GetAllMergeDocs();
1879 if (!mergedocs.IsEmpty())
1881 pCmdUI->Enable(TRUE);
1885 const DirDocList &dirdocs = GetAllDirDocs();
1886 pCmdUI->Enable(!dirdocs.IsEmpty());
1890 * @brief Access to the singleton main frame (where we have some globals)
1892 CMainFrame * GetMainFrame()
1894 CWnd * mainwnd = AfxGetMainWnd();
1896 CMainFrame *pMainframe = dynamic_cast<CMainFrame*>(mainwnd);
1902 * @brief Opens dialog for user to Load, edit and save project files.
1903 * This dialog gets current compare paths and filter (+other properties
1904 * possible in project files) and initializes the dialog with them.
1906 void CMainFrame::OnSaveProject()
1908 if (!m_pMenus[MENU_OPENVIEW])
1909 theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
1910 COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
1913 CFrameWnd * pFrame = GetActiveFrame();
1914 FRAMETYPE frame = GetFrameType(pFrame);
1916 if (frame == FRAME_FILE)
1918 CMergeDoc * pMergeDoc = static_cast<CMergeDoc *>(pFrame->GetActiveDocument());
1919 pOpenDoc->m_files = pMergeDoc->m_filePaths;
1920 for (size_t pane = 0; pane < pOpenDoc->m_files.size(); ++pane)
1921 pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pMergeDoc->m_ptBuf[pane]->GetReadOnly() ? FFILEOPEN_PROJECT : 0);
1922 pOpenDoc->m_bRecurse = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
1923 pOpenDoc->m_strExt = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1925 else if (frame == FRAME_FOLDER)
1927 // Get paths currently in compare
1928 const CDirDoc * pDoc = static_cast<const CDirDoc*>(pFrame->GetActiveDocument());
1929 const CDiffContext& ctxt = pDoc->GetDiffContext();
1931 // Set-up the dialog
1932 for (int pane = 0; pane < ctxt.GetCompareDirs(); ++pane)
1934 pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pDoc->GetReadOnly(pane) ? FFILEOPEN_READONLY : 0);
1935 pOpenDoc->m_files.SetPath(pane, paths::AddTrailingSlash(ctxt.GetNormalizedPath(pane)));
1937 pOpenDoc->m_bRecurse = ctxt.m_bRecursive;
1938 pOpenDoc->m_strExt = static_cast<FileFilterHelper *>(ctxt.m_piFilterGlobal)->GetFilterNameOrMask();
1941 CFrameWnd *pOpenFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
1942 theApp.m_pOpenTemplate->InitialUpdateFrame(pOpenFrame, pOpenDoc);
1946 * @brief Start flashing window if window is inactive.
1948 void CMainFrame::StartFlashing()
1950 CWnd * activeWindow = GetActiveWindow();
1951 if (activeWindow != this)
1952 FlashWindowEx(FLASHW_ALL | FLASHW_TIMERNOFG, 0, 0);
1955 #if _MFC_VER > 0x0600
1956 void CMainFrame::OnActivateApp(BOOL bActive, DWORD dwThreadID)
1958 void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
1961 #if _MFC_VER > 0x0600
1962 CMDIFrameWnd::OnActivateApp(bActive, dwThreadID);
1964 CMDIFrameWnd::OnActivateApp(bActive, hTask);
1967 CFrameWnd * pFrame = GetActiveFrame();
1970 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
1972 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
1974 PostMessage(WM_USER+1);
1978 BOOL CMainFrame::CreateToolbar()
1980 if (!m_wndToolBar.CreateEx(this) ||
1981 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
1986 if (!m_wndReBar.Create(this))
1991 VERIFY(m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT));
1993 // Remove this if you don't want tool tips or a resizable toolbar
1994 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
1995 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
1996 m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
1998 m_wndReBar.AddBar(&m_wndToolBar);
2000 LoadToolbarImages();
2004 int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(ID_OPTIONS);
2005 m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
2006 nStyle |= TBSTYLE_DROPDOWN;
2007 m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
2009 if (GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR) == false)
2011 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
2017 /** @brief Load toolbar images from the resource. */
2018 void CMainFrame::LoadToolbarImages()
2020 const int toolbarSize = 16 << GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2021 CToolBarCtrl& BarCtrl = m_wndToolBar.GetToolBarCtrl();
2023 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].DeleteImageList();
2024 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].DeleteImageList();
2025 CSize sizeButton(0, 0);
2027 LoadToolbarImageList(toolbarSize,
2028 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
2029 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
2030 false, m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2031 LoadToolbarImageList(toolbarSize,
2032 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
2033 toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
2034 true, m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2035 sizeButton = CSize(toolbarSize + 8, toolbarSize + 8);
2037 BarCtrl.SetButtonSize(sizeButton);
2038 BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2039 BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2040 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
2041 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
2043 // resize the rebar.
2044 REBARBANDINFO rbbi = { sizeof REBARBANDINFO };
2045 rbbi.fMask = RBBIM_CHILDSIZE;
2046 rbbi.cyMinChild = sizeButton.cy;
2047 m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);
2052 * @brief Load a transparent 24-bit color image list.
2054 static void LoadHiColImageList(UINT nIDResource, UINT nIDResourceMask, int nWidth, int nHeight, int nCount, bool bGrayscale, CImageList& ImgList)
2057 bm.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_DEFAULTCOLOR));
2058 bmMask.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResourceMask), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_MONOCHROME));
2061 VERIFY(ImgList.Create(nWidth, nHeight, ILC_COLORDDB|ILC_MASK, nCount, 0));
2062 int nIndex = ImgList.Add(&bm, &bmMask);
2063 ASSERT(-1 != nIndex);
2067 * @brief Load toolbar image list.
2069 static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale,
2070 CImageList& ImgList)
2072 const int ImageCount = 22;
2073 const int imageHeight = imageWidth - 1;
2074 LoadHiColImageList(nIDResource, nIDResourceMask, imageWidth, imageHeight, ImageCount, bGrayscale, ImgList);
2078 * @brief Called when the document title is modified.
2080 void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
2082 CFrameWnd::OnUpdateFrameTitle(bAddToTitle);
2084 if (m_wndTabBar.m_hWnd)
2085 m_wndTabBar.UpdateTabs();
2088 /** @brief Show none/small/big/huge toolbar. */
2089 void CMainFrame::OnToolbarSize(UINT id)
2091 if (id == ID_TOOLBAR_NONE)
2093 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, false);
2094 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
2098 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2099 GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, id - ID_TOOLBAR_SMALL);
2101 LoadToolbarImages();
2103 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, true, 0);
2107 /** @brief Show none/small/big/huge toolbar. */
2108 void CMainFrame::OnUpdateToolbarSize(CCmdUI *pCmdUI)
2110 if (!GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR))
2111 pCmdUI->SetRadio(pCmdUI->m_nID == ID_TOOLBAR_NONE);
2113 pCmdUI->SetRadio((pCmdUI->m_nID - ID_TOOLBAR_SMALL) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE)));
2116 /** @brief Lang aware version of CFrameWnd::OnToolTipText() */
2117 BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
2119 ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
2121 // need to handle both ANSI and UNICODE versions of the message
2122 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2123 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2126 UINT_PTR nID = pNMHDR->idFrom;
2127 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
2128 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2130 // idFrom is actually the HWND of the tool
2131 nID = ::GetDlgCtrlID((HWND)nID);
2134 if (nID != 0) // will be zero on a separator
2136 strFullText = theApp.LoadString(static_cast<UINT>(nID));
2137 // don't handle the message if no string resource found
2138 if (strFullText.empty())
2141 // this is the command id, not the button index
2142 AfxExtractSubString(strTipText, strFullText.c_str(), 1, '\n');
2145 if (pNMHDR->code == TTN_NEEDTEXTA)
2146 lstrcpyn(pTTTA->szText, strTipText, countof(pTTTA->szText));
2148 _mbstowcsz(pTTTW->szText, strTipText, countof(pTTTW->szText));
2150 if (pNMHDR->code == TTN_NEEDTEXTA)
2151 _wcstombsz(pTTTA->szText, strTipText, countof(pTTTA->szText));
2153 lstrcpyn(pTTTW->szText, strTipText, countof(pTTTW->szText));
2157 // bring the tooltip window above other popup windows
2158 ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
2159 SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
2161 return TRUE; // message was handled
2165 * @brief Ask user for close confirmation when closing the mainframe.
2166 * This function asks if user wants to close multiple open windows when user
2167 * selects (perhaps accidentally) to close WinMerge (application).
2168 * @return true if user agreeds to close all windows.
2170 bool CMainFrame::AskCloseConfirmation()
2172 const DirDocList &dirdocs = GetAllDirDocs();
2173 const MergeDocList &mergedocs = GetAllMergeDocs();
2176 const size_t count = dirdocs.GetCount() + mergedocs.GetCount();
2179 // Check that we don't have one empty dirdoc + mergedoc situation.
2180 // That happens since we open "hidden" dirdoc for every file compare.
2181 if (dirdocs.GetCount() == 1)
2183 CDirDoc *pDoc = dirdocs.GetHead();
2184 if (!pDoc->HasDiffs())
2187 ret = LangMessageBox(IDS_CLOSEALL_WINDOWS, MB_YESNO | MB_ICONWARNING);
2189 return (ret == IDYES);
2193 * @brief Shows the release notes for user.
2194 * This function opens release notes HTML document into browser.
2196 void CMainFrame::OnHelpReleasenotes()
2198 const String sPath = paths::ConcatPath(env::GetProgPath(), RelNotes);
2199 ShellExecute(NULL, _T("open"), sPath.c_str(), NULL, NULL, SW_SHOWNORMAL);
2203 * @brief Shows the translations page.
2204 * This function opens translations page URL into browser.
2206 void CMainFrame::OnHelpTranslations()
2208 ShellExecute(NULL, _T("open"), TranslationsUrl, NULL, NULL, SW_SHOWNORMAL);
2212 * @brief Called when user selects File/Open Conflict...
2214 void CMainFrame::OnFileOpenConflict()
2216 String conflictFile;
2217 if (SelectFile(GetSafeHwnd(), conflictFile))
2219 DoOpenConflict(conflictFile);
2224 * @brief Select and open conflict file for resolving.
2225 * This function lets user to select conflict file to resolve.
2226 * Then we parse conflict file to two files to "merge" and
2227 * save resulting file over original file.
2229 * Set left-side file read-only as it is the repository file which cannot
2230 * be modified anyway. Right-side file is user's file which is set as
2231 * modified by default so user can just save it and accept workspace
2232 * file as resolved file.
2233 * @param [in] conflictFile Full path to conflict file to open.
2234 * @param [in] checked If true, do not check if it really is project file.
2235 * @return TRUE if conflict file was opened for resolving.
2237 BOOL CMainFrame::DoOpenConflict(const String& conflictFile, const String strDesc[], bool checked)
2239 BOOL conflictCompared = FALSE;
2243 bool confFile = IsConflictFile(conflictFile);
2246 String message = strutils::format_string1(_("The file\n%1\nis not a conflict file."), conflictFile);
2247 AfxMessageBox(message.c_str(), MB_ICONSTOP);
2252 // Create temp files and put them into the list,
2253 // from where they get deleted when MainFrame is deleted.
2254 String ext = paths::FindExtension(conflictFile);
2255 TempFilePtr wTemp(new TempFile());
2256 String workFile = wTemp->Create(_T("confw_"), ext);
2257 m_tempFiles.push_back(wTemp);
2258 TempFilePtr vTemp(new TempFile());
2259 String revFile = vTemp->Create(_T("confv_"), ext);
2260 m_tempFiles.push_back(vTemp);
2261 TempFilePtr bTemp(new TempFile());
2262 String baseFile = vTemp->Create(_T("confb_"), ext);
2263 m_tempFiles.push_back(bTemp);
2265 // Parse conflict file into two files.
2266 bool inners, threeWay;
2267 int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
2268 bool success = ParseConflictFile(conflictFile, workFile, revFile, baseFile, iGuessEncodingType, inners, threeWay);
2272 // Open two parsed files to WinMerge, telling WinMerge to
2273 // save over original file (given as third filename).
2274 theApp.m_strSaveAsPath = conflictFile;
2277 String strDesc2[2] = {
2278 (strDesc && !strDesc[0].empty()) ? strDesc[0] : _("Theirs File"),
2279 (strDesc && !strDesc[2].empty()) ? strDesc[2] : _("Mine File") };
2280 DWORD dwFlags[2] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2281 PathContext tmpPathContext(revFile, workFile);
2282 conflictCompared = DoFileOpen(&tmpPathContext, dwFlags, strDesc2);
2286 String strDesc3[3] = {
2287 (strDesc && !strDesc[0].empty()) ? strDesc[0] : _("Base File"),
2288 (strDesc && !strDesc[1].empty()) ? strDesc[1] : _("Theirs File"),
2289 (strDesc && !strDesc[2].empty()) ? strDesc[2] : _("Mine File") };
2290 PathContext tmpPathContext(baseFile, revFile, workFile);
2291 DWORD dwFlags[3] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2292 conflictCompared = DoFileOpen(&tmpPathContext, dwFlags, strDesc3);
2297 LangMessageBox(IDS_ERROR_CONF_RESOLVE, MB_ICONSTOP);
2299 return conflictCompared;
2303 * @brief Get type of frame (File/Folder compare).
2304 * @param [in] pFrame Pointer to frame to check.
2305 * @return FRAMETYPE of the given frame.
2307 CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame) const
2309 BOOL bMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame));
2310 BOOL bHexMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame));
2311 BOOL bImgMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame));
2312 BOOL bDirFrame = pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame));
2316 else if (bHexMergeFrame)
2317 return FRAME_HEXFILE;
2318 else if (bImgMergeFrame)
2319 return FRAME_IMGFILE;
2321 return FRAME_FOLDER;
2327 * @brief Show the plugins list dialog.
2329 void CMainFrame::OnPluginsList()
2335 void CMainFrame::OnDiffOptionsDropDown(NMHDR* pNMHDR, LRESULT* pResult)
2337 LPNMTOOLBAR pToolBar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);
2338 ClientToScreen(&(pToolBar->rcButton));
2340 VERIFY(menu.LoadMenu(IDR_POPUP_DIFF_OPTIONS));
2341 theApp.TranslateMenu(menu.m_hMenu);
2342 CMenu* pPopup = menu.GetSubMenu(0);
2345 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2346 pToolBar->rcButton.left, pToolBar->rcButton.bottom, this);
2350 void CMainFrame::OnDiffWhitespace(UINT nID)
2352 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_WHITESPACE, nID - IDC_DIFF_WHITESPACE_COMPARE);
2356 void CMainFrame::OnUpdateDiffWhitespace(CCmdUI* pCmdUI)
2358 pCmdUI->SetRadio((pCmdUI->m_nID - IDC_DIFF_WHITESPACE_COMPARE) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_CMP_IGNORE_WHITESPACE)));
2362 void CMainFrame::OnDiffCaseSensitive()
2364 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CASE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2368 void CMainFrame::OnUpdateDiffCaseSensitive(CCmdUI* pCmdUI)
2370 pCmdUI->SetCheck(!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2374 void CMainFrame::OnDiffIgnoreEOL()
2376 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_EOL, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2380 void CMainFrame::OnUpdateDiffIgnoreEOL(CCmdUI* pCmdUI)
2382 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2386 void CMainFrame::OnIncludeSubfolders()
2388 GetOptionsMgr()->SaveOption(OPT_CMP_INCLUDE_SUBDIRS, !GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS));
2389 // Update all dirdoc settings
2390 for (auto pDirDoc : GetAllDirDocs())
2391 pDirDoc->RefreshOptions();
2392 for (auto pOpenDoc : GetAllOpenDocs())
2393 pOpenDoc->RefreshOptions();
2396 void CMainFrame::OnUpdateIncludeSubfolders(CCmdUI* pCmdUI)
2398 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS));
2402 void CMainFrame::OnCompareMethod(UINT nID)
2404 GetOptionsMgr()->SaveOption(OPT_CMP_METHOD, nID - ID_COMPMETHOD_FULL_CONTENTS);
2407 void CMainFrame::OnUpdateCompareMethod(CCmdUI* pCmdUI)
2409 pCmdUI->SetRadio((pCmdUI->m_nID - ID_COMPMETHOD_FULL_CONTENTS) == static_cast<UINT>(GetOptionsMgr()->GetInt(OPT_CMP_METHOD)));
2413 void CMainFrame::OnMRUs(UINT nID)
2415 std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(GetOptionsMgr()->GetInt(OPT_MRU_MAX));
2416 const size_t idx = nID - ID_MRU_FIRST;
2417 if (idx < mrus.size())
2419 MergeCmdLineInfo cmdInfo((_T("\"") + mrus[idx].path + _T("\" ") + mrus[idx].params).c_str());
2420 theApp.ParseArgsAndDoOpen(cmdInfo, this);
2424 void CMainFrame::OnUpdateNoMRUs(CCmdUI* pCmdUI)
2426 // append the MRU submenu
2427 HMENU hMenu = GetSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu, ID_FILE_NEW, false);
2432 size_t i = ::GetMenuItemCount(hMenu);
2434 ::DeleteMenu(hMenu, 0, MF_BYPOSITION);
2436 std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(GetOptionsMgr()->GetInt(OPT_MRU_MAX));
2438 if (mrus.size() == 0)
2440 // no script : create a <empty> entry
2441 ::AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, theApp.LoadString(IDS_NO_EDIT_SCRIPTS).c_str());
2445 // or fill in the submenu with the scripts names
2446 int ID = ID_MRU_FIRST; // first ID in menu
2447 for (i = 0 ; i < mrus.size() ; i++, ID++)
2448 ::AppendMenu(hMenu, MF_STRING, ID,
2449 ((i < 9 ? strutils::format(_T("&%d "), i+1) : strutils::format(_T("&%c "), 'a' + i - 9))
2450 + mrus[i].title).c_str());
2453 pCmdUI->Enable(true);
2457 * @brief Update plugin name
2458 * @param [in] pCmdUI UI component to update.
2460 void CMainFrame::OnUpdatePluginName(CCmdUI* pCmdUI)
2462 pCmdUI->SetText(_T(""));
2465 void CMainFrame::ReloadMenu()
2467 // set the menu of the main frame window
2468 UINT idMenu = IDR_MAINFRAME;
2469 CMergeApp *pApp = dynamic_cast<CMergeApp *> (AfxGetApp());
2470 CMainFrame * pMainFrame = dynamic_cast<CMainFrame *> ((CFrameWnd*)pApp->m_pMainWnd);
2471 HMENU hNewDefaultMenu = pMainFrame->NewDefaultMenu(idMenu);
2472 HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
2473 HMENU hNewImgMergeMenu = pMainFrame->NewImgMergeViewMenu();
2474 HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
2475 if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
2477 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
2478 CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
2479 CMenu * pNewMergeMenu = CMenu::FromHandle(hNewMergeMenu);
2480 CMenu * pNewImgMergeMenu = CMenu::FromHandle(hNewImgMergeMenu);
2481 CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
2483 CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
2486 if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2487 static_cast<CChildFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2488 if (pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2489 static_cast<CHexMergeFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2490 if (pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2491 static_cast<CImgMergeFrame *>(pFrame)->SetSharedMenu(hNewImgMergeMenu);
2492 else if (pFrame->IsKindOf(RUNTIME_CLASS(COpenFrame)))
2493 static_cast<COpenFrame *>(pFrame)->SetSharedMenu(hNewDefaultMenu);
2494 else if (pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2495 static_cast<CDirFrame *>(pFrame)->SetSharedMenu(hNewDirMenu);
2496 pFrame = pFrame->GetNextWindow();
2499 CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
2502 if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2503 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2504 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2505 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2506 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2507 pMainFrame->MDISetMenu(pNewImgMergeMenu, NULL);
2508 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2509 pMainFrame->MDISetMenu(pNewDirMenu, NULL);
2511 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2514 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2516 // Don't delete the old menu
2517 // There is a bug in BCMenu or in Windows98 : the new menu does not
2518 // appear correctly if we destroy the old one
2519 // if (pOldDefaultMenu)
2520 // pOldDefaultMenu->DestroyMenu();
2521 // if (pOldMergeMenu)
2522 // pOldMergeMenu->DestroyMenu();
2524 // pOldDirMenu->DestroyMenu();
2526 // m_hMenuDefault is used to redraw the main menu when we close a child frame
2527 // if this child frame had a different menu
2528 pMainFrame->m_hMenuDefault = hNewDefaultMenu;
2529 pApp->m_pOpenTemplate->m_hMenuShared = hNewDefaultMenu;
2530 pApp->m_pDiffTemplate->m_hMenuShared = hNewMergeMenu;
2531 pApp->m_pDirTemplate->m_hMenuShared = hNewDirMenu;
2533 // force redrawing the menu bar
2534 pMainFrame->DrawMenuBar();
2538 void CMainFrame::UpdateDocTitle()
2540 CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
2541 POSITION posTemplate = pDocManager->GetFirstDocTemplatePosition();
2542 ASSERT(posTemplate != NULL);
2544 while (posTemplate != NULL)
2546 CDocTemplate* pTemplate = pDocManager->GetNextDocTemplate(posTemplate);
2548 ASSERT(pTemplate != NULL);
2550 for (auto pDoc : GetDocList(static_cast<CMultiDocTemplate *>(pTemplate)))
2552 static_cast<CDocument *>(const_cast<void *>(pDoc))->SetTitle(NULL);
2553 ((CFrameWnd*)AfxGetApp()->m_pMainWnd)->OnUpdateFrameTitle(TRUE);