1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - Stefan Kueng
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "TortoiseProc.h"
\r
21 #include "MessageBox.h"
\r
22 #include ".\lockdlg.h"
\r
23 #include "UnicodeUtils.h"
\r
24 #include "SVNProperties.h"
\r
26 #include "HistoryDlg.h"
\r
28 #define REFRESHTIMER 100
\r
30 IMPLEMENT_DYNAMIC(CLockDlg, CResizableStandAloneDialog)
\r
31 CLockDlg::CLockDlg(CWnd* pParent /*=NULL*/)
\r
32 : CResizableStandAloneDialog(CLockDlg::IDD, pParent)
\r
34 , m_bStealLocks(FALSE)
\r
36 , m_bCancelled(false)
\r
37 , m_ProjectProperties(NULL)
\r
41 CLockDlg::~CLockDlg()
\r
43 if(m_pThread != NULL)
\r
49 void CLockDlg::DoDataExchange(CDataExchange* pDX)
\r
51 CResizableStandAloneDialog::DoDataExchange(pDX);
\r
52 DDX_Check(pDX, IDC_STEALLOCKS, m_bStealLocks);
\r
53 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
\r
54 DDX_Control(pDX, IDC_LOCKMESSAGE, m_cEdit);
\r
55 DDX_Control(pDX, IDC_SELECTALL, m_SelectAll);
\r
59 BEGIN_MESSAGE_MAP(CLockDlg, CResizableStandAloneDialog)
\r
60 ON_BN_CLICKED(IDHELP, OnBnClickedHelp)
\r
61 ON_EN_CHANGE(IDC_LOCKMESSAGE, OnEnChangeLockmessage)
\r
62 ON_REGISTERED_MESSAGE(CSVNStatusListCtrl::SVNSLNM_NEEDSREFRESH, OnSVNStatusListCtrlNeedsRefresh)
\r
63 ON_BN_CLICKED(IDC_SELECTALL, &CLockDlg::OnBnClickedSelectall)
\r
64 ON_BN_CLICKED(IDC_HISTORY, &CLockDlg::OnBnClickedHistory)
\r
65 ON_REGISTERED_MESSAGE(CSVNStatusListCtrl::SVNSLNM_ADDFILE, OnFileDropped)
\r
69 BOOL CLockDlg::OnInitDialog()
\r
71 CResizableStandAloneDialog::OnInitDialog();
\r
73 m_History.SetMaxHistoryItems((LONG)CRegDWORD(_T("Software\\TortoiseGit\\MaxHistoryItems"), 25));
\r
74 m_History.Load(_T("Software\\TortoiseGit\\History\\commit"), _T("logmsgs"));
\r
76 m_cFileList.Init(SVNSLC_COLEXT | SVNSLC_COLLOCK | SVNSLC_COLSVNNEEDSLOCK, _T("LockDlg"));
\r
77 m_cFileList.SetSelectButton(&m_SelectAll);
\r
78 m_cFileList.SetConfirmButton((CButton*)GetDlgItem(IDOK));
\r
79 m_cFileList.SetCancelBool(&m_bCancelled);
\r
80 m_cFileList.EnableFileDrop();
\r
81 m_cFileList.SetBackgroundImage(IDI_LOCK_BKG);
\r
82 if (m_ProjectProperties)
\r
83 m_cEdit.Init(*m_ProjectProperties);
\r
86 m_cEdit.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
\r
88 if (!m_sLockMessage.IsEmpty())
\r
89 m_cEdit.SetText(m_sLockMessage);
\r
91 m_tooltips.Create(this);
\r
92 m_tooltips.AddTool(IDC_LOCKWARNING, IDS_WARN_SVNNEEDSLOCK);
\r
94 m_SelectAll.SetCheck(BST_INDETERMINATE);
\r
96 AdjustControlSize(IDC_STEALLOCKS);
\r
97 AdjustControlSize(IDC_SELECTALL);
\r
99 AddAnchor(IDC_HISTORY, TOP_LEFT);
\r
100 AddAnchor(IDC_LOCKTITLELABEL, TOP_LEFT, TOP_RIGHT);
\r
101 AddAnchor(IDC_LOCKMESSAGE, TOP_LEFT, TOP_RIGHT);
\r
102 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
\r
103 AddAnchor(IDC_STEALLOCKS, BOTTOM_LEFT);
\r
104 AddAnchor(IDC_SELECTALL, BOTTOM_LEFT);
\r
105 AddAnchor(IDOK, BOTTOM_RIGHT);
\r
106 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
\r
107 AddAnchor(IDHELP, BOTTOM_RIGHT);
\r
108 AddAnchor(IDC_LOCKWARNING, TOP_RIGHT);
\r
111 CenterWindow(CWnd::FromHandle(hWndExplorer));
\r
112 EnableSaveRestore(_T("LockDlg"));
\r
114 // start a thread to obtain the file list with the status without
\r
115 // blocking the dialog
\r
116 m_pThread = AfxBeginThread(StatusThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
\r
117 if (m_pThread==NULL)
\r
119 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
\r
123 m_pThread->m_bAutoDelete = FALSE;
\r
124 m_pThread->ResumeThread();
\r
128 GetDlgItem(IDC_LOCKMESSAGE)->SetFocus();
\r
132 void CLockDlg::OnOK()
\r
136 m_cFileList.WriteCheckedNamesToPathList(m_pathList);
\r
138 m_sLockMessage = m_cEdit.GetText();
\r
139 m_History.AddEntry(m_sLockMessage);
\r
142 CResizableStandAloneDialog::OnOK();
\r
145 void CLockDlg::OnCancel()
\r
147 m_bCancelled = true;
\r
151 if ((m_ProjectProperties == 0)||(m_ProjectProperties->sLogTemplate.Compare(m_cEdit.GetText()) != 0))
\r
152 m_sLockMessage = m_cEdit.GetText();
\r
153 m_History.AddEntry(m_sLockMessage);
\r
155 CResizableStandAloneDialog::OnCancel();
\r
158 UINT CLockDlg::StatusThreadEntry(LPVOID pVoid)
\r
160 return ((CLockDlg*)pVoid)->StatusThread();
\r
163 UINT CLockDlg::StatusThread()
\r
165 // get the status of all selected file/folders recursively
\r
166 // and show the ones which can be locked to the user
\r
167 // in a list control.
\r
169 DialogEnableWindow(IDOK, false);
\r
170 m_bCancelled = false;
\r
171 // Initialise the list control with the status of the files/folders below us
\r
172 if (!m_cFileList.GetStatus(m_pathList))
\r
174 m_cFileList.SetEmptyString(m_cFileList.GetLastErrorMessage());
\r
177 // Check if any file doesn't have svn:needs-lock set in BASE. If at least
\r
178 // one file is found then show the warning that this property should by set.
\r
179 BOOL bShowWarning = FALSE;
\r
180 const int nCount = m_cFileList.GetItemCount();
\r
181 for (int i=0; i<nCount;i++)
\r
183 CSVNStatusListCtrl::FileEntry* entry = m_cFileList.GetListEntry(i);
\r
186 if (entry->HasNeedsLock())
\r
188 bShowWarning = TRUE;
\r
193 if ( bShowWarning )
\r
195 GetDlgItem(IDC_LOCKWARNING)->ShowWindow(SW_SHOW);
\r
196 DialogEnableWindow(IDC_LOCKWARNING, TRUE);
\r
200 GetDlgItem(IDC_LOCKWARNING)->ShowWindow(SW_HIDE);
\r
201 DialogEnableWindow(IDC_LOCKWARNING, FALSE);
\r
204 DWORD dwShow = SVNSLC_SHOWNORMAL | SVNSLC_SHOWMODIFIED | SVNSLC_SHOWMERGED | SVNSLC_SHOWLOCKS;
\r
205 m_cFileList.Show(dwShow, dwShow, false);
\r
209 GetDlgItemText(IDC_LOCKMESSAGE, logmsg);
\r
210 DialogEnableWindow(IDOK, m_ProjectProperties ? m_ProjectProperties->nMinLockMsgSize <= logmsg.GetLength() : TRUE);
\r
215 BOOL CLockDlg::PreTranslateMessage(MSG* pMsg)
\r
218 m_tooltips.RelayEvent(pMsg);
\r
220 if (pMsg->message == WM_KEYDOWN)
\r
222 switch (pMsg->wParam)
\r
227 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
\r
233 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
\r
235 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
\r
237 PostMessage(WM_COMMAND, IDOK);
\r
246 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
\r
249 void CLockDlg::Refresh()
\r
252 if (AfxBeginThread(StatusThreadEntry, this)==NULL)
\r
254 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
\r
258 void CLockDlg::OnBnClickedHelp()
\r
263 void CLockDlg::OnEnChangeLockmessage()
\r
266 GetDlgItemText(IDC_LOCKMESSAGE, sTemp);
\r
267 if ((m_ProjectProperties == NULL)||((m_ProjectProperties)&&(sTemp.GetLength() >= m_ProjectProperties->nMinLockMsgSize)))
\r
270 DialogEnableWindow(IDOK, TRUE);
\r
274 DialogEnableWindow(IDOK, FALSE);
\r
278 LRESULT CLockDlg::OnSVNStatusListCtrlNeedsRefresh(WPARAM, LPARAM)
\r
284 void CLockDlg::OnBnClickedSelectall()
\r
286 m_tooltips.Pop(); // hide the tooltips
\r
287 UINT state = (m_SelectAll.GetState() & 0x0003);
\r
288 if (state == BST_INDETERMINATE)
\r
290 // It is not at all useful to manually place the checkbox into the indeterminate state...
\r
291 // We will force this on to the unchecked state
\r
292 state = BST_UNCHECKED;
\r
293 m_SelectAll.SetCheck(state);
\r
295 m_cFileList.SelectAll(state == BST_CHECKED);
\r
298 void CLockDlg::OnBnClickedHistory()
\r
300 m_tooltips.Pop(); // hide the tooltips
\r
301 if (m_pathList.GetCount() == 0)
\r
304 CHistoryDlg historyDlg;
\r
305 historyDlg.SetHistory(m_History);
\r
306 if (historyDlg.DoModal()==IDOK)
\r
308 if (historyDlg.GetSelectedText().Compare(m_cEdit.GetText().Left(historyDlg.GetSelectedText().GetLength()))!=0)
\r
310 if ((m_ProjectProperties)&&(m_ProjectProperties->sLogTemplate.Compare(m_cEdit.GetText())!=0))
\r
311 m_cEdit.InsertText(historyDlg.GetSelectedText(), !m_cEdit.GetText().IsEmpty());
\r
313 m_cEdit.SetText(historyDlg.GetSelectedText());
\r
316 OnEnChangeLockmessage();
\r
317 GetDlgItem(IDC_LOCKMESSAGE)->SetFocus();
\r
321 LRESULT CLockDlg::OnFileDropped(WPARAM, LPARAM lParam)
\r
323 BringWindowToTop();
\r
324 SetForegroundWindow();
\r
326 // if multiple files/folders are dropped
\r
327 // this handler is called for every single item
\r
329 // To avoid creating multiple refresh threads and
\r
330 // causing crashes, we only add the items to the
\r
331 // list control and start a timer.
\r
332 // When the timer expires, we start the refresh thread,
\r
333 // but only if it isn't already running - otherwise we
\r
334 // restart the timer.
\r
336 path.SetFromWin((LPCTSTR)lParam);
\r
338 if (!m_cFileList.HasPath(path))
\r
340 if (m_pathList.AreAllPathsFiles())
\r
342 m_pathList.AddPath(path);
\r
343 m_pathList.RemoveDuplicates();
\r
347 // if the path list contains folders, we have to check whether
\r
348 // our just (maybe) added path is a child of one of those. If it is
\r
349 // a child of a folder already in the list, we must not add it. Otherwise
\r
350 // that path could show up twice in the list.
\r
351 bool bHasParentInList = false;
\r
352 for (int i=0; i<m_pathList.GetCount(); ++i)
\r
354 if (m_pathList[i].IsAncestorOf(path))
\r
356 bHasParentInList = true;
\r
360 if (!bHasParentInList)
\r
362 m_pathList.AddPath(path);
\r
363 m_pathList.RemoveDuplicates();
\r
368 // Always start the timer, since the status of an existing item might have changed
\r
369 SetTimer(REFRESHTIMER, 200, NULL);
\r
370 ATLTRACE(_T("Item %s dropped, timer started\n"), path.GetWinPath());
\r
374 void CLockDlg::OnTimer(UINT_PTR nIDEvent)
\r
381 SetTimer(REFRESHTIMER, 200, NULL);
\r
382 ATLTRACE("Wait some more before refreshing\n");
\r
386 KillTimer(REFRESHTIMER);
\r
387 ATLTRACE("Refreshing after items dropped\n");
\r
392 __super::OnTimer(nIDEvent);
\r