1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2012, 2018, 2021 - TortoiseSVN
4 // Copyright (C) 2012-2016, 2018-2020, 2023-2024 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (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 Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "TortoiseProc.h"
22 #include "RevisionGraphDlg.h"
25 #include "StringUtils.h"
27 #include "UnicodeUtils.h"
29 #include "RevGraphFilterDlg.h"
31 #include "LogDlgFilter.h"
32 #include "GitLogList.h"
37 static char THIS_FILE
[] = __FILE__
;
40 using namespace Gdiplus
;
42 const UINT
CRevisionGraphDlg::m_FindDialogMessage
= RegisterWindowMessage(FINDMSGSTRING
);
50 //WORD aItems[wItemCount]
54 return reinterpret_cast<WORD
*>(this + 1);
58 IMPLEMENT_DYNAMIC(CRevisionGraphDlg
, CResizableStandAloneDialog
)
59 CRevisionGraphDlg::CRevisionGraphDlg(CWnd
* pParent
/*=nullptr*/)
60 : CResizableStandAloneDialog(CRevisionGraphDlg::IDD
, pParent
)
62 , m_fZoomFactor(DEFAULT_ZOOM
)
65 // GDI+ initialization
67 GdiplusStartupInput input
;
68 GdiplusStartup(&m_gdiPlusToken
, &input
, nullptr);
74 CRevisionGraphDlg::~CRevisionGraphDlg()
77 GdiplusShutdown (m_gdiPlusToken
);
80 void CRevisionGraphDlg::DoDataExchange(CDataExchange
* pDX
)
82 CResizableStandAloneDialog::DoDataExchange(pDX
);
86 BEGIN_MESSAGE_MAP(CRevisionGraphDlg
, CResizableStandAloneDialog
)
90 ON_COMMAND(ID_VIEW_ZOOMIN
, OnViewZoomin
)
91 ON_COMMAND(ID_VIEW_ZOOMOUT
, OnViewZoomout
)
92 ON_COMMAND(ID_VIEW_ZOOM100
, OnViewZoom100
)
93 ON_COMMAND(ID_VIEW_ZOOMHEIGHT
, OnViewZoomHeight
)
94 ON_COMMAND(ID_VIEW_ZOOMWIDTH
, OnViewZoomWidth
)
95 ON_COMMAND(ID_VIEW_ZOOMALL
, OnViewZoomAll
)
96 ON_CBN_SELCHANGE(ID_REVGRAPH_ZOOMCOMBO
, OnChangeZoom
)
97 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
98 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
99 ON_COMMAND(ID_MENUEXIT
, OnMenuexit
)
100 ON_COMMAND(ID_MENUHELP
, OnMenuhelp
)
101 ON_COMMAND(ID_FILE_SAVEGRAPHAS
, OnFileSavegraphas
)
102 ON_COMMAND(ID_VIEW_SHOWOVERVIEW
, OnViewShowoverview
)
103 ON_COMMAND(ID_VIEW_FILTER
, OnViewFilter
)
104 ON_COMMAND(ID_VIEW_COMPAREHEADREVISIONS
, OnViewCompareheadrevisions
)
105 ON_COMMAND(ID_VIEW_COMPAREREVISIONS
, OnViewComparerevisions
)
106 ON_COMMAND(ID_VIEW_UNIFIEDDIFF
, OnViewUnifieddiff
)
107 ON_COMMAND(ID_VIEW_UNIFIEDDIFFOFHEADREVISIONS
, OnViewUnifieddiffofheadrevisions
)
108 ON_WM_WINDOWPOSCHANGING()
109 ON_COMMAND(ID_VIEW_SHOWBRANCHINGSANDMERGES
, OnViewShowBranchingsMerges
)
110 ON_COMMAND(ID_VIEW_SHOWALLTAGS
, OnViewShowAllTags
)
111 ON_COMMAND(ID_VIEW_ARROW_POINT_TO_MERGES
, OnViewArrowPointToMerges
)
112 ON_COMMAND(ID_FIND
, OnFind
)
113 ON_REGISTERED_MESSAGE(m_FindDialogMessage
, OnFindDialogMessage
)
114 ON_MESSAGE(WM_DPICHANGED
, OnDPIChanged
)
117 BOOL
CRevisionGraphDlg::InitializeToolbar()
119 // set up the toolbar
120 // add the tool bar to the dialog
121 m_ToolBar
.CreateEx(this, TBSTYLE_FLAT
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
| CBRS_SIZE_DYNAMIC
);
123 // LoadToolBar() asserts in debug mode because the bitmap
124 // fails to load. That's not a problem because we load the bitmap
125 // further down manually.
126 // but the assertion is ugly, so we load the button resource here
127 // manually as well and call SetButtons().
128 HINSTANCE hInst
= AfxFindResourceHandle(MAKEINTRESOURCE(IDR_REVGRAPHBAR
), RT_TOOLBAR
);
129 HRSRC hRsrc
= ::FindResource(hInst
, MAKEINTRESOURCE(IDR_REVGRAPHBAR
), RT_TOOLBAR
);
133 HGLOBAL hGlobal
= LoadResource(hInst
, hRsrc
);
137 auto pData
= reinterpret_cast<CToolBarData
*>(LockResource(hGlobal
));
140 ASSERT(pData
->wVersion
== 1);
142 auto pItems
= std::make_unique
<UINT
[]>(pData
->wItemCount
);
143 for (int i
= 0; i
< pData
->wItemCount
; ++i
)
144 pItems
[i
] = pData
->items()[i
];
145 m_ToolBar
.SetButtons(pItems
.get(), pData
->wItemCount
);
147 UnlockResource(hGlobal
);
148 FreeResource(hGlobal
);
150 m_ToolBar
.ShowWindow(SW_SHOW
);
151 m_ToolBar
.SetBarStyle(CBRS_ALIGN_TOP
| CBRS_TOOLTIPS
| CBRS_FLYBY
);
153 // toolbars aren't true-color without some tweaking:
155 CImageList cImageList
;
159 // load the toolbar with the dimensions of the bitmap itself
160 cBitmap
.Attach(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHBAR
),
162 LR_DEFAULTSIZE
| LR_CREATEDIBSECTION
));
163 cBitmap
.GetBitmap(&bmBitmap
);
164 cBitmap
.DeleteObject();
165 // now load the toolbar again, but this time with the dpi-scaled dimensions
166 // note: we could just load it once and then resize the bitmap, but
167 // that's not faster. So loading it again is what we do.
168 cBitmap
.Attach(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHBAR
),
170 CDPIAware::Instance().ScaleX(GetSafeHwnd(), bmBitmap
.bmWidth
),
171 CDPIAware::Instance().ScaleY(GetSafeHwnd(), bmBitmap
.bmHeight
),
172 LR_CREATEDIBSECTION
));
173 cBitmap
.GetBitmap(&bmBitmap
);
176 CSize
cSize(bmBitmap
.bmWidth
, bmBitmap
.bmHeight
);
177 int nNbBtn
= cSize
.cx
/ CDPIAware::Instance().ScaleX(GetSafeHwnd(), 20);
178 auto rgb
= static_cast<RGBTRIPLE
*>(bmBitmap
.bmBits
);
179 COLORREF rgbMask
= RGB(rgb
[0].rgbtRed
, rgb
[0].rgbtGreen
, rgb
[0].rgbtBlue
);
181 cImageList
.Create(CDPIAware::Instance().ScaleX(GetSafeHwnd(), 20), cSize
.cy
, ILC_COLOR32
| ILC_MASK
| ILC_HIGHQUALITYSCALE
, nNbBtn
, 0);
182 cImageList
.Add(&cBitmap
, rgbMask
);
183 // set the sizes of the button and images:
184 // note: buttonX must be 7 pixels more than imageX, and buttonY must be 6 pixels more than imageY.
185 // See the source of SetSizes().
186 m_ToolBar
.SetSizes(CSize(CDPIAware::Instance().ScaleX(GetSafeHwnd(), 27), CDPIAware::Instance().ScaleY(GetSafeHwnd(), 26)),
187 CSize(CDPIAware::Instance().ScaleX(GetSafeHwnd(), 20), CDPIAware::Instance().ScaleY(GetSafeHwnd(), 20)));
188 m_ToolBar
.SendMessage(TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(cImageList
.m_hImageList
));
193 RepositionBars(AFX_IDW_CONTROLBAR_FIRST
, AFX_IDW_CONTROLBAR_LAST
, 0);
195 #define SNAP_WIDTH CDPIAware::Instance().ScaleX(GetSafeHwnd(), 60) // the width of the combo box
196 // set up the ComboBox control as a snap mode select box
197 // First get the index of the placeholders position in the toolbar
198 int zoomComboIndex
= 0;
199 while (m_ToolBar
.GetItemID(zoomComboIndex
) != ID_REVGRAPH_ZOOMCOMBO
) ++zoomComboIndex
;
201 // next convert that button to a separator and get its position
202 m_ToolBar
.SetButtonInfo(zoomComboIndex
, ID_REVGRAPH_ZOOMCOMBO
, TBBS_SEPARATOR
,
205 m_ToolBar
.GetItemRect(zoomComboIndex
, &rect
);
207 // expand the rectangle to allow the combo box room to drop down
208 rect
.top
+= CDPIAware::Instance().ScaleY(GetSafeHwnd(), 3);
209 rect
.bottom
+= CDPIAware::Instance().ScaleY(GetSafeHwnd(), 200);
211 // then create the combo box and show it
212 if (!m_ToolBar
.m_ZoomCombo
.CreateEx(WS_EX_RIGHT
, WS_CHILD
|WS_VISIBLE
|CBS_AUTOHSCROLL
|CBS_DROPDOWN
,
213 rect
, &m_ToolBar
, ID_REVGRAPH_ZOOMCOMBO
))
215 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Failed to create combo-box\n");
218 m_ToolBar
.m_ZoomCombo
.ShowWindow(SW_SHOW
);
220 // fill the combo box
222 const wchar_t* texts
[] = { L
"5%"
232 COMBOBOXEXITEM cbei
= { 0 };
233 cbei
.mask
= CBEIF_TEXT
;
235 for (const wchar_t** text
= texts
; *text
; ++text
)
237 cbei
.pszText
= const_cast<wchar_t*>(*text
);
238 m_ToolBar
.m_ZoomCombo
.InsertItem(&cbei
);
241 m_ToolBar
.m_ZoomCombo
.SetCurSel(1);
246 BOOL
CRevisionGraphDlg::OnInitDialog()
248 CResizableStandAloneDialog::OnInitDialog();
249 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
252 CAppUtils::SetWindowTitle(*this, g_Git
.m_CurrentDir
);
255 // set up the status bar
256 m_StatusBar
.Create(WS_CHILD
|WS_VISIBLE
|SBT_OWNERDRAW
,
257 CRect(0,0,0,0), this, 1);
258 int strPartDim
[2] = { CDPIAware::Instance().ScaleX(GetSafeHwnd(), 120), -1 };
259 m_StatusBar
.SetParts(2, strPartDim
);
261 if (InitializeToolbar() != TRUE
)
264 m_pTaskbarList
.Release();
265 if (FAILED(m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
)))
266 m_pTaskbarList
= nullptr;
268 m_Graph
.SetShowOverview(InitialSetMenu(L
"ShowRevGraphOverview", false, ID_VIEW_SHOWOVERVIEW
));
269 m_Graph
.m_bShowBranchingsMerges
= InitialSetMenu(L
"ShowRevGraphBranchesMerges", false, ID_VIEW_SHOWBRANCHINGSANDMERGES
);
270 m_Graph
.m_bShowAllTags
= InitialSetMenu(L
"ShowRevGraphAllTags", true, ID_VIEW_SHOWALLTAGS
);
271 m_Graph
.m_bArrowPointToMerges
= InitialSetMenu(L
"ArrowPointToMerges", false, ID_VIEW_ARROW_POINT_TO_MERGES
);
273 // m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_REVISIONGRAPH));
276 CRect graphrect
= GetGraphRect();
277 m_Graph
.Init(this, &graphrect
);
278 m_Graph
.SetOwner(this);
279 m_Graph
.UpdateWindow();
280 DoZoom (DEFAULT_ZOOM
);
282 // begin background operation
285 EnableSaveRestore(L
"RevisionGraphDlg");
286 // if (GetExplorerHWND())
287 // CenterWindow(CWnd::FromHandle(GetExplorerHWND()));
289 SetTheme(CTheme::Instance().IsDarkTheme());
291 return TRUE
; // return TRUE unless you set the focus to a control
294 bool CRevisionGraphDlg::InitialSetMenu(const CString
& settingName
, bool defaultValue
, int nId
)
296 CRegDWORD reg
= CRegDWORD(L
"Software\\TortoiseGit\\" + settingName
, defaultValue
? TRUE
: FALSE
);
297 CMenu
* pMenu
= GetMenu();
299 return static_cast<DWORD
>(reg
) != FALSE
;
300 pMenu
->CheckMenuItem(nId
, MF_BYCOMMAND
| (DWORD(reg
) ? MF_CHECKED
: 0));
301 int tbstate
= m_ToolBar
.GetToolBarCtrl().GetState(nId
);
302 m_ToolBar
.GetToolBarCtrl().SetState(nId
, tbstate
| (DWORD(reg
) ? TBSTATE_CHECKED
: 0));
303 return static_cast<DWORD
>(reg
) != FALSE
;
306 bool CRevisionGraphDlg::ToggleSetMenu(const CString
& settingName
, int nId
)
308 CMenu
* pMenu
= GetMenu();
311 int tbstate
= m_ToolBar
.GetToolBarCtrl().GetState(nId
);
312 UINT state
= pMenu
->GetMenuState(nId
, MF_BYCOMMAND
);
314 if (state
& MF_CHECKED
)
316 pMenu
->CheckMenuItem(nId
, MF_BYCOMMAND
| MF_UNCHECKED
);
317 m_ToolBar
.GetToolBarCtrl().SetState(nId
, tbstate
& (~TBSTATE_CHECKED
));
322 pMenu
->CheckMenuItem(nId
, MF_BYCOMMAND
| MF_CHECKED
);
323 m_ToolBar
.GetToolBarCtrl().SetState(nId
, tbstate
| TBSTATE_CHECKED
);
327 CRegDWORD reg
= CRegDWORD(L
"Software\\TortoiseGit\\" + settingName
, FALSE
);
333 bool CRevisionGraphDlg::UpdateData()
335 CoInitialize(nullptr);
337 if (!m_Graph
.FetchRevisionData (m_Graph
.m_sPath
, nullptr, m_pTaskbarList
, m_hWnd
))
339 // only show the error dialog if we're not in hidden mode
342 // TGitMessageBox( m_hWnd
343 // , // m_Graph.m_state.GetLastErrorMessage()
350 m_Graph
.PostMessage (CRevisionGraphWnd::WM_WORKERTHREADDONE
, 0, 0);
355 void CRevisionGraphDlg::SetTheme(bool bDark
)
357 __super::SetTheme(bDark
);
358 DarkModeHelper::Instance().AllowDarkModeForWindow(m_Graph
.GetSafeHwnd(), bDark
);
359 DarkModeHelper::Instance().AllowDarkModeForWindow(m_StatusBar
.GetSafeHwnd(), bDark
);
360 DarkModeHelper::Instance().AllowDarkModeForWindow(m_ToolBar
.GetSafeHwnd(), bDark
);
362 SetWindowTheme(m_Graph
.GetSafeHwnd(), L
"Explorer", nullptr);
363 SetWindowTheme(m_StatusBar
.GetSafeHwnd(), L
"Explorer", nullptr);
364 SetWindowTheme(m_ToolBar
.GetSafeHwnd(), L
"Explorer", nullptr);
367 void CRevisionGraphDlg::OnSize(UINT nType
, int cx
, int cy
)
369 __super::OnSize(nType
, cx
, cy
);
371 GetClientRect(&rect
);
372 if (IsWindow(m_ToolBar
))
374 RepositionBars(AFX_IDW_CONTROLBAR_FIRST
, AFX_IDW_CONTROLBAR_LAST
, 0);
376 if (IsWindow(m_StatusBar
))
379 m_StatusBar
.GetClientRect(&statusbarrect
);
380 statusbarrect
.top
= rect
.bottom
- statusbarrect
.top
+ statusbarrect
.bottom
;
381 m_StatusBar
.MoveWindow(&statusbarrect
);
383 if (IsWindow(m_Graph
))
385 m_Graph
.MoveWindow (GetGraphRect());
389 BOOL
CRevisionGraphDlg::PreTranslateMessage(MSG
* pMsg
)
391 #define SCROLL_STEP 20
392 if (pMsg
->message
== WM_KEYDOWN
)
395 switch (pMsg
->wParam
)
398 pos
= m_Graph
.GetScrollPos(SB_VERT
);
399 m_Graph
.SetScrollPos(SB_VERT
, pos
- SCROLL_STEP
);
400 m_Graph
.Invalidate();
403 pos
= m_Graph
.GetScrollPos(SB_VERT
);
404 m_Graph
.SetScrollPos(SB_VERT
, pos
+ SCROLL_STEP
);
405 m_Graph
.Invalidate();
408 pos
= m_Graph
.GetScrollPos(SB_HORZ
);
409 m_Graph
.SetScrollPos(SB_HORZ
, pos
- SCROLL_STEP
);
410 m_Graph
.Invalidate();
413 pos
= m_Graph
.GetScrollPos(SB_HORZ
);
414 m_Graph
.SetScrollPos(SB_HORZ
, pos
+ SCROLL_STEP
);
415 m_Graph
.Invalidate();
418 pos
= m_Graph
.GetScrollPos(SB_VERT
);
419 m_Graph
.SetScrollPos(SB_VERT
, pos
- GetGraphRect().Height() / 2);
420 m_Graph
.Invalidate();
423 pos
= m_Graph
.GetScrollPos(SB_VERT
);
424 m_Graph
.SetScrollPos(SB_VERT
, pos
+ GetGraphRect().Height() / 2);
425 m_Graph
.Invalidate();
431 if (GetKeyState(VK_CONTROL
) < 0)
436 if ((m_hAccel
)&&(pMsg
->message
>= WM_KEYFIRST
&& pMsg
->message
<= WM_KEYLAST
))
438 if (pMsg
->wParam
== VK_ESCAPE
)
439 if (m_Graph
.CancelMouseZoom())
441 return TranslateAccelerator(m_hWnd
,m_hAccel
,pMsg
);
443 return __super::PreTranslateMessage(pMsg
);
446 void CRevisionGraphDlg::OnViewShowBranchingsMerges()
448 m_Graph
.m_bShowBranchingsMerges
= ToggleSetMenu(L
"ShowRevGraphBranchesMerges", ID_VIEW_SHOWBRANCHINGSANDMERGES
);
453 void CRevisionGraphDlg::OnViewShowAllTags()
455 m_Graph
.m_bShowAllTags
= ToggleSetMenu(L
"ShowRevGraphAllTags", ID_VIEW_SHOWALLTAGS
);
460 void CRevisionGraphDlg::OnViewArrowPointToMerges()
462 m_Graph
.m_bArrowPointToMerges
= ToggleSetMenu(L
"ArrowPointToMerges", ID_VIEW_ARROW_POINT_TO_MERGES
);
466 void CRevisionGraphDlg::DoZoom (float zoom
)
468 m_fZoomFactor
= zoom
;
469 m_Graph
.DoZoom (zoom
);
473 void CRevisionGraphDlg::OnViewZoomin()
475 DoZoom (min (MAX_ZOOM
, m_fZoomFactor
/ ZOOM_STEP
));
478 void CRevisionGraphDlg::OnViewZoomout()
480 DoZoom (max (MIN_ZOOM
, m_fZoomFactor
* ZOOM_STEP
));
483 void CRevisionGraphDlg::OnViewZoom100()
485 DoZoom (DEFAULT_ZOOM
);
488 void CRevisionGraphDlg::OnViewZoomHeight()
490 CRect graphRect
= m_Graph
.GetGraphRect();
491 CRect windowRect
= m_Graph
.GetWindowRect();
493 float horzfact
= (windowRect
.Width() - 4.0f
)/(4.0f
+ graphRect
.Width());
494 float vertfact
= (windowRect
.Height() - 4.0f
)/(4.0f
+ graphRect
.Height());
495 if ((horzfact
< vertfact
) && (horzfact
< MAX_ZOOM
))
496 vertfact
= (windowRect
.Height() - CDPIAware::Instance().ScaleY(GetSafeHwnd(), 20)) / (4.0f
+ graphRect
.Height());
498 DoZoom (min (MAX_ZOOM
, vertfact
));
501 void CRevisionGraphDlg::OnViewZoomWidth()
503 // zoom the graph so that it is completely visible in the window
504 CRect graphRect
= m_Graph
.GetGraphRect();
505 CRect windowRect
= m_Graph
.GetWindowRect();
507 float horzfact
= (windowRect
.Width() - 4.0f
)/(4.0f
+ graphRect
.Width());
508 float vertfact
= (windowRect
.Height() - 4.0f
)/(4.0f
+ graphRect
.Height());
509 if ((vertfact
< horzfact
) && (vertfact
< MAX_ZOOM
))
510 horzfact
= (windowRect
.Width() - CDPIAware::Instance().ScaleX(GetSafeHwnd(), 20)) / (4.0f
+ graphRect
.Width());
512 DoZoom (min (MAX_ZOOM
, horzfact
));
515 void CRevisionGraphDlg::OnViewZoomAll()
517 // zoom the graph so that it is completely visible in the window
518 CRect graphRect
= m_Graph
.GetGraphRect();
519 CRect windowRect
= m_Graph
.GetWindowRect();
521 float horzfact
= (windowRect
.Width() - 4.0f
)/(4.0f
+ graphRect
.Width());
522 float vertfact
= (windowRect
.Height() - 4.0f
)/(4.0f
+ graphRect
.Height());
524 DoZoom (min (MAX_ZOOM
, min(horzfact
, vertfact
)));
527 void CRevisionGraphDlg::OnFind()
531 m_pFindDialog
= new CFindDlg(this);
532 m_pFindDialog
->Create(this);
536 m_pFindDialog
->SetFocus();
541 LRESULT
CRevisionGraphDlg::OnFindDialogMessage(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
543 ASSERT(m_pFindDialog
);
547 if (m_pFindDialog
->IsTerminating())
549 // invalidate the handle identifying the dialog box.
550 m_pFindDialog
= nullptr;
554 bool bShift
= (GetAsyncKeyState(VK_SHIFT
) & 0x8000) != 0;
555 int cnt
= static_cast<int>(m_Graph
.m_logEntries
.size());
556 if (m_pFindDialog
->IsRef())
559 str
= m_pFindDialog
->GetFindString();
565 if (g_Git
.GetHash(hash
, str
+ L
"^{}")) // add ^{} in order to get the correct SHA-1 (especially for signed tags)
566 MessageBox(g_Git
.GetGitLastErr(L
"Could not get hash of ref \"" + str
+ L
"^{}\"."), L
"TortoiseGit", MB_ICONERROR
);
571 for (i
= 0; i
< cnt
; ++i
)
573 if (m_Graph
.m_logEntries
.at(i
) == hash
)
582 m_pFindDialog
->FlashWindowEx(FLASHW_ALL
, 2, 100);
587 if (m_pFindDialog
->FindNext() && !bFound
)
589 //read data from dialog
590 CLogDlgFilter filter
{ m_pFindDialog
->GetFindString(), m_pFindDialog
->Regex(), LOGFILTER_SUBJECT
| LOGFILTER_MESSAGES
| LOGFILTER_AUTHORS
| LOGFILTER_EMAILS
| LOGFILTER_REVS
| LOGFILTER_REFNAME
, m_pFindDialog
->MatchCase() == TRUE
};
592 for (i
= m_nSearchIndex
+ 1;; ++i
)
597 m_pFindDialog
->FlashWindowEx(FLASHW_ALL
, 2, 100);
599 if (m_nSearchIndex
>= 0)
601 if (i
== m_nSearchIndex
)
603 ::MessageBeep(0xFFFFFFFF);
604 m_pFindDialog
->FlashWindowEx(FLASHW_ALL
, 3, 100);
609 if (filter(m_Graph
.m_LogCache
.GetCacheData(m_Graph
.m_logEntries
.at(i
)), nullptr, m_Graph
.m_HashMap
))
615 } // if(m_pFindDialog->FindNext())
620 m_Graph
.ScrollTo(i
, !bShift
);
627 void CRevisionGraphDlg::OnMenuexit()
629 if (!m_Graph
.IsUpdateJobRunning())
633 void CRevisionGraphDlg::OnMenuhelp()
638 void CRevisionGraphDlg::OnViewCompareheadrevisions()
640 m_Graph
.CompareRevs(L
"HEAD");
643 void CRevisionGraphDlg::OnViewComparerevisions()
645 m_Graph
.CompareRevs(L
"");
648 void CRevisionGraphDlg::OnViewUnifieddiff()
650 m_Graph
.UnifiedDiffRevs(false);
653 void CRevisionGraphDlg::OnViewUnifieddiffofheadrevisions()
655 m_Graph
.UnifiedDiffRevs(true);
658 void CRevisionGraphDlg::UpdateFullHistory()
665 void CRevisionGraphDlg::StartWorkerThread()
667 if (!m_Graph
.IsUpdateJobRunning())
668 m_Graph
.updateJob
= std::make_unique
<CFuture
<bool>>(this, &CRevisionGraphDlg::UpdateData
);
671 void CRevisionGraphDlg::OnCancel()
673 if (!m_Graph
.IsUpdateJobRunning())
677 void CRevisionGraphDlg::OnOK()
682 void CRevisionGraphDlg::OnFileSavegraphas()
686 if (CAppUtils::FileOpenSave(tempfile
, &filterindex
, IDS_REVGRAPH_SAVEPIC
, IDS_PICTUREFILEFILTER
, false, m_hWnd
))
688 // if the user doesn't specify a file extension, default to
689 // svg and add that extension to the filename. But only if the
690 // user chose the 'pictures' filter. The filename isn't changed
691 // if the 'All files' filter was chosen.
693 int dotPos
= tempfile
.ReverseFind('.');
694 int slashPos
= tempfile
.ReverseFind('\\');
695 if (dotPos
> slashPos
)
696 extension
= tempfile
.Mid(dotPos
);
697 if ((filterindex
== 1)&&(extension
.IsEmpty()))
700 tempfile
+= extension
;
702 if ((filterindex
== 2)&&(extension
.IsEmpty()))
705 tempfile
+= extension
;
707 m_Graph
.SaveGraphAs(tempfile
);
711 CRect
CRevisionGraphDlg::GetGraphRect()
714 GetClientRect(&rect
);
717 m_StatusBar
.GetClientRect(&statusbarrect
);
718 rect
.bottom
-= statusbarrect
.Height();
721 m_ToolBar
.GetClientRect(&toolbarrect
);
722 rect
.top
+= toolbarrect
.Height();
727 void CRevisionGraphDlg::UpdateStatusBar()
730 // sFormat.Format(IDS_REVGRAPH_STATUSBARURL, static_cast<LPCWSTR>(m_Graph.m_sPath));
731 // m_StatusBar.SetText(sFormat,1,0);
732 // sFormat.Format(IDS_REVGRAPH_STATUSBARNUMNODES, m_Graph.m_state.GetNodeCount());
733 // m_StatusBar.SetText(sFormat,0,0);
736 void CRevisionGraphDlg::OnChangeZoom()
738 if (!IsWindow(m_Graph
.GetSafeHwnd()))
741 auto pCBox
= static_cast<CComboBoxEx
*>(m_ToolBar
.GetDlgItem(ID_REVGRAPH_ZOOMCOMBO
));
742 pCBox
->GetWindowText(strItem
);
743 if (strItem
.IsEmpty())
746 DoZoom(static_cast<float>(_wtof(strItem
) / 100.0));
749 void CRevisionGraphDlg::UpdateZoomBox()
753 auto pCBox
= static_cast<CComboBoxEx
*>(m_ToolBar
.GetDlgItem(ID_REVGRAPH_ZOOMCOMBO
));
754 pCBox
->GetWindowText(strItem
);
755 strText
.Format(L
"%.0f%%", (m_fZoomFactor
* 100.0));
756 if (strText
.Compare(strItem
) != 0)
757 pCBox
->SetWindowText(strText
);
760 BOOL
CRevisionGraphDlg::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
762 // need to handle both ANSI and UNICODE versions of the message
763 auto pTTTA
= reinterpret_cast<TOOLTIPTEXTA
*>(pNMHDR
);
764 auto pTTTW
= reinterpret_cast<TOOLTIPTEXTW
*>(pNMHDR
);
767 UINT_PTR nID
= pNMHDR
->idFrom
;
769 if (pNMHDR
->code
== TTN_NEEDTEXTA
&& (pTTTA
->uFlags
& TTF_IDISHWND
) ||
770 pNMHDR
->code
== TTN_NEEDTEXTW
&& (pTTTW
->uFlags
& TTF_IDISHWND
))
772 // idFrom is actually the HWND of the tool
773 nID
= ::GetDlgCtrlID(reinterpret_cast<HWND
>(nID
));
776 if (nID
!= 0) // will be zero on a separator
777 strTipText
.LoadString (static_cast<UINT
>(nID
));
780 if (strTipText
.IsEmpty())
783 if (strTipText
.GetLength() >= MAX_TT_LENGTH
)
784 strTipText
= strTipText
.Left(MAX_TT_LENGTH
);
786 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
788 ::SendMessage(pNMHDR
->hwndFrom
, TTM_SETMAXTIPWIDTH
, 0, 600);
789 pTTTA
->lpszText
= m_szTip
;
790 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
794 ::SendMessage(pNMHDR
->hwndFrom
, TTM_SETMAXTIPWIDTH
, 0, 600);
795 lstrcpyn(m_wszTip
, strTipText
, strTipText
.GetLength()+1);
796 pTTTW
->lpszText
= m_wszTip
;
798 // bring the tooltip window above other pop up windows
799 ::SetWindowPos(pNMHDR
->hwndFrom
, HWND_TOP
, 0, 0, 0, 0,
800 SWP_NOACTIVATE
|SWP_NOSIZE
|SWP_NOMOVE
|SWP_NOOWNERZORDER
);
801 return TRUE
; // message was handled
804 void CRevisionGraphDlg::OnViewFilter()
806 CRevGraphFilterDlg dlg
;
808 dlg
.m_bCurrentBranch
= this->m_Graph
.m_bCurrentBranch
;
809 dlg
.m_bLocalBranches
= m_Graph
.m_bLocalBranches
;
810 dlg
.SetRevisionRange(m_Graph
.m_FromRev
, m_Graph
.m_ToRev
);
812 if (dlg
.DoModal()==IDOK
)
814 // user pressed OK to dismiss the dialog, which means
815 // we have to accept the new filter settings and apply them
817 dlg
.GetRevisionRange(m_Graph
.m_FromRev
, m_Graph
.m_ToRev
);
818 // update menu & toolbar
820 this->m_Graph
.m_bCurrentBranch
= dlg
.m_bCurrentBranch
;
821 m_Graph
.m_bLocalBranches
= dlg
.m_bLocalBranches
;
823 CMenu
* pMenu
= GetMenu();
824 int tbstate
= m_ToolBar
.GetToolBarCtrl().GetState(ID_VIEW_FILTER
);
825 if (m_Graph
.m_bCurrentBranch
|| m_Graph
.m_bLocalBranches
|| !m_Graph
.m_FromRev
.IsEmpty() || !m_Graph
.m_ToRev
.IsEmpty())
828 pMenu
->CheckMenuItem(ID_VIEW_FILTER
, MF_BYCOMMAND
| MF_CHECKED
);
829 m_ToolBar
.GetToolBarCtrl().SetState(ID_VIEW_FILTER
, tbstate
| TBSTATE_CHECKED
);
834 pMenu
->CheckMenuItem(ID_VIEW_FILTER
, MF_BYCOMMAND
| MF_UNCHECKED
);
835 m_ToolBar
.GetToolBarCtrl().SetState(ID_VIEW_FILTER
, tbstate
& (~TBSTATE_CHECKED
));
844 void CRevisionGraphDlg::OnViewShowoverview()
846 m_Graph
.SetShowOverview(ToggleSetMenu(L
"ShowRevGraphOverview", ID_VIEW_SHOWOVERVIEW
));
848 m_Graph
.Invalidate(FALSE
);
851 void CRevisionGraphDlg::OnWindowPosChanging(WINDOWPOS
* lpwndpos
)
854 lpwndpos
->flags
&= ~SWP_SHOWWINDOW
;
855 CResizableStandAloneDialog::OnWindowPosChanging(lpwndpos
);
858 LRESULT
CRevisionGraphDlg::OnDPIChanged(WPARAM wParam
, LPARAM lParam
)
860 CDPIAware::Instance().Invalidate();
861 m_ToolBar
.CloseWindow();
862 m_ToolBar
.DestroyWindow();
864 return __super::OnDPIChanged(wParam
, lParam
);