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 COpenDlg class
26 // ID line follows -- this is updated by SVN
27 // $Id: OpenDlg.cpp 6861 2009-06-25 12:11:07Z kimmov $
30 #include <sys/types.h>
32 #include "UnicodeString.h"
34 #include "ProjectFile.h"
36 #include "coretools.h"
38 #include "SelectUnpackerDlg.h"
39 #include "OptionsDef.h"
41 #include "OptionsMgr.h"
42 #include "FileOrFolderSelect.h"
45 #ifdef COMPILE_MULTIMON_STUBS
46 #undef COMPILE_MULTIMON_STUBS
53 static char THIS_FILE[] = __FILE__;
56 // Timer ID and timeout for delaying path validity check
57 const UINT IDT_CHECKFILES = 1;
58 const UINT CHECKFILES_TIMEOUT = 1000; // milliseconds
59 static const TCHAR EMPTY_EXTENSION[] = _T(".*");
61 /** @brief Location for Open-dialog specific help to open. */
62 static TCHAR OpenDlgHelpLocation[] = _T("::/htmlhelp/Open_paths.html");
64 /////////////////////////////////////////////////////////////////////////////
68 * @brief Standard constructor.
70 COpenDlg::COpenDlg(CWnd* pParent /*=NULL*/)
71 : CDialog(COpenDlg::IDD, pParent)
72 , m_pathsType(DOES_NOT_EXIST)
73 , m_bOverwriteRecursive(FALSE)
75 , m_pProjectFile(NULL)
76 , m_pUpdateButtonStatusThread(NULL)
81 * @brief Standard destructor.
85 TerminateThreadIfRunning();
86 delete m_pProjectFile;
89 void COpenDlg::DoDataExchange(CDataExchange* pDX)
91 CDialog::DoDataExchange(pDX);
92 //{{AFX_DATA_MAP(COpenDlg)
93 DDX_Control(pDX, IDC_SELECT_UNPACKER, m_ctlSelectUnpacker);
94 DDX_Control(pDX, IDC_UNPACKER_EDIT, m_ctlUnpacker);
95 DDX_Control(pDX, IDC_EXT_COMBO, m_ctlExt);
96 DDX_Control(pDX, IDOK, m_ctlOk);
97 DDX_Control(pDX, IDC_RECURS_CHECK, m_ctlRecurse);
98 DDX_Control(pDX, IDC_PATH0_COMBO, m_ctlPath[0]);
99 DDX_Control(pDX, IDC_PATH1_COMBO, m_ctlPath[1]);
100 DDX_Control(pDX, IDC_PATH2_COMBO, m_ctlPath[2]);
101 DDX_CBStringExact(pDX, IDC_PATH0_COMBO, m_strPath[0]);
102 DDX_CBStringExact(pDX, IDC_PATH1_COMBO, m_strPath[1]);
103 DDX_CBStringExact(pDX, IDC_PATH2_COMBO, m_strPath[2]);
104 DDX_Check(pDX, IDC_RECURS_CHECK, m_bRecurse);
105 DDX_CBStringExact(pDX, IDC_EXT_COMBO, m_strExt);
106 DDX_Text(pDX, IDC_UNPACKER_EDIT, m_strUnpacker);
111 BEGIN_MESSAGE_MAP(COpenDlg, CDialog)
112 //{{AFX_MSG_MAP(COpenDlg)
113 ON_BN_CLICKED(IDC_PATH0_BUTTON, OnPath0Button)
114 ON_BN_CLICKED(IDC_PATH1_BUTTON, OnPath1Button)
115 ON_BN_CLICKED(IDC_PATH2_BUTTON, OnPath2Button)
116 ON_CBN_SELCHANGE(IDC_PATH0_COMBO, OnSelchangePath0Combo)
117 ON_CBN_SELCHANGE(IDC_PATH1_COMBO, OnSelchangePath1Combo)
118 ON_CBN_SELCHANGE(IDC_PATH2_COMBO, OnSelchangePath2Combo)
119 ON_CBN_EDITCHANGE(IDC_PATH0_COMBO, OnEditEvent)
120 ON_CBN_EDITCHANGE(IDC_PATH1_COMBO, OnEditEvent)
121 ON_CBN_EDITCHANGE(IDC_PATH2_COMBO, OnEditEvent)
122 ON_BN_CLICKED(IDC_SELECT_UNPACKER, OnSelectUnpacker)
123 ON_CBN_SELENDCANCEL(IDC_PATH0_COMBO, UpdateButtonStates)
124 ON_CBN_SELENDCANCEL(IDC_PATH1_COMBO, UpdateButtonStates)
125 ON_CBN_SELENDCANCEL(IDC_PATH2_COMBO, UpdateButtonStates)
127 ON_BN_CLICKED(IDC_SELECT_FILTER, OnSelectFilter)
129 ON_COMMAND(ID_HELP, OnHelp)
131 ON_MESSAGE(WM_USER + 1, OnUpdateStatus)
135 /////////////////////////////////////////////////////////////////////////////
136 // COpenDlg message handlers
139 * @brief Handler for WM_INITDIALOG; conventional location to initialize controls
140 * At this point dialog and control windows exist
142 BOOL COpenDlg::OnInitDialog()
144 theApp.TranslateDialog(m_hWnd);
145 CDialog::OnInitDialog();
147 // setup handler for resizing this dialog
148 m_constraint.InitializeCurrentSize(this);
149 // configure how individual controls adjust when dialog resizes
150 m_constraint.ConstrainItem(IDC_PATH0_COMBO, 0, 1, 0, 0); // grows right
151 m_constraint.ConstrainItem(IDC_PATH1_COMBO, 0, 1, 0, 0); // grows right
152 m_constraint.ConstrainItem(IDC_PATH2_COMBO, 0, 1, 0, 0); // grows right
153 m_constraint.ConstrainItem(IDC_EXT_COMBO, 0, 1, 0, 0); // grows right
154 m_constraint.ConstrainItem(IDC_UNPACKER_EDIT, 0, 1, 0, 0); // grows right
155 m_constraint.ConstrainItem(IDC_FILES_DIRS_GROUP, 0, 1, 0, 0); // grows right
156 m_constraint.ConstrainItem(IDC_PATH0_BUTTON, 1, 0, 0, 0); // slides right
157 m_constraint.ConstrainItem(IDC_PATH1_BUTTON, 1, 0, 0, 0); // slides right
158 m_constraint.ConstrainItem(IDC_PATH2_BUTTON, 1, 0, 0, 0); // slides right
159 m_constraint.ConstrainItem(IDC_SELECT_UNPACKER, 1, 0, 0, 0); // slides right
160 m_constraint.ConstrainItem(IDC_OPEN_STATUS, 0, 1, 0, 0); // grows right
161 m_constraint.ConstrainItem(IDC_SELECT_FILTER, 1, 0, 0, 0); // slides right
162 m_constraint.ConstrainItem(IDOK, 1, 0, 0, 0); // slides right
163 m_constraint.ConstrainItem(IDCANCEL, 1, 0, 0, 0); // slides right
164 m_constraint.ConstrainItem(ID_HELP, 1, 0, 0, 0); // slides right
165 m_constraint.DisallowHeightGrowth();
166 m_constraint.SubclassWnd(); // install subclassing
167 m_constraint.LoadPosition(_T("ResizeableDialogs"), _T("OpenDlg"), false); // persist size via registry
169 CMainFrame::CenterToMainFrame(this);
171 for (int file = 0; file < m_files.GetSize(); file++)
173 m_strPath[file] = m_files[file].c_str();
174 m_ctlPath[file].SetWindowText(m_files[file].c_str());
177 m_ctlPath[0].AttachSystemImageList();
178 m_ctlPath[1].AttachSystemImageList();
179 m_ctlPath[2].AttachSystemImageList();
180 m_ctlPath[0].LoadState(_T("Files\\Left"));
181 m_ctlPath[1].LoadState(_T("Files\\Right"));
182 m_ctlPath[2].LoadState(_T("Files\\Option"));
183 m_ctlExt.LoadState(_T("Files\\Ext"));
185 BOOL bIsEmptyThirdItem = theApp.GetProfileInt(_T("Files\\Option"), _T("Empty"), TRUE);
186 if (bIsEmptyThirdItem)
187 m_ctlPath[2].SetWindowText(_T(""));
189 BOOL bDoUpdateData = TRUE;
190 for (int index = 0; index < countof(m_strPath); index++)
192 if (!m_strPath[index].IsEmpty())
193 bDoUpdateData = FALSE;
195 UpdateData(bDoUpdateData);
197 int nSource = GetOptionsMgr()->GetInt(OPT_AUTO_COMPLETE_SOURCE);
200 m_ctlPath[0].SetAutoComplete(nSource);
201 m_ctlPath[1].SetAutoComplete(nSource);
202 m_ctlPath[2].SetAutoComplete(nSource);
205 String filterNameOrMask = theApp.m_globalFileFilter.GetFilterNameOrMask();
206 BOOL bMask = theApp.m_globalFileFilter.IsUsingMask();
210 String filterPrefix = theApp.LoadString(IDS_FILTER_PREFIX);
211 filterNameOrMask = filterPrefix + filterNameOrMask;
214 int ind = m_ctlExt.FindStringExact(0, filterNameOrMask.c_str());
216 m_ctlExt.SetCurSel(ind);
219 ind = m_ctlExt.InsertString(0, filterNameOrMask.c_str());
221 m_ctlExt.SetCurSel(ind);
223 LogErrorString(_T("Failed to add string to filters combo list!"));
226 if (!GetOptionsMgr()->GetBool(OPT_VERIFY_OPEN_PATHS))
228 m_ctlOk.EnableWindow(TRUE);
229 m_ctlUnpacker.EnableWindow(TRUE);
230 m_ctlSelectUnpacker.EnableWindow(TRUE);
233 UpdateButtonStates();
235 if (!m_bOverwriteRecursive)
236 m_bRecurse = theApp.GetProfileInt(_T("Settings"), _T("Recurse"), 0) == 1;
238 m_strUnpacker = m_infoHandler.pluginName.c_str();
240 SetStatus(IDS_OPEN_FILESDIRS);
241 SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
245 void COpenDlg::OnButton(int index)
251 PATH_EXISTENCE existence = paths_DoesPathExist(m_strPath[index]);
254 case IS_EXISTING_DIR:
255 sfolder = m_strPath[index];
257 case IS_EXISTING_FILE:
258 sfolder = GetPathOnly(m_strPath[index]);
261 // Do nothing, empty foldername will be passed to dialog
264 _RPTF0(_CRT_ERROR, "Invalid return value from paths_DoesPathExist()");
268 if (SelectFileOrFolder(GetSafeHwnd(), s, sfolder.c_str()))
270 m_strPath[index] = s;
271 m_strBrowsePath[index] = s;
273 UpdateButtonStates();
278 * @brief Called when "Browse..." button is selected for first path.
280 void COpenDlg::OnPath0Button()
286 * @brief Called when "Browse..." button is selected for second path.
288 void COpenDlg::OnPath1Button()
294 * @brief Called when "Browse..." button is selected for third path.
296 void COpenDlg::OnPath2Button()
302 * @brief Called when dialog is closed with "OK".
304 * Checks that paths are valid and sets filters.
306 void COpenDlg::OnOK()
308 const String filterPrefix = theApp.LoadString(IDS_FILTER_PREFIX);
313 // If left path is a project-file, load it
315 SplitFilename(m_strPath[0], NULL, NULL, &ext);
316 CString sExt(ext.c_str());
317 if (m_strPath[1].IsEmpty() && sExt.CompareNoCase(PROJECTFILE_EXT) == 0)
318 LoadProjectFile(m_strPath[0]);
322 for (index = 0; index < countof(m_strPath); index++)
324 if (index == 2 && m_strPath[index].IsEmpty())
326 m_files.SetSize(nFiles + 1);
327 m_files[nFiles] = m_strPath[index];
330 m_pathsType = GetPairComparability(m_files, IsArchiveFile);
332 if (m_pathsType == DOES_NOT_EXIST)
334 LangMessageBox(IDS_ERROR_INCOMPARABLE, MB_ICONSTOP);
338 for (index = 0; index < nFiles; index++)
340 // If user has edited path by hand, expand environment variables
341 BOOL bExpand = FALSE;
342 if (m_strBrowsePath[index].CompareNoCase(m_files[index].c_str()) != 0)
345 m_files[index] = paths_GetLongPath(m_files[index].c_str(), bExpand);
347 // Add trailing '\' for directories if its missing
348 if (paths_DoesPathExist(m_files[index].c_str()) == IS_EXISTING_DIR)
350 if (!paths_EndsWithSlash(m_files[index].c_str()))
351 m_files[index] += '\\';
356 KillTimer(IDT_CHECKFILES);
358 String filter((LPCTSTR)m_strExt);
359 filter = string_trim_ws(filter);
361 // If prefix found from start..
362 if (filter.find(filterPrefix, 0) == 0)
364 // Remove prefix + space
365 filter.erase(0, filterPrefix.length());
366 if (!theApp.m_globalFileFilter.SetFilter(filter))
368 // If filtername is not found use default *.* mask
369 theApp.m_globalFileFilter.SetFilter(_T("*.*"));
372 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter.c_str());
376 BOOL bFilterSet = theApp.m_globalFileFilter.SetFilter(filter);
378 m_strExt = theApp.m_globalFileFilter.GetFilterNameOrMask().c_str();
379 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter.c_str());
382 SaveComboboxStates();
383 theApp.WriteProfileInt(_T("Settings"), _T("Recurse"), m_bRecurse);
389 * @brief Called when dialog is closed via Cancel.
391 * Open-dialog is canceled when 'Cancel' button is selected or
392 * Esc-key is pressed. Save combobox states, since user may have
393 * removed items from them and don't want them to re-appear.
395 void COpenDlg::OnCancel()
397 SaveComboboxStates();
402 * @brief Save File- and filter-combobox states.
404 void COpenDlg::SaveComboboxStates()
406 m_ctlPath[0].SaveState(_T("Files\\Left"));
407 m_ctlPath[1].SaveState(_T("Files\\Right"));
408 m_ctlPath[2].SaveState(_T("Files\\Option"));
409 m_ctlExt.SaveState(_T("Files\\Ext"));
412 m_ctlPath[2].GetWindowText(strOption);
413 theApp.WriteProfileInt(_T("Files\\Option"), _T("Empty"), strOption.IsEmpty());
416 struct UpdateButtonStatesThreadParams
422 UINT UpdateButtonStatesThread(LPVOID lpParam)
426 while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
430 if (msg.message != WM_USER)
433 BOOL bButtonEnabled = TRUE;
434 BOOL bInvalid[3] = {FALSE, FALSE, FALSE};
436 int iUnpackerStatusMsgId;
438 UpdateButtonStatesThreadParams *pParams = (UpdateButtonStatesThreadParams *)msg.wParam;
439 PathContext paths = pParams->m_paths;
440 HWND hWnd = pParams->m_hWnd;
443 // Check if we have project file as left side path
444 BOOL bProject = FALSE;
446 SplitFilename(paths[0].c_str(), NULL, NULL, &ext);
447 CString sExt(ext.c_str());
448 if (paths[1].empty() && sExt.CompareNoCase(PROJECTFILE_EXT) == 0)
453 if (paths_DoesPathExist(paths[0].c_str()) == DOES_NOT_EXIST)
455 if (paths_DoesPathExist(paths[1].c_str()) == DOES_NOT_EXIST)
457 if (paths.GetSize() > 2 && paths_DoesPathExist(paths[2].c_str()) == DOES_NOT_EXIST)
461 // Enable buttons as appropriate
462 if (GetOptionsMgr()->GetBool(OPT_VERIFY_OPEN_PATHS))
464 PATH_EXISTENCE pathsType = DOES_NOT_EXIST;
466 if (paths.GetSize() <= 2)
468 if (bInvalid[0] && bInvalid[1])
469 iStatusMsgId = IDS_OPEN_BOTHINVALID;
470 else if (bInvalid[0])
471 iStatusMsgId = IDS_OPEN_LEFTINVALID;
472 else if (bInvalid[1])
473 iStatusMsgId = IDS_OPEN_RIGHTINVALID;
474 else if (!bInvalid[0] && !bInvalid[1])
476 pathsType = GetPairComparability(paths, IsArchiveFile);
477 if (pathsType == DOES_NOT_EXIST)
478 iStatusMsgId = IDS_OPEN_MISMATCH;
480 iStatusMsgId = IDS_OPEN_FILESDIRS;
485 if (bInvalid[0] && bInvalid[1] && bInvalid[2])
486 iStatusMsgId = IDS_OPEN_ALLINVALID;
487 else if (!bInvalid[0] && bInvalid[1] && bInvalid[2])
488 iStatusMsgId = IDS_OPEN_MIDDLERIGHTINVALID;
489 else if (bInvalid[0] && !bInvalid[1] && bInvalid[2])
490 iStatusMsgId = IDS_OPEN_LEFTRIGHTINVALID;
491 else if (!bInvalid[0] && !bInvalid[1] && bInvalid[2])
492 iStatusMsgId = IDS_OPEN_RIGHTINVALID;
493 else if (bInvalid[0] && bInvalid[1] && !bInvalid[2])
494 iStatusMsgId = IDS_OPEN_LEFTMIDDLEINVALID;
495 else if (!bInvalid[0] && bInvalid[1] && !bInvalid[2])
496 iStatusMsgId = IDS_OPEN_MIDDLEINVALID;
497 else if (bInvalid[0] && !bInvalid[1] && !bInvalid[2])
498 iStatusMsgId = IDS_OPEN_LEFTINVALID;
499 else if (!bInvalid[0] && !bInvalid[1] && !bInvalid[2])
501 pathsType = GetPairComparability(paths, IsArchiveFile);
502 if (pathsType == DOES_NOT_EXIST)
503 iStatusMsgId = IDS_OPEN_MISMATCH;
505 iStatusMsgId = IDS_OPEN_FILESDIRS;
508 if (pathsType == IS_EXISTING_FILE || bProject)
509 iUnpackerStatusMsgId = 0; //Empty field
511 iUnpackerStatusMsgId = IDS_OPEN_UNPACKERDISABLED;
514 bButtonEnabled = TRUE;
516 bButtonEnabled = (pathsType != DOES_NOT_EXIST);
519 PostMessage(hWnd, WM_USER + 1, bButtonEnabled, MAKELPARAM(iStatusMsgId, iUnpackerStatusMsgId));
526 * @brief Enable/disable components based on validity of paths.
528 void COpenDlg::UpdateButtonStates()
530 UpdateData(TRUE); // load member variables from screen
531 KillTimer(IDT_CHECKFILES);
534 if (!m_pUpdateButtonStatusThread)
536 m_pUpdateButtonStatusThread = AfxBeginThread(
537 UpdateButtonStatesThread, NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
538 m_pUpdateButtonStatusThread->m_bAutoDelete = FALSE;
539 m_pUpdateButtonStatusThread->ResumeThread();
540 while (PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_NULL, 0, 0) == FALSE)
544 UpdateButtonStatesThreadParams *pParams = new UpdateButtonStatesThreadParams;
545 pParams->m_hWnd = this->m_hWnd;
546 if (m_strPath[2].IsEmpty())
547 pParams->m_paths = PathContext(m_strPath[0], m_strPath[1]);
549 pParams->m_paths = PathContext(m_strPath[0], m_strPath[1], m_strPath[2]);
551 PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_USER, (WPARAM)pParams, 0);
554 void COpenDlg::TerminateThreadIfRunning()
556 if (!m_pUpdateButtonStatusThread)
559 PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_QUIT, 0, 0);
560 DWORD dwResult = WaitForSingleObject(m_pUpdateButtonStatusThread->m_hThread, 100);
561 if (dwResult != WAIT_OBJECT_0)
563 m_pUpdateButtonStatusThread->SuspendThread();
564 TerminateThread(m_pUpdateButtonStatusThread->m_hThread, 0);
566 delete m_pUpdateButtonStatusThread;
567 m_pUpdateButtonStatusThread = NULL;
571 * @brief Called when user changes selection in left/middle/right path's combo box.
573 void COpenDlg::OnSelchangeCombo(int index)
575 int sel = m_ctlPath[index].GetCurSel();
578 m_ctlPath[index].GetLBText(sel, m_strPath[index]);
579 m_ctlPath[index].SetWindowText(m_strPath[index]);
582 UpdateButtonStates();
585 void COpenDlg::OnSelchangePath0Combo()
590 void COpenDlg::OnSelchangePath1Combo()
595 void COpenDlg::OnSelchangePath2Combo()
601 * @brief Called every time paths are edited.
603 void COpenDlg::OnEditEvent()
605 // (Re)start timer to path validity check delay
606 // If timer starting fails, update buttonstates immediately
607 if (!SetTimer(IDT_CHECKFILES, CHECKFILES_TIMEOUT, NULL))
608 UpdateButtonStates();
612 * @brief Handle timer events.
613 * Checks if paths are valid and sets control states accordingly.
614 * @param [in] nIDEvent Timer ID that fired.
616 void COpenDlg::OnTimer(UINT_PTR nIDEvent)
618 if (nIDEvent == IDT_CHECKFILES)
619 UpdateButtonStates();
621 CDialog::OnTimer(nIDEvent);
625 * @brief Called when users selects plugin browse button.
627 void COpenDlg::OnSelectUnpacker()
633 for (index = 0; index < countof(m_strPath); index++)
635 if (index == 2 && m_strPath[index].IsEmpty())
637 m_files.SetSize(nFiles + 1);
638 m_files[nFiles] = m_strPath[index];
641 m_pathsType = GetPairComparability(m_files);
643 if (m_pathsType != IS_EXISTING_FILE)
646 // let the user select a handler
647 CSelectUnpackerDlg dlg(m_files[0].c_str(), this);
648 dlg.SetInitialInfoHandler(&m_infoHandler);
650 if (dlg.DoModal() == IDOK)
652 m_infoHandler = dlg.GetInfoHandler();
654 m_strUnpacker = m_infoHandler.pluginName.c_str();
660 LRESULT COpenDlg::OnUpdateStatus(WPARAM wParam, LPARAM lParam)
662 BOOL bEnabledButtons = (BOOL)wParam;
664 m_ctlOk.EnableWindow(bEnabledButtons);
665 m_ctlUnpacker.EnableWindow(bEnabledButtons);
666 m_ctlSelectUnpacker.EnableWindow(bEnabledButtons);
668 SetStatus(HIWORD(lParam));
669 SetStatus(LOWORD(lParam));
675 * @brief Sets the path status text.
676 * The open dialog shows a status text of selected paths. This function
677 * is used to set that status text.
678 * @param [in] msgID Resource ID of status text to set.
680 void COpenDlg::SetStatus(UINT msgID)
682 String msg = theApp.LoadString(msgID);
683 SetDlgItemText(IDC_OPEN_STATUS, msg.c_str());
687 * @brief Set the plugin edit box text.
688 * Plugin edit box is at the same time a plugin status view. This function
689 * sets the status text.
690 * @param [in] msgID Resource ID of status text to set.
692 void COpenDlg::SetUnpackerStatus(UINT msgID)
694 String msg = theApp.LoadString(msgID);
695 SetDlgItemText(IDC_UNPACKER_EDIT, msg.c_str());
699 * @brief Called when "Select..." button for filters is selected.
701 void COpenDlg::OnSelectFilter()
703 String filterPrefix = theApp.LoadString(IDS_FILTER_PREFIX);
706 const BOOL bUseMask = theApp.m_globalFileFilter.IsUsingMask();
707 GetDlgItemText(IDC_EXT_COMBO, curFilter);
708 curFilter.TrimLeft();
709 curFilter.TrimRight();
711 GetMainFrame()->SelectFilter();
713 String filterNameOrMask = theApp.m_globalFileFilter.GetFilterNameOrMask();
714 if (theApp.m_globalFileFilter.IsUsingMask())
716 // If we had filter chosen and now has mask we can overwrite filter
717 if (!bUseMask || curFilter[0] != '*')
719 SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask.c_str());
724 filterNameOrMask = filterPrefix + filterNameOrMask;
725 SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask.c_str());
731 * @brief Read paths and filter from project file.
732 * Reads the given project file. After the file is read, found paths and
733 * filter is updated to dialog GUI. Other possible settings found in the
734 * project file are kept in memory and used later when loading paths
736 * @param [in] path Path to the project file.
737 * @return TRUE if the project file was successfully loaded, FALSE otherwise.
739 BOOL COpenDlg::LoadProjectFile(const CString &path)
741 String filterPrefix = theApp.LoadString(IDS_FILTER_PREFIX);
744 m_pProjectFile = new ProjectFile;
745 if (m_pProjectFile == NULL)
748 if (!m_pProjectFile->Read(path, &err))
753 LangFormatString2(msg, IDS_ERROR_FILEOPEN, path, err.c_str());
754 AfxMessageBox(msg, MB_ICONSTOP);
760 m_pProjectFile->GetPaths(m_files, m_bRecurse);
761 if (m_pProjectFile->HasFilter())
763 m_strExt = m_pProjectFile->GetFilter().c_str();
765 m_strExt.TrimRight();
766 if (m_strExt[0] != '*')
767 m_strExt.Insert(0, filterPrefix.c_str());
774 * @brief Removes whitespaces from left and right paths
775 * @note Assumes UpdateData(TRUE) is called before this function.
777 void COpenDlg::TrimPaths()
779 for (int index = 0; index < countof(m_strPath); index++)
781 m_strPath[index].TrimLeft();
782 m_strPath[index].TrimRight();
787 * @brief Update control states when dialog is activated.
789 * Update control states when user re-activates dialog. User might have
790 * switched for other program to e.g. update files/folders and then
791 * swiches back to WinMerge. Its nice to see WinMerge detects updated
794 void COpenDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
796 CDialog::OnActivate(nState, pWndOther, bMinimized);
798 if (nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
799 UpdateButtonStates();
803 * @brief Open help from mainframe when user presses F1.
805 void COpenDlg::OnHelp()
807 GetMainFrame()->ShowHelp(OpenDlgHelpLocation);
810 /////////////////////////////////////////////////////////////////////////////
812 // OnDropFiles code from CDropEdit
813 // Copyright 1997 Chris Losinger
815 // shortcut expansion code modified from :
816 // CShortcut, 1996 Rob Warner
820 * @brief Drop paths(s) to the dialog.
821 * One or two paths can be dropped to the dialog. The behaviour is:
823 * - drop to empty path edit box (check left first)
824 * - if both boxes have a path, drop to left path
826 * - overwrite both paths, empty or not
827 * @param [in] dropInfo Dropped data, including paths.
829 void COpenDlg::OnDropFiles(HDROP dropInfo)
831 // Get the number of pathnames that have been dropped
832 UINT wNumFilesDropped = DragQueryFile(dropInfo, 0xFFFFFFFF, NULL, 0);
836 // get all file names. but we'll only need the first one.
837 for (WORD x = 0 ; x < wNumFilesDropped; x++)
839 // Get the number of bytes required by the file's full pathname
840 UINT wPathnameSize = DragQueryFile(dropInfo, x, NULL, 0);
842 // Allocate memory to contain full pathname & zero byte
844 LPTSTR npszFile = (TCHAR *) new TCHAR[wPathnameSize];
846 // If not enough memory, skip this one
847 if (npszFile == NULL)
850 // Copy the pathname into the buffer
851 DragQueryFile(dropInfo, x, npszFile, wPathnameSize);
861 // Free the memory block containing the dropped-file information
862 DragFinish(dropInfo);
864 for (UINT i = 0; i < fileCount; i++)
866 if (paths_IsShortcut((LPCTSTR)files[i]))
868 // if this was a shortcut, we need to expand it to the target path
869 CString expandedFile = ExpandShortcut((LPCTSTR)files[i]).c_str();
871 // if that worked, we should have a real file name
872 if (!expandedFile.IsEmpty())
873 files[i] = expandedFile;
877 // Add dropped paths to the dialog
881 m_strPath[0] = files[0];
882 m_strPath[1] = files[1];
883 m_strPath[2] = files[2];
885 UpdateButtonStates();
887 else if (fileCount == 2)
889 m_strPath[0] = files[0];
890 m_strPath[1] = files[1];
892 UpdateButtonStates();
894 else if (fileCount == 1)
896 if (m_strPath[0].IsEmpty())
897 m_strPath[0] = files[0];
898 else if (m_strPath[1].IsEmpty())
899 m_strPath[1] = files[0];
900 else if (m_strPath[2].IsEmpty())
901 m_strPath[2] = files[0];
903 m_strPath[0] = files[0];
905 UpdateButtonStates();