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 // RCS ID line follows -- this is updated by CVS
32 #include "coretools.h"
34 #include "SelectUnpackerDlg.h"
35 #include "OptionsDef.h"
41 static char THIS_FILE[] = __FILE__;
44 // Timer ID and timeout for delaying path validity check
45 const UINT IDT_CHECKFILES = 1;
46 const UINT CHECKFILES_TIMEOUT = 1000; // milliseconds
47 static const TCHAR EMPTY_EXTENSION[] = _T(".*");
49 /////////////////////////////////////////////////////////////////////////////
53 COpenDlg::COpenDlg(CWnd* pParent /*=NULL*/)
54 : CDialog(COpenDlg::IDD, pParent)
56 //{{AFX_DATA_INIT(COpenDlg)
61 m_strUnpacker = _T("");
64 m_pathsType = DOES_NOT_EXIST;
65 m_bFileFilterSelected = FALSE;
66 m_bOverwriteRecursive = FALSE;
69 void COpenDlg::DoDataExchange(CDataExchange* pDX)
71 CDialog::DoDataExchange(pDX);
72 //{{AFX_DATA_MAP(COpenDlg)
73 DDX_Control(pDX, IDC_SELECT_UNPACKER, m_ctlSelectUnpacker);
74 DDX_Control(pDX, IDC_UNPACKER_EDIT, m_ctlUnpacker);
75 DDX_Control(pDX, IDC_EXT_COMBO, m_ctlExt);
76 DDX_Control(pDX, IDOK, m_ctlOk);
77 DDX_Control(pDX, IDC_RECURS_CHECK, m_ctlRecurse);
78 DDX_Control(pDX, IDC_RIGHT_COMBO, m_ctlRight);
79 DDX_Control(pDX, IDC_LEFT_COMBO, m_ctlLeft);
80 DDX_CBStringExact(pDX, IDC_LEFT_COMBO, m_strLeft);
81 DDX_CBStringExact(pDX, IDC_RIGHT_COMBO, m_strRight);
82 DDX_Check(pDX, IDC_RECURS_CHECK, m_bRecurse);
83 DDX_CBStringExact(pDX, IDC_EXT_COMBO, m_strExt);
84 DDX_Text(pDX, IDC_UNPACKER_EDIT, m_strUnpacker);
89 BEGIN_MESSAGE_MAP(COpenDlg, CDialog)
90 //{{AFX_MSG_MAP(COpenDlg)
91 ON_BN_CLICKED(IDC_LEFT_BUTTON, OnLeftButton)
92 ON_BN_CLICKED(IDC_RIGHT_BUTTON, OnRightButton)
93 ON_CBN_SELCHANGE(IDC_LEFT_COMBO, OnSelchangeLeftCombo)
94 ON_CBN_SELCHANGE(IDC_RIGHT_COMBO, OnSelchangeRightCombo)
95 ON_CBN_EDITCHANGE(IDC_LEFT_COMBO, OnEditEvent)
96 ON_BN_CLICKED(IDC_SELECT_UNPACKER, OnSelectUnpacker)
97 ON_CBN_SELENDCANCEL(IDC_LEFT_COMBO, UpdateButtonStates)
98 ON_CBN_EDITCHANGE(IDC_RIGHT_COMBO, OnEditEvent)
99 ON_CBN_SELENDCANCEL(IDC_RIGHT_COMBO, UpdateButtonStates)
101 ON_BN_CLICKED(IDC_SELECT_FILTER, OnSelectFilter)
105 /////////////////////////////////////////////////////////////////////////////
106 // COpenDlg message handlers
109 * @brief Called when "Browse..." button is selected for left path.
111 void COpenDlg::OnLeftButton()
114 CString sfolder, sname;
119 VERIFY(dirSelTag.LoadString(IDS_DIRSEL_TAG));
120 if (CFile::GetStatus(m_strLeft, status)
121 && (status.m_attribute & CFile::Attribute::directory))
124 sfolder = GetPathOnly(m_strLeft);
125 if (SelectFile(s, sfolder))
127 SplitFilename(s, &sfolder, &sname, 0);
128 if (sname == dirSelTag)
130 m_strLeft = sfolder + '\\';
135 UpdateButtonStates();
140 * @brief Called when "Browse..." button is selected for right path.
142 void COpenDlg::OnRightButton()
145 CString sfolder, sname;
150 VERIFY(dirSelTag.LoadString(IDS_DIRSEL_TAG));
151 if (CFile::GetStatus(m_strRight, status)
152 && (status.m_attribute & CFile::Attribute::directory))
153 sfolder = m_strRight;
155 sfolder = GetPathOnly(m_strRight);
156 if (SelectFile(s, sfolder))
158 SplitFilename(s, &sfolder, &sname, 0);
159 if (sname == dirSelTag)
160 m_strRight = sfolder + '\\';
164 UpdateButtonStates();
169 * @brief Called when dialog is closed with "OK".
171 * Checks that paths are valid and sets filters.
173 void COpenDlg::OnOK()
175 CString filterPrefix;
176 VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
180 m_pathsType = GetPairComparability(m_strLeft, m_strRight);
182 if (m_pathsType == DOES_NOT_EXIST)
184 AfxMessageBox(IDS_ERROR_INCOMPARABLE, MB_ICONSTOP);
188 m_strRight = paths_GetLongPath(m_strRight, DIRSLASH);
189 m_strLeft = paths_GetLongPath(m_strLeft, DIRSLASH);
192 KillTimer(IDT_CHECKFILES);
194 // If prefix found from start..
195 if (m_strExt.Find(filterPrefix, 0) == 0)
197 // Remove prefix + space
198 m_strExt.Delete(0, filterPrefix.GetLength());
199 CString path = theApp.m_globalFileFilter.GetFileFilterPath(m_strExt);
200 m_bFileFilterSelected = TRUE;
201 theApp.m_globalFileFilter.SetFileFilterPath(path);
202 theApp.m_globalFileFilter.UseMask(FALSE);
207 VERIFY(strNone.LoadString(IDS_USERCHOICE_NONE));
208 theApp.m_globalFileFilter.SetFileFilterPath(strNone);
209 theApp.m_globalFileFilter.SetMask(m_strExt);
210 m_bFileFilterSelected = FALSE;
211 theApp.m_globalFileFilter.UseMask(TRUE);
214 m_ctlLeft.SaveState(_T("Files\\Left"));
215 m_ctlRight.SaveState(_T("Files\\Right"));
216 m_ctlExt.SaveState(_T("Files\\Ext"));
218 theApp.WriteProfileInt(_T("Settings"), _T("Recurse"), m_bRecurse);
223 /** @brief Handler for WM_INITDIALOG; conventional location to initialize controls */
224 BOOL COpenDlg::OnInitDialog()
226 CDialog::OnInitDialog();
228 // Note: LoadImage gets shared icon, its not needed to destroy
229 HICON hMergeIcon = (HICON) LoadImage(AfxGetInstanceHandle(),
230 MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16, 16,
231 LR_DEFAULTSIZE | LR_SHARED);
232 SetIcon(hMergeIcon, TRUE);
234 // setup handler for resizing this dialog
235 m_constraint.InitializeCurrentSize(this);
236 // configure how individual controls adjust when dialog resizes
237 m_constraint.ConstrainItem(IDC_LEFT_COMBO, 0, 1, 0, 0); // grows right
238 m_constraint.ConstrainItem(IDC_RIGHT_COMBO, 0, 1, 0, 0); // grows right
239 m_constraint.ConstrainItem(IDC_EXT_COMBO, 0, 1, 0, 0); // grows right
240 m_constraint.ConstrainItem(IDC_UNPACKER_EDIT, 0, 1, 0, 0); // grows right
241 m_constraint.ConstrainItem(IDC_FILES_DIRS_GROUP, 0, 1, 0, 0); // grows right
242 m_constraint.ConstrainItem(IDC_LEFT_BUTTON, 1, 0, 0, 0); // slides right
243 m_constraint.ConstrainItem(IDC_RIGHT_BUTTON, 1, 0, 0, 0); // slides right
244 m_constraint.ConstrainItem(IDC_SELECT_UNPACKER, 1, 0, 0, 0); // slides right
245 m_constraint.ConstrainItem(IDC_OPEN_STATUS, 0, 1, 0, 0); // grows right
246 m_constraint.ConstrainItem(IDC_SELECT_FILTER, 1, 0, 0, 0); // slides right
247 m_constraint.ConstrainItem(IDOK, 1, 0, 0, 0); // slides right
248 m_constraint.ConstrainItem(IDCANCEL, 1, 0, 0, 0); // slides right
249 m_constraint.DisallowHeightGrowth();
250 m_constraint.SubclassWnd(); // install subclassing
251 m_constraint.LoadPosition(_T("OpenDlg"), false); // persist size via registry
255 m_ctlLeft.LoadState(_T("Files\\Left"));
256 m_ctlRight.LoadState(_T("Files\\Right"));
257 m_ctlExt.LoadState(_T("Files\\Ext"));
258 UpdateData(m_strLeft.IsEmpty() && m_strRight.IsEmpty());
260 // Select active filter from filter list or add it to list
261 CString filterPrefix;
262 VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
264 VERIFY(strNone.LoadString(IDS_USERCHOICE_NONE));
265 CString filterFile, filterExt;
266 CString filterPath = theApp.m_globalFileFilter.GetFileFilterPath();
267 SplitFilename(filterPath, NULL, &filterFile, &filterExt);
268 filterFile += _T(".");
269 filterFile += filterExt;
271 if (filterFile.Find(strNone, 0) > -1)
272 // No filter selected, select last used extension
273 m_ctlExt.SetCurSel(0);
276 // Filter selected, search filter first from list, add if not found
277 m_bFileFilterSelected = TRUE;
278 CString filterName = theApp.m_globalFileFilter.GetFileFilterName(filterPath);
279 filterName.Insert(0, filterPrefix);
280 int ind = m_ctlExt.FindStringExact(0, filterName);
282 m_ctlExt.SetCurSel(ind);
285 ind = m_ctlExt.InsertString(0, filterName);
287 m_ctlExt.SetCurSel(ind);
289 LogErrorString(_T("Failed to add string to filters combo list!"));
293 if (!mf->m_options.GetInt(OPT_VERIFY_OPEN_PATHS))
295 m_ctlOk.EnableWindow(TRUE);
296 m_ctlUnpacker.EnableWindow(TRUE);
297 m_ctlSelectUnpacker.EnableWindow(TRUE);
300 UpdateButtonStates();
302 if (!m_bOverwriteRecursive)
303 m_bRecurse = theApp.GetProfileInt(_T("Settings"), _T("Recurse"), 0) == 1;
305 m_strUnpacker = m_infoHandler.pluginName;
307 SetStatus(IDS_OPEN_FILESDIRS);
308 SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
313 * @brief Enable/disable components based on validity of paths.
315 void COpenDlg::UpdateButtonStates()
317 BOOL bLeftInvalid = FALSE;
318 BOOL bRightInvalid = FALSE;
320 UpdateData(TRUE); // load member variables from screen
321 KillTimer(IDT_CHECKFILES);
323 // Enable buttons as appropriate
324 PATH_EXISTENCE pathsType = GetPairComparability(m_strLeft, m_strRight);
325 if (mf->m_options.GetInt(OPT_VERIFY_OPEN_PATHS))
327 m_ctlOk.EnableWindow(pathsType != DOES_NOT_EXIST);
328 m_ctlUnpacker.EnableWindow(pathsType == IS_EXISTING_FILE);
329 m_ctlSelectUnpacker.EnableWindow(pathsType == IS_EXISTING_FILE);
332 if (paths_DoesPathExist(m_strLeft) == DOES_NOT_EXIST)
334 if (paths_DoesPathExist(m_strRight) == DOES_NOT_EXIST)
335 bRightInvalid = TRUE;
337 if (bLeftInvalid && bRightInvalid)
338 SetStatus(IDS_OPEN_BOTHINVALID);
339 else if (bLeftInvalid)
340 SetStatus(IDS_OPEN_LEFTINVALID);
341 else if (bRightInvalid)
342 SetStatus(IDS_OPEN_RIGHTINVALID);
343 else if (!bLeftInvalid && !bRightInvalid && pathsType == DOES_NOT_EXIST)
344 SetStatus(IDS_OPEN_MISMATCH);
346 SetStatus(IDS_OPEN_FILESDIRS);
348 if (pathsType == IS_EXISTING_FILE)
349 SetUnpackerStatus(0); //Empty field
351 SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
355 * @brief Shows file/folder selection dialog.
357 BOOL COpenDlg::SelectFile(CString& path, LPCTSTR pszFolder)
362 VERIFY(dirSelTag.LoadString(IDS_DIRSEL_TAG));
363 VERIFY(s.LoadString(IDS_ALLFILES));
364 DWORD flags = OFN_NOTESTFILECREATE | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
365 CFileDialog pdlg(TRUE, NULL, dirSelTag, flags, s);
367 VERIFY(title.LoadString(IDS_OPEN_TITLE));
368 pdlg.m_ofn.lpstrTitle = title;
369 pdlg.m_ofn.lpstrInitialDir = pszFolder;
371 if (pdlg.DoModal()==IDOK)
373 path = pdlg.GetPathName();
382 void COpenDlg::OnSelchangeLeftCombo()
384 int sel = m_ctlLeft.GetCurSel();
387 m_ctlLeft.GetLBText(sel, m_strLeft);
388 m_ctlLeft.SetWindowText(m_strLeft);
391 UpdateButtonStates();
394 void COpenDlg::OnSelchangeRightCombo()
396 int sel = m_ctlRight.GetCurSel();
399 m_ctlRight.GetLBText(sel, m_strRight);
400 m_ctlRight.SetWindowText(m_strRight);
403 UpdateButtonStates();
406 void COpenDlg::RemoveTrailingSlash(CString & s)
408 // Do not remove slash from "X:\"
409 if (s[s.GetLength()-2] == _T(':'))
411 while (s.Right(1) == _T('\\') || s.Right(1) == _T('/'))
412 s.Delete(s.GetLength()-1);
416 * @brief Called every time paths are edited.
418 void COpenDlg::OnEditEvent()
420 // (Re)start timer to path validity check delay
421 // If timer starting fails, update buttonstates immediately
422 if (!SetTimer(IDT_CHECKFILES, CHECKFILES_TIMEOUT, NULL))
423 UpdateButtonStates();
426 void COpenDlg::OnTimer(UINT nIDEvent)
428 if (nIDEvent == IDT_CHECKFILES)
429 UpdateButtonStates();
431 CDialog::OnTimer(nIDEvent);
434 void COpenDlg::OnSelectUnpacker()
438 m_pathsType = GetPairComparability(m_strLeft, m_strRight);
440 if (m_pathsType != IS_EXISTING_FILE)
443 // let the user select a handler
444 CSelectUnpackerDlg dlg(m_strLeft, m_strRight, this);
445 dlg.SetInitialInfoHandler(&m_infoHandler);
447 if (dlg.DoModal() == IDOK)
449 m_infoHandler = dlg.GetInfoHandler();
451 m_strUnpacker = m_infoHandler.pluginName;
457 void COpenDlg::SetStatus(UINT msgID)
461 VERIFY(msg.LoadString(msgID));
462 SetDlgItemText(IDC_OPEN_STATUS, msg);
465 void COpenDlg::SetUnpackerStatus(UINT msgID)
469 VERIFY(msg.LoadString(msgID));
470 SetDlgItemText(IDC_UNPACKER_EDIT, msg);
474 * @brief Called when "Select..." button for filters is selected.
476 void COpenDlg::OnSelectFilter()
480 CString filterPrefix;
481 VERIFY(strNone.LoadString(IDS_USERCHOICE_NONE));
482 VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
485 CString filter = theApp.m_globalFileFilter.GetFileFilterPath();
487 if (filter == strNone)
488 SetDlgItemText(IDC_EXT_COMBO, _T("*.*"));
491 filterName = theApp.m_globalFileFilter.GetFileFilterName(filter);
492 filterName.Insert(0, filterPrefix);
493 SetDlgItemText(IDC_EXT_COMBO, filterName);
498 * @brief Move Open-dialog to center of MainFrame
500 void COpenDlg::CenterToMainFrame()
504 mf->GetWindowRect(&rectFrame);
505 GetClientRect(&rectBar);
506 // Middlepoint of MainFrame
507 int x = rectFrame.left + (rectFrame.right - rectFrame.left) / 2;
508 int y = rectFrame.top + (rectFrame.bottom - rectFrame.top) / 2;
509 // Reduce by half of dialog's size
510 x -= rectBar.right / 2;
511 y -= rectBar.bottom / 2;
513 SetWindowPos(&CWnd::wndTop, x, y, rectBar.right,
514 rectBar.bottom, SWP_NOOWNERZORDER | SWP_NOSIZE );