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