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
30 #include <Poco/Exception.h>
32 #include <Poco/Exception.h>
34 #include "Constants.h"
36 #include "FileFilterHelper.h"
37 #include "UnicodeString.h"
40 #include "DirFrame.h" // Include type information
42 #include "HexMergeFrm.h"
48 #include "MergeEditView.h"
49 #include "HexMergeDoc.h"
50 #include "ImgMergeFrm.h"
51 #include "LineFiltersList.h"
52 #include "ConflictFileParser.h"
53 #include "LineFiltersDlg.h"
55 #include "Environment.h"
56 #include "CustomStatusCursor.h"
57 #include "PatchTool.h"
59 #include "SelectUnpackerDlg.h"
60 #include "ConfigLog.h"
62 #include "Merge7zFormatMergePluginImpl.h"
63 #include "FileFiltersDlg.h"
64 #include "OptionsMgr.h"
65 #include "OptionsDef.h"
66 #include "codepage_detect.h"
69 #include "PreferencesDlg.h"
70 #include "ProjectFilePathsDlg.h"
71 #include "FileOrFolderSelect.h"
73 #include "PluginsListDlg.h"
74 #include "stringdiffs.h"
75 #include "MergeCmdLineInfo.h"
76 #include "OptionsFont.h"
79 #include "DropHandler.h"
80 #include "LanguageSelect.h"
86 One source file must compile the stubs for multimonitor
87 by defining the symbol COMPILE_MULTIMON_STUBS & including <multimon.h>
89 #ifdef COMPILE_MULTIMON_STUBS
90 #undef COMPILE_MULTIMON_STUBS
92 #define COMPILE_MULTIMON_STUBS
98 static char THIS_FILE[] = __FILE__;
101 static void LoadToolbarImageList(CMainFrame::TOOLBAR_SIZE size, UINT nIDResource, CImageList& ImgList);
102 static const CPtrList &GetDocList(const CMultiDocTemplate *pTemplate);
103 template<class DocClass>
104 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles);
107 * @brief A table associating menuitem id, icon and menus to apply.
109 const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
110 { ID_FILE_OPENCONFLICT, IDB_FILE_OPENCONFLICT, CMainFrame::MENU_ALL },
111 { ID_EDIT_COPY, IDB_EDIT_COPY, CMainFrame::MENU_ALL },
112 { ID_EDIT_CUT, IDB_EDIT_CUT, CMainFrame::MENU_ALL },
113 { ID_EDIT_PASTE, IDB_EDIT_PASTE, CMainFrame::MENU_ALL },
114 { ID_EDIT_FIND, IDB_EDIT_SEARCH, CMainFrame::MENU_ALL },
115 { ID_WINDOW_CASCADE, IDB_WINDOW_CASCADE, CMainFrame::MENU_ALL },
116 { ID_WINDOW_TILE_HORZ, IDB_WINDOW_HORIZONTAL, CMainFrame::MENU_ALL },
117 { ID_WINDOW_TILE_VERT, IDB_WINDOW_VERTICAL, CMainFrame::MENU_ALL },
118 { ID_FILE_CLOSE, IDB_WINDOW_CLOSE, CMainFrame::MENU_ALL },
119 { ID_WINDOW_CHANGE_PANE, IDB_WINDOW_CHANGEPANE, CMainFrame::MENU_ALL },
120 { ID_EDIT_WMGOTO, IDB_EDIT_GOTO, CMainFrame::MENU_ALL },
121 { ID_EDIT_REPLACE, IDB_EDIT_REPLACE, CMainFrame::MENU_ALL },
122 { ID_VIEW_SELECTFONT, IDB_VIEW_SELECTFONT, CMainFrame::MENU_ALL },
123 { ID_APP_EXIT, IDB_FILE_EXIT, CMainFrame::MENU_ALL },
124 { ID_HELP_CONTENTS, IDB_HELP_CONTENTS, CMainFrame::MENU_ALL },
125 { ID_EDIT_SELECT_ALL, IDB_EDIT_SELECTALL, CMainFrame::MENU_ALL },
126 { ID_TOOLS_FILTERS, IDB_TOOLS_FILTERS, CMainFrame::MENU_ALL },
127 { ID_TOOLS_CUSTOMIZECOLUMNS, IDB_TOOLS_COLUMNS, CMainFrame::MENU_ALL },
128 { ID_TOOLS_GENERATEPATCH, IDB_TOOLS_GENERATEPATCH, CMainFrame::MENU_ALL },
129 { ID_PLUGINS_LIST, IDB_PLUGINS_LIST, CMainFrame::MENU_ALL },
130 { ID_COPY_FROM_LEFT, IDB_COPY_FROM_LEFT, CMainFrame::MENU_ALL },
131 { ID_COPY_FROM_RIGHT, IDB_COPY_FROM_RIGHT, CMainFrame::MENU_ALL },
132 { ID_FILE_PRINT, IDB_FILE_PRINT, CMainFrame::MENU_FILECMP },
133 { ID_TOOLS_GENERATEREPORT, IDB_TOOLS_GENERATEREPORT, CMainFrame::MENU_FILECMP },
134 { ID_EDIT_TOGGLE_BOOKMARK, IDB_EDIT_TOGGLE_BOOKMARK, CMainFrame::MENU_FILECMP },
135 { ID_EDIT_GOTO_NEXT_BOOKMARK, IDB_EDIT_GOTO_NEXT_BOOKMARK, CMainFrame::MENU_FILECMP },
136 { ID_EDIT_GOTO_PREV_BOOKMARK, IDB_EDIT_GOTO_PREV_BOOKMARK, CMainFrame::MENU_FILECMP },
137 { ID_EDIT_CLEAR_ALL_BOOKMARKS, IDB_EDIT_CLEAR_ALL_BOOKMARKS, CMainFrame::MENU_FILECMP },
138 { ID_VIEW_ZOOMIN, IDB_VIEW_ZOOMIN, CMainFrame::MENU_FILECMP },
139 { ID_VIEW_ZOOMOUT, IDB_VIEW_ZOOMOUT, CMainFrame::MENU_FILECMP },
140 { ID_MERGE_COMPARE, IDB_MERGE_COMPARE, CMainFrame::MENU_FOLDERCMP },
141 { ID_MERGE_COMPARE_LEFT1_LEFT2, IDB_MERGE_COMPARE_LEFT1_LEFT2, CMainFrame::MENU_FOLDERCMP },
142 { ID_MERGE_COMPARE_RIGHT1_RIGHT2, IDB_MERGE_COMPARE_RIGHT1_RIGHT2,CMainFrame::MENU_FOLDERCMP },
143 { ID_MERGE_COMPARE_LEFT1_RIGHT2, IDB_MERGE_COMPARE_LEFT1_RIGHT2, CMainFrame::MENU_FOLDERCMP },
144 { ID_MERGE_COMPARE_LEFT2_RIGHT1, IDB_MERGE_COMPARE_LEFT2_RIGHT1, CMainFrame::MENU_FOLDERCMP },
145 { ID_MERGE_DELETE, IDB_MERGE_DELETE, CMainFrame::MENU_FOLDERCMP },
146 { ID_TOOLS_GENERATEREPORT, IDB_TOOLS_GENERATEREPORT, CMainFrame::MENU_FOLDERCMP },
147 { ID_DIR_COPY_LEFT_TO_RIGHT, IDB_LEFT_TO_RIGHT, CMainFrame::MENU_FOLDERCMP },
148 { ID_DIR_COPY_RIGHT_TO_LEFT, IDB_RIGHT_TO_LEFT, CMainFrame::MENU_FOLDERCMP },
149 { ID_DIR_COPY_LEFT_TO_BROWSE, IDB_LEFT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
150 { ID_DIR_COPY_RIGHT_TO_BROWSE, IDB_RIGHT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
151 { ID_DIR_MOVE_LEFT_TO_BROWSE, IDB_MOVE_LEFT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
152 { ID_DIR_MOVE_RIGHT_TO_BROWSE, IDB_MOVE_RIGHT_TO_BROWSE, CMainFrame::MENU_FOLDERCMP },
153 { ID_DIR_DEL_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
154 { ID_DIR_DEL_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
155 { ID_DIR_DEL_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
156 { ID_DIR_COPY_PATHNAMES_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
157 { ID_DIR_COPY_PATHNAMES_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
158 { ID_DIR_COPY_PATHNAMES_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
159 { ID_DIR_COPY_LEFT_TO_CLIPBOARD, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
160 { ID_DIR_COPY_RIGHT_TO_CLIPBOARD, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
161 { ID_DIR_COPY_BOTH_TO_CLIPBOARD, IDB_BOTH, CMainFrame::MENU_FOLDERCMP },
162 { ID_DIR_ZIP_LEFT, IDB_LEFT, CMainFrame::MENU_FOLDERCMP },
163 { ID_DIR_ZIP_RIGHT, IDB_RIGHT, CMainFrame::MENU_FOLDERCMP },
164 { ID_DIR_ZIP_BOTH, IDB_BOTH, CMainFrame::MENU_FOLDERCMP }
168 /////////////////////////////////////////////////////////////////////////////
171 IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
173 BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
174 //{{AFX_MSG_MAP(CMainFrame)
177 ON_WM_INITMENUPOPUP()
179 ON_COMMAND(ID_OPTIONS_SHOWDIFFERENT, OnOptionsShowDifferent)
180 ON_COMMAND(ID_OPTIONS_SHOWIDENTICAL, OnOptionsShowIdentical)
181 ON_COMMAND(ID_OPTIONS_SHOWUNIQUELEFT, OnOptionsShowUniqueLeft)
182 ON_COMMAND(ID_OPTIONS_SHOWUNIQUERIGHT, OnOptionsShowUniqueRight)
183 ON_COMMAND(ID_OPTIONS_SHOWBINARIES, OnOptionsShowBinaries)
184 ON_COMMAND(ID_OPTIONS_SHOWSKIPPED, OnOptionsShowSkipped)
185 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENT, OnUpdateOptionsShowdifferent)
186 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWIDENTICAL, OnUpdateOptionsShowidentical)
187 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUELEFT, OnUpdateOptionsShowuniqueleft)
188 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUERIGHT, OnUpdateOptionsShowuniqueright)
189 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWBINARIES, OnUpdateOptionsShowBinaries)
190 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWSKIPPED, OnUpdateOptionsShowSkipped)
192 ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
193 ON_COMMAND(ID_HELP_GNULICENSE, OnHelpGnulicense)
194 ON_COMMAND(ID_OPTIONS, OnOptions)
195 ON_COMMAND(ID_VIEW_SELECTFONT, OnViewSelectfont)
196 ON_UPDATE_COMMAND_UI(ID_VIEW_SELECTFONT, OnUpdateViewSelectfont)
197 ON_COMMAND(ID_VIEW_USEDEFAULTFONT, OnViewUsedefaultfont)
198 ON_UPDATE_COMMAND_UI(ID_VIEW_USEDEFAULTFONT, OnUpdateViewUsedefaultfont)
199 ON_COMMAND(ID_HELP_CONTENTS, OnHelpContents)
200 ON_UPDATE_COMMAND_UI(ID_HELP_CONTENTS, OnUpdateHelpContents)
202 ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
203 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
204 ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
207 ON_COMMAND_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnPluginUnpackMode)
208 ON_UPDATE_COMMAND_UI_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnUpdatePluginUnpackMode)
209 ON_COMMAND_RANGE(ID_PREDIFFER_MANUAL, ID_PREDIFFER_AUTO, OnPluginPrediffMode)
210 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFER_MANUAL, ID_PREDIFFER_AUTO, OnUpdatePluginPrediffMode)
211 ON_UPDATE_COMMAND_UI(ID_RELOAD_PLUGINS, OnUpdateReloadPlugins)
212 ON_COMMAND(ID_RELOAD_PLUGINS, OnReloadPlugins)
213 ON_COMMAND(ID_HELP_GETCONFIG, OnSaveConfigData)
214 ON_COMMAND(ID_FILE_NEW, OnFileNew)
215 ON_COMMAND(ID_FILE_NEW3, OnFileNew3)
216 ON_COMMAND(ID_TOOLS_FILTERS, OnToolsFilters)
217 ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
218 ON_UPDATE_COMMAND_UI(ID_VIEW_TAB_BAR, OnUpdateViewTabBar)
219 ON_COMMAND(ID_VIEW_TAB_BAR, OnViewTabBar)
220 ON_UPDATE_COMMAND_UI(ID_VIEW_RESIZE_PANES, OnUpdateResizePanes)
221 ON_COMMAND(ID_VIEW_RESIZE_PANES, OnResizePanes)
222 ON_COMMAND(ID_FILE_OPENPROJECT, OnFileOpenproject)
223 ON_MESSAGE(WM_COPYDATA, OnCopyData)
224 ON_MESSAGE(WM_USER+1, OnUser1)
225 ON_COMMAND(ID_WINDOW_CLOSEALL, OnWindowCloseAll)
226 ON_UPDATE_COMMAND_UI(ID_WINDOW_CLOSEALL, OnUpdateWindowCloseAll)
227 ON_COMMAND(ID_FILE_SAVEPROJECT, OnSaveProject)
231 ON_COMMAND(ID_TOOLBAR_NONE, OnToolbarNone)
232 ON_UPDATE_COMMAND_UI(ID_TOOLBAR_NONE, OnUpdateToolbarNone)
233 ON_COMMAND(ID_TOOLBAR_SMALL, OnToolbarSmall)
234 ON_UPDATE_COMMAND_UI(ID_TOOLBAR_SMALL, OnUpdateToolbarSmall)
235 ON_COMMAND(ID_TOOLBAR_BIG, OnToolbarBig)
236 ON_UPDATE_COMMAND_UI(ID_TOOLBAR_BIG, OnUpdateToolbarBig)
237 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
238 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
239 ON_COMMAND(ID_HELP_CHECKFORUPDATES, OnHelpCheckForUpdates)
240 ON_COMMAND(ID_FILE_OPENCONFLICT, OnFileOpenConflict)
241 ON_COMMAND(ID_PLUGINS_LIST, OnPluginsList)
242 ON_UPDATE_COMMAND_UI(ID_STATUS_PLUGIN, OnUpdatePluginName)
243 ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDiffOptionsDropDown)
244 ON_COMMAND_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnDiffWhitespace)
245 ON_UPDATE_COMMAND_UI_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnUpdateDiffWhitespace)
246 ON_COMMAND(IDC_DIFF_CASESENSITIVE, OnDiffCaseSensitive)
247 ON_UPDATE_COMMAND_UI(IDC_DIFF_CASESENSITIVE, OnUpdateDiffCaseSensitive)
248 ON_COMMAND(IDC_DIFF_IGNOREEOL, OnDiffIgnoreEOL)
249 ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNOREEOL, OnUpdateDiffIgnoreEOL)
250 ON_COMMAND_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnCompareMethod)
251 ON_UPDATE_COMMAND_UI_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnUpdateCompareMethod)
252 ON_COMMAND_RANGE(ID_MRU_FIRST, ID_MRU_LAST, OnMRUs)
253 ON_UPDATE_COMMAND_UI(ID_MRU_FIRST, OnUpdateNoMRUs)
254 ON_UPDATE_COMMAND_UI(ID_NO_MRU, OnUpdateNoMRUs)
259 * @brief MainFrame statusbar panels/indicators
261 static UINT StatusbarIndicators[] =
263 ID_SEPARATOR, // Plugin name
264 ID_SEPARATOR, // status line indicator
265 ID_SEPARATOR, // Merge mode
266 ID_SEPARATOR, // Diff number
267 ID_INDICATOR_CAPS, // Caps Lock
268 ID_INDICATOR_NUM, // Num Lock
269 ID_INDICATOR_OVR, // Insert
272 /** @brief Timer ID for window flashing timer. */
273 static const UINT ID_TIMER_FLASH = 1;
275 /** @brief Timeout for window flashing timer, in milliseconds. */
276 static const UINT WINDOW_FLASH_TIMEOUT = 500;
279 * @brief Return a const reference to a CMultiDocTemplate's list of documents.
281 static const CPtrList &GetDocList(const CMultiDocTemplate *pTemplate)
283 struct Template : public CMultiDocTemplate
286 using CMultiDocTemplate::m_docList;
288 return static_cast<const struct Template *>(pTemplate)->m_docList;
291 /////////////////////////////////////////////////////////////////////////////
292 // CMainFrame construction/destruction
295 * @brief MainFrame constructor. Loads settings from registry.
296 * @todo Preference for logging?
298 CMainFrame::CMainFrame()
301 , m_pDropHandler(NULL)
303 ZeroMemory(&m_pMenus[0], sizeof(m_pMenus));
306 CMainFrame::~CMainFrame()
308 GetOptionsMgr()->SaveOption(OPT_TABBAR_AUTO_MAXWIDTH, m_wndTabBar.GetAutoMaxWidth());
312 // This is a bridge to implement IStatusDisplay for WaitStatusCursor
313 // by forwarding all calls to the main frame
314 class StatusDisplay : public IStatusDisplay
317 StatusDisplay() : m_pfrm(0) { }
318 void SetFrame(CMainFrame * frm) { m_pfrm = frm; }
319 // Implement IStatusDisplay
320 virtual CString BeginStatus(LPCTSTR str) { return m_pfrm->SetStatus(str); }
321 virtual void ChangeStatus(LPCTSTR str) { m_pfrm->SetStatus(str); }
322 virtual void EndStatus(LPCTSTR str, LPCTSTR oldstr) { m_pfrm->SetStatus(oldstr); }
328 static StatusDisplay myStatusDisplay;
331 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassW");
333 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassA");
336 * @brief Change MainFrame window class name
337 * see http://support.microsoft.com/kb/403825/ja
339 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
342 BOOL bRes = CMDIFrameWnd::PreCreateWindow(cs);
343 HINSTANCE hInst = AfxGetInstanceHandle();
344 // see if the class already exists
345 if (!::GetClassInfo(hInst, szClassName, &wndcls))
348 ::GetClassInfo(hInst, cs.lpszClass, &wndcls);
349 // register a new class
350 wndcls.lpszClassName = szClassName;
351 wndcls.hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(IDR_MAINFRAME));
352 ::RegisterClass(&wndcls);
354 cs.lpszClass = szClassName;
358 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
360 if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
363 m_lfDiff = Options::Font::Load(OPT_FONT_FILECMP);
364 m_lfDir = Options::Font::Load(OPT_FONT_DIRCMP);
366 if (!CreateToolbar())
368 TRACE0("Failed to create toolbar\n");
369 return -1; // fail to create
372 if (!m_wndTabBar.Create(this))
374 TRACE0("Failed to create tab bar\n");
375 return -1; // fail to create
377 m_wndTabBar.SetAutoMaxWidth(GetOptionsMgr()->GetBool(OPT_TABBAR_AUTO_MAXWIDTH));
379 if (GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR) == false)
380 CMDIFrameWnd::ShowControlBar(&m_wndTabBar, false, 0);
382 if (!m_wndStatusBar.Create(this))
384 TRACE0("Failed to create status bar\n");
385 return -1; // fail to create
387 theApp.SetIndicators(m_wndStatusBar, StatusbarIndicators,
388 countof(StatusbarIndicators));
390 m_wndStatusBar.SetPaneInfo(1, ID_STATUS_PLUGIN, 0, 300);
391 m_wndStatusBar.SetPaneInfo(2, ID_STATUS_MERGINGMODE, 0, 100);
392 m_wndStatusBar.SetPaneInfo(3, ID_STATUS_DIFFNUM, 0, 150);
394 if (GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR) == false)
395 CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, false, 0);
397 // Start handling status messages from CustomStatusCursors
398 myStatusDisplay.SetFrame(this);
399 CustomStatusCursor::SetStatusDisplay(&myStatusDisplay);
401 m_pDropHandler = new DropHandler(std::bind(&CMainFrame::OnDropFiles, this, std::placeholders::_1));
402 RegisterDragDrop(m_hWnd, m_pDropHandler);
407 void CMainFrame::OnDestroy(void)
410 RevokeDragDrop(m_hWnd);
413 static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
416 for (i = 0 ; i < ::GetMenuItemCount(mainMenu) ; i++)
417 if (::GetMenuItemID(::GetSubMenu(mainMenu, i), 0) == nIDFirstMenuItem)
419 HMENU menu = ::GetSubMenu(mainMenu, i);
423 // look for last submenu
424 for (i = ::GetMenuItemCount(menu) ; i >= 0 ; i--)
425 if (::GetSubMenu(menu, i) != NULL)
426 return ::GetSubMenu(menu, i);
430 // look for first submenu
431 for (i = 0 ; i < ::GetMenuItemCount(menu) ; i++)
432 if (::GetSubMenu(menu, i) != NULL)
433 return ::GetSubMenu(menu, i);
436 // error, submenu not found
441 * @brief Find the scripts submenu from the main menu
442 * As now this is the first submenu in "Edit" menu
443 * We find the "Edit" menu by looking for a menu
444 * starting with ID_EDIT_UNDO.
446 HMENU CMainFrame::GetScriptsSubmenu(HMENU mainMenu)
448 return GetSubmenu(mainMenu, ID_PLUGINS_LIST, false);
452 * @brief Find the scripts submenu from the main menu
453 * As now this is the first submenu in "Plugins" menu
454 * We find the "Plugins" menu by looking for a menu
455 * starting with ID_UNPACK_MANUAL.
457 HMENU CMainFrame::GetPrediffersSubmenu(HMENU mainMenu)
459 return GetSubmenu(mainMenu, ID_PLUGINS_LIST, true);
463 * @brief Create a new menu for the view..
464 * @param [in] view Menu view either MENU_DEFAULT, MENU_MERGEVIEW or MENU_DIRVIEW.
465 * @param [in] ID Menu's resource ID.
466 * @return Menu for the view.
468 HMENU CMainFrame::NewMenu(int view, int ID)
470 int menu_view, index;
471 if (m_pMenus[view] == NULL)
473 m_pMenus[view].reset(new BCMenu());
474 if (m_pMenus[view] == NULL)
481 menu_view = MENU_FILECMP;
484 menu_view = MENU_FOLDERCMP;
488 menu_view = MENU_MAINFRM;
492 if (!m_pMenus[view]->LoadMenu(ID))
498 if (view == MENU_IMGMERGEVIEW)
500 BCMenu *pMenu = new BCMenu;
501 pMenu->LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
502 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()));
505 // Load bitmaps to menuitems
506 for (index = 0; index < countof(m_MenuIcons); index ++)
508 if (menu_view == (m_MenuIcons[index].menusToApply & menu_view))
510 m_pMenus[view]->ModifyODMenu(NULL, m_MenuIcons[index].menuitemID, m_MenuIcons[index].iconResID);
514 m_pMenus[view]->LoadToolbar(IDR_MAINFRAME);
516 theApp.TranslateMenu(m_pMenus[view]->m_hMenu);
518 return (m_pMenus[view]->Detach());
522 * @brief Create new default (CMainFrame) menu.
524 HMENU CMainFrame::NewDefaultMenu(int ID /*=0*/)
528 return NewMenu( MENU_DEFAULT, ID );
532 * @brief Create new File compare (CMergeEditView) menu.
534 HMENU CMainFrame::NewMergeViewMenu()
536 return NewMenu( MENU_MERGEVIEW, IDR_MERGEDOCTYPE);
540 * @brief Create new Dir compare (CDirView) menu
542 HMENU CMainFrame::NewDirViewMenu()
544 return NewMenu(MENU_DIRVIEW, IDR_DIRDOCTYPE );
548 * @brief Create new File compare (CHexMergeView) menu.
550 HMENU CMainFrame::NewHexMergeViewMenu()
552 return NewMenu( MENU_HEXMERGEVIEW, IDR_MERGEDOCTYPE);
556 * @brief Create new Image compare (CImgMergeView) menu.
558 HMENU CMainFrame::NewImgMergeViewMenu()
560 return NewMenu( MENU_IMGMERGEVIEW, IDR_MERGEDOCTYPE);
564 * @brief Create new File compare (COpenView) menu.
566 HMENU CMainFrame::NewOpenViewMenu()
568 return NewMenu( MENU_OPENVIEW, IDR_MAINFRAME);
572 * @brief This handler ensures that the popup menu items are drawn correctly.
574 void CMainFrame::OnMeasureItem(int nIDCtl,
575 LPMEASUREITEMSTRUCT lpMeasureItemStruct)
577 BOOL setflag = FALSE;
578 if (lpMeasureItemStruct->CtlType == ODT_MENU)
580 if (IsMenu((HMENU)lpMeasureItemStruct->itemID))
583 CMenu::FromHandle((HMENU)lpMeasureItemStruct->itemID);
585 if (m_pMenus[MENU_DEFAULT]->IsMenu(cmenu))
587 m_pMenus[MENU_DEFAULT]->MeasureItem(lpMeasureItemStruct);
594 CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
598 * @brief This handler ensures that keyboard shortcuts work.
600 LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags,
604 if(m_pMenus[MENU_DEFAULT]->IsMenu(pMenu))
605 lresult=BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
607 lresult=CMDIFrameWnd::OnMenuChar(nChar, nFlags, pMenu);
612 * @brief This handler updates the menus from time to time.
614 void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
616 CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
620 if (BCMenu::IsMenu(pPopupMenu))
622 BCMenu::UpdateMenu(pPopupMenu);
627 /////////////////////////////////////////////////////////////////////////////
628 // CMainFrame message handlers
630 void CMainFrame::OnFileOpen()
636 * @brief Check for BOM, and also, if bGuessEncoding, try to deduce codepage
638 * Unpacks info from FileLocation & delegates all work to codepage_detect module
641 FileLocationGuessEncodings(FileLocation & fileloc, int iGuessEncoding)
643 fileloc.encoding = GuessCodepageEncoding(fileloc.filepath, iGuessEncoding);
646 int CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
647 int nFiles, const FileLocation ifileloc[],
648 const DWORD dwFlags[] /*=0*/, const PackingInfo * infoUnpacker /*= NULL*/)
651 FileFilterHelper filterImg, filterBin;
652 filterImg.UseMask(true);
653 filterImg.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
654 filterBin.UseMask(true);
655 filterBin.SetMask(GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS));
656 for (pane = 0; pane < nFiles; ++pane)
658 if (filterImg.includeFile(ifileloc[pane].filepath))
659 return ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, infoUnpacker);
660 else if (filterBin.includeFile(ifileloc[pane].filepath))
663 for (int pane = 0; pane < nFiles; pane++)
664 bRO[pane] = (dwFlags) ? ((dwFlags[pane] & FFILEOPEN_READONLY) > 0) : FALSE;
665 ShowHexMergeDoc(pDirDoc,
667 PathContext(ifileloc[0].filepath, ifileloc[1].filepath) :
668 PathContext(ifileloc[0].filepath, ifileloc[1].filepath, ifileloc[2].filepath)
673 return ShowMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, infoUnpacker);
677 * @brief Creates new MergeDoc instance and shows documents.
678 * @param [in] pDirDoc Dir compare document to create a new Merge document for.
679 * @param [in] ifilelocLeft Left side file location info.
680 * @param [in] ifilelocRight Right side file location info.
681 * @param [in] dwLeftFlags Left side flags.
682 * @param [in] dwRightFlags Right side flags.
683 * @param [in] infoUnpacker Plugin info.
684 * @return OPENRESULTS_TYPE for success/failure code.
686 int CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
687 int nFiles, const FileLocation ifileloc[],
688 const DWORD dwFlags[] /*=0*/, const PackingInfo * infoUnpacker /*= NULL*/)
690 if (!m_pMenus[MENU_MERGEVIEW])
691 theApp.m_pDiffTemplate->m_hMenuShared = NewMergeViewMenu();
692 CMergeDoc * pMergeDoc = GetMergeDocForDiff<CMergeDoc>(theApp.m_pDiffTemplate, pDirDoc, nFiles);
694 // Make local copies, so we can change encoding if we guess it below
695 FileLocation fileloc[3];
698 for (pane = 0; pane < nFiles; pane++)
700 fileloc[pane] = ifileloc[pane];
702 bRO[pane] = (dwFlags[pane] & FFILEOPEN_READONLY) > 0;
707 ASSERT(pMergeDoc); // must ASSERT to get an answer to the question below ;-)
709 return OPENRESULTS_FAILED_MISC; // when does this happen ?
711 // if an unpacker is selected, it must be used during LoadFromFile
712 // MergeDoc must memorize it for SaveToFile
713 // Warning : this unpacker may differ from the pDirDoc one
714 // (through menu : "Plugins"->"Open with unpacker")
715 pMergeDoc->SetUnpacker(infoUnpacker);
718 int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
719 for (pane = 0; pane < nFiles; pane++)
721 if (fileloc[pane].encoding.m_unicoding == -1)
722 fileloc[pane].encoding.m_unicoding = ucr::NONE;
723 if (fileloc[pane].encoding.m_unicoding == ucr::NONE && fileloc[pane].encoding.m_codepage == -1)
725 FileLocationGuessEncodings(fileloc[pane], iGuessEncodingType);
728 // TODO (Perry, 2005-12-04)
729 // Should we do any unification if unicodings are different?
733 // In ANSI (8-bit) build, character loss can occur in merging
734 // if the two buffers use different encodings
735 if (pane > 0 && fileloc[pane - 1].encoding.m_codepage != fileloc[pane].encoding.m_codepage)
738 msg.Format(theApp.LoadString(IDS_SUGGEST_IGNORECODEPAGE).c_str(), fileloc[pane - 1].encoding.m_codepage,fileloc[pane].encoding.m_codepage);
739 int msgflags = MB_YESNO | MB_ICONQUESTION | MB_DONT_ASK_AGAIN;
740 // Two files with different codepages
741 // Warn and propose to use the default codepage for both
742 int userChoice = AfxMessageBox(msg, msgflags);
743 if (userChoice == IDYES)
745 fileloc[pane - 1].encoding.SetCodepage(ucr::getDefaultCodepage());
746 fileloc[pane - 1].encoding.m_bom = false;
747 fileloc[pane].encoding.SetCodepage(ucr::getDefaultCodepage());
748 fileloc[pane].encoding.m_bom = false;
754 int nActivePane = -1;
755 for (pane = 0; pane < nFiles; pane++)
757 if (dwFlags && (dwFlags[pane] & FFILEOPEN_SETFOCUS))
761 // Note that OpenDocs() takes care of closing compare window when needed.
762 OPENRESULTS_TYPE openResults = pMergeDoc->OpenDocs(fileloc, bRO, nActivePane);
764 if (openResults == OPENRESULTS_SUCCESS)
766 for (pane = 0; pane < nFiles; pane++)
770 BOOL bModified = (dwFlags[pane] & FFILEOPEN_MODIFIED) > 0;
773 pMergeDoc->m_ptBuf[pane]->SetModified(TRUE);
774 pMergeDoc->UpdateHeaderPath(pane);
776 if (dwFlags[pane] & FFILEOPEN_AUTOMERGE)
778 pMergeDoc->DoAutoMerge(pane);
786 void CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc,
787 const PathContext &paths, const bool bRO[])
789 if (!m_pMenus[MENU_HEXMERGEVIEW])
790 theApp.m_pHexMergeTemplate->m_hMenuShared = NewHexMergeViewMenu();
791 if (CHexMergeDoc *pHexMergeDoc = GetMergeDocForDiff<CHexMergeDoc>(theApp.m_pHexMergeTemplate, pDirDoc, paths.GetSize()))
792 pHexMergeDoc->OpenDocs(paths, bRO);
795 int CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
796 const DWORD dwFlags[], const PackingInfo * infoUnpacker/* = NULL*/)
798 CImgMergeFrame *pImgMergeFrame = new CImgMergeFrame();
799 if (!CImgMergeFrame::menu.m_hMenu)
800 CImgMergeFrame::menu.m_hMenu = NewImgMergeViewMenu();
801 pImgMergeFrame->SetSharedMenu(CImgMergeFrame::menu.m_hMenu);
804 for (pane = 0; pane < nFiles; ++pane)
805 files.SetPath(pane, fileloc[pane].filepath, false);
808 for (pane = 0; pane < nFiles; pane++)
811 bRO[pane] = (dwFlags[pane] & FFILEOPEN_READONLY) > 0;
816 int nActivePane = -1;
817 for (pane = 0; pane < nFiles; pane++)
819 if (dwFlags && (dwFlags[pane] & FFILEOPEN_SETFOCUS))
823 pImgMergeFrame->SetDirDoc(pDirDoc);
824 pDirDoc->AddMergeDoc(pImgMergeFrame);
826 if (!pImgMergeFrame->OpenImages(files, bRO, nActivePane, this))
828 ShowMergeDoc(pDirDoc, nFiles, fileloc, dwFlags, infoUnpacker);
832 for (pane = 0; pane < nFiles; pane++)
834 if (dwFlags[pane] & FFILEOPEN_AUTOMERGE)
835 pImgMergeFrame->DoAutoMerge(pane);
842 void CMainFrame::RedisplayAllDirDocs()
844 const DirDocList &dirdocs = GetAllDirDocs();
845 POSITION pos = dirdocs.GetHeadPosition();
848 CDirDoc * pDirDoc = dirdocs.GetNext(pos);
849 pDirDoc->Redisplay();
854 * @brief Show/Hide different files/directories
856 void CMainFrame::OnOptionsShowDifferent()
858 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_DIFFERENT);
859 GetOptionsMgr()->SaveOption(OPT_SHOW_DIFFERENT, !val); // reverse
860 RedisplayAllDirDocs();
864 * @brief Show/Hide identical files/directories
866 void CMainFrame::OnOptionsShowIdentical()
868 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_IDENTICAL);
869 GetOptionsMgr()->SaveOption(OPT_SHOW_IDENTICAL, !val); // reverse
870 RedisplayAllDirDocs();
874 * @brief Show/Hide left-only files/directories
876 void CMainFrame::OnOptionsShowUniqueLeft()
878 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_UNIQUE_LEFT);
879 GetOptionsMgr()->SaveOption(OPT_SHOW_UNIQUE_LEFT, !val); // reverse
880 RedisplayAllDirDocs();
884 * @brief Show/Hide right-only files/directories
886 void CMainFrame::OnOptionsShowUniqueRight()
888 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_UNIQUE_RIGHT);
889 GetOptionsMgr()->SaveOption(OPT_SHOW_UNIQUE_RIGHT, !val); // reverse
890 RedisplayAllDirDocs();
894 * @brief Show/Hide binary files
896 void CMainFrame::OnOptionsShowBinaries()
898 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_BINARIES);
899 GetOptionsMgr()->SaveOption(OPT_SHOW_BINARIES, !val); // reverse
900 RedisplayAllDirDocs();
904 * @brief Show/Hide skipped files/directories
906 void CMainFrame::OnOptionsShowSkipped()
908 bool val = GetOptionsMgr()->GetBool(OPT_SHOW_SKIPPED);
909 GetOptionsMgr()->SaveOption(OPT_SHOW_SKIPPED, !val); // reverse
910 RedisplayAllDirDocs();
913 void CMainFrame::OnUpdateOptionsShowdifferent(CCmdUI* pCmdUI)
915 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_DIFFERENT));
918 void CMainFrame::OnUpdateOptionsShowidentical(CCmdUI* pCmdUI)
920 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_IDENTICAL));
923 void CMainFrame::OnUpdateOptionsShowuniqueleft(CCmdUI* pCmdUI)
925 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_UNIQUE_LEFT));
928 void CMainFrame::OnUpdateOptionsShowuniqueright(CCmdUI* pCmdUI)
930 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_UNIQUE_RIGHT));
933 void CMainFrame::OnUpdateOptionsShowBinaries(CCmdUI* pCmdUI)
935 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_BINARIES));
938 void CMainFrame::OnUpdateOptionsShowSkipped(CCmdUI* pCmdUI)
940 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_SKIPPED));
944 * @brief Show GNU licence information in notepad (local file) or in Web Browser
946 void CMainFrame::OnHelpGnulicense()
948 const String spath = paths_ConcatPath(env_GetProgPath(), LicenseFile);
949 theApp.OpenFileOrUrl(spath.c_str(), LicenceUrl);
952 /// Wrapper to set the global option 'm_bAllowMixedEol'
953 void CMainFrame::SetEOLMixed(BOOL bAllow)
955 GetOptionsMgr()->SaveOption(OPT_ALLOW_MIXED_EOL, bAllow == TRUE);
956 ApplyViewWhitespace();
960 * @brief Opens Options-dialog and saves changed options
962 void CMainFrame::OnOptions()
964 // Using singleton shared syntax colors
965 CPreferencesDlg dlg(GetOptionsMgr(), theApp.GetMainSyntaxColors());
966 INT_PTR rv = dlg.DoModal();
970 LANGID lang = GetOptionsMgr()->GetInt(OPT_SELECTED_LANGUAGE);
971 if (lang != theApp.m_pLangDlg->GetLangId())
973 theApp.m_pLangDlg->SetLanguage(lang, TRUE);
975 // Update status bar inicator texts
976 theApp.SetIndicators(m_wndStatusBar, 0, 0);
978 // Update the current menu
981 // update the title text of the document
987 // Set new filterpath
988 String filterPath = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
989 theApp.m_pGlobalFileFilter->SetUserFilterPath(filterPath);
991 theApp.UpdateCodepageModule();
992 // Call the wrapper to set m_bAllowMixedEol (the wrapper updates the registry)
993 SetEOLMixed(GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL));
995 sd_SetBreakChars(GetOptionsMgr()->GetString(OPT_BREAK_SEPARATORS).c_str());
997 // make an attempt at rescanning any open diff sessions
1000 // Update all dirdoc settings
1001 const DirDocList &dirDocs = GetAllDirDocs();
1002 POSITION pos = dirDocs.GetHeadPosition();
1005 CDirDoc * pDirDoc = dirDocs.GetNext(pos);
1006 pDirDoc->RefreshOptions();
1009 const HexMergeDocList &hexdocs = GetAllHexMergeDocs();
1010 pos = hexdocs.GetHeadPosition();
1013 CHexMergeDoc * pMergeDoc = hexdocs.GetNext(pos);
1014 pMergeDoc->RefreshOptions();
1019 static bool AddToRecentDocs(const PathContext& paths, const unsigned flags[], bool recurse, const String& filter)
1021 String params, title;
1022 for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
1024 if (flags[nIndex] & FFILEOPEN_READONLY)
1028 case 0: params += _T("/wl "); break;
1029 case 1: params += paths.GetSize() == 2 ? _T("/wr ") : _T("/wm "); break;
1030 case 2: params += _T("/wr "); break;
1033 params += _T("\"") + paths[nIndex] + _T("\" ");
1035 String path = paths[nIndex];
1036 paths_normalize(path);
1037 title += paths_FindFileName(path);
1038 if (nIndex < paths.GetSize() - 1)
1042 params += _T("/r ");
1043 if (!filter.empty())
1044 params += _T("/f \"") + filter + _T("\" ");
1045 return JumpList::AddToRecentDocs(_T(""), params, title, params, 0);
1049 * @brief Begin a diff: open dirdoc if it is directories, else open a mergedoc for editing.
1050 * @param [in] pszLeft Left-side path.
1051 * @param [in] pszRight Right-side path.
1052 * @param [in] dwLeftFlags Left-side flags.
1053 * @param [in] dwRightFlags Right-side flags.
1054 * @param [in] bRecurse Do we run recursive (folder) compare?
1055 * @param [in] pDirDoc Dir compare document to use.
1056 * @param [in] prediffer Prediffer plugin name.
1057 * @return TRUE if opening files and compare succeeded, FALSE otherwise.
1059 BOOL CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
1060 const DWORD dwFlags[] /*=0*/, bool bRecurse /*=FALSE*/, CDirDoc *pDirDoc/*=NULL*/,
1061 String prediffer /*=_T("")*/, const PackingInfo *infoUnpacker/*=NULL*/)
1063 if (pDirDoc && !pDirDoc->CloseMergeDocs())
1066 g_bUnpackerMode = theApp.GetProfileInt(_T("Settings"), _T("UnpackerMode"), PLUGIN_MANUAL);
1067 g_bPredifferMode = theApp.GetProfileInt(_T("Settings"), _T("PredifferMode"), PLUGIN_MANUAL);
1069 Merge7zFormatMergePluginScope scope(infoUnpacker);
1077 bRO[0] = (dwFlags[0] & FFILEOPEN_READONLY) != 0;
1078 bRO[1] = (dwFlags[1] & FFILEOPEN_READONLY) != 0;
1079 bRO[2] = (dwFlags[2] & FFILEOPEN_READONLY) != 0;
1082 // pop up dialog unless arguments exist (and are compatible)
1083 PATH_EXISTENCE pathsType = GetPairComparability(files, IsArchiveFile);
1084 if (pathsType == DOES_NOT_EXIST)
1086 if (!m_pMenus[MENU_OPENVIEW])
1087 theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
1088 COpenDoc *pOpenDoc = (COpenDoc *)theApp.m_pOpenTemplate->CreateNewDocument();
1091 pOpenDoc->m_dwFlags[0] = dwFlags[0];
1092 pOpenDoc->m_dwFlags[1] = dwFlags[1];
1093 pOpenDoc->m_dwFlags[2] = dwFlags[2];
1095 pOpenDoc->m_files = files;
1097 pOpenDoc->m_infoHandler = *infoUnpacker;
1098 CFrameWnd *pFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
1099 theApp.m_pOpenTemplate->InitialUpdateFrame(pFrame, pOpenDoc);
1104 // Add trailing '\' for directories if its missing
1105 if (pathsType == IS_EXISTING_DIR)
1107 if (!paths_EndsWithSlash(files[0]) && !IsArchiveFile(files[0]))
1108 files[0] = paths_AddTrailingSlash(files[0]);
1109 if (!paths_EndsWithSlash(files[1]) && !IsArchiveFile(files[1]))
1110 files[1] = paths_AddTrailingSlash(files[1]);
1111 if (files.GetSize() == 3 && !paths_EndsWithSlash(files[2]) && !IsArchiveFile(files[1]))
1112 files[2] = paths_AddTrailingSlash(files[2]);
1115 //save the MRU left and right files.
1118 if (!(dwFlags[0] & FFILEOPEN_NOMRU))
1119 addToMru(files[0].c_str(), _T("Files\\Left"));
1120 if (!(dwFlags[1] & FFILEOPEN_NOMRU))
1121 addToMru(files[1].c_str(), _T("Files\\Right"));
1122 if (files.GetSize() == 3 && !(dwFlags[2] & FFILEOPEN_NOMRU))
1123 addToMru(files[2].c_str(), _T("Files\\Option"));
1127 DecompressResult res = DecompressArchive(m_hWnd, files);
1128 if (res.pTempPathContext)
1130 pathsType = res.pathsType;
1134 // Determine if we want new a dirview open now that we know if it was
1135 // and archive. Don't open new dirview if we are comparing files.
1138 if (pathsType == IS_EXISTING_DIR)
1140 CDirDoc::m_nDirsTemp = files.GetSize();
1141 if (!m_pMenus[MENU_DIRVIEW])
1142 theApp.m_pDirTemplate->m_hMenuShared = NewDirViewMenu();
1143 pDirDoc = (CDirDoc*)theApp.m_pDirTemplate->OpenDocumentFile(NULL);
1147 pDirDoc = (CDirDoc*)theApp.m_pDirTemplate->CreateNewDocument();
1152 if (pathsType == IS_EXISTING_DIR)
1156 if (files.GetSize() == 3)
1158 AfxMessageBox(_T("3-way folder compare feature is in progress"), MB_ICONWARNING | MB_DONT_ASK_AGAIN);
1160 // Anything that can go wrong inside InitCompare() will yield an
1161 // exception. There is no point in checking return value.
1162 pDirDoc->InitCompare(files, bRecurse, res.pTempPathContext);
1164 pDirDoc->SetDescriptions(theApp.m_strDescriptions);
1165 pDirDoc->SetTitle(NULL);
1166 for (int nIndex = 0; nIndex < files.GetSize(); nIndex++)
1168 pDirDoc->SetReadOnly(nIndex, bRO[nIndex]);
1169 theApp.m_strDescriptions[nIndex].erase();
1177 FileLocation fileloc[3];
1179 for (int nPane = 0; nPane < files.GetSize(); nPane++)
1180 fileloc[nPane].setPath(files[nPane]);
1182 if (!prediffer.empty())
1184 String strBothFilenames = string_join(files.begin(), files.end(), _T("|"));
1185 pDirDoc->GetPluginManager().SetPrediffer(strBothFilenames, prediffer);
1188 ShowAutoMergeDoc(pDirDoc, files.GetSize(), fileloc, dwFlags,
1192 if (pFiles && !(dwFlags[0] & FFILEOPEN_NOMRU))
1194 String filter = GetOptionsMgr()->GetString(OPT_FILEFILTER_CURRENT);
1195 AddToRecentDocs(*pFiles, (unsigned *)dwFlags, bRecurse, filter);
1201 void CMainFrame::UpdateFont(FRAMETYPE frame)
1203 if (frame == FRAME_FOLDER)
1205 const DirDocList &dirdocs = GetAllDirDocs();
1206 POSITION pos = dirdocs.GetHeadPosition();
1209 CDirDoc * pDoc = dirdocs.GetNext(pos);
1211 pDoc->GetMainView()->SetFont(m_lfDir);
1216 const MergeDocList &mergedocs = GetAllMergeDocs();
1217 POSITION pos = mergedocs.GetHeadPosition();
1220 CMergeDoc * pDoc = mergedocs.GetNext(pos);
1221 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
1223 CMergeEditView * pView = pDoc->GetView(pane);
1224 CMergeEditView * pDetailView = pDoc->GetDetailView(pane);
1226 pView->SetFont(m_lfDiff);
1228 pDetailView->SetFont(m_lfDiff);
1235 * @brief Select font for Merge/Dir view
1237 * Shows font selection dialog to user, sets current font and saves
1238 * selected font properties to registry. Selects fon type to active
1239 * view (Merge/Dir compare). If there is no open views, then font
1240 * is selected for Merge view (for example user may want to change to
1241 * unicode font before comparing files).
1243 void CMainFrame::OnViewSelectfont()
1245 FRAMETYPE frame = GetFrameType(GetActiveFrame());
1248 ZeroMemory(&cf, sizeof(CHOOSEFONT));
1249 cf.lStructSize = sizeof(CHOOSEFONT);
1250 cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCREENFONTS;
1251 if (frame == FRAME_FILE)
1252 cf.Flags |= CF_FIXEDPITCHONLY; // Only fixed-width fonts for merge view
1254 // CF_FIXEDPITCHONLY = 0x00004000L
1255 // in case you are a developer and want to disable it to test with, eg, a Chinese capable font
1256 if (frame == FRAME_FOLDER)
1263 if (ChooseFont(&cf))
1265 Options::Font::Save(frame == FRAME_FOLDER ? OPT_FONT_DIRCMP : OPT_FONT_FILECMP, lf, true);
1271 * @brief Enable 'Select font'.
1273 void CMainFrame::OnUpdateViewSelectfont(CCmdUI* pCmdUI)
1275 pCmdUI->Enable(TRUE);
1279 * @brief Use default font for active view type
1281 * Disable user-selected font for active view type (Merge/Dir compare).
1282 * If there is no open views, then Merge view font is changed.
1284 void CMainFrame::OnViewUsedefaultfont()
1286 FRAMETYPE frame = GetFrameType(GetActiveFrame());
1288 if (frame == FRAME_FOLDER)
1290 Options::Font::Reset(OPT_FONT_DIRCMP);
1291 m_lfDir = Options::Font::Load(OPT_FONT_DIRCMP);
1292 Options::Font::Save(OPT_FONT_DIRCMP, &m_lfDir, false);
1296 Options::Font::Reset(OPT_FONT_FILECMP);
1297 m_lfDiff = Options::Font::Load(OPT_FONT_FILECMP);
1298 Options::Font::Save(OPT_FONT_FILECMP, &m_lfDiff, false);
1305 * @brief Enable 'Use Default font'.
1307 void CMainFrame::OnUpdateViewUsedefaultfont(CCmdUI* pCmdUI)
1309 pCmdUI->Enable(TRUE);
1313 * @brief Update any resources necessary after a GUI language change
1315 void CMainFrame::UpdateResources()
1317 m_wndStatusBar.SetPaneText(0, theApp.LoadString(AFX_IDS_IDLEMESSAGE).c_str());
1319 const DirDocList &dirdocs = GetAllDirDocs();
1320 POSITION pos = dirdocs.GetHeadPosition();
1323 CDirDoc * pDoc = dirdocs.GetNext(pos);
1324 pDoc->UpdateResources();
1327 const MergeDocList &mergedocs = GetAllMergeDocs();
1328 pos = mergedocs.GetHeadPosition();
1331 CMergeDoc * pDoc = mergedocs.GetNext(pos);
1332 pDoc->UpdateResources();
1337 * @brief Open WinMerge help.
1339 * If local HTMLhelp file is found, open it, otherwise open HTML page from web.
1341 void CMainFrame::OnHelpContents()
1347 * @brief Enable Open WinMerge help -menuitem.
1349 void CMainFrame::OnUpdateHelpContents(CCmdUI* pCmdUI)
1351 pCmdUI->Enable(TRUE);
1355 * @brief Handle translation of default messages on the status bar
1357 void CMainFrame::GetMessageString(UINT nID, CString& rMessage) const
1359 // load appropriate string
1360 const String s = theApp.LoadString(nID);
1362 AfxExtractSubString(rMessage, s.c_str(), 0);
1365 void CMainFrame::ActivateFrame(int nCmdShow)
1369 CMDIFrameWnd::ActivateFrame(nCmdShow);
1373 m_bFirstTime = FALSE;
1376 wp.length = sizeof(WINDOWPLACEMENT);
1377 GetWindowPlacement(&wp);
1378 wp.rcNormalPosition.left=theApp.GetProfileInt(_T("Settings"), _T("MainLeft"),0);
1379 wp.rcNormalPosition.top=theApp.GetProfileInt(_T("Settings"), _T("MainTop"),0);
1380 wp.rcNormalPosition.right=theApp.GetProfileInt(_T("Settings"), _T("MainRight"),0);
1381 wp.rcNormalPosition.bottom=theApp.GetProfileInt(_T("Settings"), _T("MainBottom"),0);
1382 if (nCmdShow != SW_MINIMIZE && theApp.GetProfileInt(_T("Settings"), _T("MainMax"), FALSE))
1383 wp.showCmd = SW_MAXIMIZE;
1385 wp.showCmd = nCmdShow;
1387 CRect dsk_rc,rc(wp.rcNormalPosition);
1389 dsk_rc.left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
1390 dsk_rc.top = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
1391 dsk_rc.right = dsk_rc.left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
1392 dsk_rc.bottom = dsk_rc.top + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
1393 if (rc.Width() != 0 && rc.Height() != 0)
1395 // Ensure top-left corner is on visible area,
1396 // 20 points margin is added to prevent "lost" window
1397 CPoint ptTopLeft(rc.TopLeft());
1398 ptTopLeft += CPoint(20, 20);
1400 if (dsk_rc.PtInRect(ptTopLeft))
1401 SetWindowPlacement(&wp);
1403 CMDIFrameWnd::ActivateFrame(nCmdShow);
1406 CMDIFrameWnd::ActivateFrame(nCmdShow);
1410 * @brief Called when mainframe is about to be closed.
1411 * This function is called when mainframe is to be closed (not for
1412 * file/compare windows.
1414 void CMainFrame::OnClose()
1416 if (theApp.GetActiveOperations())
1419 // Check if there are multiple windows open and ask for closing them
1420 BOOL bAskClosing = GetOptionsMgr()->GetBool(OPT_ASK_MULTIWINDOW_CLOSE);
1423 bool quit = AskCloseConfirmation();
1428 // Save last selected filter
1429 String filter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1430 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
1432 // save main window position
1434 wp.length = sizeof(WINDOWPLACEMENT);
1435 GetWindowPlacement(&wp);
1436 theApp.WriteProfileInt(_T("Settings"), _T("MainLeft"),wp.rcNormalPosition.left);
1437 theApp.WriteProfileInt(_T("Settings"), _T("MainTop"),wp.rcNormalPosition.top);
1438 theApp.WriteProfileInt(_T("Settings"), _T("MainRight"),wp.rcNormalPosition.right);
1439 theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom);
1440 theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE));
1442 // tell all merge docs to save position
1443 // don't call SavePosition, it is called when the child frame is destroyed
1445 while (!mergedocs.IsEmpty())
1447 CMergeDoc * pMergeDoc = mergedocs.RemoveHead();
1448 CMergeEditView * pLeft = pMergeDoc->GetLeftView();
1450 pMergeDoc->GetParentFrame()->SavePosition();
1454 // Stop handling status messages from CustomStatusCursors
1455 CustomStatusCursor::SetStatusDisplay(0);
1456 myStatusDisplay.SetFrame(0);
1458 // Close Non-Document/View frame with confirmation
1459 CMDIChildWnd *pChild = static_cast<CMDIChildWnd *>(CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD));
1462 CMDIChildWnd *pNextChild = static_cast<CMDIChildWnd *>(pChild->GetWindow(GW_HWNDNEXT));
1463 if (GetFrameType(pChild) == FRAME_IMGFILE)
1465 if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
1468 pChild = pNextChild;
1471 CMDIFrameWnd::OnClose();
1475 * @brief Utility function to update CSuperComboBox format MRU
1477 void CMainFrame::addToMru(LPCTSTR szItem, LPCTSTR szRegSubKey, UINT nMaxItems)
1479 std::vector<CString> list;
1481 UINT cnt = AfxGetApp()->GetProfileInt(szRegSubKey, _T("Count"), 0);
1482 list.push_back(szItem);
1483 for (UINT i=0 ; i<cnt; ++i)
1485 s = AfxGetApp()->GetProfileString(szRegSubKey, string_format(_T("Item_%d"), i).c_str());
1489 cnt = list.size() > nMaxItems ? nMaxItems : list.size();
1490 for (UINT i=0 ; i<cnt; ++i)
1491 AfxGetApp()->WriteProfileString(szRegSubKey, string_format(_T("Item_%d"), i).c_str(), list[i]);
1493 AfxGetApp()->WriteProfileInt(szRegSubKey, _T("Count"), cnt);
1496 void CMainFrame::ApplyDiffOptions()
1498 const MergeDocList &docs = GetAllMergeDocs();
1499 POSITION pos = docs.GetHeadPosition();
1502 CMergeDoc * pMergeDoc = docs.GetNext(pos);
1503 // Re-read MergeDoc settings (also updates view settings)
1504 // and rescan using new options
1505 pMergeDoc->RefreshOptions();
1506 pMergeDoc->FlushAndRescan(TRUE);
1511 * @brief Apply tabs and eols settings to all merge documents
1513 void CMainFrame::ApplyViewWhitespace()
1515 const MergeDocList &mergedocs = GetAllMergeDocs();
1516 POSITION pos = mergedocs.GetHeadPosition();
1519 CMergeDoc *pMergeDoc = mergedocs.GetNext(pos);
1520 for (int pane = 0; pane < pMergeDoc->m_nBuffers; pane++)
1522 CMergeEditView * pView = pMergeDoc->GetView(pane);
1523 CMergeEditView * pDetailView = pMergeDoc->GetDetailView(pane);
1526 pView->SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
1527 pView->SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
1528 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
1529 pView->GetDocument()->IsMixedEOL(pView->m_nThisPane));
1533 pDetailView->SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
1534 pDetailView->SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
1535 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
1536 pDetailView->GetDocument()->IsMixedEOL(pDetailView->m_nThisPane));
1542 void CMainFrame::OnViewWhitespace()
1544 bool bViewWhitespace = GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE);
1545 GetOptionsMgr()->SaveOption(OPT_VIEW_WHITESPACE, !bViewWhitespace);
1546 ApplyViewWhitespace();
1549 /// Enables View/View Whitespace menuitem when merge view is active
1550 void CMainFrame::OnUpdateViewWhitespace(CCmdUI* pCmdUI)
1552 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
1555 /// Get list of OpenDocs (documents underlying edit sessions)
1556 const OpenDocList &CMainFrame::GetAllOpenDocs()
1558 return static_cast<const OpenDocList &>(GetDocList(theApp.m_pOpenTemplate));
1561 /// Get list of MergeDocs (documents underlying edit sessions)
1562 const MergeDocList &CMainFrame::GetAllMergeDocs()
1564 return static_cast<const MergeDocList &>(GetDocList(theApp.m_pDiffTemplate));
1567 /// Get list of DirDocs (documents underlying a scan)
1568 const DirDocList &CMainFrame::GetAllDirDocs()
1570 return static_cast<const DirDocList &>(GetDocList(theApp.m_pDirTemplate));
1573 /// Get list of HexMergeDocs (documents underlying edit sessions)
1574 const HexMergeDocList &CMainFrame::GetAllHexMergeDocs()
1576 return static_cast<const HexMergeDocList &>(GetDocList(theApp.m_pHexMergeTemplate));
1580 * @brief Obtain a merge doc to display a difference in files.
1581 * @return Pointer to CMergeDoc to use.
1583 template<class DocClass>
1584 DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles)
1586 // Create a new merge doc
1587 DocClass::m_nBuffersTemp = nFiles;
1588 DocClass *pMergeDoc = (DocClass*)pTemplate->OpenDocumentFile(NULL);
1591 pDirDoc->AddMergeDoc(pMergeDoc);
1592 pMergeDoc->SetDirDoc(pDirDoc);
1597 // Set status in the main status pane
1598 CString CMainFrame::SetStatus(LPCTSTR status)
1600 CString old = m_wndStatusBar.GetPaneText(0);
1601 m_wndStatusBar.SetPaneText(0, status);
1605 // Clear the item count in the main status pane
1606 void CMainFrame::ClearStatusbarItemCount()
1608 m_wndStatusBar.SetPaneText(2, _T(""));
1612 * @brief Generate patch from files selected.
1614 * Creates a patch from selected files in active directory compare, or
1615 * active file compare. Files in file compare must be saved before
1618 void CMainFrame::OnToolsGeneratePatch()
1621 CFrameWnd * pFrame = GetActiveFrame();
1622 FRAMETYPE frame = GetFrameType(pFrame);
1623 BOOL bOpenDialog = TRUE;
1626 if (frame == FRAME_FILE)
1628 CMergeDoc * pMergeDoc = (CMergeDoc *) pFrame->GetActiveDocument();
1629 // If there are changes in files, tell user to save them first
1630 BOOL bModified = FALSE;
1631 for (int pane = 0; pane < pMergeDoc->m_nBuffers; pane++)
1633 if (pMergeDoc->m_ptBuf[pane]->IsModified())
1638 bOpenDialog = FALSE;
1639 LangMessageBox(IDS_SAVEFILES_FORPATCH, MB_ICONSTOP);
1643 patcher.AddFiles(pMergeDoc->m_filePaths.GetLeft(),
1644 pMergeDoc->m_filePaths.GetRight());
1648 else if (frame == FRAME_FOLDER)
1650 CDirDoc * pDoc = (CDirDoc*)pFrame->GetActiveDocument();
1651 const CDiffContext& ctxt = pDoc->GetDiffContext();
1652 CDirView *pView = pDoc->GetMainView();
1654 // Get selected items from folder compare
1655 BOOL bValidFiles = TRUE;
1656 for (DirItemIterator it = pView->SelBegin(); bValidFiles && it != pView->SelEnd(); ++it)
1658 const DIFFITEM &item = *it;
1659 if (item.diffcode.isBin())
1661 LangMessageBox(IDS_CANNOT_CREATE_BINARYPATCH, MB_ICONWARNING |
1662 MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_BINARYPATCH);
1663 bValidFiles = FALSE;
1665 else if (item.diffcode.isDirectory())
1667 LangMessageBox(IDS_CANNOT_CREATE_DIRPATCH, MB_ICONWARNING |
1668 MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_DIRPATCH);
1669 bValidFiles = FALSE;
1674 // Format full paths to files (leftFile/rightFile)
1675 String leftFile = item.getFilepath(0, ctxt.GetNormalizedPath(0));
1676 if (!leftFile.empty())
1677 leftFile = paths_ConcatPath(leftFile, item.diffFileInfo[0].filename);
1678 String rightFile = item.getFilepath(1, ctxt.GetNormalizedPath(1));
1679 if (!rightFile.empty())
1680 rightFile = paths_ConcatPath(rightFile, item.diffFileInfo[1].filename);
1682 // Format relative paths to files in folder compare
1683 String leftpatch = item.diffFileInfo[0].path;
1684 if (!leftpatch.empty())
1685 leftpatch += _T("/");
1686 leftpatch += item.diffFileInfo[0].filename;
1687 String rightpatch = item.diffFileInfo[1].path;
1688 if (!rightpatch.empty())
1689 rightpatch += _T("/");
1690 rightpatch += item.diffFileInfo[1].filename;
1691 patcher.AddFiles(leftFile, leftpatch, rightFile, rightpatch);
1698 if (patcher.CreatePatch())
1700 if (patcher.GetOpenToEditor())
1702 theApp.OpenFileToExternalEditor(patcher.GetPatchFile().c_str());
1708 void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
1710 PathContext files(dropped_files);
1711 const size_t fileCount = files.GetSize();
1713 // If Ctrl pressed, do recursive compare
1714 bool recurse = !!::GetAsyncKeyState(VK_CONTROL) || !!theApp.GetProfileInt(_T("Settings"), _T("Recurse"), 0);
1716 // If user has <Shift> pressed with one file selected,
1717 // assume it is an archive and set filenames to same
1718 if (::GetAsyncKeyState(VK_SHIFT) < 0 && fileCount == 1)
1720 files.SetRight(files[0]);
1723 // Check if they dropped a project file
1724 DWORD dwFlags[3] = {FFILEOPEN_NONE, FFILEOPEN_NONE, FFILEOPEN_NONE};
1727 if (theApp.IsProjectFile(files[0].c_str()))
1729 theApp.LoadAndOpenProjectFile(files[0].c_str());
1732 if (IsConflictFile(files[0]))
1734 DoOpenConflict(files[0], true);
1739 DoFileOpen(&files, dwFlags, recurse);
1742 BOOL CMainFrame::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1744 if (CustomStatusCursor::HasWaitCursor())
1746 CustomStatusCursor::RestoreWaitCursor();
1749 return CMDIFrameWnd::OnSetCursor(pWnd, nHitTest, message);
1752 void CMainFrame::OnPluginUnpackMode(UINT nID )
1756 case ID_UNPACK_MANUAL:
1757 g_bUnpackerMode = PLUGIN_MANUAL;
1759 case ID_UNPACK_AUTO:
1760 g_bUnpackerMode = PLUGIN_AUTO;
1763 theApp.WriteProfileInt(_T("Settings"), _T("UnpackerMode"), g_bUnpackerMode);
1766 void CMainFrame::OnUpdatePluginUnpackMode(CCmdUI* pCmdUI)
1768 if (GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
1769 pCmdUI->Enable(TRUE);
1771 pCmdUI->Enable(FALSE);
1773 if (pCmdUI->m_nID == ID_UNPACK_MANUAL)
1774 pCmdUI->SetRadio(PLUGIN_MANUAL == g_bUnpackerMode);
1775 if (pCmdUI->m_nID == ID_UNPACK_AUTO)
1776 pCmdUI->SetRadio(PLUGIN_AUTO == g_bUnpackerMode);
1778 void CMainFrame::OnPluginPrediffMode(UINT nID )
1782 case ID_PREDIFFER_MANUAL:
1783 g_bPredifferMode = PLUGIN_MANUAL;
1785 case ID_PREDIFFER_AUTO:
1786 g_bPredifferMode = PLUGIN_AUTO;
1789 PrediffingInfo infoPrediffer;
1790 const MergeDocList &mergedocs = GetAllMergeDocs();
1791 POSITION pos = mergedocs.GetHeadPosition();
1793 mergedocs.GetNext(pos)->SetPrediffer(&infoPrediffer);
1794 const DirDocList &dirdocs = GetAllDirDocs();
1795 pos = dirdocs.GetHeadPosition();
1797 dirdocs.GetNext(pos)->GetPluginManager().SetPrediffSettingAll(g_bPredifferMode);
1798 theApp.WriteProfileInt(_T("Settings"), _T("PredifferMode"), g_bPredifferMode);
1801 void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI)
1803 if (GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
1804 pCmdUI->Enable(TRUE);
1806 pCmdUI->Enable(FALSE);
1808 if (pCmdUI->m_nID == ID_PREDIFFER_MANUAL)
1809 pCmdUI->SetRadio(PLUGIN_MANUAL == g_bPredifferMode);
1810 if (pCmdUI->m_nID == ID_PREDIFFER_AUTO)
1811 pCmdUI->SetRadio(PLUGIN_AUTO == g_bPredifferMode);
1814 * @brief Called when "Reload Plugins" item is updated
1816 void CMainFrame::OnUpdateReloadPlugins(CCmdUI* pCmdUI)
1818 if (GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
1819 pCmdUI->Enable(TRUE);
1821 pCmdUI->Enable(FALSE);
1824 void CMainFrame::OnReloadPlugins()
1826 // delete all script interfaces
1827 // (interfaces will be created again automatically when WinMerge needs them)
1828 CAllThreadsScripts::GetActiveSet()->FreeAllScripts();
1830 // update the editor scripts submenu
1831 HMENU scriptsSubmenu = GetScriptsSubmenu(m_hMenuDefault);
1832 if (scriptsSubmenu != NULL)
1833 CMergeEditView::createScriptsSubmenu(scriptsSubmenu);
1834 UpdatePrediffersMenu();
1837 /** @brief Return active merge edit view, if can figure it out/is available */
1838 CMergeEditView * CMainFrame::GetActiveMergeEditView()
1840 // NB: GetActiveDocument does not return the Merge Doc
1841 // even when the merge edit view is in front
1842 // NB: CChildFrame::GetActiveView returns NULL when location view active
1843 // So we have this rather complicated logic to try to get a merge edit view
1844 // We look at the front child window, which should be a frame
1845 // and we can get a MergeEditView from it, if it is a CChildFrame
1846 // (DirViews use a different frame type)
1847 CChildFrame * pFrame = dynamic_cast<CChildFrame *>(GetActiveFrame());
1848 if (!pFrame) return 0;
1849 // Try to get the active MergeEditView (ie, left or right)
1850 if (pFrame->GetActiveView() && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
1852 return dynamic_cast<CMergeEditView *>(pFrame->GetActiveView());
1854 return pFrame->GetMergeDoc()->GetActiveMergeView();
1857 void CMainFrame::UpdatePrediffersMenu()
1859 CMenu* menu = GetMenu();
1865 HMENU hMainMenu = menu->m_hMenu;
1866 HMENU prediffersSubmenu = GetPrediffersSubmenu(hMainMenu);
1867 if (prediffersSubmenu != NULL)
1869 CMergeEditView * pEditView = GetActiveMergeEditView();
1871 pEditView->createPrediffersSubmenu(prediffersSubmenu);
1874 // no view or dir view : display an empty submenu
1875 int i = GetMenuItemCount(prediffersSubmenu);
1877 ::DeleteMenu(prediffersSubmenu, 0, MF_BYPOSITION);
1878 ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, NULL);
1884 * @brief Save WinMerge configuration and info to file
1886 void CMainFrame::OnSaveConfigData()
1888 CConfigLog configLog;
1891 if (configLog.WriteLogFile(sError))
1893 String sFileName = configLog.GetFileName();
1894 theApp.OpenFileToExternalEditor(sFileName);
1898 String sFileName = configLog.GetFileName();
1899 String msg = string_format_string2(_("Cannot open file\n%1\n\n%2"), sFileName, sError);
1900 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1905 * @brief Open two new empty docs, 'Scratchpads'
1907 * Allows user to open two empty docs, to paste text to
1908 * compare from clipboard.
1909 * @note File filenames are set emptys and filedescriptors
1910 * are loaded from resource.
1911 * @sa CMergeDoc::OpenDocs()
1912 * @sa CMergeDoc::TrySaveAs()
1914 void CMainFrame::FileNew(int nPanes)
1916 CDirDoc *pDirDoc = (CDirDoc*)theApp.m_pDirTemplate->CreateNewDocument();
1918 // Load emptyfile descriptors and open empty docs
1919 // Use default codepage
1920 DWORD dwFlags[3] = {0, 0, 0};
1921 FileLocation fileloc[3];
1924 theApp.m_strDescriptions[0] = _("Untitled left");
1925 theApp.m_strDescriptions[1] = _("Untitled right");
1926 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1927 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1928 ShowMergeDoc(pDirDoc, 2, fileloc, dwFlags);
1932 theApp.m_strDescriptions[0] = _("Untitled left");
1933 theApp.m_strDescriptions[1] = _("Untitled middle");
1934 theApp.m_strDescriptions[2] = _("Untitled right");
1935 fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
1936 fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
1937 fileloc[2].encoding.SetCodepage(ucr::getDefaultCodepage());
1938 ShowMergeDoc(pDirDoc, 3, fileloc, dwFlags);
1941 // Empty descriptors now that docs are open
1942 theApp.m_strDescriptions[0].erase();
1943 theApp.m_strDescriptions[1].erase();
1944 theApp.m_strDescriptions[2].erase();
1948 * @brief Open two new empty docs, 'Scratchpads'
1950 * Allows user to open two empty docs, to paste text to
1951 * compare from clipboard.
1952 * @note File filenames are set emptys and filedescriptors
1953 * are loaded from resource.
1954 * @sa CMergeDoc::OpenDocs()
1955 * @sa CMergeDoc::TrySaveAs()
1957 void CMainFrame::OnFileNew()
1962 void CMainFrame::OnFileNew3()
1968 * @brief Open Filters dialog
1970 void CMainFrame::OnToolsFilters()
1972 String title = _("Filters");
1973 CPropertySheet sht(title.c_str());
1974 LineFiltersDlg lineFiltersDlg;
1975 FileFiltersDlg fileFiltersDlg;
1976 vector<FileFilterInfo> fileFilters;
1977 std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
1978 String selectedFilter;
1979 const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
1980 sht.AddPage(&fileFiltersDlg);
1981 sht.AddPage(&lineFiltersDlg);
1982 sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
1984 // Make sure all filters are up-to-date
1985 theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
1987 theApp.m_pGlobalFileFilter->GetFileFilters(&fileFilters, selectedFilter);
1988 fileFiltersDlg.SetFilterArray(&fileFilters);
1989 fileFiltersDlg.SetSelected(selectedFilter);
1990 const bool lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
1991 lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig;
1993 lineFilters->CloneFrom(theApp.m_pLineFilters.get());
1994 lineFiltersDlg.SetList(lineFilters.get());
1996 if (sht.DoModal() == IDOK)
1998 String strNone = _("<None>");
1999 String path = fileFiltersDlg.GetSelected();
2000 if (path.find(strNone) != String::npos)
2002 // Don't overwrite mask we already have
2003 if (!theApp.m_pGlobalFileFilter->IsUsingMask())
2005 String sFilter(_T("*.*"));
2006 theApp.m_pGlobalFileFilter->SetFilter(sFilter);
2007 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
2012 theApp.m_pGlobalFileFilter->SetFileFilterPath(path);
2013 theApp.m_pGlobalFileFilter->UseMask(FALSE);
2014 String sFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
2015 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter);
2017 bool linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp;
2018 GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled);
2020 // Check if compare documents need rescanning
2021 BOOL bFileCompareRescan = FALSE;
2022 BOOL bFolderCompareRescan = FALSE;
2023 CFrameWnd * pFrame = GetActiveFrame();
2024 FRAMETYPE frame = GetFrameType(pFrame);
2025 if (frame == FRAME_FILE)
2027 if (lineFiltersEnabledOrig != linefiltersEnabled ||
2028 !theApp.m_pLineFilters->Compare(lineFilters.get()))
2030 bFileCompareRescan = TRUE;
2033 else if (frame == FRAME_FOLDER)
2035 const String newFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
2036 if (lineFiltersEnabledOrig != linefiltersEnabled ||
2037 !theApp.m_pLineFilters->Compare(lineFilters.get()) || origFilter != newFilter)
2039 int res = LangMessageBox(IDS_FILTERCHANGED, MB_ICONWARNING | MB_YESNO);
2041 bFolderCompareRescan = TRUE;
2045 // Save new filters before (possibly) rescanning
2046 theApp.m_pLineFilters->CloneFrom(lineFilters.get());
2047 theApp.m_pLineFilters->SaveFilters();
2049 if (bFileCompareRescan)
2051 const MergeDocList &docs = GetAllMergeDocs();
2052 POSITION pos = docs.GetHeadPosition();
2055 CMergeDoc * pMergeDoc = docs.GetNext(pos);
2056 pMergeDoc->FlushAndRescan(TRUE);
2059 else if (bFolderCompareRescan)
2061 const DirDocList &dirDocs = GetAllDirDocs();
2062 POSITION pos = dirDocs.GetHeadPosition();
2065 CDirDoc * pDirDoc = dirDocs.GetNext(pos);
2073 * @brief Open Filters dialog.
2075 void CMainFrame::SelectFilter()
2081 * @brief Closes application with ESC.
2083 * Application is closed if:
2084 * - 'Close Windows with ESC' option is enabled and
2085 * there is no open document windows
2086 * - '-e' commandline switch is given
2088 BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
2090 // Check if we got 'ESC pressed' -message
2091 if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
2093 if (theApp.m_bEscShutdown && m_wndTabBar.GetItemCount() <= 1)
2095 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
2098 else if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC) && m_wndTabBar.GetItemCount() == 0)
2100 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
2104 return CMDIFrameWnd::PreTranslateMessage(pMsg);
2108 * @brief Show/hide statusbar.
2110 void CMainFrame::OnViewStatusBar()
2112 bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_STATUSBAR);
2113 GetOptionsMgr()->SaveOption(OPT_SHOW_STATUSBAR, bShow);
2115 CMDIFrameWnd::ShowControlBar(&m_wndStatusBar, bShow, 0);
2119 * @brief Updates "Show Tabbar" menuitem.
2121 void CMainFrame::OnUpdateViewTabBar(CCmdUI* pCmdUI)
2123 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR));
2127 * @brief Show/hide tabbar.
2129 void CMainFrame::OnViewTabBar()
2131 bool bShow = !GetOptionsMgr()->GetBool(OPT_SHOW_TABBAR);
2132 GetOptionsMgr()->SaveOption(OPT_SHOW_TABBAR, bShow);
2134 CMDIFrameWnd::ShowControlBar(&m_wndTabBar, bShow, 0);
2138 * @brief Updates "Automatically Resize Panes" menuitem.
2140 void CMainFrame::OnUpdateResizePanes(CCmdUI* pCmdUI)
2142 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
2147 * @brief Enable/disable automatic pane resizing.
2149 void CMainFrame::OnResizePanes()
2151 bool bResize = !GetOptionsMgr()->GetBool(OPT_RESIZE_PANES);
2152 GetOptionsMgr()->SaveOption(OPT_RESIZE_PANES, bResize);
2153 // TODO: Introduce a common merge frame superclass?
2154 CFrameWnd *pActiveFrame = GetActiveFrame();
2155 if (CChildFrame *pFrame = DYNAMIC_DOWNCAST(CChildFrame, pActiveFrame))
2157 pFrame->UpdateAutoPaneResize();
2159 pFrame->UpdateSplitter();
2161 else if (CHexMergeFrame *pFrame = DYNAMIC_DOWNCAST(CHexMergeFrame, pActiveFrame))
2163 pFrame->UpdateAutoPaneResize();
2165 pFrame->UpdateSplitter();
2170 * @brief Open project-file.
2172 void CMainFrame::OnFileOpenproject()
2176 // get the default projects path
2177 String strProjectPath = GetOptionsMgr()->GetString(OPT_PROJECTS_PATH);
2178 if (!SelectFile(GetSafeHwnd(), sFilepath, strProjectPath.c_str(), _("Open"),
2179 _("WinMerge Project Files (*.WinMerge)|*.WinMerge||"), TRUE))
2182 strProjectPath = paths_GetParentPath(sFilepath);
2183 // store this as the new project path
2184 GetOptionsMgr()->SaveOption(OPT_PROJECTS_PATH, strProjectPath);
2186 theApp.LoadAndOpenProjectFile(sFilepath.c_str());
2190 * @brief Receive command line from another instance.
2192 * This function receives command line when only single-instance
2193 * is allowed. New instance tried to start sends its command line
2194 * to here so we can open paths it was meant to.
2196 LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
2198 COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
2199 LPCTSTR pchData = (LPCTSTR)pCopyData->lpData;
2200 // Bail out if data isn't zero-terminated
2201 DWORD cchData = pCopyData->cbData / sizeof(TCHAR);
2202 if (cchData == 0 || pchData[cchData - 1] != _T('\0'))
2205 MergeCmdLineInfo cmdInfo = pchData;
2206 theApp.ParseArgsAndDoOpen(cmdInfo, this);
2210 LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
2212 CFrameWnd * pFrame = GetActiveFrame();
2215 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
2217 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
2219 pMergeDoc->CheckFileChanged();
2225 * @brief Handle timer events.
2226 * @param [in] nIDEvent Timer that timed out.
2228 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
2232 case WM_NONINTERACTIVE:
2233 KillTimer(WM_NONINTERACTIVE);
2234 PostMessage(WM_CLOSE);
2237 case ID_TIMER_FLASH:
2238 // This timer keeps running until window is activated
2243 CMDIFrameWnd::OnTimer(nIDEvent);
2247 * @brief Close all open windows.
2249 * Asks about saving unsaved files and then closes all open windows.
2251 void CMainFrame::OnWindowCloseAll()
2253 CMDIChildWnd *pChild = MDIGetActive();
2257 if ((pDoc = pChild->GetActiveDocument()) != NULL)
2259 if (!pDoc->SaveModified())
2261 pDoc->OnCloseDocument();
2263 else if (GetFrameType(pChild) == FRAME_IMGFILE)
2265 if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
2270 pChild->DestroyWindow();
2272 pChild = MDIGetActive();
2278 * @brief Enables Window/Close All item if there are open windows.
2280 void CMainFrame::OnUpdateWindowCloseAll(CCmdUI* pCmdUI)
2282 const MergeDocList &mergedocs = GetAllMergeDocs();
2283 if (!mergedocs.IsEmpty())
2285 pCmdUI->Enable(TRUE);
2289 const DirDocList &dirdocs = GetAllDirDocs();
2290 if (!dirdocs.IsEmpty())
2291 pCmdUI->Enable(TRUE);
2293 pCmdUI->Enable(FALSE);
2297 * @brief Access to the singleton main frame (where we have some globals)
2299 CMainFrame * GetMainFrame()
2301 CWnd * mainwnd = AfxGetMainWnd();
2303 CMainFrame *pMainframe = dynamic_cast<CMainFrame*>(mainwnd);
2309 * @brief Opens dialog for user to Load, edit and save project files.
2310 * This dialog gets current compare paths and filter (+other properties
2311 * possible in project files) and initializes the dialog with them.
2313 void CMainFrame::OnSaveProject()
2315 String title = _("Project File");
2316 CPropertySheet sht(title.c_str());
2317 ProjectFilePathsDlg pathsDlg;
2318 sht.AddPage(&pathsDlg);
2319 sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
2323 CFrameWnd * pFrame = GetActiveFrame();
2324 FRAMETYPE frame = GetFrameType(pFrame);
2326 if (frame == FRAME_FILE)
2328 CMergeDoc * pMergeDoc = (CMergeDoc *) pFrame->GetActiveDocument();
2329 left = pMergeDoc->m_filePaths.GetLeft();
2330 right = pMergeDoc->m_filePaths.GetRight();
2331 pathsDlg.SetPaths(left, right);
2332 pathsDlg.m_bLeftPathReadOnly = pMergeDoc->m_ptBuf[0]->GetReadOnly();
2333 pathsDlg.m_bRightPathReadOnly = pMergeDoc->m_ptBuf[1]->GetReadOnly();
2335 else if (frame == FRAME_FOLDER)
2337 // Get paths currently in compare
2338 const CDirDoc * pDoc = (const CDirDoc*)pFrame->GetActiveDocument();
2339 const CDiffContext& ctxt = pDoc->GetDiffContext();
2340 left = paths_AddTrailingSlash(ctxt.GetNormalizedLeft());
2341 right = paths_AddTrailingSlash(ctxt.GetNormalizedRight());
2343 // Set-up the dialog
2344 pathsDlg.SetPaths(left, right);
2345 pathsDlg.m_bIncludeSubfolders = ctxt.m_bRecursive;
2346 pathsDlg.m_bLeftPathReadOnly = pDoc->GetReadOnly(0);
2347 pathsDlg.m_bRightPathReadOnly = pDoc->GetReadOnly(pDoc->m_nDirs - 1);
2350 pathsDlg.m_sFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
2355 * @brief Start flashing window if window is inactive.
2357 void CMainFrame::StartFlashing()
2359 CWnd * activeWindow = GetActiveWindow();
2360 if (activeWindow != this)
2364 SetTimer(ID_TIMER_FLASH, WINDOW_FLASH_TIMEOUT, NULL);
2369 * @brief Stop flashing window when window is activated.
2371 * If WinMerge is inactive when compare finishes, we start flashing window
2372 * to alert user. When user activates WinMerge window we stop flashing.
2373 * @param [in] nState Tells if window is being activated or deactivated.
2374 * @param [in] pWndOther Pointer to window whose status is changing.
2375 * @param [in] Is window minimized?
2377 void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
2379 CMDIFrameWnd::OnActivate(nState, pWndOther, bMinimized);
2381 if (nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
2385 m_bFlashing = FALSE;
2387 KillTimer(ID_TIMER_FLASH);
2392 #if _MFC_VER > 0x0600
2393 void CMainFrame::OnActivateApp(BOOL bActive, DWORD dwThreadID)
2395 void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
2398 #if _MFC_VER > 0x0600
2399 CMDIFrameWnd::OnActivateApp(bActive, dwThreadID);
2401 CMDIFrameWnd::OnActivateApp(bActive, hTask);
2404 CFrameWnd * pFrame = GetActiveFrame();
2407 IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
2409 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
2411 PostMessage(WM_USER+1);
2415 BOOL CMainFrame::CreateToolbar()
2417 if (!m_wndToolBar.CreateEx(this) ||
2418 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
2423 if (!m_wndReBar.Create(this))
2428 VERIFY(m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT));
2430 // Remove this if you don't want tool tips or a resizable toolbar
2431 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
2432 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
2433 m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
2435 m_wndReBar.AddBar(&m_wndToolBar);
2437 LoadToolbarImages();
2441 int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(ID_OPTIONS);
2442 m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
2443 nStyle |= TBSTYLE_DROPDOWN;
2444 m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
2446 if (GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR) == false)
2448 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, false, 0);
2454 /** @brief Load toolbar images from the resource. */
2455 void CMainFrame::LoadToolbarImages()
2457 int toolbarSize = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2458 CToolBarCtrl& BarCtrl = m_wndToolBar.GetToolBarCtrl();
2460 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].DeleteImageList();
2461 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].DeleteImageList();
2462 CSize sizeButton(0, 0);
2464 if (toolbarSize == 0)
2466 LoadToolbarImageList(TOOLBAR_SIZE_16x16, IDB_TOOLBAR_ENABLED,
2467 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2468 LoadToolbarImageList(TOOLBAR_SIZE_16x16, IDB_TOOLBAR_DISABLED,
2469 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2470 sizeButton = CSize(24, 24);
2472 else if (toolbarSize == 1)
2474 LoadToolbarImageList(TOOLBAR_SIZE_32x32, IDB_TOOLBAR_ENABLED32,
2475 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2476 LoadToolbarImageList(TOOLBAR_SIZE_32x32, IDB_TOOLBAR_DISABLED32,
2477 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2478 sizeButton = CSize(40, 40);
2481 BarCtrl.SetButtonSize(sizeButton);
2482 BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
2483 BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
2484 m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
2485 m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
2487 // resize the rebar.
2489 rbbi.cbSize = sizeof(rbbi);
2490 rbbi.fMask = RBBIM_CHILDSIZE;
2491 rbbi.cyMinChild = sizeButton.cy;
2492 m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);
2497 * @brief Load a transparent 24-bit color image list.
2499 static void LoadHiColImageList(UINT nIDResource, int nWidth, int nHeight, int nCount, CImageList& ImgList, COLORREF crMask = RGB(255,0,255))
2502 VERIFY(bm.LoadBitmap(nIDResource));
2503 VERIFY(ImgList.Create(nWidth, nHeight, ILC_COLORDDB|ILC_MASK, nCount, 0));
2504 int nIndex = ImgList.Add(&bm, crMask);
2505 ASSERT(-1 != nIndex);
2509 * @brief Load toolbar image list.
2511 static void LoadToolbarImageList(CMainFrame::TOOLBAR_SIZE size, UINT nIDResource,
2512 CImageList& ImgList)
2514 const int ImageCount = 22;
2516 int imageHeight = 0;
2520 case CMainFrame::TOOLBAR_SIZE_16x16:
2524 case CMainFrame::TOOLBAR_SIZE_32x32:
2530 LoadHiColImageList(nIDResource, imageWidth, imageHeight, ImageCount, ImgList);
2534 * @brief Called when the document title is modified.
2536 void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
2538 CFrameWnd::OnUpdateFrameTitle(bAddToTitle);
2540 if (m_wndTabBar.m_hWnd)
2541 m_wndTabBar.UpdateTabs();
2544 /** @brief Hide the toolbar. */
2545 void CMainFrame::OnToolbarNone()
2547 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, false);
2548 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, FALSE, 0);
2551 /** @brief Update menuitem for hidden toolbar. */
2552 void CMainFrame::OnUpdateToolbarNone(CCmdUI* pCmdUI)
2554 bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2555 pCmdUI->SetRadio(!enabled);
2558 /** @brief Show small toolbar. */
2559 void CMainFrame::OnToolbarSmall()
2561 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2562 GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, 0);
2564 LoadToolbarImages();
2566 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, TRUE, 0);
2569 /** @brief Update menuitem for small toolbar. */
2570 void CMainFrame::OnUpdateToolbarSmall(CCmdUI* pCmdUI)
2572 bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2573 int toolbar = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2574 pCmdUI->SetRadio(enabled && toolbar == 0);
2577 /** @brief Show big toolbar. */
2578 void CMainFrame::OnToolbarBig()
2580 GetOptionsMgr()->SaveOption(OPT_SHOW_TOOLBAR, true);
2581 GetOptionsMgr()->SaveOption(OPT_TOOLBAR_SIZE, 1);
2583 LoadToolbarImages();
2585 CMDIFrameWnd::ShowControlBar(&m_wndToolBar, TRUE, 0);
2588 /** @brief Update menuitem for big toolbar. */
2589 void CMainFrame::OnUpdateToolbarBig(CCmdUI* pCmdUI)
2591 bool enabled = GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR);
2592 int toolbar = GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
2593 pCmdUI->SetRadio(enabled && toolbar == 1);
2596 /** @brief Lang aware version of CFrameWnd::OnToolTipText() */
2597 BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
2599 ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
2601 // need to handle both ANSI and UNICODE versions of the message
2602 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2603 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2606 UINT_PTR nID = pNMHDR->idFrom;
2607 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
2608 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2610 // idFrom is actually the HWND of the tool
2611 nID = ::GetDlgCtrlID((HWND)nID);
2614 if (nID != 0) // will be zero on a separator
2616 strFullText = theApp.LoadString(nID);
2617 // don't handle the message if no string resource found
2618 if (strFullText.empty())
2621 // this is the command id, not the button index
2622 AfxExtractSubString(strTipText, strFullText.c_str(), 1, '\n');
2625 if (pNMHDR->code == TTN_NEEDTEXTA)
2626 lstrcpyn(pTTTA->szText, strTipText, countof(pTTTA->szText));
2628 _mbstowcsz(pTTTW->szText, strTipText, countof(pTTTW->szText));
2630 if (pNMHDR->code == TTN_NEEDTEXTA)
2631 _wcstombsz(pTTTA->szText, strTipText, countof(pTTTA->szText));
2633 lstrcpyn(pTTTW->szText, strTipText, countof(pTTTW->szText));
2637 // bring the tooltip window above other popup windows
2638 ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
2639 SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
2641 return TRUE; // message was handled
2645 * @brief Ask user for close confirmation when closing the mainframe.
2646 * This function asks if user wants to close multiple open windows when user
2647 * selects (perhaps accidentally) to close WinMerge (application).
2648 * @return true if user agreeds to close all windows.
2650 bool CMainFrame::AskCloseConfirmation()
2652 const DirDocList &dirdocs = GetAllDirDocs();
2653 const MergeDocList &mergedocs = GetAllMergeDocs();
2656 const int count = dirdocs.GetCount() + mergedocs.GetCount();
2659 // Check that we don't have one empty dirdoc + mergedoc situation.
2660 // That happens since we open "hidden" dirdoc for every file compare.
2661 if (dirdocs.GetCount() == 1)
2663 CDirDoc *pDoc = dirdocs.GetHead();
2664 if (!pDoc->HasDiffs())
2667 ret = LangMessageBox(IDS_CLOSEALL_WINDOWS, MB_YESNO | MB_ICONWARNING);
2669 return (ret == IDYES);
2672 void CMainFrame::OnHelpCheckForUpdates()
2674 CVersionInfo version(AfxGetResourceHandle());
2675 CInternetSession session;
2678 CHttpFile *file = (CHttpFile *)session.OpenURL(CurrentVersionURL);
2681 char buf[256] = { 0 };
2682 file->Read(buf, sizeof(buf));
2684 String current_version = ucr::toTString(buf);
2685 string_replace(current_version, _T("\r\n"), _T(""));
2688 int exe_vers[4] = { 0 }, cur_vers[4] = { 0 };
2689 _stscanf(version.GetProductVersion().c_str(), _T("%d.%d.%d.%d"), &exe_vers[0], &exe_vers[1], &exe_vers[2], &exe_vers[3]);
2690 _stscanf(current_version.c_str(), _T("%d.%d.%d.%d"), &cur_vers[0], &cur_vers[1], &cur_vers[2], &cur_vers[3]);
2691 String exe_version_hex = string_format(_T("%08x%08x%08x%08x"), exe_vers[0], exe_vers[1], exe_vers[2], exe_vers[3]);
2692 String cur_version_hex = string_format(_T("%08x%08x%08x%08x"), cur_vers[0], cur_vers[1], cur_vers[2], cur_vers[3]);
2694 switch (exe_version_hex.compare(cur_version_hex))
2697 if (cur_version_hex == _T("00000000000000000000000000000000"))
2699 String msg = _("Failed to download latest version information");
2700 AfxMessageBox(msg.c_str(), MB_ICONERROR);
2706 String msg = _("Your software is up to date");
2707 AfxMessageBox(msg.c_str(), MB_ICONINFORMATION);
2712 String msg = string_format_string2(_("A new version of WinMerge is available.\n%1 is now available (you have %2). Would you like to download it now?"), current_version, version.GetProductVersion());
2713 if (AfxMessageBox(msg.c_str(), MB_ICONINFORMATION | MB_YESNO) == IDYES)
2714 ShellExecute(NULL, _T("open"), DownloadUrl, NULL, NULL, SW_SHOWNORMAL);
2719 catch (CException& e)
2722 e.GetErrorMessage(msg, sizeof(msg)/sizeof(msg[0]));
2723 AfxMessageBox(msg, MB_ICONERROR);
2728 * @brief Called when user selects File/Open Conflict...
2730 void CMainFrame::OnFileOpenConflict()
2732 String conflictFile;
2733 if (SelectFile(GetSafeHwnd(), conflictFile))
2735 DoOpenConflict(conflictFile);
2740 * @brief Select and open conflict file for resolving.
2741 * This function lets user to select conflict file to resolve.
2742 * Then we parse conflict file to two files to "merge" and
2743 * save resulting file over original file.
2745 * Set left-side file read-only as it is the repository file which cannot
2746 * be modified anyway. Right-side file is user's file which is set as
2747 * modified by default so user can just save it and accept workspace
2748 * file as resolved file.
2749 * @param [in] conflictFile Full path to conflict file to open.
2750 * @param [in] checked If true, do not check if it really is project file.
2751 * @return TRUE if conflict file was opened for resolving.
2753 BOOL CMainFrame::DoOpenConflict(const String& conflictFile, bool checked)
2755 BOOL conflictCompared = FALSE;
2759 bool confFile = IsConflictFile(conflictFile);
2762 String message = string_format_string1(_("The file\n%1\nis not a conflict file."), conflictFile);
2763 AfxMessageBox(message.c_str(), MB_ICONSTOP);
2768 // Create temp files and put them into the list,
2769 // from where they get deleted when MainFrame is deleted.
2770 String ext = paths_FindExtension(conflictFile);
2771 TempFilePtr wTemp(new TempFile());
2772 String workFile = wTemp->Create(_T("confw_"), ext.c_str());
2773 m_tempFiles.push_back(wTemp);
2774 TempFilePtr vTemp(new TempFile());
2775 String revFile = vTemp->Create(_T("confv_"), ext.c_str());
2776 m_tempFiles.push_back(vTemp);
2778 // Parse conflict file into two files.
2780 int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
2781 bool success = ParseConflictFile(conflictFile, workFile, revFile, iGuessEncodingType, inners);
2785 // Open two parsed files to WinMerge, telling WinMerge to
2786 // save over original file (given as third filename).
2787 theApp.m_strSaveAsPath = conflictFile.c_str();
2788 String theirs = _("Theirs File");
2789 String my = _("Mine File");
2790 theApp.m_strDescriptions[0] = theirs;
2791 theApp.m_strDescriptions[1] = my;
2793 DWORD dwFlags[2] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU | FFILEOPEN_MODIFIED};
2794 conflictCompared = DoFileOpen(&PathContext(revFile, workFile),
2799 LangMessageBox(IDS_ERROR_CONF_RESOLVE, MB_ICONSTOP);
2801 return conflictCompared;
2805 * @brief Get type of frame (File/Folder compare).
2806 * @param [in] pFrame Pointer to frame to check.
2807 * @return FRAMETYPE of the given frame.
2809 CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame) const
2811 BOOL bMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame));
2812 BOOL bHexMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame));
2813 BOOL bImgMergeFrame = pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame));
2814 BOOL bDirFrame = pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame));
2818 else if (bHexMergeFrame)
2819 return FRAME_HEXFILE;
2820 else if (bImgMergeFrame)
2821 return FRAME_IMGFILE;
2823 return FRAME_FOLDER;
2829 * @brief Show the plugins list dialog.
2831 void CMainFrame::OnPluginsList()
2837 void CMainFrame::OnDiffOptionsDropDown(NMHDR* pNMHDR, LRESULT* pResult)
2839 LPNMTOOLBAR pToolBar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);
2840 ClientToScreen(&(pToolBar->rcButton));
2842 VERIFY(menu.LoadMenu(IDR_POPUP_DIFF_OPTIONS));
2843 theApp.TranslateMenu(menu.m_hMenu);
2844 CMenu* pPopup = menu.GetSubMenu(0);
2847 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2848 pToolBar->rcButton.left, pToolBar->rcButton.bottom, this);
2852 void CMainFrame::OnDiffWhitespace(UINT nID)
2854 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_WHITESPACE, nID - IDC_DIFF_WHITESPACE_COMPARE);
2858 void CMainFrame::OnUpdateDiffWhitespace(CCmdUI* pCmdUI)
2860 pCmdUI->SetRadio((pCmdUI->m_nID - IDC_DIFF_WHITESPACE_COMPARE) == GetOptionsMgr()->GetInt(OPT_CMP_IGNORE_WHITESPACE));
2864 void CMainFrame::OnDiffCaseSensitive()
2866 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CASE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2870 void CMainFrame::OnUpdateDiffCaseSensitive(CCmdUI* pCmdUI)
2872 pCmdUI->SetCheck(!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
2876 void CMainFrame::OnDiffIgnoreEOL()
2878 GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_EOL, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2882 void CMainFrame::OnUpdateDiffIgnoreEOL(CCmdUI* pCmdUI)
2884 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_EOL));
2888 void CMainFrame::OnCompareMethod(UINT nID)
2890 GetOptionsMgr()->SaveOption(OPT_CMP_METHOD, nID - ID_COMPMETHOD_FULL_CONTENTS);
2893 void CMainFrame::OnUpdateCompareMethod(CCmdUI* pCmdUI)
2895 pCmdUI->SetRadio((pCmdUI->m_nID - ID_COMPMETHOD_FULL_CONTENTS) == GetOptionsMgr()->GetInt(OPT_CMP_METHOD));
2899 void CMainFrame::OnMRUs(UINT nID)
2901 std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(9);
2902 const int idx = nID - ID_MRU_FIRST;
2903 if (idx < mrus.size())
2905 MergeCmdLineInfo cmdInfo((_T("\"") + mrus[idx].path + _T("\" ") + mrus[idx].params).c_str());
2906 theApp.ParseArgsAndDoOpen(cmdInfo, this);
2910 void CMainFrame::OnUpdateNoMRUs(CCmdUI* pCmdUI)
2912 // append the MRU submenu
2913 HMENU hMenu = GetSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu, ID_FILE_NEW, false);
2918 int i = ::GetMenuItemCount(hMenu);
2920 ::DeleteMenu(hMenu, 0, MF_BYPOSITION);
2922 std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(9);
2924 if (mrus.size() == 0)
2926 // no script : create a <empty> entry
2927 ::AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, theApp.LoadString(IDS_NO_EDIT_SCRIPTS).c_str());
2931 // or fill in the submenu with the scripts names
2932 int ID = ID_MRU_FIRST; // first ID in menu
2933 for (i = 0 ; i < mrus.size() ; i++, ID++)
2934 ::AppendMenu(hMenu, MF_STRING, ID, (string_format(_T("&%d "), i+1) + mrus[i].title).c_str());
2937 pCmdUI->Enable(true);
2941 * @brief Update plugin name
2942 * @param [in] pCmdUI UI component to update.
2944 void CMainFrame::OnUpdatePluginName(CCmdUI* pCmdUI)
2946 pCmdUI->SetText(_T(""));
2949 void CMainFrame::ReloadMenu()
2951 // set the menu of the main frame window
2952 UINT idMenu = IDR_MAINFRAME;
2953 CMergeApp *pApp = dynamic_cast<CMergeApp *> (AfxGetApp());
2954 CMainFrame * pMainFrame = dynamic_cast<CMainFrame *> ((CFrameWnd*)pApp->m_pMainWnd);
2955 HMENU hNewDefaultMenu = pMainFrame->NewDefaultMenu(idMenu);
2956 HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
2957 HMENU hNewImgMergeMenu = pMainFrame->NewImgMergeViewMenu();
2958 HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
2959 if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
2961 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
2962 CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
2963 CMenu * pNewMergeMenu = CMenu::FromHandle(hNewMergeMenu);
2964 CMenu * pNewImgMergeMenu = CMenu::FromHandle(hNewImgMergeMenu);
2965 CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
2967 CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
2970 if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2971 static_cast<CChildFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2972 if (pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2973 static_cast<CHexMergeFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
2974 if (pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2975 static_cast<CImgMergeFrame *>(pFrame)->SetSharedMenu(hNewImgMergeMenu);
2976 else if (pFrame->IsKindOf(RUNTIME_CLASS(COpenFrame)))
2977 static_cast<COpenFrame *>(pFrame)->SetSharedMenu(hNewDefaultMenu);
2978 else if (pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2979 static_cast<CDirFrame *>(pFrame)->SetSharedMenu(hNewDirMenu);
2980 pFrame = pFrame->GetNextWindow();
2983 CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
2986 if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
2987 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2988 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
2989 pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
2990 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
2991 pMainFrame->MDISetMenu(pNewImgMergeMenu, NULL);
2992 else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
2993 pMainFrame->MDISetMenu(pNewDirMenu, NULL);
2995 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
2998 pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
3000 // Don't delete the old menu
3001 // There is a bug in BCMenu or in Windows98 : the new menu does not
3002 // appear correctly if we destroy the old one
3003 // if (pOldDefaultMenu)
3004 // pOldDefaultMenu->DestroyMenu();
3005 // if (pOldMergeMenu)
3006 // pOldMergeMenu->DestroyMenu();
3008 // pOldDirMenu->DestroyMenu();
3010 // m_hMenuDefault is used to redraw the main menu when we close a child frame
3011 // if this child frame had a different menu
3012 pMainFrame->m_hMenuDefault = hNewDefaultMenu;
3013 pApp->m_pOpenTemplate->m_hMenuShared = hNewDefaultMenu;
3014 pApp->m_pDiffTemplate->m_hMenuShared = hNewMergeMenu;
3015 pApp->m_pDirTemplate->m_hMenuShared = hNewDirMenu;
3017 // force redrawing the menu bar
3018 pMainFrame->DrawMenuBar();
3022 void CMainFrame::UpdateDocTitle()
3024 CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
3025 POSITION posTemplate = pDocManager->GetFirstDocTemplatePosition();
3026 ASSERT(posTemplate != NULL);
3028 while (posTemplate != NULL)
3030 CDocTemplate* pTemplate = pDocManager->GetNextDocTemplate(posTemplate);
3032 ASSERT(pTemplate != NULL);
3034 POSITION pos = pTemplate->GetFirstDocPosition();
3039 pDoc = pTemplate->GetNextDoc(pos);
3040 pDoc->SetTitle(NULL);
3041 ((CFrameWnd*)AfxGetApp()->m_pMainWnd)->OnUpdateFrameTitle(TRUE);