OSDN Git Service

8f7db2e91a247aed612cf44de64e4c9e10d9ac54
[winmerge-jp/winmerge-jp.git] / Src / OpenDlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
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.
10 //
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.
15 //
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.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /** 
22  * @file  OpenDlg.cpp
23  *
24  * @brief Implementation of the COpenDlg class
25  */
26 // RCS ID line follows -- this is updated by CVS
27 // $Id$
28
29 #include "stdafx.h"
30 #include "Merge.h"
31 #include "OpenDlg.h"
32 #include "coretools.h"
33 #include "paths.h"
34 #include "SelectUnpackerDlg.h"
35 #include "OptionsDef.h"
36 #include "MainFrm.h"
37
38 #ifdef _DEBUG
39 #define new DEBUG_NEW
40 #undef THIS_FILE
41 static char THIS_FILE[] = __FILE__;
42 #endif
43
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(".*");
48
49 /////////////////////////////////////////////////////////////////////////////
50 // COpenDlg dialog
51
52
53 COpenDlg::COpenDlg(CWnd* pParent /*=NULL*/)
54         : CDialog(COpenDlg::IDD, pParent)
55 {
56         //{{AFX_DATA_INIT(COpenDlg)
57         m_strLeft = _T("");
58         m_strRight = _T("");
59         m_bRecurse = FALSE;
60         m_strExt = _T("");
61         m_strUnpacker = _T("");
62         //}}AFX_DATA_INIT
63
64         m_pathsType = DOES_NOT_EXIST;
65         m_bFileFilterSelected = FALSE;
66         m_bOverwriteRecursive = FALSE;
67 }
68
69 void COpenDlg::DoDataExchange(CDataExchange* pDX)
70 {
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);
85         //}}AFX_DATA_MAP
86 }
87
88
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)
100         ON_WM_TIMER()
101         ON_BN_CLICKED(IDC_SELECT_FILTER, OnSelectFilter)
102         //}}AFX_MSG_MAP
103 END_MESSAGE_MAP()
104
105 /////////////////////////////////////////////////////////////////////////////
106 // COpenDlg message handlers
107
108 /** 
109  * @brief Called when "Browse..." button is selected for left path.
110  */
111 void COpenDlg::OnLeftButton()
112 {
113         CString s;
114         CString sfolder, sname;
115         CString dirSelTag;
116         CFileStatus status;
117         UpdateData(TRUE); 
118
119         VERIFY(dirSelTag.LoadString(IDS_DIRSEL_TAG));
120         if (CFile::GetStatus(m_strLeft, status)
121                 && (status.m_attribute & CFile::Attribute::directory))
122                         sfolder = m_strLeft;
123         else
124                 sfolder = GetPathOnly(m_strLeft);
125         if (SelectFile(s, sfolder))
126         {
127                 SplitFilename(s, &sfolder, &sname, 0);
128                 if (sname == dirSelTag)
129                 {
130                         m_strLeft = sfolder + '\\';
131                 }
132                 else
133                         m_strLeft = s;
134                 UpdateData(FALSE);
135                 UpdateButtonStates();
136         }       
137 }
138
139 /** 
140  * @brief Called when "Browse..." button is selected for right path.
141  */
142 void COpenDlg::OnRightButton() 
143 {
144         CString s;
145         CString sfolder, sname;
146         CString dirSelTag;
147         CFileStatus status;
148         UpdateData(TRUE);
149
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;
154         else 
155                 sfolder = GetPathOnly(m_strRight);
156         if (SelectFile(s, sfolder))
157         {
158                 SplitFilename(s, &sfolder, &sname, 0);
159                 if (sname == dirSelTag)
160                         m_strRight = sfolder + '\\';
161                 else
162                         m_strRight = s;
163                 UpdateData(FALSE);
164                 UpdateButtonStates();
165         }       
166 }
167
168 /** 
169  * @brief Called when dialog is closed with "OK".
170  *
171  * Checks that paths are valid and sets filters.
172  */
173 void COpenDlg::OnOK() 
174 {
175         CString filterPrefix;
176         VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
177
178         UpdateData(TRUE);
179
180         m_pathsType = GetPairComparability(m_strLeft, m_strRight);
181
182         if (m_pathsType == DOES_NOT_EXIST)
183         {
184                 AfxMessageBox(IDS_ERROR_INCOMPARABLE, MB_ICONSTOP);
185                 return;
186         }
187
188         m_strRight = paths_GetLongPath(m_strRight, DIRSLASH);
189         m_strLeft = paths_GetLongPath(m_strLeft, DIRSLASH);
190
191         UpdateData(FALSE);
192         KillTimer(IDT_CHECKFILES);
193
194         // If prefix found from start..
195         if (m_strExt.Find(filterPrefix, 0) == 0)
196         {
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);
203         }
204         else
205         {
206                 CString strNone;
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);
212         }
213
214         m_ctlLeft.SaveState(_T("Files\\Left"));
215         m_ctlRight.SaveState(_T("Files\\Right"));
216         m_ctlExt.SaveState(_T("Files\\Ext"));
217
218         theApp.WriteProfileInt(_T("Settings"), _T("Recurse"), m_bRecurse);
219
220         CDialog::OnOK();
221 }
222
223 /** @brief Handler for WM_INITDIALOG; conventional location to initialize controls */
224 BOOL COpenDlg::OnInitDialog() 
225 {
226         CDialog::OnInitDialog();
227         
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);
233
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
252
253         CenterToMainFrame();
254
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());
259         
260         // Select active filter from filter list or add it to list
261         CString filterPrefix;
262         VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
263         CString strNone;
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;
270
271         if (filterFile.Find(strNone, 0) > -1)
272                 // No filter selected, select last used extension
273                 m_ctlExt.SetCurSel(0);
274         else
275         {
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);
281                 if (ind != CB_ERR)
282                         m_ctlExt.SetCurSel(ind);
283                 else
284                 {
285                         ind = m_ctlExt.InsertString(0, filterName);
286                         if (ind != CB_ERR)
287                                 m_ctlExt.SetCurSel(ind);
288                         else
289                                 LogErrorString(_T("Failed to add string to filters combo list!"));
290                 }
291         }
292
293         if (!mf->m_options.GetInt(OPT_VERIFY_OPEN_PATHS))
294         {
295                 m_ctlOk.EnableWindow(TRUE);
296                 m_ctlUnpacker.EnableWindow(TRUE);
297                 m_ctlSelectUnpacker.EnableWindow(TRUE);
298         }
299
300         UpdateButtonStates();
301
302         if (!m_bOverwriteRecursive)
303                 m_bRecurse = theApp.GetProfileInt(_T("Settings"), _T("Recurse"), 0) == 1;
304
305         m_strUnpacker = m_infoHandler.pluginName;
306         UpdateData(FALSE);
307         SetStatus(IDS_OPEN_FILESDIRS);
308         SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
309         return TRUE;
310 }
311
312 /** 
313  * @brief Enable/disable components based on validity of paths.
314  */
315 void COpenDlg::UpdateButtonStates()
316 {
317         BOOL bLeftInvalid = FALSE;
318         BOOL bRightInvalid = FALSE;
319
320         UpdateData(TRUE); // load member variables from screen
321         KillTimer(IDT_CHECKFILES);
322         
323         // Enable buttons as appropriate
324         PATH_EXISTENCE pathsType = GetPairComparability(m_strLeft, m_strRight);
325         if (mf->m_options.GetInt(OPT_VERIFY_OPEN_PATHS))
326         {
327                 m_ctlOk.EnableWindow(pathsType != DOES_NOT_EXIST);
328                 m_ctlUnpacker.EnableWindow(pathsType == IS_EXISTING_FILE);
329                 m_ctlSelectUnpacker.EnableWindow(pathsType == IS_EXISTING_FILE);
330         }
331
332         if (paths_DoesPathExist(m_strLeft) == DOES_NOT_EXIST)
333                 bLeftInvalid = TRUE;
334         if (paths_DoesPathExist(m_strRight) == DOES_NOT_EXIST)
335                 bRightInvalid = TRUE;
336
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);
345         else
346                 SetStatus(IDS_OPEN_FILESDIRS);
347
348         if (pathsType == IS_EXISTING_FILE)
349                 SetUnpackerStatus(0);   //Empty field
350         else
351                 SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
352 }
353
354 /** 
355  * @brief Shows file/folder selection dialog.
356  */
357 BOOL COpenDlg::SelectFile(CString& path, LPCTSTR pszFolder) 
358 {
359         CString s;
360         CString dirSelTag;
361
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);
366         CString title;
367         VERIFY(title.LoadString(IDS_OPEN_TITLE));
368         pdlg.m_ofn.lpstrTitle = title;
369         pdlg.m_ofn.lpstrInitialDir = pszFolder;
370
371         if (pdlg.DoModal()==IDOK)
372         {
373                 path = pdlg.GetPathName(); 
374                 return TRUE;
375         }
376         else
377         {
378                 return FALSE;
379         }
380 }
381
382 void COpenDlg::OnSelchangeLeftCombo() 
383 {
384         int sel = m_ctlLeft.GetCurSel();
385         if (sel != CB_ERR)
386         {
387                 m_ctlLeft.GetLBText(sel, m_strLeft);
388                 m_ctlLeft.SetWindowText(m_strLeft);
389                 UpdateData(TRUE);
390         }
391         UpdateButtonStates();
392 }
393
394 void COpenDlg::OnSelchangeRightCombo() 
395 {
396         int sel = m_ctlRight.GetCurSel();
397         if (sel != CB_ERR)
398         {
399                 m_ctlRight.GetLBText(sel, m_strRight);
400                 m_ctlRight.SetWindowText(m_strRight);
401                 UpdateData(TRUE);
402         }
403         UpdateButtonStates();
404 }
405
406 void COpenDlg::RemoveTrailingSlash(CString & s)
407 {
408         // Do not remove slash from "X:\"
409         if (s[s.GetLength()-2] == _T(':'))
410                 return;
411         while (s.Right(1) == _T('\\') || s.Right(1) == _T('/'))
412                 s.Delete(s.GetLength()-1);
413 }
414
415 /** 
416  * @brief Called every time paths are edited.
417  */
418 void COpenDlg::OnEditEvent()
419 {
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();
424 }
425
426 void COpenDlg::OnTimer(UINT nIDEvent) 
427 {
428         if (nIDEvent == IDT_CHECKFILES)
429                 UpdateButtonStates();
430
431         CDialog::OnTimer(nIDEvent);
432 }
433
434 void COpenDlg::OnSelectUnpacker() 
435 {
436         UpdateData(TRUE);
437
438         m_pathsType = GetPairComparability(m_strLeft, m_strRight);
439
440         if (m_pathsType != IS_EXISTING_FILE) 
441                 return;
442
443         // let the user select a handler
444         CSelectUnpackerDlg dlg(m_strLeft, m_strRight, this);
445         dlg.SetInitialInfoHandler(&m_infoHandler);
446
447         if (dlg.DoModal() == IDOK)
448         {
449                 m_infoHandler = dlg.GetInfoHandler();
450
451                 m_strUnpacker = m_infoHandler.pluginName;
452
453                 UpdateData(FALSE);
454         }
455 }
456
457 void COpenDlg::SetStatus(UINT msgID)
458 {
459         CString msg;
460         if (msgID > 0)
461                 VERIFY(msg.LoadString(msgID));
462         SetDlgItemText(IDC_OPEN_STATUS, msg);
463 }
464
465 void COpenDlg::SetUnpackerStatus(UINT msgID)
466 {
467         CString msg;
468         if (msgID > 0)
469                 VERIFY(msg.LoadString(msgID));
470         SetDlgItemText(IDC_UNPACKER_EDIT, msg);
471 }
472
473 /** 
474  * @brief Called when "Select..." button for filters is selected.
475  */
476 void COpenDlg::OnSelectFilter()
477 {
478         CString filterName;
479         CString strNone;
480         CString filterPrefix;
481         VERIFY(strNone.LoadString(IDS_USERCHOICE_NONE));
482         VERIFY(filterPrefix.LoadString(IDS_FILTER_PREFIX));
483
484         mf->SelectFilter();
485         CString filter = theApp.m_globalFileFilter.GetFileFilterPath();
486         
487         if (filter == strNone)
488                 SetDlgItemText(IDC_EXT_COMBO, _T("*.*"));
489         else
490         {
491                 filterName = theApp.m_globalFileFilter.GetFileFilterName(filter);
492                 filterName.Insert(0, filterPrefix);
493                 SetDlgItemText(IDC_EXT_COMBO, filterName);
494         }
495 }
496
497 /** 
498  * @brief Move Open-dialog to center of MainFrame
499  */
500 void COpenDlg::CenterToMainFrame()
501 {
502         CRect rectFrame;
503         CRect rectBar;
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;
512
513         SetWindowPos(&CWnd::wndTop, x, y, rectBar.right,
514                 rectBar.bottom, SWP_NOOWNERZORDER | SWP_NOSIZE );
515 }