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 Defines the class behaviors for the application.
27 // ID line follows -- this is updated by SVN
28 // $Id: Merge.cpp 6861 2009-06-25 12:11:07Z kimmov $
31 #include "Constants.h"
32 #include "UnicodeString.h"
33 #include "Environment.h"
34 #include "OptionsMgr.h"
36 #include "HexMergeDoc.h"
37 #include "HexMergeFrm.h"
38 #include "HexMergeView.h"
48 #include "coretools.h"
50 #include "FileFilterHelper.h"
52 #include "DirScan.h" // for DirScan_InitializeDefaultCodepage
53 #include "ProjectFile.h"
54 #include "MergeEditView.h"
55 #include "LanguageSelect.h"
56 #include "OptionsDef.h"
57 #include "MergeCmdLineInfo.h"
58 #include "ConflictFileParser.h"
61 // For shutdown cleanup
67 static char THIS_FILE[] = __FILE__;
72 /** @brief Location for command line help to open. */
73 static TCHAR CommandLineHelpLocation[] = _T("::/htmlhelp/Command_line.html");
75 // registry dir to WinMerge
76 static CString f_RegDir = _T("Software\\Thingamahoochie\\WinMerge");
80 * @brief Turn STL exceptions into MFC exceptions.
81 * Based on the article "Visual C++ Exception-Handling Instrumentation"
82 * by Eugene Gershnik, published at http://www.drdobbs.com/184416600.
83 * Rethrow fix inspired by http://www.spinics.net/lists/wine/msg05996.html.
85 namespace Turn_STL_exceptions_into_MFC_exceptions
87 # ifndef _STATIC_CPPLIB
88 # error This hack only works with _STATIC_CPPLIB defined.
91 class CDisguisedSTLException : public CException
94 std::exception *m_pSTLException;
96 CDisguisedSTLException(std::exception *pSTLException)
97 : m_pSTLException(pSTLException)
100 virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError, PUINT)
102 _sntprintf(lpszError, nMaxError, _T("%hs"), m_pSTLException->what());
107 const DWORD CPP_EXCEPTION = 0xE06D7363;
108 const DWORD MS_MAGIC = 0x19930520;
110 extern "C" void __stdcall _CxxThrowException(void *pObject, _s__ThrowInfo const *pObjectInfo)
112 __declspec(thread) static ULONG_PTR args[3] = { MS_MAGIC, 0, 0 };
115 pObject = reinterpret_cast<void *>(args[1]);
116 pObjectInfo = reinterpret_cast<_s__ThrowInfo const *>(args[2]);
120 args[1] = (ULONG_PTR)pObject;
121 args[2] = (ULONG_PTR)pObjectInfo;
124 if (pObjectInfo->pCatchableTypeArray && (i = pObjectInfo->pCatchableTypeArray->nCatchableTypes))
126 const char *name = typeid(std::exception).raw_name();
127 if (pObjectInfo->pCatchableTypeArray->arrayOfCatchableTypes[i - 1]->pType->name == name)
129 throw new CDisguisedSTLException(static_cast<std::exception *>(pObject));
132 RaiseException(CPP_EXCEPTION, EXCEPTION_NONCONTINUABLE, sizeof(args)/sizeof(args[0]), args);
136 /////////////////////////////////////////////////////////////////////////////
139 BEGIN_MESSAGE_MAP(CMergeApp, CWinApp)
140 //{{AFX_MSG_MAP(CMergeApp)
141 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
142 ON_COMMAND(ID_VIEW_LANGUAGE, OnViewLanguage)
143 ON_UPDATE_COMMAND_UI(ID_VIEW_LANGUAGE, OnUpdateViewLanguage)
144 ON_COMMAND(ID_HELP, OnHelp)
145 ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, OnOpenRecentFile)
147 // Standard file based document commands
148 //ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
149 //ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
150 // Standard print setup command
151 ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
154 static void AddEnglishResourceHook();
157 * @brief Mapping from command line argument name (eg, ignorews) to WinMerge
158 * option name (eg, Settings/IgnoreSpace).
160 * These arguments take an optional colon and number, like so:
162 * "/ignoreblanklines" (makes WinMerge ignore blank lines)
163 * "/ignoreblanklines:1" (makes WinMerge ignore blank lines)
164 * "/ignoreblanklines:0" (makes WinMerge not ignore blank lines)
169 LPCTSTR WinMergeOptionName;
173 * @brief Get Options Manager.
174 * @return Pointer to OptionsMgr.
176 COptionsMgr * GetOptionsMgr()
178 CMergeApp *pApp = static_cast<CMergeApp *>(AfxGetApp());
179 return pApp->GetMergeOptionsMgr();
184 * @return Pointer to Log.
188 CMergeApp *pApp = static_cast<CMergeApp *>(AfxGetApp());
189 return pApp->GetMergeLog();
193 /////////////////////////////////////////////////////////////////////////////
194 // CMergeApp construction
196 CMergeApp::CMergeApp() :
197 m_bNeedIdleTimer(FALSE)
199 , m_pHexMergeTemplate(0)
201 , m_mainThreadScripts(NULL)
202 , m_nLastCompareResult(0)
203 , m_bNonInteractive(false)
206 , m_nActiveOperations(0)
208 // add construction code here,
209 // Place all significant initialization in InitInstance
210 m_pLangDlg = new CLanguageSelect(IDR_MAINFRAME, IDR_MAINFRAME);
213 CMergeApp::~CMergeApp()
219 /////////////////////////////////////////////////////////////////////////////
220 // The one and only CMergeApp object
224 /////////////////////////////////////////////////////////////////////////////
225 // CMergeApp initialization
228 * @brief Initialize WinMerge application instance.
229 * @return TRUE if application initialization succeeds (and we'll run it),
230 * FALSE if something failed and we exit the instance.
231 * @todo We could handle these failure situations more gratefully, i.e. show
232 * at least some error message to the user..
234 BOOL CMergeApp::InitInstance()
236 // Prevents DLL hijacking
237 HMODULE hLibrary = GetModuleHandle(_T("kernel32.dll"));
238 BOOL (WINAPI *pfnSetSearchPathMode)(DWORD) = (BOOL (WINAPI *)(DWORD))GetProcAddress(hLibrary, "SetSearchPathMode");
239 if (pfnSetSearchPathMode)
240 pfnSetSearchPathMode(0x00000001L /*BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE*/ | 0x00008000L /*BASE_SEARCH_PATH_PERMANENT*/);
241 BOOL (WINAPI *pfnSetDllDirectoryA)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR))GetProcAddress(hLibrary, "SetDllDirectoryA");
242 if (pfnSetDllDirectoryA)
243 pfnSetDllDirectoryA("");
245 InitCommonControls(); // initialize common control library
246 CWinApp::InitInstance(); // call parent class method
248 // Runtime switch so programmer may set this in interactive debugger
252 // get current setting
253 int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
254 // Keep freed memory blocks in the heap's linked list and mark them as freed
255 tmpFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
256 // Call _CrtCheckMemory at every allocation and deallocation request.
257 // WARNING: This slows down WinMerge *A LOT*
258 tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
259 // Set the new state for the flag
260 _CrtSetDbgFlag( tmpFlag );
263 // CCrystalEdit Drag and Drop functionality needs AfxOleInit.
266 TRACE(_T("AfxOleInitFailed. OLE functionality disabled"));
269 // Standard initialization
270 // If you are not using these features and wish to reduce the size
271 // of your final executable, you should remove from the following
272 // the specific initialization routines you do not need.
274 // Revoke the standard OLE Message Filter to avoid drawing frame while loading files.
275 COleMessageFilter* pOldFilter = AfxOleGetMessageFilter();
276 pOldFilter->Revoke();
278 // Only needed by VC6
281 Enable3dControls(); // Call this when using MFC in a shared DLL
283 Enable3dControlsStatic(); // Call this when linking to MFC statically
287 // Load registry file if existing
288 CString sRegPath = CString(GetModulePath(m_hInstance).c_str()) + _T("\\WinMerge.reg");
289 if (paths_DoesPathExist(sRegPath) == IS_EXISTING_FILE)
292 if (SearchPath(NULL, _T("reg.exe"), NULL, 0, NULL, NULL) == 0)
293 sCmdArg = _T("regedit /s \"") + sRegPath + _T("\"");
295 sCmdArg = _T("reg import \"") + sRegPath + _T("\"");
296 HANDLE hProcess = RunIt(NULL, sCmdArg, TRUE, FALSE);
299 WaitForSingleObject(hProcess, INFINITE);
300 CloseHandle(hProcess);
304 m_pOptions = new CRegOptionsMgr;
305 OptionsInit(); // Implementation in OptionsInit.cpp
307 // Initialize temp folder
308 String instTemp = env_GetPerInstanceString(_T("WM_"));
309 env_SetInstanceFolder(instTemp.c_str());
311 // Cleanup left over tempfiles from previous instances.
312 // Normally this should not neet to do anything - but if for some reason
313 // WinMerge did not delete temp files this makes sure they are removed.
316 m_pLog = new CLogFile();
318 int logging = GetOptionsMgr()->GetInt(OPT_LOGGING);
321 m_pLog->EnableLogging(TRUE);
322 String logfile = env_GetMyDocuments(NULL);
323 logfile = paths_ConcatPath(logfile, _T("WinMerge\\WinMerge.log"));
324 m_pLog->SetFile(logfile);
327 m_pLog->SetMaskLevel(CLogFile::LALL);
328 else if (logging == 2)
329 m_pLog->SetMaskLevel(CLogFile::LERROR | CLogFile::LWARNING);
332 // Parse command-line arguments.
333 MergeCmdLineInfo cmdInfo = GetCommandLine();
335 // If paths were given to commandline we consider this being an invoke from
336 // commandline (from other application, shellextension etc).
337 BOOL bCommandLineInvoke = cmdInfo.m_Files.GetSize() > 0;
339 // Set default codepage
340 DirScan_InitializeDefaultCodepage();
342 // WinMerge registry settings are stored under HKEY_CURRENT_USER/Software/Thingamahoochie
343 // This is the name of the company of the original author (Dean Grimm)
344 SetRegistryKey(_T("Thingamahoochie"));
346 BOOL bSingleInstance = GetOptionsMgr()->GetBool(OPT_SINGLE_INSTANCE) ||
347 (true == cmdInfo.m_bSingleInstance);
349 // Create exclusion mutex name
350 TCHAR szDesktopName[MAX_PATH] = _T("Win9xDesktop");
351 DWORD dwLengthNeeded;
352 GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME,
353 szDesktopName, sizeof(szDesktopName), &dwLengthNeeded);
354 TCHAR szMutexName[MAX_PATH + 40];
355 // Combine window class name and desktop name to form a unique mutex name.
356 // As the window class name is decorated to distinguish between ANSI and
357 // UNICODE build, so will be the mutex name.
358 wsprintf(szMutexName, _T("%s-%s"), CMainFrame::szClassName, szDesktopName);
359 HANDLE hMutex = CreateMutex(NULL, FALSE, szMutexName);
361 WaitForSingleObject(hMutex, INFINITE);
362 if (bSingleInstance && GetLastError() == ERROR_ALREADY_EXISTS)
364 // Activate previous instance and send commandline to it
365 HWND hWnd = FindWindow(CMainFrame::szClassName, NULL);
369 ShowWindow(hWnd, SW_RESTORE);
370 SetForegroundWindow(GetLastActivePopup(hWnd));
371 LPTSTR cmdLine = GetCommandLine();
372 COPYDATASTRUCT data = { 0, (lstrlen(cmdLine) + 1) * sizeof(TCHAR), cmdLine};
373 if (SendMessage(hWnd, WM_COPYDATA, NULL, (LPARAM)&data))
375 ReleaseMutex(hMutex);
382 LoadStdProfileSettings(8); // Load standard INI file options (including MRU)
383 BOOL bDisableSplash = GetOptionsMgr()->GetBool(OPT_DISABLE_SPLASH);
385 InitializeFileFilters();
387 // Read last used filter from registry
388 // If filter fails to set, reset to default
389 const String filterString = m_pOptions->GetString(OPT_FILEFILTER_CURRENT);
390 BOOL bFilterSet = theApp.m_globalFileFilter.SetFilter(filterString.c_str());
393 String filter = theApp.m_globalFileFilter.GetFilterNameOrMask();
394 m_pOptions->SaveOption(OPT_FILEFILTER_CURRENT, filter.c_str());
397 CSplashWnd::EnableSplashScreen(!bDisableSplash && !bCommandLineInvoke);
399 // Initialize i18n (multiple language) support
401 m_pLangDlg->SetLogFile(GetLog());
402 m_pLangDlg->InitializeLanguage();
404 AddEnglishResourceHook(); // Use English string when l10n (foreign) string missing
406 m_mainThreadScripts = new CAssureScriptsForThread;
408 // Register the application's document templates. Document templates
409 // serve as the connection between documents, frame windows and views.
412 m_pDiffTemplate = new CMultiDocTemplate(
414 RUNTIME_CLASS(CMergeDoc),
415 RUNTIME_CLASS(CChildFrame), // custom MDI child frame
416 RUNTIME_CLASS(CMergeEditView));
417 AddDocTemplate(m_pDiffTemplate);
420 m_pHexMergeTemplate = new CMultiDocTemplate(
422 RUNTIME_CLASS(CHexMergeDoc),
423 RUNTIME_CLASS(CHexMergeFrame), // custom MDI child frame
424 RUNTIME_CLASS(CHexMergeView));
425 AddDocTemplate(m_pHexMergeTemplate);
428 m_pDirTemplate = new CMultiDocTemplate(
430 RUNTIME_CLASS(CDirDoc),
431 RUNTIME_CLASS(CDirFrame), // custom MDI child frame
432 RUNTIME_CLASS(CDirView));
433 AddDocTemplate(m_pDirTemplate);
435 // create main MDI Frame window
436 CMainFrame* pMainFrame = new CMainFrame;
437 if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
441 ReleaseMutex(hMutex);
446 m_pMainWnd = pMainFrame;
447 // Enable drag&drop files
448 pMainFrame->ModifyStyleEx(NULL, WS_EX_ACCEPTFILES);
450 // Init menus -- hMenuDefault is for MainFrame, other
451 // two are for dirdoc and mergedoc (commented out for now)
452 m_pDiffTemplate->m_hMenuShared = pMainFrame->NewMergeViewMenu();
453 m_pHexMergeTemplate->m_hMenuShared = pMainFrame->NewHexMergeViewMenu();
454 m_pDirTemplate->m_hMenuShared = pMainFrame->NewDirViewMenu();
455 pMainFrame->m_hMenuDefault = pMainFrame->NewDefaultMenu();
458 // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
459 CMenu * pNewMenu = CMenu::FromHandle(pMainFrame->m_hMenuDefault);
460 pMainFrame->MDISetMenu(pNewMenu, NULL);
462 // The main window has been initialized, so activate and update it.
463 pMainFrame->ActivateFrame(cmdInfo.m_nCmdShow);
464 pMainFrame->UpdateWindow();
466 // Since this function actually opens paths for compare it must be
467 // called after initializing CMainFrame!
468 BOOL bContinue = TRUE;
469 if (ParseArgsAndDoOpen(cmdInfo, pMainFrame) == FALSE && bCommandLineInvoke)
473 ReleaseMutex(hMutex);
475 if (m_bNonInteractive)
480 // If user wants to cancel the compare, close WinMerge
481 if (bContinue == FALSE)
483 pMainFrame->PostMessage(WM_CLOSE, 0, 0);
489 // App command to run the dialog
490 void CMergeApp::OnAppAbout()
496 /////////////////////////////////////////////////////////////////////////////
497 // CMergeApp commands
500 BOOL CMergeApp::PreTranslateMessage(MSG* pMsg)
502 // CG: The following lines were added by the Splash Screen component.
503 if (CSplashWnd::PreTranslateAppMessage(pMsg))
506 return CWinApp::PreTranslateMessage(pMsg);
509 void CMergeApp::OnViewLanguage()
511 if (m_pLangDlg->DoModal()==IDOK)
513 //m_lang.ReloadMenu();
514 //m_LangDlg.UpdateDocTitle();
515 GetMainFrame()->UpdateResources();
520 * @brief Updates Language select menu item.
521 * If there are no languages installed we disable menuitem to
522 * open language selection dialog.
524 void CMergeApp::OnUpdateViewLanguage(CCmdUI* pCmdUI)
526 BOOL bLangsInstalled = m_pLangDlg->AreLangsInstalled();
527 pCmdUI->Enable(bLangsInstalled);
531 * @brief Called when application is about to exit.
532 * This functions is called when application is exiting, so this is
533 * good place to do cleanups.
534 * @return Application's exit value (returned from WinMain()).
536 int CMergeApp::ExitInstance()
539 CString sRegPath = CString(GetModulePath(m_hInstance).c_str()) + _T("\\WinMerge.reg");
540 if (paths_DoesPathExist(sRegPath) == IS_EXISTING_FILE)
542 DeleteFile(sRegPath);
544 if (SearchPath(NULL, _T("reg.exe"), NULL, 0, NULL, NULL) == 0)
545 sCmdArg = _T("regedit /s /e \"") + sRegPath + _T("\" ") + _T("HKEY_CURRENT_USER\\") + f_RegDir;
547 sCmdArg = _T("reg export HKCU\\") + f_RegDir + _T(" \"") + sRegPath + _T("\"");
548 HANDLE hProcess = RunIt(NULL, sCmdArg, TRUE, FALSE);
550 CloseHandle(hProcess);
554 const String temp = env_GetTempPath(NULL);
555 ClearTempfolder(temp);
556 delete m_mainThreadScripts;
557 CWinApp::ExitInstance();
561 static void AddEnglishResourceHook()
563 // After calling AfxSetResourceHandle to point to a language
564 // resource DLL, then the application is no longer on the
565 // resource lookup (defined by AfxFindResourceHandle).
567 // Add a dummy extension DLL record whose resource handle
568 // points to the application resources, just to provide
569 // fallback to English for any resources missing from
570 // the language resource DLL.
572 // (Why didn't Microsoft think of this? Bruno Haible who
573 // made gettext certainly thought of this.)
575 // NB: This does not fix the problem that if a control is
576 // missing from a dialog (because it was added only to the
577 // English version, for example) then the DDX_ function is
578 // going to fail. I see no easy way to intercept all DDX
579 // functions except by macro overriding the call--Perry, 2002-12-07.
581 static AFX_EXTENSION_MODULE FakeEnglishDLL = { NULL, NULL };
582 memset(&FakeEnglishDLL, 0, sizeof(FakeEnglishDLL));
583 FakeEnglishDLL.hModule = AfxGetApp()->m_hInstance;
584 FakeEnglishDLL.hResource = FakeEnglishDLL.hModule;
585 FakeEnglishDLL.bInitialized = TRUE;
586 new CDynLinkLibrary(FakeEnglishDLL); // hook into MFC extension DLL chain
590 int CMergeApp::DoMessageBox( LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt )
592 // This is a convenient point for breakpointing !!!
594 // Create a handle to store the parent window of the message box.
595 CWnd* pParentWnd = CWnd::GetActiveWindow();
597 // Check whether an active window was retrieved successfully.
598 if ( pParentWnd == NULL )
600 // Try to retrieve a handle to the last active popup.
601 CWnd * mainwnd = GetMainWnd();
603 pParentWnd = mainwnd->GetLastActivePopup();
606 // Use our own message box implementation, which adds the
607 // do not show again checkbox, and implements it on subsequent calls
608 // (if caller set the style)
610 if (m_bNonInteractive)
613 // Create the message box dialog.
614 CMessageBoxDialog dlgMessage(pParentWnd, lpszPrompt, _T(""), nType,
617 // Display the message box dialog and return the result.
618 return (int) dlgMessage.DoModal();
622 * @brief Set flag so that application will broadcast notification at next
623 * idle time (via WM_TIMER id=IDLE_TIMER)
625 void CMergeApp::SetNeedIdleTimer()
627 m_bNeedIdleTimer = TRUE;
630 BOOL CMergeApp::OnIdle(LONG lCount)
632 if (CWinApp::OnIdle(lCount))
635 // If anyone has requested notification when next idle occurs, send it
636 if (m_bNeedIdleTimer)
638 m_bNeedIdleTimer = FALSE;
639 m_pMainWnd->SendMessageToDescendants(WM_TIMER, IDLE_TIMER, lCount, TRUE, FALSE);
645 * @brief Load any known file filters.
647 * This function loads filter files from paths we know contain them.
648 * @note User's filter location may not be set yet.
650 void CMergeApp::InitializeFileFilters()
652 CString filterPath = GetProfileString(_T("Settings"), _T("UserFilterPath"), _T(""));
654 if (!filterPath.IsEmpty())
656 m_globalFileFilter.SetUserFilterPath((LPCTSTR)filterPath);
658 m_globalFileFilter.LoadAllFileFilters();
661 /** @brief Read command line arguments and open files for comparison.
663 * The name of the function is a legacy code from the time that this function
664 * actually parsed the command line. Today the parsing is done using the
665 * MergeCmdLineInfo class.
666 * @param [in] cmdInfo Commandline parameters info.
667 * @param [in] pMainFrame Pointer to application main frame.
668 * @return TRUE if we opened the compare, FALSE if the compare was canceled.
670 BOOL CMergeApp::ParseArgsAndDoOpen(MergeCmdLineInfo& cmdInfo, CMainFrame* pMainFrame)
672 BOOL bCompared = FALSE;
673 m_bNonInteractive = cmdInfo.m_bNonInteractive;
675 // Set the global file filter.
676 if (!cmdInfo.m_sFileFilter.empty())
678 m_globalFileFilter.SetFilter(cmdInfo.m_sFileFilter.c_str());
682 if (cmdInfo.m_nCodepage)
684 updateDefaultCodepage(2,cmdInfo.m_nCodepage);
687 // Unless the user has requested to see WinMerge's usage open files for
689 if (cmdInfo.m_bShowUsage)
691 pMainFrame->ShowHelp(CommandLineHelpLocation);
695 // Set the required information we need from the command line:
697 pMainFrame->m_bClearCaseTool = cmdInfo.m_bClearCaseTool;
698 pMainFrame->m_bExitIfNoDiff = cmdInfo.m_bExitIfNoDiff;
699 pMainFrame->m_bEscShutdown = cmdInfo.m_bEscShutdown;
701 pMainFrame->m_strSaveAsPath = cmdInfo.m_sOutputpath.c_str();
703 pMainFrame->m_strDescriptions[0] = cmdInfo.m_sLeftDesc;
704 if (cmdInfo.m_Files.GetSize() < 3)
706 pMainFrame->m_strDescriptions[1] = cmdInfo.m_sRightDesc;
710 pMainFrame->m_strDescriptions[1] = cmdInfo.m_sMiddleDesc;
711 pMainFrame->m_strDescriptions[2] = cmdInfo.m_sRightDesc;
714 if (cmdInfo.m_Files.GetSize() > 2)
716 cmdInfo.m_dwLeftFlags |= FFILEOPEN_CMDLINE;
717 cmdInfo.m_dwMiddleFlags |= FFILEOPEN_CMDLINE;
718 cmdInfo.m_dwRightFlags |= FFILEOPEN_CMDLINE;
719 DWORD dwFlags[3] = {cmdInfo.m_dwLeftFlags, cmdInfo.m_dwMiddleFlags, cmdInfo.m_dwRightFlags};
720 bCompared = pMainFrame->DoFileOpen(&cmdInfo.m_Files,
721 dwFlags, cmdInfo.m_bRecurse, NULL,
722 cmdInfo.m_sPreDiffer.c_str());
724 else if (cmdInfo.m_Files.GetSize() > 1)
726 DWORD dwFlags[3] = {cmdInfo.m_dwLeftFlags, cmdInfo.m_dwRightFlags, FFILEOPEN_NONE};
727 bCompared = pMainFrame->DoFileOpen(&cmdInfo.m_Files,
728 dwFlags, cmdInfo.m_bRecurse, NULL,
729 cmdInfo.m_sPreDiffer.c_str());
731 else if (cmdInfo.m_Files.GetSize() == 1)
733 String sFilepath = cmdInfo.m_Files[0];
734 if (IsProjectFile(sFilepath.c_str()))
736 bCompared = LoadAndOpenProjectFile(sFilepath.c_str());
738 else if (IsConflictFile(sFilepath.c_str()))
740 bCompared = pMainFrame->DoOpenConflict(sFilepath.c_str());
744 DWORD dwFlags[3] = {cmdInfo.m_dwLeftFlags, cmdInfo.m_dwRightFlags, FFILEOPEN_NONE};
745 bCompared = pMainFrame->DoFileOpen(&cmdInfo.m_Files,
746 dwFlags, cmdInfo.m_bRecurse, NULL,
747 cmdInfo.m_sPreDiffer.c_str());
750 else if (cmdInfo.m_Files.GetSize() == 0) // if there are no input args, we can check the display file dialog flag
752 BOOL showFiles = m_pOptions->GetBool(OPT_SHOW_SELECT_FILES_AT_STARTUP);
754 pMainFrame->DoFileOpen();
760 /** @brief Open help from mainframe when user presses F1*/
761 void CMergeApp::OnHelp()
763 GetMainFrame()->ShowHelp();
767 * @brief Is specified file a project file?
768 * @param [in] filepath Full path to file to check.
769 * @return true if file is a projectfile.
771 bool CMergeApp::IsProjectFile(LPCTSTR filepath) const
774 SplitFilename(filepath, NULL, NULL, &ext);
775 CString sExt(ext.c_str());
776 if (sExt.CompareNoCase(PROJECTFILE_EXT) == 0)
783 * @brief Read project and perform comparison specified
784 * @param [in] sProject Full path to project file.
785 * @return TRUE if loading project file and starting compare succeeded.
787 bool CMergeApp::LoadAndOpenProjectFile(LPCTSTR sProject)
789 if (*sProject == '\0')
794 if (!project.Read(sProject, &sErr))
797 sErr = theApp.LoadString(IDS_UNK_ERROR_READING_PROJECT);
799 LangFormatString2(msg, IDS_ERROR_FILEOPEN, sProject, sErr.c_str());
800 AfxMessageBox(msg, MB_ICONSTOP);
804 BOOL bLeftReadOnly = FALSE;
805 BOOL bMiddleReadOnly = FALSE;
806 BOOL bRightReadOnly = FALSE;
807 BOOL bRecursive = FALSE;
808 project.GetPaths(files, bRecursive);
809 bLeftReadOnly = project.GetLeftReadOnly();
810 bMiddleReadOnly = project.GetMiddleReadOnly();
811 bRightReadOnly = project.GetRightReadOnly();
812 if (project.HasFilter())
814 String filter = project.GetFilter();
815 filter = string_trim_ws(filter);
816 m_globalFileFilter.SetFilter(filter);
818 if (project.HasSubfolders())
819 bRecursive = project.GetSubfolders() > 0;
822 files.GetPath(0).empty() ? FFILEOPEN_NONE : FFILEOPEN_PROJECT,
823 files.GetPath(1).empty() ? FFILEOPEN_NONE : FFILEOPEN_PROJECT,
824 files.GetPath(2).empty() ? FFILEOPEN_NONE : FFILEOPEN_PROJECT
827 dwFlags[0] |= FFILEOPEN_READONLY;
828 if (files.GetSize() == 2)
831 dwFlags[1] |= FFILEOPEN_READONLY;
836 dwFlags[1] |= FFILEOPEN_READONLY;
838 dwFlags[2] |= FFILEOPEN_READONLY;
841 WriteProfileInt(_T("Settings"), _T("Recurse"), bRecursive);
843 BOOL rtn = GetMainFrame()->DoFileOpen(&files, dwFlags, bRecursive);
845 AddToRecentProjectsMRU(sProject);
850 * @brief Return windows language ID of current WinMerge GUI language
852 WORD CMergeApp::GetLangId() const
854 return m_pLangDlg->GetLangId();
858 * @brief Lang aware version of CStatusBar::SetIndicators()
860 void CMergeApp::SetIndicators(CStatusBar &sb, const UINT *rgid, int n) const
862 m_pLangDlg->SetIndicators(sb, rgid, n);
866 * @brief Translate menu to current WinMerge GUI language
868 void CMergeApp::TranslateMenu(HMENU h) const
870 m_pLangDlg->TranslateMenu(h);
874 * @brief Translate dialog to current WinMerge GUI language
876 void CMergeApp::TranslateDialog(HWND h) const
878 m_pLangDlg->TranslateDialog(h);
882 * @brief Load string and translate to current WinMerge GUI language
884 String CMergeApp::LoadString(UINT id) const
886 return m_pLangDlg->LoadString(id);
890 * @brief Load dialog caption and translate to current WinMerge GUI language
892 std::wstring CMergeApp::LoadDialogCaption(LPCTSTR lpDialogTemplateID) const
894 return m_pLangDlg->LoadDialogCaption(lpDialogTemplateID);
898 * @brief Reload main menu(s) (for language change)
900 void CMergeApp::ReloadMenu()
902 m_pLangDlg->ReloadMenu();
905 /** @brief Wrap one line of cmdline help in appropriate whitespace */
906 static String CmdlineOption(int idres)
908 String str = theApp.LoadString(idres) + _T(" \n");
913 * @brief Get default editor path.
914 * @return full path to the editor program executable.
916 CString CMergeApp::GetDefaultEditor()
918 CString path = env_GetWindowsDirectory().c_str();
919 path += _T("\\NOTEPAD.EXE");
924 * @brief Get default user filter folder path.
925 * This function returns the default filter path for user filters.
926 * If wanted so (@p bCreate) path can be created if it does not
927 * exist yet. But you really want to create the patch only when
928 * there is no user path defined.
929 * @param [in] bCreate If TRUE filter path is created if it does
931 * @return Default folder for user filters.
933 CString CMergeApp::GetDefaultFilterUserPath(BOOL bCreate /*=FALSE*/)
935 String pathMyFolders = env_GetMyDocuments(NULL);
936 String pathFilters(pathMyFolders);
937 pathFilters = paths_ConcatPath(pathFilters, DefaultRelativeFilterPath);
939 if (bCreate && !paths_CreateIfNeeded(pathFilters.c_str()))
941 // Failed to create a folder, check it didn't already
943 DWORD errCode = GetLastError();
944 if (errCode != ERROR_ALREADY_EXISTS)
946 // Failed to create a folder for filters, fallback to
947 // "My Documents"-folder. It is not worth the trouble to
948 // bother user about this or user more clever solutions.
949 pathFilters = pathMyFolders;
952 return pathFilters.c_str();
957 * @brief Adds specified file to the recent projects list.
958 * @param [in] sPathName Path to project file
960 void CMergeApp::AddToRecentProjectsMRU(LPCTSTR sPathName)
962 // sPathName will be added to the top of the MRU list.
963 // If sPathName already exists in the MRU list, it will be moved to the top
964 if (m_pRecentFileList != NULL) {
965 m_pRecentFileList->Add(sPathName);
966 m_pRecentFileList->WriteList();
971 * @brief Handles menu selection from recent projects list
972 * @param [in] nID Menu ID of the selected item
974 BOOL CMergeApp::OnOpenRecentFile(UINT nID)
976 return LoadAndOpenProjectFile(m_pRecentFileList->m_arrNames[nID-ID_FILE_MRU_FILE1]);