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 COpenView class
30 #include <sys/types.h>
32 #include "UnicodeString.h"
35 #include "ProjectFile.h"
37 #include "SelectUnpackerDlg.h"
38 #include "OptionsDef.h"
40 #include "OptionsMgr.h"
41 #include "FileOrFolderSelect.h"
43 #include "Constants.h"
46 #include "FileFilterHelper.h"
52 static char THIS_FILE[] = __FILE__;
55 // Timer ID and timeout for delaying path validity check
56 const UINT IDT_CHECKFILES = 1;
57 const UINT CHECKFILES_TIMEOUT = 1000; // milliseconds
58 static const TCHAR EMPTY_EXTENSION[] = _T(".*");
60 /** @brief Location for Open-dialog specific help to open. */
61 static TCHAR OpenDlgHelpLocation[] = _T("::/htmlhelp/Open_paths.html");
65 IMPLEMENT_DYNCREATE(COpenView, CFormView)
67 BEGIN_MESSAGE_MAP(COpenView, CFormView)
68 //{{AFX_MSG_MAP(COpenView)
69 ON_BN_CLICKED(IDC_PATH0_BUTTON, OnPath0Button)
70 ON_BN_CLICKED(IDC_PATH1_BUTTON, OnPath1Button)
71 ON_BN_CLICKED(IDC_PATH2_BUTTON, OnPath2Button)
72 ON_BN_CLICKED(IDC_SWAP01_BUTTON, (OnSwapButton<IDC_PATH0_COMBO, IDC_PATH1_COMBO>))
73 ON_BN_CLICKED(IDC_SWAP12_BUTTON, (OnSwapButton<IDC_PATH1_COMBO, IDC_PATH2_COMBO>))
74 ON_BN_CLICKED(IDC_SWAP02_BUTTON, (OnSwapButton<IDC_PATH0_COMBO, IDC_PATH2_COMBO>))
75 ON_CBN_SELCHANGE(IDC_PATH0_COMBO, OnSelchangePath0Combo)
76 ON_CBN_SELCHANGE(IDC_PATH1_COMBO, OnSelchangePath1Combo)
77 ON_CBN_SELCHANGE(IDC_PATH2_COMBO, OnSelchangePath2Combo)
78 ON_CBN_EDITCHANGE(IDC_PATH0_COMBO, OnEditEvent)
79 ON_CBN_EDITCHANGE(IDC_PATH1_COMBO, OnEditEvent)
80 ON_CBN_EDITCHANGE(IDC_PATH2_COMBO, OnEditEvent)
81 ON_BN_CLICKED(IDC_SELECT_UNPACKER, OnSelectUnpacker)
82 ON_CBN_SELENDCANCEL(IDC_PATH0_COMBO, UpdateButtonStates)
83 ON_CBN_SELENDCANCEL(IDC_PATH1_COMBO, UpdateButtonStates)
84 ON_CBN_SELENDCANCEL(IDC_PATH2_COMBO, UpdateButtonStates)
85 ON_NOTIFY_RANGE(CBEN_BEGINEDIT, IDC_PATH0_COMBO, IDC_PATH2_COMBO, OnSetfocusPathCombo)
87 ON_BN_CLICKED(IDC_SELECT_FILTER, OnSelectFilter)
89 ON_COMMAND(IDOK, OnOK)
90 ON_COMMAND(IDCANCEL, OnCancel)
91 ON_COMMAND(ID_HELP, OnHelp)
92 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
93 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
94 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
95 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
97 ON_MESSAGE(WM_USER + 1, OnUpdateStatus)
100 ON_WM_WINDOWPOSCHANGED()
106 // COpenView construction/destruction
108 COpenView::COpenView()
109 : CFormView(COpenView::IDD)
110 , m_pUpdateButtonStatusThread(NULL)
113 m_bAutoCompleteReady[0] = false;
114 m_bAutoCompleteReady[1] = false;
115 m_bAutoCompleteReady[2] = false;
116 memset(m_dwFlags, 0, sizeof(m_dwFlags));
119 COpenView::~COpenView()
121 TerminateThreadIfRunning();
124 void COpenView::DoDataExchange(CDataExchange* pDX)
126 CFormView::DoDataExchange(pDX);
127 //{{AFX_DATA_MAP(COpenView)
128 DDX_Control(pDX, IDC_SELECT_UNPACKER, m_ctlSelectUnpacker);
129 DDX_Control(pDX, IDC_UNPACKER_EDIT, m_ctlUnpacker);
130 DDX_Control(pDX, IDC_EXT_COMBO, m_ctlExt);
131 DDX_Control(pDX, IDOK, m_ctlOk);
132 DDX_Control(pDX, IDC_RECURS_CHECK, m_ctlRecurse);
133 DDX_Control(pDX, IDC_PATH0_COMBO, m_ctlPath[0]);
134 DDX_Control(pDX, IDC_PATH1_COMBO, m_ctlPath[1]);
135 DDX_Control(pDX, IDC_PATH2_COMBO, m_ctlPath[2]);
136 DDX_CBStringExact(pDX, IDC_PATH0_COMBO, m_strPath[0]);
137 DDX_CBStringExact(pDX, IDC_PATH1_COMBO, m_strPath[1]);
138 DDX_CBStringExact(pDX, IDC_PATH2_COMBO, m_strPath[2]);
139 DDX_Check(pDX, IDC_RECURS_CHECK, m_bRecurse);
140 DDX_CBStringExact(pDX, IDC_EXT_COMBO, m_strExt);
141 DDX_Text(pDX, IDC_UNPACKER_EDIT, m_strUnpacker);
145 BOOL COpenView::PreCreateWindow(CREATESTRUCT& cs)
147 // TODO: Modify the Window class or styles here by modifying
148 // the CREATESTRUCT cs
149 cs.style &= ~WS_BORDER;
150 cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
151 return CFormView::PreCreateWindow(cs);
154 void COpenView::OnInitialUpdate()
156 m_sizeOrig = GetTotalSize();
158 theApp.TranslateDialog(m_hWnd);
160 if (!m_picture.Load(IDR_LOGO))
163 CFormView::OnInitialUpdate();
166 // set caption to "swap paths" button
168 GetDlgItem(IDC_SWAP01_BUTTON)->GetFont()->GetObject(sizeof(lf), &lf);
169 lf.lfCharSet = SYMBOL_CHARSET;
170 lstrcpy(lf.lfFaceName, _T("Wingdings"));
171 m_fontSwapButton.CreateFontIndirect(&lf);
172 const int ids[] = {IDC_SWAP01_BUTTON, IDC_SWAP12_BUTTON, IDC_SWAP02_BUTTON};
173 for (int i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i)
175 GetDlgItem(ids[i])->SetFont(&m_fontSwapButton);
176 SetDlgItemText(ids[i], _T("\xf4"));
179 m_constraint.InitializeCurrentSize(this);
180 m_constraint.InitializeSpecificSize(this, m_sizeOrig.cx, m_sizeOrig.cy);
181 m_constraint.SetMaxSizePixels(-1, m_sizeOrig.cy);
182 m_constraint.SetScrollScale(this, 1.0, 1.0);
183 m_constraint.SetSizeGrip(prdlg::CMoveConstraint::SG_NONE);
184 // configure how individual controls adjust when dialog resizes
185 m_constraint.ConstrainItem(IDC_PATH0_COMBO, 0, 1, 0, 0); // grows right
186 m_constraint.ConstrainItem(IDC_PATH1_COMBO, 0, 1, 0, 0); // grows right
187 m_constraint.ConstrainItem(IDC_PATH2_COMBO, 0, 1, 0, 0); // grows right
188 m_constraint.ConstrainItem(IDC_EXT_COMBO, 0, 1, 0, 0); // grows right
189 m_constraint.ConstrainItem(IDC_UNPACKER_EDIT, 0, 1, 0, 0); // grows right
190 m_constraint.ConstrainItem(IDC_FILES_DIRS_GROUP, 0, 1, 0, 0); // grows right
191 m_constraint.ConstrainItem(IDC_PATH0_BUTTON, 1, 0, 0, 0); // slides right
192 m_constraint.ConstrainItem(IDC_PATH1_BUTTON, 1, 0, 0, 0); // slides right
193 m_constraint.ConstrainItem(IDC_PATH2_BUTTON, 1, 0, 0, 0); // slides right
194 m_constraint.ConstrainItem(IDC_SWAP01_BUTTON, 1, 0, 0, 0); // slides right
195 m_constraint.ConstrainItem(IDC_SWAP12_BUTTON, 1, 0, 0, 0); // slides right
196 m_constraint.ConstrainItem(IDC_SWAP02_BUTTON, 1, 0, 0, 0); // slides right
197 m_constraint.ConstrainItem(IDC_SELECT_UNPACKER, 1, 0, 0, 0); // slides right
198 m_constraint.ConstrainItem(IDC_OPEN_STATUS, 0, 1, 0, 0); // grows right
199 m_constraint.ConstrainItem(IDC_SELECT_FILTER, 1, 0, 0, 0); // slides right
200 m_constraint.ConstrainItem(IDOK, 1, 0, 0, 0); // slides right
201 m_constraint.ConstrainItem(IDCANCEL, 1, 0, 0, 0); // slides right
202 m_constraint.ConstrainItem(ID_HELP, 1, 0, 0, 0); // slides right
203 m_constraint.DisallowHeightGrowth();
204 //m_constraint.SubclassWnd(); // install subclassing
206 m_constraint.LoadPosition(_T("ResizeableDialogs"), _T("OpenView"), false); // persist size via registry
207 m_constraint.UpdateSizes();
209 COpenDoc *pDoc = GetDocument();
212 GetWindowText(strTitle);
213 pDoc->SetTitle(strTitle);
215 m_files = pDoc->m_files;
216 m_bRecurse = pDoc->m_bRecurse;
217 m_strExt = pDoc->m_strExt;
218 m_strUnpacker = pDoc->m_strUnpacker;
219 m_infoHandler = pDoc->m_infoHandler;
220 m_dwFlags[0] = pDoc->m_dwFlags[0];
221 m_dwFlags[1] = pDoc->m_dwFlags[1];
222 m_dwFlags[2] = pDoc->m_dwFlags[2];
224 for (int file = 0; file < m_files.GetSize(); file++)
226 m_strPath[file] = m_files[file].c_str();
227 m_ctlPath[file].SetWindowText(m_files[file].c_str());
230 m_ctlPath[0].AttachSystemImageList();
231 m_ctlPath[1].AttachSystemImageList();
232 m_ctlPath[2].AttachSystemImageList();
233 LoadComboboxStates();
235 BOOL bDoUpdateData = TRUE;
236 for (int index = 0; index < countof(m_strPath); index++)
238 if (!m_strPath[index].IsEmpty())
239 bDoUpdateData = FALSE;
241 UpdateData(bDoUpdateData);
243 String filterNameOrMask = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
244 BOOL bMask = theApp.m_pGlobalFileFilter->IsUsingMask();
248 String filterPrefix = _("[F] ");
249 filterNameOrMask = filterPrefix + filterNameOrMask;
252 int ind = m_ctlExt.FindStringExact(0, filterNameOrMask.c_str());
254 m_ctlExt.SetCurSel(ind);
257 ind = m_ctlExt.InsertString(0, filterNameOrMask.c_str());
259 m_ctlExt.SetCurSel(ind);
261 LogErrorString(_T("Failed to add string to filters combo list!"));
264 if (!GetOptionsMgr()->GetBool(OPT_VERIFY_OPEN_PATHS))
266 m_ctlOk.EnableWindow(TRUE);
267 m_ctlUnpacker.EnableWindow(TRUE);
268 m_ctlSelectUnpacker.EnableWindow(TRUE);
271 UpdateButtonStates();
273 BOOL bOverwriteRecursive = FALSE;
274 if (m_dwFlags[0] & FFILEOPEN_PROJECT || m_dwFlags[1] & FFILEOPEN_PROJECT)
275 bOverwriteRecursive = TRUE;
276 if (m_dwFlags[0] & FFILEOPEN_CMDLINE || m_dwFlags[1] & FFILEOPEN_CMDLINE)
277 bOverwriteRecursive = TRUE;
278 if (!bOverwriteRecursive)
279 m_bRecurse = theApp.GetProfileInt(_T("Settings"), _T("Recurse"), 0) == 1;
281 m_strUnpacker = m_infoHandler.pluginName.c_str();
283 SetStatus(IDS_OPEN_FILESDIRS);
284 SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
287 // COpenView diagnostics
290 void COpenView::AssertValid() const
292 CFormView::AssertValid();
295 void COpenView::Dump(CDumpContext& dc) const
300 COpenDoc* COpenView::GetDocument() const // non-debug version is inline
302 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(COpenDoc)));
303 return (COpenDoc*)m_pDocument;
308 /////////////////////////////////////////////////////////////////////////////
309 // COpenView message handlers
311 void COpenView::OnPaint()
314 m_picture.Render(&dc);
315 CSize size = m_picture.GetImageSize(&dc);
318 dc.PatBlt(size.cx, 0, rc.Width() - size.cx, size.cy, PATCOPY);
320 rc.left = rc.right - GetSystemMetrics(SM_CXVSCROLL);
321 rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
322 dc.DrawFrameControl(&rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
324 CFormView::OnPaint();
327 void COpenView::OnLButtonDown(UINT nFlags, CPoint point)
330 if (m_rectTracker.Track(this, point, FALSE, GetParentFrame()))
332 CRect rc = m_rectTracker.m_rect;
333 MapWindowPoints(GetParentFrame(), &rc);
335 GetParentFrame()->GetClientRect(&rcFrame);
336 int width = rc.Width() > rcFrame.Width() ? rcFrame.Width() : rc.Width();
337 if (width < m_sizeOrig.cx)
338 width = m_sizeOrig.cx;
339 rc.right = rc.left + width;
340 rc.bottom = rc.top + m_sizeOrig.cy;
341 m_rectTracker.m_rect.right = m_rectTracker.m_rect.left + width;
342 m_rectTracker.m_rect.bottom = m_rectTracker.m_rect.top + m_sizeOrig.cy;
343 SetWindowPos(NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER);
344 m_constraint.UpdateSizes();
348 void COpenView::OnWindowPosChanged(WINDOWPOS* lpwndpos)
352 m_rectTracker.m_rect = rc;
353 CFormView::OnWindowPosChanged(lpwndpos);
356 void COpenView::OnDestroy()
358 m_constraint.Persist(true, false);
360 CFormView::OnDestroy();
363 BOOL COpenView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
365 if (pWnd == this && m_rectTracker.SetCursor(this, nHitTest))
368 return CView::OnSetCursor(pWnd, nHitTest, message);
371 void COpenView::OnButton(int index)
377 PATH_EXISTENCE existence = paths_DoesPathExist((const TCHAR *)m_strPath[index], IsArchiveFile);
380 case IS_EXISTING_DIR:
381 sfolder = m_strPath[index];
383 case IS_EXISTING_FILE:
384 sfolder = paths_GetPathOnly(String(m_strPath[index]));
387 // Do nothing, empty foldername will be passed to dialog
390 _RPTF0(_CRT_ERROR, "Invalid return value from paths_DoesPathExist()");
394 if (SelectFileOrFolder(GetSafeHwnd(), s, sfolder.c_str()))
396 m_strPath[index] = s.c_str();
397 m_strBrowsePath[index] = s.c_str();
399 UpdateButtonStates();
404 * @brief Called when "Browse..." button is selected for first path.
406 void COpenView::OnPath0Button()
412 * @brief Called when "Browse..." button is selected for second path.
414 void COpenView::OnPath1Button()
420 * @brief Called when "Browse..." button is selected for third path.
422 void COpenView::OnPath2Button()
427 template<int id1, int id2>
428 void COpenView::OnSwapButton()
431 GetDlgItem(id1)->GetWindowText(s1);
432 GetDlgItem(id2)->GetWindowText(s2);
434 GetDlgItem(id1)->SetWindowText(s1);
435 GetDlgItem(id2)->SetWindowText(s2);
439 * @brief Called when dialog is closed with "OK".
441 * Checks that paths are valid and sets filters.
443 void COpenView::OnOK()
445 int pathsType; // enum from PATH_EXISTENCE in paths.h
446 const String filterPrefix = _("[F] ");
453 for (index = 0; index < countof(m_strPath); index++)
455 if (index == 2 && m_strPath[index].IsEmpty())
457 m_files.SetSize(nFiles + 1);
458 m_files[nFiles] = m_strPath[index];
461 // If left path is a project-file, load it
463 paths_SplitFilename((LPCTSTR)m_strPath[0], NULL, NULL, &ext);
464 CString sExt(ext.c_str());
465 if (m_strPath[1].IsEmpty() && sExt.CompareNoCase(ProjectFile::PROJECTFILE_EXT.c_str()) == 0)
466 LoadProjectFile((const TCHAR *)m_strPath[0]);
468 pathsType = GetPairComparability(m_files, IsArchiveFile);
470 if (pathsType == DOES_NOT_EXIST)
472 LangMessageBox(IDS_ERROR_INCOMPARABLE, MB_ICONSTOP);
476 for (index = 0; index < nFiles; index++)
478 // If user has edited path by hand, expand environment variables
479 bool bExpand = false;
480 if (m_strBrowsePath[index].CompareNoCase(m_files[index].c_str()) != 0)
483 if (!paths_IsURLorCLSID(m_files[index]))
485 m_files[index] = paths_GetLongPath(m_files[index], bExpand);
487 // Add trailing '\' for directories if its missing
488 if (paths_DoesPathExist(m_files[index]) == IS_EXISTING_DIR)
489 m_files[index] = paths_AddTrailingSlash(m_files[index]);
494 KillTimer(IDT_CHECKFILES);
496 String filter((LPCTSTR)m_strExt);
497 filter = string_trim_ws(filter);
499 // If prefix found from start..
500 if (filter.find(filterPrefix, 0) == 0)
502 // Remove prefix + space
503 filter.erase(0, filterPrefix.length());
504 if (!theApp.m_pGlobalFileFilter->SetFilter(filter))
506 // If filtername is not found use default *.* mask
507 theApp.m_pGlobalFileFilter->SetFilter(_T("*.*"));
510 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
514 BOOL bFilterSet = theApp.m_pGlobalFileFilter->SetFilter(filter);
516 m_strExt = theApp.m_pGlobalFileFilter->GetFilterNameOrMask().c_str();
517 GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
520 SaveComboboxStates();
521 theApp.WriteProfileInt(_T("Settings"), _T("Recurse"), m_bRecurse);
522 LoadComboboxStates();
524 m_constraint.Persist(true, false);
526 COpenDoc *pDoc = GetDocument();
527 pDoc->m_files = m_files;
528 pDoc->m_bRecurse = m_bRecurse;
529 pDoc->m_strExt = m_strExt;
530 pDoc->m_strUnpacker = m_strUnpacker;
531 pDoc->m_infoHandler = m_infoHandler;
532 pDoc->m_dwFlags[0] = m_dwFlags[0];
533 pDoc->m_dwFlags[1] = m_dwFlags[1];
534 pDoc->m_dwFlags[2] = m_dwFlags[2];
536 if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_OK))
537 GetParentFrame()->PostMessage(WM_CLOSE);
539 GetMainFrame()->DoFileOpen(
540 &PathContext(pDoc->m_files), &std::vector<DWORD>(pDoc->m_dwFlags, pDoc->m_dwFlags + 3)[0], !!pDoc->m_bRecurse, NULL, _T(""), &PackingInfo(pDoc->m_infoHandler));
544 * @brief Called when dialog is closed via Cancel.
546 * Open-dialog is canceled when 'Cancel' button is selected or
547 * Esc-key is pressed. Save combobox states, since user may have
548 * removed items from them and don't want them to re-appear.
550 void COpenView::OnCancel()
552 SaveComboboxStates();
553 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
557 * @brief Load File- and filter-combobox states.
559 void COpenView::LoadComboboxStates()
561 m_ctlPath[0].CComboBox::ResetContent();
562 m_ctlPath[1].CComboBox::ResetContent();
563 m_ctlPath[2].CComboBox::ResetContent();
564 m_ctlExt.CComboBox::ResetContent();
566 m_ctlPath[0].LoadState(_T("Files\\Left"));
567 m_ctlPath[1].LoadState(_T("Files\\Right"));
568 m_ctlPath[2].LoadState(_T("Files\\Option"));
569 m_ctlExt.LoadState(_T("Files\\Ext"));
571 BOOL bIsEmptyThirdItem = theApp.GetProfileInt(_T("Files\\Option"), _T("Empty"), TRUE);
572 if (bIsEmptyThirdItem)
573 m_ctlPath[2].SetCurSel(-1);
577 * @brief Save File- and filter-combobox states.
579 void COpenView::SaveComboboxStates()
581 m_ctlPath[0].SaveState(_T("Files\\Left"));
582 m_ctlPath[1].SaveState(_T("Files\\Right"));
583 m_ctlPath[2].SaveState(_T("Files\\Option"));
584 m_ctlExt.SaveState(_T("Files\\Ext"));
587 m_ctlPath[2].GetWindowText(strOption);
588 theApp.WriteProfileInt(_T("Files\\Option"), _T("Empty"), strOption.IsEmpty());
591 struct UpdateButtonStatesThreadParams
597 static UINT UpdateButtonStatesThread(LPVOID lpParam)
603 CAssureScriptsForThread scriptsForRescan;
605 while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
609 if (msg.message != WM_USER + 2)
612 BOOL bButtonEnabled = TRUE;
613 BOOL bInvalid[3] = {FALSE, FALSE, FALSE};
615 int iUnpackerStatusMsgId;
617 UpdateButtonStatesThreadParams *pParams = (UpdateButtonStatesThreadParams *)msg.wParam;
618 PathContext paths = pParams->m_paths;
619 HWND hWnd = pParams->m_hWnd;
622 // Check if we have project file as left side path
623 BOOL bProject = FALSE;
625 paths_SplitFilename(paths[0], NULL, NULL, &ext);
626 CString sExt(ext.c_str());
627 if (paths[1].empty() && sExt.CompareNoCase(ProjectFile::PROJECTFILE_EXT.c_str()) == 0)
632 if (paths_DoesPathExist(paths[0], IsArchiveFile) == DOES_NOT_EXIST)
634 if (paths_DoesPathExist(paths[1], IsArchiveFile) == DOES_NOT_EXIST)
636 if (paths.GetSize() > 2 && paths_DoesPathExist(paths[2], IsArchiveFile) == DOES_NOT_EXIST)
640 // Enable buttons as appropriate
641 if (GetOptionsMgr()->GetBool(OPT_VERIFY_OPEN_PATHS))
643 PATH_EXISTENCE pathsType = DOES_NOT_EXIST;
645 if (paths.GetSize() <= 2)
647 if (bInvalid[0] && bInvalid[1])
648 iStatusMsgId = IDS_OPEN_BOTHINVALID;
649 else if (bInvalid[0])
650 iStatusMsgId = IDS_OPEN_LEFTINVALID;
651 else if (bInvalid[1])
652 iStatusMsgId = IDS_OPEN_RIGHTINVALID;
653 else if (!bInvalid[0] && !bInvalid[1])
655 pathsType = GetPairComparability(paths, IsArchiveFile);
656 if (pathsType == DOES_NOT_EXIST)
657 iStatusMsgId = IDS_OPEN_MISMATCH;
659 iStatusMsgId = IDS_OPEN_FILESDIRS;
664 if (bInvalid[0] && bInvalid[1] && bInvalid[2])
665 iStatusMsgId = IDS_OPEN_ALLINVALID;
666 else if (!bInvalid[0] && bInvalid[1] && bInvalid[2])
667 iStatusMsgId = IDS_OPEN_MIDDLERIGHTINVALID;
668 else if (bInvalid[0] && !bInvalid[1] && bInvalid[2])
669 iStatusMsgId = IDS_OPEN_LEFTRIGHTINVALID;
670 else if (!bInvalid[0] && !bInvalid[1] && bInvalid[2])
671 iStatusMsgId = IDS_OPEN_RIGHTINVALID;
672 else if (bInvalid[0] && bInvalid[1] && !bInvalid[2])
673 iStatusMsgId = IDS_OPEN_LEFTMIDDLEINVALID;
674 else if (!bInvalid[0] && bInvalid[1] && !bInvalid[2])
675 iStatusMsgId = IDS_OPEN_MIDDLEINVALID;
676 else if (bInvalid[0] && !bInvalid[1] && !bInvalid[2])
677 iStatusMsgId = IDS_OPEN_LEFTINVALID;
678 else if (!bInvalid[0] && !bInvalid[1] && !bInvalid[2])
680 pathsType = GetPairComparability(paths, IsArchiveFile);
681 if (pathsType == DOES_NOT_EXIST)
682 iStatusMsgId = IDS_OPEN_MISMATCH;
684 iStatusMsgId = IDS_OPEN_FILESDIRS;
687 if (pathsType == IS_EXISTING_FILE || bProject)
688 iUnpackerStatusMsgId = 0; //Empty field
690 iUnpackerStatusMsgId = IDS_OPEN_UNPACKERDISABLED;
693 bButtonEnabled = TRUE;
695 bButtonEnabled = (pathsType != DOES_NOT_EXIST);
698 PostMessage(hWnd, WM_USER + 1, bButtonEnabled, MAKELPARAM(iStatusMsgId, iUnpackerStatusMsgId));
707 * @brief Enable/disable components based on validity of paths.
709 void COpenView::UpdateButtonStates()
711 UpdateData(TRUE); // load member variables from screen
712 KillTimer(IDT_CHECKFILES);
715 if (!m_pUpdateButtonStatusThread)
717 m_pUpdateButtonStatusThread = AfxBeginThread(
718 UpdateButtonStatesThread, NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
719 m_pUpdateButtonStatusThread->m_bAutoDelete = FALSE;
720 m_pUpdateButtonStatusThread->ResumeThread();
721 while (PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_NULL, 0, 0) == FALSE)
725 UpdateButtonStatesThreadParams *pParams = new UpdateButtonStatesThreadParams;
726 pParams->m_hWnd = this->m_hWnd;
727 if (m_strPath[2].IsEmpty())
728 pParams->m_paths = PathContext((const TCHAR *)m_strPath[0], (const TCHAR *)m_strPath[1]);
730 pParams->m_paths = PathContext((const TCHAR *)m_strPath[0], (const TCHAR *)m_strPath[1], (const TCHAR *)m_strPath[2]);
732 PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_USER + 2, (WPARAM)pParams, 0);
735 void COpenView::TerminateThreadIfRunning()
737 if (!m_pUpdateButtonStatusThread)
740 PostThreadMessage(m_pUpdateButtonStatusThread->m_nThreadID, WM_QUIT, 0, 0);
741 DWORD dwResult = WaitForSingleObject(m_pUpdateButtonStatusThread->m_hThread, 100);
742 if (dwResult != WAIT_OBJECT_0)
744 m_pUpdateButtonStatusThread->SuspendThread();
745 TerminateThread(m_pUpdateButtonStatusThread->m_hThread, 0);
747 delete m_pUpdateButtonStatusThread;
748 m_pUpdateButtonStatusThread = NULL;
752 * @brief Called when user changes selection in left/middle/right path's combo box.
754 void COpenView::OnSelchangeCombo(int index)
756 int sel = m_ctlPath[index].GetCurSel();
759 m_ctlPath[index].GetLBText(sel, m_strPath[index]);
760 m_ctlPath[index].SetWindowText(m_strPath[index]);
763 UpdateButtonStates();
766 void COpenView::OnSelchangePath0Combo()
771 void COpenView::OnSelchangePath1Combo()
776 void COpenView::OnSelchangePath2Combo()
781 void COpenView::OnSetfocusPathCombo(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
783 if (!m_bAutoCompleteReady[id - IDC_PATH0_COMBO])
785 int nSource = GetOptionsMgr()->GetInt(OPT_AUTO_COMPLETE_SOURCE);
787 m_ctlPath[id - IDC_PATH0_COMBO].SetAutoComplete(nSource);
788 m_bAutoCompleteReady[id - IDC_PATH0_COMBO] = true;
794 * @brief Called every time paths are edited.
796 void COpenView::OnEditEvent()
798 // (Re)start timer to path validity check delay
799 // If timer starting fails, update buttonstates immediately
800 if (!SetTimer(IDT_CHECKFILES, CHECKFILES_TIMEOUT, NULL))
801 UpdateButtonStates();
805 * @brief Handle timer events.
806 * Checks if paths are valid and sets control states accordingly.
807 * @param [in] nIDEvent Timer ID that fired.
809 void COpenView::OnTimer(UINT_PTR nIDEvent)
811 if (nIDEvent == IDT_CHECKFILES)
812 UpdateButtonStates();
814 CFormView::OnTimer(nIDEvent);
818 * @brief Called when users selects plugin browse button.
820 void COpenView::OnSelectUnpacker()
822 PATH_EXISTENCE pathsType;
827 for (index = 0; index < countof(m_strPath); index++)
829 if (index == 2 && m_strPath[index].IsEmpty())
831 m_files.SetSize(nFiles + 1);
832 m_files[nFiles] = m_strPath[index];
835 pathsType = GetPairComparability(m_files);
837 if (pathsType != IS_EXISTING_FILE)
840 // let the user select a handler
841 CSelectUnpackerDlg dlg(m_files[0], this);
842 PackingInfo infoUnpacker(PLUGIN_AUTO);
843 dlg.SetInitialInfoHandler(&infoUnpacker);
845 if (dlg.DoModal() == IDOK)
847 m_infoHandler = dlg.GetInfoHandler();
849 m_strUnpacker = m_infoHandler.pluginName.c_str();
855 LRESULT COpenView::OnUpdateStatus(WPARAM wParam, LPARAM lParam)
857 BOOL bEnabledButtons = (BOOL)wParam;
859 m_ctlOk.EnableWindow(bEnabledButtons);
860 m_ctlUnpacker.EnableWindow(bEnabledButtons);
861 m_ctlSelectUnpacker.EnableWindow(bEnabledButtons);
863 SetStatus(HIWORD(lParam));
864 SetStatus(LOWORD(lParam));
870 * @brief Sets the path status text.
871 * The open dialog shows a status text of selected paths. This function
872 * is used to set that status text.
873 * @param [in] msgID Resource ID of status text to set.
875 void COpenView::SetStatus(UINT msgID)
877 String msg = theApp.LoadString(msgID);
878 SetDlgItemText(IDC_OPEN_STATUS, msg.c_str());
882 * @brief Set the plugin edit box text.
883 * Plugin edit box is at the same time a plugin status view. This function
884 * sets the status text.
885 * @param [in] msgID Resource ID of status text to set.
887 void COpenView::SetUnpackerStatus(UINT msgID)
889 String msg = theApp.LoadString(msgID);
890 SetDlgItemText(IDC_UNPACKER_EDIT, msg.c_str());
894 * @brief Called when "Select..." button for filters is selected.
896 void COpenView::OnSelectFilter()
898 String filterPrefix = _("[F] ");
901 const BOOL bUseMask = theApp.m_pGlobalFileFilter->IsUsingMask();
902 GetDlgItemText(IDC_EXT_COMBO, curFilter);
903 curFilter.TrimLeft();
904 curFilter.TrimRight();
906 GetMainFrame()->SelectFilter();
908 String filterNameOrMask = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
909 if (theApp.m_pGlobalFileFilter->IsUsingMask())
911 // If we had filter chosen and now has mask we can overwrite filter
912 if (!bUseMask || curFilter[0] != '*')
914 SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask.c_str());
919 filterNameOrMask = filterPrefix + filterNameOrMask;
920 SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask.c_str());
926 * @brief Read paths and filter from project file.
927 * Reads the given project file. After the file is read, found paths and
928 * filter is updated to dialog GUI. Other possible settings found in the
929 * project file are kept in memory and used later when loading paths
931 * @param [in] path Path to the project file.
932 * @return TRUE if the project file was successfully loaded, FALSE otherwise.
934 BOOL COpenView::LoadProjectFile(const String &path)
936 String filterPrefix = _("[F] ");
939 if (!theApp.LoadProjectFile(path, prj))
943 prj.GetPaths(m_files, recurse);
944 m_bRecurse = recurse;
945 m_dwFlags[0] &= ~FFILEOPEN_READONLY;
946 m_dwFlags[0] |= prj.GetLeftReadOnly() ? FFILEOPEN_READONLY : 0;
947 if (m_files.GetSize() < 3)
949 m_dwFlags[1] &= ~FFILEOPEN_READONLY;
950 m_dwFlags[1] |= prj.GetRightReadOnly() ? FFILEOPEN_READONLY : 0;
954 m_dwFlags[1] &= ~FFILEOPEN_READONLY;
955 m_dwFlags[1] |= prj.GetMiddleReadOnly() ? FFILEOPEN_READONLY : 0;
956 m_dwFlags[2] &= ~FFILEOPEN_READONLY;
957 m_dwFlags[2] |= prj.GetRightReadOnly() ? FFILEOPEN_READONLY : 0;
961 m_strExt = prj.GetFilter().c_str();
963 m_strExt.TrimRight();
964 if (m_strExt[0] != '*')
965 m_strExt.Insert(0, filterPrefix.c_str());
971 * @brief Removes whitespaces from left and right paths
972 * @note Assumes UpdateData(TRUE) is called before this function.
974 void COpenView::TrimPaths()
976 for (int index = 0; index < countof(m_strPath); index++)
978 m_strPath[index].TrimLeft();
979 m_strPath[index].TrimRight();
984 * @brief Update control states when dialog is activated.
986 * Update control states when user re-activates dialog. User might have
987 * switched for other program to e.g. update files/folders and then
988 * swiches back to WinMerge. Its nice to see WinMerge detects updated
991 void COpenView::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
993 CFormView::OnActivate(nState, pWndOther, bMinimized);
995 if (nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
996 UpdateButtonStates();
1000 * @brief Copy selected text to clipboard
1002 void COpenView::OnEditCopy()
1004 CWnd *pCtl = GetFocus();
1006 pCtl->PostMessage(WM_COPY);
1010 * @brief Cut current selection to clipboard
1012 void COpenView::OnEditCut()
1014 CWnd *pCtl = GetFocus();
1016 pCtl->PostMessage(WM_CUT);
1020 * @brief Paste text from clipboard
1022 void COpenView::OnEditPaste()
1024 CWnd *pCtl = GetFocus();
1026 pCtl->PostMessage(WM_PASTE);
1030 * @brief Undo last action
1032 void COpenView::OnEditUndo()
1034 CWnd *pCtl = GetFocus();
1036 pCtl->PostMessage(WM_UNDO);
1040 * @brief Open help from mainframe when user presses F1.
1042 void COpenView::OnHelp()
1044 theApp.ShowHelp(OpenDlgHelpLocation);
1047 /////////////////////////////////////////////////////////////////////////////
1049 // OnDropFiles code from CDropEdit
1050 // Copyright 1997 Chris Losinger
1052 // shortcut expansion code modified from :
1053 // CShortcut, 1996 Rob Warner
1057 * @brief Drop paths(s) to the dialog.
1058 * One or two paths can be dropped to the dialog. The behaviour is:
1060 * - drop to empty path edit box (check left first)
1061 * - if both boxes have a path, drop to left path
1063 * - overwrite both paths, empty or not
1064 * @param [in] dropInfo Dropped data, including paths.
1066 void COpenView::OnDropFiles(HDROP dropInfo)
1068 std::vector<String> files;
1069 GetDroppedFiles(dropInfo, files);
1070 const size_t fileCount = files.size();
1072 // Add dropped paths to the dialog
1076 m_strPath[0] = files[0].c_str();
1077 m_strPath[1] = files[1].c_str();
1078 m_strPath[2] = files[2].c_str();
1080 UpdateButtonStates();
1082 else if (fileCount == 2)
1084 m_strPath[0] = files[0].c_str();
1085 m_strPath[1] = files[1].c_str();
1087 UpdateButtonStates();
1089 else if (fileCount == 1)
1091 if (m_strPath[0].IsEmpty())
1092 m_strPath[0] = files[0].c_str();
1093 else if (m_strPath[1].IsEmpty())
1094 m_strPath[1] = files[0].c_str();
1095 else if (m_strPath[2].IsEmpty())
1096 m_strPath[2] = files[0].c_str();
1098 m_strPath[0] = files[0].c_str();
1100 UpdateButtonStates();
1104 BOOL COpenView::PreTranslateMessage(MSG* pMsg)
1106 if (pMsg->message == WM_SYSKEYDOWN)
1108 if (::GetAsyncKeyState(VK_MENU))
1111 switch (pMsg->wParam)
1113 case '1': id = IDC_PATH0_COMBO; goto LABEL_NUM_KEY;
1114 case '2': id = IDC_PATH1_COMBO; goto LABEL_NUM_KEY;
1115 case '3': id = IDC_PATH2_COMBO;
1117 GetDlgItem(id)->SetFocus();
1120 case 'S': id = IDC_SELECT_UNPACKER;
1121 PostMessage(WM_COMMAND, id, 0);
1126 return CFormView::PreTranslateMessage(pMsg);