1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
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.
19 // ImportPatchDlg.cpp : implementation file
23 #include "TortoiseProc.h"
24 #include "ImportPatchDlg.h"
26 #include "MessageBox.h"
28 // CImportPatchDlg dialog
30 IMPLEMENT_DYNAMIC(CImportPatchDlg
, CResizableStandAloneDialog
)
32 CImportPatchDlg::CImportPatchDlg(CWnd
* pParent
/*=NULL*/)
33 : CResizableStandAloneDialog(CImportPatchDlg::IDD
, pParent
)
35 m_cList
.m_ContextMenuMask
&=~ m_cList
.GetMenuMask(CPatchListCtrl::MENU_APPLY
);
39 m_bExitThread
= false;
40 m_bThreadRunning
=false;
44 m_bAddSignedOffBy
= FALSE
;
47 CImportPatchDlg::~CImportPatchDlg()
52 void CImportPatchDlg::DoDataExchange(CDataExchange
* pDX
)
54 CDialog::DoDataExchange(pDX
);
55 DDX_Control(pDX
, IDC_LIST_PATCH
,m_cList
);
56 DDX_Control(pDX
, IDC_AM_SPLIT
, m_wndSplitter
);
57 DDX_Check(pDX
, IDC_CHECK_3WAY
, m_b3Way
);
58 DDX_Check(pDX
, IDC_CHECK_IGNORE_SPACE
, m_bIgnoreSpace
);
59 DDX_Check(pDX
, IDC_SIGN_OFF
, m_bAddSignedOffBy
);
62 void CImportPatchDlg::AddAmAnchor()
64 AddAnchor(IDC_AM_TAB
,TOP_LEFT
,BOTTOM_RIGHT
);
65 AddAnchor(IDC_LIST_PATCH
, TOP_LEFT
, TOP_RIGHT
);
66 AddAnchor(IDC_AM_SPLIT
, TOP_LEFT
, TOP_RIGHT
);
67 AddAnchor(IDC_BUTTON_ADD
, TOP_RIGHT
);
68 AddAnchor(IDC_BUTTON_UP
, TOP_RIGHT
);
69 AddAnchor(IDC_BUTTON_DOWN
, TOP_RIGHT
);
70 AddAnchor(IDC_BUTTON_REMOVE
, TOP_RIGHT
);
72 AddAnchor(IDOK
,BOTTOM_RIGHT
);
73 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
74 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
76 AddAnchor(IDC_AM_DUMY_TAB
, TOP_LEFT
, BOTTOM_RIGHT
);
78 this->AddOthersToAnchor();
81 void CImportPatchDlg::SetSplitterRange()
83 if ((m_cList
)&&(m_ctrlTabCtrl
))
86 m_cList
.GetWindowRect(rcTop
);
87 ScreenToClient(rcTop
);
89 m_ctrlTabCtrl
.GetWindowRect(rcMiddle
);
90 ScreenToClient(rcMiddle
);
91 if (rcMiddle
.Height() && rcMiddle
.Width())
92 m_wndSplitter
.SetRange(rcTop
.top
+160, rcMiddle
.bottom
-160);
96 BOOL
CImportPatchDlg::OnInitDialog()
98 CResizableStandAloneDialog::OnInitDialog();
100 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
101 // do this, Explorer would be unable to send that message to our window if we
102 // were running elevated. It's OK to make the call all the time, since if we're
103 // not elevated, this is a no-op.
104 CHANGEFILTERSTRUCT cfs
= { sizeof(CHANGEFILTERSTRUCT
) };
105 typedef BOOL STDAPICALLTYPE
ChangeWindowMessageFilterExDFN(HWND hWnd
, UINT message
, DWORD action
, PCHANGEFILTERSTRUCT pChangeFilterStruct
);
106 HMODULE hUser
= ::LoadLibrary(_T("user32.dll"));
109 ChangeWindowMessageFilterExDFN
*pfnChangeWindowMessageFilterEx
= (ChangeWindowMessageFilterExDFN
*)GetProcAddress(hUser
, "ChangeWindowMessageFilterEx");
110 if (pfnChangeWindowMessageFilterEx
)
112 pfnChangeWindowMessageFilterEx(m_hWnd
, WM_TASKBARBTNCREATED
, MSGFLT_ALLOW
, &cfs
);
116 m_pTaskbarList
.Release();
117 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);
121 GetClientRect(m_DlgOrigRect
);
122 m_cList
.GetClientRect(m_PatchListOrigRect
);
124 CWnd
*pwnd
=this->GetDlgItem(IDC_AM_DUMY_TAB
);
125 pwnd
->GetWindowRect(&rectDummy
);
126 this->ScreenToClient(rectDummy
);
128 if (!m_ctrlTabCtrl
.Create(CMFCTabCtrl::STYLE_FLAT
, rectDummy
, this, IDC_AM_TAB
))
130 TRACE0("Failed to create output tab window\n");
131 return FALSE
; // fail to create
133 m_ctrlTabCtrl
.SetResizeMode(CMFCTabCtrl::RESIZE_NO
);
134 // Create output panes:
135 //const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
136 DWORD dwStyle
=LVS_REPORT
| LVS_SHOWSELALWAYS
| LVS_ALIGNLEFT
| WS_BORDER
| WS_TABSTOP
|LVS_SINGLESEL
|WS_CHILD
| WS_VISIBLE
;
138 if( ! this->m_PatchCtrl
.Create(_T("Scintilla"),_T("source"),0,rectDummy
,&m_ctrlTabCtrl
,0,0) )
140 TRACE0("Failed to create log message control");
144 m_PatchCtrl
.Call(SCI_SETREADONLY
, TRUE
);
145 m_PatchCtrl
.SetUDiffStyle();
147 dwStyle
= LBS_NOINTEGRALHEIGHT
| WS_CHILD
| WS_VISIBLE
| WS_HSCROLL
| WS_VSCROLL
;
149 if (!m_wndOutput
.Create(_T("Scintilla"),_T("source"),0,rectDummy
, &m_ctrlTabCtrl
, 0,0) )
151 TRACE0("Failed to create output windows\n");
152 return -1; // fail to create
155 m_wndOutput
.Call(SCI_SETREADONLY
, TRUE
);
157 m_tooltips
.Create(this);
159 m_tooltips
.AddTool(IDC_CHECK_3WAY
,IDS_AM_3WAY_TT
);
160 m_tooltips
.AddTool(IDC_CHECK_IGNORE_SPACE
,IDS_AM_IGNORE_SPACE_TT
);
162 m_ctrlTabCtrl
.AddTab(&m_PatchCtrl
,_T("Patch"),0);
163 m_ctrlTabCtrl
.AddTab(&m_wndOutput
,_T("Log"),1);
167 m_PathList
.SortByPathname(true);
168 m_cList
.SetExtendedStyle( m_cList
.GetExtendedStyle()| LVS_EX_CHECKBOXES
);
170 for(int i
=0;i
<m_PathList
.GetCount();i
++)
172 m_cList
.InsertItem(0,m_PathList
[i
].GetWinPath());
173 m_cList
.SetCheck(0,true);
176 DWORD yPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\AMDlgSizer"));
177 RECT rcDlg
, rcLogMsg
, rcFileList
;
178 GetClientRect(&rcDlg
);
179 m_cList
.GetWindowRect(&rcLogMsg
);
180 ScreenToClient(&rcLogMsg
);
181 this->m_ctrlTabCtrl
.GetWindowRect(&rcFileList
);
182 ScreenToClient(&rcFileList
);
186 m_wndSplitter
.GetWindowRect(&rectSplitter
);
187 ScreenToClient(&rectSplitter
);
188 int delta
= yPos
- rectSplitter
.top
;
189 if ((rcLogMsg
.bottom
+ delta
> rcLogMsg
.top
)&&(rcLogMsg
.bottom
+ delta
< rcFileList
.bottom
- 30))
191 m_wndSplitter
.SetWindowPos(NULL
, 0, yPos
, 0, 0, SWP_NOSIZE
);
196 //CAppUtils::SetListCtrlBackgroundImage(m_cList.GetSafeHwnd(), nID);
199 this->GetWindowText(title
);
200 this->SetWindowText(title
+_T(" - ")+g_Git
.m_CurrentDir
);
201 EnableSaveRestore(_T("ImportDlg"));
208 BEGIN_MESSAGE_MAP(CImportPatchDlg
, CResizableStandAloneDialog
)
209 ON_LBN_SELCHANGE(IDC_LIST_PATCH
, &CImportPatchDlg::OnLbnSelchangeListPatch
)
210 ON_BN_CLICKED(IDC_BUTTON_ADD
, &CImportPatchDlg::OnBnClickedButtonAdd
)
211 ON_BN_CLICKED(IDC_BUTTON_UP
, &CImportPatchDlg::OnBnClickedButtonUp
)
212 ON_BN_CLICKED(IDC_BUTTON_DOWN
, &CImportPatchDlg::OnBnClickedButtonDown
)
213 ON_BN_CLICKED(IDC_BUTTON_REMOVE
, &CImportPatchDlg::OnBnClickedButtonRemove
)
214 ON_BN_CLICKED(IDOK
, &CImportPatchDlg::OnBnClickedOk
)
216 ON_BN_CLICKED(IDCANCEL
, &CImportPatchDlg::OnBnClickedCancel
)
217 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_PATCH
, &CImportPatchDlg::OnHdnItemchangedListPatch
)
218 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED
, OnTaskbarBtnCreated
)
222 // CImportPatchDlg message handlers
224 void CImportPatchDlg::OnLbnSelchangeListPatch()
226 if(m_cList
.GetSelectedCount() == 0)
228 this->GetDlgItem(IDC_BUTTON_UP
)->EnableWindow(FALSE
);
229 this->GetDlgItem(IDC_BUTTON_DOWN
)->EnableWindow(FALSE
);
230 this->GetDlgItem(IDC_BUTTON_REMOVE
)->EnableWindow(FALSE
);
233 this->GetDlgItem(IDC_BUTTON_UP
)->EnableWindow(TRUE
);
234 this->GetDlgItem(IDC_BUTTON_DOWN
)->EnableWindow(TRUE
);
235 this->GetDlgItem(IDC_BUTTON_REMOVE
)->EnableWindow(TRUE
);
239 void CImportPatchDlg::OnBnClickedButtonAdd()
241 CFileDialog
dlg(TRUE
,NULL
,
243 OFN_HIDEREADONLY
| OFN_OVERWRITEPROMPT
|OFN_ALLOWMULTISELECT
,
244 _T("Patch Files(*.patch)|*.patch|Diff Files(*.diff)|*.diff|All Files(*.*)|*.*||"));
245 if(dlg
.DoModal()==IDOK
)
248 pos
=dlg
.GetStartPosition();
251 CString file
=dlg
.GetNextPathName(pos
);
255 m_cList
.InsertItem(0,file
);
256 m_cList
.SetCheck(0,true);
262 void CImportPatchDlg::OnBnClickedButtonUp()
265 pos
=m_cList
.GetFirstSelectedItemPosition();
268 int index
=m_cList
.GetNextSelectedItem(pos
);
271 CString old
=m_cList
.GetItemText(index
,0);
272 m_cList
.DeleteItem(index
);
274 m_cList
.InsertItem(index
-1,old
);
280 void CImportPatchDlg::OnBnClickedButtonDown()
283 pos
=m_cList
.GetFirstSelectedItemPosition();
286 int index
=m_cList
.GetNextSelectedItem(pos
);
288 CString old
=m_cList
.GetItemText(index
,0);
289 m_cList
.DeleteItem(index
);
291 m_cList
.InsertItem(index
+1,old
);
296 void CImportPatchDlg::OnBnClickedButtonRemove()
299 pos
=m_cList
.GetFirstSelectedItemPosition();
302 int index
=m_cList
.GetNextSelectedItem(pos
);
303 m_cList
.DeleteItem(index
);
304 pos
=m_cList
.GetFirstSelectedItemPosition();
308 UINT
CImportPatchDlg::PatchThread()
311 path
.SetFromWin(g_Git
.m_CurrentDir
);
315 for(i
=m_CurrentItem
;i
<m_cList
.GetItemCount();i
++)
319 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
320 m_pTaskbarList
->SetProgressValue(m_hWnd
, i
+1, m_cList
.GetItemCount());
323 m_cList
.SetItemData(i
, CPatchListCtrl::STATUS_APPLYING
|m_cList
.GetItemData(i
));
328 if(m_cList
.GetCheck(i
))
332 while(path
.HasRebaseApply())
335 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
337 int ret
= CMessageBox::Show(NULL
, _T("<ct=0x0000FF>previous rebase directory rebase-apply still exists but mbox given</ct>\n\n Do you want to"),
339 1,IDI_ERROR
,_T("&Abort"), _T("&Skip"),_T("&Resolved"));
344 cmd
=_T("git.exe am --abort");
347 cmd
=_T("git.exe am --skip");
351 cmd
=_T("git.exe am --resolved");
358 m_bExitThread
= TRUE
;
362 this->AddLogString(cmd
);
364 if(g_Git
.Run(cmd
,&output
,CP_ACP
))
366 this->AddLogString(output
);
367 this->AddLogString(_T("Fail"));
370 this->AddLogString(_T("Done"));
377 cmd
=_T("git.exe am ");
379 if(this->m_bAddSignedOffBy
)
380 cmd
+=_T("--signoff ");
385 if(this->m_bIgnoreSpace
)
386 cmd
+=_T("--ignore-space-change ");
389 cmd
+= m_cList
.GetItemText(i
,0);
392 this->AddLogString(cmd
);
394 if(g_Git
.Run(cmd
,&output
,CP_ACP
))
396 //keep STATUS_APPLYING to let user retry failed patch
397 m_cList
.SetItemData(i
, CPatchListCtrl::STATUS_APPLY_FAIL
|CPatchListCtrl::STATUS_APPLYING
);
398 this->AddLogString(output
);
399 this->AddLogString(_T("Fail"));
401 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
406 m_cList
.SetItemData(i
, CPatchListCtrl::STATUS_APPLY_SUCCESS
);
407 this->AddLogString(_T("Success"));
412 AddLogString(CString(_T("Skip Patch: "))+m_cList
.GetItemText(i
,0));
413 m_cList
.SetItemData(i
, CPatchListCtrl::STATUS_APPLY_SKIP
);
416 m_cList
.SetItemData(m_CurrentItem
, (~CPatchListCtrl::STATUS_APPLYING
)&m_cList
.GetItemData(i
));
418 m_cList
.SetItemData(m_CurrentItem
, CPatchListCtrl::STATUS_APPLYING
|m_cList
.GetItemData(i
));
421 this->m_cList
.GetItemRect(i
,&rect
,LVIR_BOUNDS
);
422 this->m_cList
.InvalidateRect(rect
);
424 this->m_cList
.GetItemRect(m_CurrentItem
,&rect
,LVIR_BOUNDS
);
425 this->m_cList
.InvalidateRect(rect
);
428 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
);
430 UpdateOkCancelText();
433 //in case am fail, need refresh finial item status
435 this->m_cList
.GetItemRect(i
,&rect
,LVIR_BOUNDS
);
436 this->m_cList
.InvalidateRect(rect
);
438 this->m_cList
.GetItemRect(m_CurrentItem
,&rect
,LVIR_BOUNDS
);
439 this->m_cList
.InvalidateRect(rect
);
441 EnableInputCtrl(true);
442 InterlockedExchange(&m_bThreadRunning
, FALSE
);
443 UpdateOkCancelText();
447 void CImportPatchDlg::OnBnClickedOk()
460 m_ctrlTabCtrl
.SetActiveTab(1);
462 EnableInputCtrl(false);
463 InterlockedExchange(&m_bThreadRunning
, TRUE
);
464 InterlockedExchange(&this->m_bExitThread
, FALSE
);
465 if ( (m_LoadingThread
=AfxBeginThread(ThreadEntry
, this)) ==NULL
)
467 InterlockedExchange(&m_bThreadRunning
, FALSE
);
468 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
473 void CImportPatchDlg::DoSize(int delta
)
475 this->RemoveAllAnchors();
477 CSplitterControl::ChangeHeight(GetDlgItem(IDC_LIST_PATCH
), delta
, CW_TOPALIGN
);
478 //CSplitterControl::ChangeHeight(GetDlgItem(), delta, CW_TOPALIGN);
479 CSplitterControl::ChangeHeight(GetDlgItem(IDC_AM_TAB
), -delta
, CW_BOTTOMALIGN
);
480 //CSplitterControl::ChangeHeight(GetDlgItem(), -delta, CW_BOTTOMALIGN);
481 CSplitterControl::ChangePos(GetDlgItem(IDC_CHECK_3WAY
),0,delta
);
482 CSplitterControl::ChangePos(GetDlgItem(IDC_CHECK_IGNORE_SPACE
),0,delta
);
483 CSplitterControl::ChangePos(GetDlgItem(IDC_SIGN_OFF
),0,delta
);
486 // adjust the minimum size of the dialog to prevent the resizing from
487 // moving the list control too far down.
489 m_cList
.GetClientRect(rcLogMsg
);
490 SetMinTrackSize(CSize(m_DlgOrigRect
.Width(), m_DlgOrigRect
.Height()-m_PatchListOrigRect
.Height()+rcLogMsg
.Height()));
493 // m_CommitList.Invalidate();
495 // GetDlgItem(IDC_LOGMESSAGE)->Invalidate();
497 this->m_ctrlTabCtrl
.Invalidate();
500 void CImportPatchDlg::OnSize(UINT nType
, int cx
, int cy
)
502 CResizableStandAloneDialog::OnSize(nType
, cx
, cy
);
507 LRESULT
CImportPatchDlg::DefWindowProc(UINT message
, WPARAM wParam
, LPARAM lParam
)
511 if (wParam
== IDC_AM_SPLIT
)
513 SPC_NMHDR
* pHdr
= (SPC_NMHDR
*) lParam
;
519 return CResizableStandAloneDialog::DefWindowProc(message
, wParam
, lParam
);
522 void CImportPatchDlg::SaveSplitterPos()
526 CRegDWORD regPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\AMDlgSizer"));
528 m_wndSplitter
.GetWindowRect(&rectSplitter
);
529 ScreenToClient(&rectSplitter
);
530 regPos
= rectSplitter
.top
;
536 void CImportPatchDlg::EnableInputCtrl(BOOL b
)
538 this->GetDlgItem(IDC_BUTTON_UP
)->EnableWindow(b
);
539 this->GetDlgItem(IDC_BUTTON_DOWN
)->EnableWindow(b
);
540 this->GetDlgItem(IDC_BUTTON_REMOVE
)->EnableWindow(b
);
541 this->GetDlgItem(IDC_BUTTON_ADD
)->EnableWindow(b
);
542 this->GetDlgItem(IDOK
)->EnableWindow(b
);
545 void CImportPatchDlg::UpdateOkCancelText()
549 this->GetDlgItem(IDOK
)->SetWindowText(_T("&Apply"));
553 this->GetDlgItem(IDOK
)->SetWindowText(_T("&OK"));
556 if(this->m_bThreadRunning
)
557 this->GetDlgItem(IDCANCEL
)->SetWindowText(_T("A&bort"));
559 this->GetDlgItem(IDCANCEL
)->SetWindowText(_T("&Cancel"));
562 void CImportPatchDlg::OnBnClickedCancel()
564 if(this->m_bThreadRunning
)
566 InterlockedExchange(&m_bExitThread
,TRUE
);
571 void CImportPatchDlg::AddLogString(CString str
)
573 this->m_wndOutput
.SendMessage(SCI_SETREADONLY
, FALSE
);
574 CStringA sTextA
= m_wndOutput
.StringForControl(str
);//CUnicodeUtils::GetUTF8(str);
575 this->m_wndOutput
.SendMessage(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)sTextA
);
576 this->m_wndOutput
.SendMessage(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)"\n");
577 this->m_wndOutput
.SendMessage(SCI_SETREADONLY
, TRUE
);
578 //this->m_wndOutput.SendMessage(SCI_LINESCROLL,0,0x7FFFFFFF);
580 BOOL
CImportPatchDlg::PreTranslateMessage(MSG
* pMsg
)
582 if (pMsg
->message
== WM_KEYDOWN
)
584 switch (pMsg
->wParam
)
587 /* Avoid TAB control destroy but dialog exist*/
592 ::GetClassName(pMsg
->hwnd
,buff
,128);
594 if(_tcsnicmp(buff
,_T("RichEdit20W"),128)==0 ||
595 _tcsnicmp(buff
,_T("Scintilla"),128)==0 ||
596 _tcsnicmp(buff
,_T("SysListView32"),128)==0||
597 ::GetParent(pMsg
->hwnd
) == this->m_ctrlTabCtrl
.m_hWnd
)
599 this->PostMessage(WM_KEYDOWN
,VK_ESCAPE
,0);
605 m_tooltips
.RelayEvent(pMsg
);
606 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
609 void CImportPatchDlg::OnHdnItemchangedListPatch(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
613 if(this->m_cList
.GetSelectedCount() != 1)
615 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, FALSE
);
616 m_PatchCtrl
.SetText(CString());
617 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, TRUE
);
623 pos
= m_cList
.GetFirstSelectedItemPosition();
624 int selected
= m_cList
.GetNextSelectedItem(pos
);
626 if(selected
>=0&& selected
< m_cList
.GetItemCount())
628 CString str
= m_cList
.GetItemText(selected
,0);
629 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, FALSE
);
630 m_PatchCtrl
.SetText(text
);
631 m_PatchCtrl
.LoadFromFile(str
);
632 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, TRUE
);
636 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, FALSE
);
637 m_PatchCtrl
.SetText(text
);
638 m_PatchCtrl
.SendMessage(SCI_SETREADONLY
, TRUE
);
643 LRESULT
CImportPatchDlg::OnTaskbarBtnCreated(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
645 m_pTaskbarList
.Release();
646 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);