1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2021, 2023 - TortoiseGit
4 // Copyright (C) 2003-2014, 2020 - TortoiseSVN
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 "TortoiseUDiff.h"
22 #include "MainWindow.h"
23 #include "UnicodeUtils.h"
24 #include "StringUtils.h"
25 #include "TaskbarUUID.h"
26 #include "CreateProcessHelper.h"
27 #include "UDiffColors.h"
30 #include "LoadIconEx.h"
32 #include "DarkModeHelper.h"
35 const UINT TaskBarButtonCreated
= RegisterWindowMessage(L
"TaskbarButtonCreated");
37 #define SEARCHBARHEIGHT 30
39 CMainWindow::CMainWindow(HINSTANCE hInst
, const WNDCLASSEX
* wcx
/* = nullptr*/)
42 SetWindowTitle(L
"TortoiseGitUDiff");
45 CMainWindow::~CMainWindow()
49 bool CMainWindow::RegisterAndCreateWindow()
53 // Fill in the window class structure with default parameters
54 wcx
.cbSize
= sizeof(WNDCLASSEX
);
55 wcx
.style
= CS_HREDRAW
| CS_VREDRAW
;
56 wcx
.lpfnWndProc
= CWindow::stWinMsgHandler
;
59 wcx
.hInstance
= hResource
;
60 wcx
.hCursor
= nullptr;
61 ResString
clsname(hResource
, IDS_APP_TITLE
);
62 wcx
.lpszClassName
= clsname
;
63 wcx
.hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_TORTOISEUDIFF
), GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
64 wcx
.hbrBackground
= reinterpret_cast<HBRUSH
>(COLOR_3DFACE
+ 1);
65 wcx
.lpszMenuName
= MAKEINTRESOURCE(IDC_TORTOISEUDIFF
);
66 wcx
.hIconSm
= LoadIconEx(wcx
.hInstance
, MAKEINTRESOURCE(IDI_TORTOISEUDIFF
));
67 if (RegisterWindow(&wcx
))
69 if (Create(WS_CAPTION
| WS_MAXIMIZEBOX
| WS_MINIMIZEBOX
| WS_SIZEBOX
| WS_SYSMENU
| WS_CLIPCHILDREN
, nullptr))
71 m_FindBar
.SetParent(*this);
72 m_FindBar
.Create(::hResource
, IDD_FINDBAR
, *this);
80 void CMainWindow::UpdateLineCount()
82 auto numberOfLines
= static_cast<intptr_t>(SendEditor(SCI_GETLINECOUNT
));
89 SendEditor(SCI_SETMARGINWIDTHN
, 0, numDigits
* static_cast<int>(SendEditor(SCI_TEXTWIDTH
, STYLE_LINENUMBER
, reinterpret_cast<LPARAM
>("8"))));
92 LRESULT CALLBACK
CMainWindow::WinMsgHandler(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
94 if (uMsg
== TaskBarButtonCreated
)
96 SetUUIDOverlayIcon(hwnd
);
98 auto optRet
= CTheme::HandleMenuBar(hwnd
, uMsg
, wParam
, lParam
);
99 if (optRet
.has_value())
100 return optRet
.value();
111 return DoCommand(LOWORD(wParam
));
116 if (GET_KEYSTATE_WPARAM(wParam
) == MK_SHIFT
)
119 SendEditor(SCI_LINESCROLL
, -GET_WHEEL_DELTA_WPARAM(wParam
)/40, 0);
122 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
128 GetClientRect(*this, &rect
);
131 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
133 rect
.right
- rect
.left
, rect
.bottom
- rect
.top
- int(SEARCHBARHEIGHT
* CDPIAware::Instance().ScaleFactorY(hwnd
)),
135 ::SetWindowPos(m_FindBar
, HWND_TOP
,
136 rect
.left
, rect
.bottom
- int((SEARCHBARHEIGHT
+ 2) * CDPIAware::Instance().ScaleFactorY(hwnd
)),
137 rect
.right
- rect
.left
, int(SEARCHBARHEIGHT
* CDPIAware::Instance().ScaleFactorY(hwnd
)),
142 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
144 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
146 ::ShowWindow(m_FindBar
, SW_HIDE
);
150 case WM_GETMINMAXINFO
:
152 auto mmi
= reinterpret_cast<MINMAXINFO
*>(lParam
);
153 mmi
->ptMinTrackSize
.x
= 100;
154 mmi
->ptMinTrackSize
.y
= 100;
162 if (!canCloseWhenModified())
164 ::DestroyWindow(m_hwnd
);
167 SetFocus(m_hWndEdit
);
169 case WM_SYSCOLORCHANGE
:
170 CTheme::Instance().OnSysColorChanged();
171 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme(), true);
175 CDPIAware::Instance().Invalidate();
176 SendMessage(m_hWndEdit
, WM_DPICHANGED
, wParam
, lParam
);
177 const RECT
* rect
= reinterpret_cast<RECT
*>(lParam
);
178 SetWindowPos(*this, NULL
, rect
->left
, rect
->top
, rect
->right
- rect
->left
, rect
->bottom
- rect
->top
, SWP_NOZORDER
| SWP_NOACTIVATE
);
179 ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME
| RDW_INVALIDATE
| RDW_ERASE
| RDW_INTERNALPAINT
| RDW_ALLCHILDREN
| RDW_UPDATENOW
);
182 case COMMITMONITOR_FINDMSGNEXT
:
183 m_bMatchCase
= !!wParam
;
184 m_findtext
= reinterpret_cast<LPCWSTR
>(lParam
);
187 case COMMITMONITOR_FINDMSGPREV
:
188 m_bMatchCase
= !!wParam
;
189 m_findtext
= reinterpret_cast<LPCWSTR
>(lParam
);
192 case COMMITMONITOR_FINDEXIT
:
195 GetClientRect(*this, &rect
);
196 m_bShowFindBar
= false;
197 ::ShowWindow(m_FindBar
, SW_HIDE
);
198 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
200 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
204 case COMMITMONITOR_FINDRESET
:
205 SendEditor(SCI_SETSELECTIONSTART
, 0);
206 SendEditor(SCI_SETSELECTIONEND
, 0);
209 if (reinterpret_cast<LPNMHDR
>(lParam
)->code
== SCN_PAINTED
)
213 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
219 LRESULT
CMainWindow::DoCommand(int id
)
224 if (!canCloseWhenModified())
226 loadOrSaveFile(true);
229 loadOrSaveFile(false);
232 loadOrSaveFile(false, m_filename
);
235 if (!canCloseWhenModified())
237 ::PostQuitMessage(0);
239 case IDM_SHOWFINDBAR
:
241 m_bShowFindBar
= true;
242 ::ShowWindow(m_FindBar
, SW_SHOW
);
244 GetClientRect(*this, &rect
);
245 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
247 rect
.right
- rect
.left
, rect
.bottom
- rect
.top
- int(SEARCHBARHEIGHT
* CDPIAware::Instance().ScaleFactorY(*this)),
249 ::SetWindowPos(m_FindBar
, HWND_TOP
,
250 rect
.left
, rect
.bottom
- int((SEARCHBARHEIGHT
+ 2) * CDPIAware::Instance().ScaleFactorY(*this)),
251 rect
.right
- rect
.left
, int(SEARCHBARHEIGHT
* CDPIAware::Instance().ScaleFactorY(*this)),
253 if (auto selstart
= SendEditor(SCI_GETSELECTIONSTART
), selend
= SendEditor(SCI_GETSELECTIONEND
); selstart
!= selend
)
255 auto linebuf
= std::make_unique
<char[]>(selend
- selstart
+ 1);
256 Sci_TextRange range
= { static_cast<Sci_PositionCR
>(selstart
), static_cast<Sci_PositionCR
>(selend
), linebuf
.get() };
257 if (SendEditor(SCI_GETTEXTRANGE
, 0, reinterpret_cast<LPARAM
>(&range
)) > 0)
258 m_FindBar
.SetSearchString(CUnicodeUtils::StdGetUnicode(linebuf
.get()).c_str());
260 m_FindBar
.SelectSearchString();
261 ::SetFocus(m_FindBar
);
265 if (m_findtext
.empty())
267 DoCommand(IDM_SHOWFINDBAR
);
273 if (m_findtext
.empty())
275 DoCommand(IDM_SHOWFINDBAR
);
281 if (IsWindowVisible(m_FindBar
))
284 GetClientRect(*this, &rect
);
285 m_bShowFindBar
= false;
286 ::ShowWindow(m_FindBar
, SW_HIDE
);
287 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
289 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
292 else if (canCloseWhenModified())
295 case ID_FILE_SETTINGS
:
297 std::wstring gitCmd
= L
" /command:settings /page:udiff";
301 case ID_FILE_APPLYPATCH
:
303 std::wstring command
= L
" /diff:\"";
304 command
+= m_filename
;
306 std::wstring tortoiseMergePath
= GetAppDirectory() + L
"TortoiseGitMerge.exe";
307 CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath
.c_str(), command
.c_str());
310 case ID_FILE_PAGESETUP
:
312 wchar_t localeInfo
[3] = { 0 };
313 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IMEASURE
, localeInfo
, 3);
314 // Metric system. '1' is US System
315 int defaultMargin
= localeInfo
[0] == '0' ? 2540 : 1000;
317 PAGESETUPDLG pdlg
= {0};
318 pdlg
.lStructSize
= sizeof(PAGESETUPDLG
);
319 pdlg
.hwndOwner
= *this;
320 pdlg
.hInstance
= nullptr;
321 pdlg
.Flags
= PSD_DEFAULTMINMARGINS
|PSD_MARGINS
|PSD_DISABLEPAPER
|PSD_DISABLEORIENTATION
;
322 if (localeInfo
[0] == '0')
323 pdlg
.Flags
|= PSD_INHUNDREDTHSOFMILLIMETERS
;
325 CRegStdDWORD m_regMargLeft
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin
);
326 CRegStdDWORD m_regMargTop
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin
);
327 CRegStdDWORD m_regMargRight
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin
);
328 CRegStdDWORD m_regMargBottom
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin
);
330 pdlg
.rtMargin
.left
= static_cast<long>(m_regMargLeft
);
331 pdlg
.rtMargin
.top
= static_cast<long>(m_regMargTop
);
332 pdlg
.rtMargin
.right
= static_cast<long>(m_regMargRight
);
333 pdlg
.rtMargin
.bottom
= static_cast<long>(m_regMargBottom
);
335 if (!PageSetupDlg(&pdlg
))
338 m_regMargLeft
= pdlg
.rtMargin
.left
;
339 m_regMargTop
= pdlg
.rtMargin
.top
;
340 m_regMargRight
= pdlg
.rtMargin
.right
;
341 m_regMargBottom
= pdlg
.rtMargin
.bottom
;
346 PRINTDLGEX pdlg
= {0};
347 pdlg
.lStructSize
= sizeof(PRINTDLGEX
);
348 pdlg
.hwndOwner
= *this;
349 pdlg
.hInstance
= nullptr;
350 pdlg
.Flags
= PD_USEDEVMODECOPIESANDCOLLATE
| PD_ALLPAGES
| PD_RETURNDC
| PD_NOCURRENTPAGE
| PD_NOPAGENUMS
;
352 pdlg
.nMaxPage
= 0xffffU
; // We do not know how many pages in the document
355 pdlg
.nStartPage
= START_PAGE_GENERAL
;
357 // See if a range has been selected
358 auto startPos
= static_cast<Sci_Position
>(SendEditor(SCI_GETSELECTIONSTART
));
359 auto endPos
= static_cast<Sci_Position
>(SendEditor(SCI_GETSELECTIONEND
));
361 if (startPos
== endPos
)
362 pdlg
.Flags
|= PD_NOSELECTION
;
364 pdlg
.Flags
|= PD_SELECTION
;
366 HRESULT hResult
= PrintDlgEx(&pdlg
);
367 if ((hResult
!= S_OK
) || (pdlg
.dwResultAction
!= PD_RESULT_PRINT
))
370 // reset all indicators
371 auto endpos
= static_cast<int>(SendEditor(SCI_GETLENGTH
));
372 for (int i
= INDIC_CONTAINER
; i
<= INDIC_MAX
; ++i
)
374 SendEditor(SCI_SETINDICATORCURRENT
, i
);
375 SendEditor(SCI_INDICATORCLEARRANGE
, 0, endpos
);
377 // store and reset UI settings
378 auto viewws
= static_cast<int>(SendEditor(SCI_GETVIEWWS
));
379 SendEditor(SCI_SETVIEWWS
, 0);
380 auto edgemode
= static_cast<int>(SendEditor(SCI_GETEDGEMODE
));
381 SendEditor(SCI_SETEDGEMODE
, EDGE_NONE
);
382 SendEditor(SCI_SETWRAPVISUALFLAGS
, SC_WRAPVISUALFLAG_END
);
386 RECT rectMargins
, rectPhysMargins
;
390 // Get printer resolution
391 ptDpi
.x
= GetDeviceCaps(hdc
, LOGPIXELSX
); // dpi in X direction
392 ptDpi
.y
= GetDeviceCaps(hdc
, LOGPIXELSY
); // dpi in Y direction
394 // Start by getting the physical page size (in device units).
395 ptPage
.x
= GetDeviceCaps(hdc
, PHYSICALWIDTH
); // device units
396 ptPage
.y
= GetDeviceCaps(hdc
, PHYSICALHEIGHT
); // device units
398 // Get the dimensions of the unprintable
399 // part of the page (in device units).
400 rectPhysMargins
.left
= GetDeviceCaps(hdc
, PHYSICALOFFSETX
);
401 rectPhysMargins
.top
= GetDeviceCaps(hdc
, PHYSICALOFFSETY
);
403 // To get the right and lower unprintable area,
404 // we take the entire width and height of the paper and
405 // subtract everything else.
406 rectPhysMargins
.right
= ptPage
.x
// total paper width
407 - GetDeviceCaps(hdc
, HORZRES
) // printable width
408 - rectPhysMargins
.left
; // left unprintable margin
410 rectPhysMargins
.bottom
= ptPage
.y
// total paper height
411 - GetDeviceCaps(hdc
, VERTRES
) // printable height
412 - rectPhysMargins
.top
; // right unprintable margin
414 wchar_t localeInfo
[3] = { 0 };
415 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IMEASURE
, localeInfo
, 3);
416 // Metric system. '1' is US System
417 int defaultMargin
= localeInfo
[0] == '0' ? 2540 : 1000;
418 RECT pagesetupMargin
;
419 CRegStdDWORD m_regMargLeft
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin
);
420 CRegStdDWORD m_regMargTop
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin
);
421 CRegStdDWORD m_regMargRight
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin
);
422 CRegStdDWORD m_regMargBottom
= CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin
);
424 pagesetupMargin
.left
= static_cast<long>(m_regMargLeft
);
425 pagesetupMargin
.top
= static_cast<long>(m_regMargTop
);
426 pagesetupMargin
.right
= static_cast<long>(m_regMargRight
);
427 pagesetupMargin
.bottom
= static_cast<long>(m_regMargBottom
);
429 if (pagesetupMargin
.left
!= 0 || pagesetupMargin
.right
!= 0 ||
430 pagesetupMargin
.top
!= 0 || pagesetupMargin
.bottom
!= 0)
434 // Convert the hundredths of millimeters (HiMetric) or
435 // thousandths of inches (HiEnglish) margin values
436 // from the Page Setup dialog to device units.
437 // (There are 2540 hundredths of a mm in an inch.)
438 if (localeInfo
[0] == '0')
440 // Metric system. '1' is US System
441 rectSetup
.left
= MulDiv (pagesetupMargin
.left
, ptDpi
.x
, 2540);
442 rectSetup
.top
= MulDiv (pagesetupMargin
.top
, ptDpi
.y
, 2540);
443 rectSetup
.right
= MulDiv(pagesetupMargin
.right
, ptDpi
.x
, 2540);
444 rectSetup
.bottom
= MulDiv(pagesetupMargin
.bottom
, ptDpi
.y
, 2540);
448 rectSetup
.left
= MulDiv(pagesetupMargin
.left
, ptDpi
.x
, 1000);
449 rectSetup
.top
= MulDiv(pagesetupMargin
.top
, ptDpi
.y
, 1000);
450 rectSetup
.right
= MulDiv(pagesetupMargin
.right
, ptDpi
.x
, 1000);
451 rectSetup
.bottom
= MulDiv(pagesetupMargin
.bottom
, ptDpi
.y
, 1000);
454 // Don't reduce margins below the minimum printable area
455 rectMargins
.left
= max(rectPhysMargins
.left
, rectSetup
.left
);
456 rectMargins
.top
= max(rectPhysMargins
.top
, rectSetup
.top
);
457 rectMargins
.right
= max(rectPhysMargins
.right
, rectSetup
.right
);
458 rectMargins
.bottom
= max(rectPhysMargins
.bottom
, rectSetup
.bottom
);
462 rectMargins
.left
= rectPhysMargins
.left
;
463 rectMargins
.top
= rectPhysMargins
.top
;
464 rectMargins
.right
= rectPhysMargins
.right
;
465 rectMargins
.bottom
= rectPhysMargins
.bottom
;
468 // rectMargins now contains the values used to shrink the printable
471 // Convert device coordinates into logical coordinates
472 DPtoLP(hdc
, reinterpret_cast<LPPOINT
>(&rectMargins
), 2);
473 DPtoLP(hdc
, reinterpret_cast<LPPOINT
>(&rectPhysMargins
), 2);
475 // Convert page size to logical units and we're done!
476 DPtoLP(hdc
, reinterpret_cast<LPPOINT
>(&ptPage
), 1);
479 DOCINFO di
= {sizeof(DOCINFO
), 0, 0, 0, 0};
480 di
.lpszDocName
= m_filename
.c_str();
484 if (::StartDoc(hdc
, &di
) < 0)
490 size_t lengthDoc
= static_cast<int>(SendEditor(SCI_GETLENGTH
));
491 size_t lengthDocMax
= lengthDoc
;
492 size_t lengthPrinted
= 0;
494 // Requested to print selection
495 if (pdlg
.Flags
& PD_SELECTION
)
497 if (startPos
> endPos
)
499 lengthPrinted
= endPos
;
500 lengthDoc
= startPos
;
504 lengthPrinted
= startPos
;
508 if (lengthDoc
> lengthDocMax
)
509 lengthDoc
= lengthDocMax
;
512 // We must subtract the physical margins from the printable area
513 Sci_RangeToFormat frPrint
;
515 frPrint
.hdcTarget
= hdc
;
516 frPrint
.rc
.left
= rectMargins
.left
- rectPhysMargins
.left
;
517 frPrint
.rc
.top
= rectMargins
.top
- rectPhysMargins
.top
;
518 frPrint
.rc
.right
= ptPage
.x
- rectMargins
.right
- rectPhysMargins
.left
;
519 frPrint
.rc
.bottom
= ptPage
.y
- rectMargins
.bottom
- rectPhysMargins
.top
;
520 frPrint
.rcPage
.left
= 0;
521 frPrint
.rcPage
.top
= 0;
522 frPrint
.rcPage
.right
= ptPage
.x
- rectPhysMargins
.left
- rectPhysMargins
.right
- 1;
523 frPrint
.rcPage
.bottom
= ptPage
.y
- rectPhysMargins
.top
- rectPhysMargins
.bottom
- 1;
526 while (lengthPrinted
< lengthDoc
)
530 frPrint
.chrg
.cpMin
= static_cast<long>(lengthPrinted
);
531 frPrint
.chrg
.cpMax
= static_cast<long>(lengthDoc
);
533 lengthPrinted
= SendEditor(SCI_FORMATRANGE
, true, reinterpret_cast<LPARAM
>(&frPrint
));
538 SendEditor(SCI_FORMATRANGE
, FALSE
, 0);
544 GlobalFree(pdlg
.hDevMode
);
546 GlobalFree(pdlg
.hDevNames
);
547 if (pdlg
.lpPageRanges
)
548 GlobalFree(pdlg
.lpPageRanges
);
551 SendEditor(SCI_SETVIEWWS
, viewws
);
552 SendEditor(SCI_SETEDGEMODE
, edgemode
);
553 SendEditor(SCI_SETWRAPVISUALFLAGS
, SC_WRAPVISUALFLAG_NONE
);
556 case ID_VIEW_DARKMODE
:
557 CTheme::Instance().SetDarkTheme(!CTheme::Instance().IsDarkTheme());
565 std::wstring
CMainWindow::GetAppDirectory()
569 DWORD bufferlen
= MAX_PATH
; // MAX_PATH is not the limit here!
572 bufferlen
+= MAX_PATH
; // MAX_PATH is not the limit here!
573 auto pBuf
= std::make_unique
<wchar_t[]>(bufferlen
);
574 len
= GetModuleFileName(nullptr, pBuf
.get(), bufferlen
);
575 path
= std::wstring(pBuf
.get(), len
);
576 } while(len
== bufferlen
);
577 path
= path
.substr(0, path
.rfind('\\') + 1);
582 void CMainWindow::RunCommand(const std::wstring
& command
)
584 std::wstring tortoiseProcPath
= GetAppDirectory() + L
"TortoiseGitProc.exe";
585 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath
.c_str(), command
.c_str());
588 LRESULT
CMainWindow::SendEditor(UINT Msg
, WPARAM wParam
, LPARAM lParam
)
590 if (m_directFunction
)
592 return reinterpret_cast<SciFnDirect
>(m_directFunction
)(m_directPointer
, Msg
, wParam
, lParam
);
594 return ::SendMessage(m_hWndEdit
, Msg
, wParam
, lParam
);
597 bool CMainWindow::Initialize()
599 m_themeCallbackId
= CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme()); });
600 m_hWndEdit
= ::CreateWindow(
603 WS_CHILD
| WS_VSCROLL
| WS_HSCROLL
| WS_CLIPCHILDREN
,
604 CW_USEDEFAULT
, CW_USEDEFAULT
,
605 CW_USEDEFAULT
, CW_USEDEFAULT
,
614 GetClientRect(*this, &rect
);
615 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
617 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
620 m_directFunction
= SendMessage(m_hWndEdit
, SCI_GETDIRECTFUNCTION
, 0, 0);
621 m_directPointer
= SendMessage(m_hWndEdit
, SCI_GETDIRECTPOINTER
, 0, 0);
623 // Set up the global default style. These attributes are used wherever no explicit choices are made.
624 SendEditor(SCI_SETTABWIDTH
, CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffTabSize", 4));
625 SendEditor(SCI_SETREADONLY
, TRUE
);
627 SendEditor(SCI_SETMARGINWIDTHN
, 1);
628 SendEditor(SCI_SETMARGINWIDTHN
, 2);
630 if (CRegStdDWORD(L
"Software\\TortoiseGit\\ScintillaDirect2D", FALSE
) != FALSE
)
632 SendEditor(SCI_SETTECHNOLOGY
, SC_TECHNOLOGY_DIRECTWRITERETAIN
);
633 SendEditor(SCI_SETBUFFEREDDRAW
, 0);
635 SendEditor(SCI_SETVIEWWS
, 1);
636 SendEditor(SCI_SETWHITESPACESIZE
, 2);
637 SendEditor(SCI_STYLESETVISIBLE
, STYLE_CONTROLCHAR
, TRUE
);
639 SetTheme(CTheme::Instance().IsDarkTheme());
644 void CMainWindow::SetTheme(bool bDark
)
646 auto fontNameW
= CRegStdString(L
"Software\\TortoiseGit\\UDiffFontName", L
"Consolas");
647 auto fontName
= CUnicodeUtils::StdGetUTF8(fontNameW
);
651 DarkModeHelper::Instance().AllowDarkModeForApp(TRUE
);
653 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, TRUE
);
654 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetStockObject(BLACK_BRUSH
)));
655 if (FAILED(SetWindowTheme(*this, L
"DarkMode_Explorer", nullptr)))
656 SetWindowTheme(*this, L
"Explorer", nullptr);
657 DarkModeHelper::Instance().AllowDarkModeForWindow(m_hWndEdit
, TRUE
);
658 if (FAILED(SetWindowTheme(m_hWndEdit
, L
"DarkMode_Explorer", nullptr)))
659 SetWindowTheme(m_hWndEdit
, L
"Explorer", nullptr);
660 BOOL darkFlag
= TRUE
;
661 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
662 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
663 DarkModeHelper::Instance().FlushMenuThemes();
664 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
668 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, FALSE
);
669 DarkModeHelper::Instance().AllowDarkModeForWindow(m_hWndEdit
, FALSE
);
670 BOOL darkFlag
= FALSE
;
671 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
672 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
673 DarkModeHelper::Instance().FlushMenuThemes();
674 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
675 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE
);
676 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetSysColorBrush(COLOR_3DFACE
)));
677 SetWindowTheme(*this, L
"Explorer", nullptr);
678 SetWindowTheme(m_hWndEdit
, L
"Explorer", nullptr);
683 SendEditor(SCI_STYLESETFORE
, STYLE_DEFAULT
, UDiffTextColorDark
);
684 SendEditor(SCI_STYLESETBACK
, STYLE_DEFAULT
, UDiffBackColorDark
);
685 SendEditor(SCI_SETSELFORE
, TRUE
, CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_HIGHLIGHTTEXT
)));
686 SendEditor(SCI_SETSELBACK
, TRUE
, CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_HIGHLIGHT
)));
687 SendEditor(SCI_SETCARETFORE
, UDiffTextColorDark
);
688 SendEditor(SCI_SETWHITESPACEFORE
, true, RGB(180, 180, 180));
689 SetAStyle(STYLE_DEFAULT
, UDiffTextColorDark
, UDiffBackColorDark
,
690 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffFontSize", 10), fontName
.c_str());
691 SetAStyle(SCE_DIFF_DEFAULT
, UDiffTextColorDark
, UDiffBackColorDark
,
692 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffFontSize", 10), fontName
.c_str());
693 SetAStyle(SCE_DIFF_COMMAND
,
694 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForeCommandColor", UDIFF_COLORFORECOMMAND_DARK
),
695 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackCommandColor", UDIFF_COLORBACKCOMMAND_DARK
));
696 SetAStyle(SCE_DIFF_POSITION
,
697 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForePositionColor", UDIFF_COLORFOREPOSITION_DARK
),
698 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackPositionColor", UDIFF_COLORBACKPOSITION_DARK
));
699 SetAStyle(SCE_DIFF_HEADER
,
700 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForeHeaderColor", UDIFF_COLORFOREHEADER_DARK
),
701 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackHeaderColor", UDIFF_COLORBACKHEADER_DARK
));
702 SetAStyle(SCE_DIFF_COMMENT
,
703 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForeCommentColor", UDIFF_COLORFORECOMMENT_DARK
),
704 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackCommentColor", UDIFF_COLORBACKCOMMENT_DARK
));
705 for (int style
: { SCE_DIFF_ADDED
, SCE_DIFF_PATCH_ADD
, SCE_DIFF_PATCH_DELETE
})
708 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForeAddedColor", UDIFF_COLORFOREADDED_DARK
),
709 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackAddedColor", UDIFF_COLORBACKADDED_DARK
));
711 for (int style
: { SCE_DIFF_DELETED
, SCE_DIFF_REMOVED_PATCH_ADD
, SCE_DIFF_REMOVED_PATCH_DELETE
})
714 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffForeRemovedColor", UDIFF_COLORFOREREMOVED_DARK
),
715 CRegStdDWORD(L
"Software\\TortoiseGit\\DarkUDiffBackRemovedColor", UDIFF_COLORBACKREMOVED_DARK
));
720 SendEditor(SCI_STYLESETFORE
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
));
721 SendEditor(SCI_STYLESETBACK
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOW
));
722 SendEditor(SCI_SETSELFORE
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
723 SendEditor(SCI_SETSELBACK
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHT
));
724 SendEditor(SCI_SETCARETFORE
, ::GetSysColor(COLOR_WINDOWTEXT
));
725 SendEditor(SCI_SETWHITESPACEFORE
, true, ::GetSysColor(COLOR_3DSHADOW
));
726 SetAStyle(STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
), ::GetSysColor(COLOR_WINDOW
),
727 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffFontSize", 10), fontName
.c_str());
728 SetAStyle(SCE_DIFF_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
), ::GetSysColor(COLOR_WINDOW
),
729 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffFontSize", 10), fontName
.c_str());
730 SetAStyle(SCE_DIFF_COMMAND
,
731 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForeCommandColor", UDIFF_COLORFORECOMMAND
),
732 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackCommandColor", UDIFF_COLORBACKCOMMAND
));
733 SetAStyle(SCE_DIFF_POSITION
,
734 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForePositionColor", UDIFF_COLORFOREPOSITION
),
735 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackPositionColor", UDIFF_COLORBACKPOSITION
));
736 SetAStyle(SCE_DIFF_HEADER
,
737 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForeHeaderColor", UDIFF_COLORFOREHEADER
),
738 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackHeaderColor", UDIFF_COLORBACKHEADER
));
739 SetAStyle(SCE_DIFF_COMMENT
,
740 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForeCommentColor", UDIFF_COLORFORECOMMENT
),
741 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackCommentColor", UDIFF_COLORBACKCOMMENT
));
742 for (int style
: { SCE_DIFF_ADDED
, SCE_DIFF_PATCH_ADD
, SCE_DIFF_PATCH_DELETE
})
745 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForeAddedColor", UDIFF_COLORFOREADDED
),
746 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackAddedColor", UDIFF_COLORBACKADDED
));
748 for (int style
: { SCE_DIFF_DELETED
, SCE_DIFF_REMOVED_PATCH_ADD
, SCE_DIFF_REMOVED_PATCH_DELETE
})
751 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffForeRemovedColor", UDIFF_COLORFOREREMOVED
),
752 CRegStdDWORD(L
"Software\\TortoiseGit\\UDiffBackRemovedColor", UDIFF_COLORBACKREMOVED
));
755 SendEditor(SCI_SETFOLDMARGINCOLOUR
, true, RGB(240, 240, 240));
756 SendEditor(SCI_SETFOLDMARGINHICOLOUR
, true, RGB(255, 255, 255));
757 SendEditor(SCI_STYLESETFORE
, STYLE_LINENUMBER
, RGB(109, 109, 109));
758 SendEditor(SCI_STYLESETBACK
, STYLE_LINENUMBER
, RGB(230, 230, 230));
760 SendEditor(SCI_STYLESETFORE
, STYLE_BRACELIGHT
, RGB(0, 150, 0));
761 SendEditor(SCI_STYLESETBOLD
, STYLE_BRACELIGHT
, 1);
762 SendEditor(SCI_STYLESETFORE
, STYLE_BRACEBAD
, RGB(255, 0, 0));
763 SendEditor(SCI_STYLESETBOLD
, STYLE_BRACEBAD
, 1);
764 DarkModeHelper::Instance().RefreshTitleBarThemeColor(*this, bDark
);
765 if (bDark
|| CTheme::Instance().IsHighContrastModeDark())
767 SendEditor(SCI_SETFOLDMARGINCOLOUR
, true, UDiffTextColorDark
);
768 SendEditor(SCI_SETFOLDMARGINHICOLOUR
, true, CTheme::darkBkColor
);
769 SendEditor(SCI_STYLESETFORE
, STYLE_LINENUMBER
, RGB(140, 140, 140));
770 SendEditor(SCI_STYLESETBACK
, STYLE_LINENUMBER
, UDiffBackColorDark
);
773 auto curlexer
= SendEditor(SCI_GETLEXER
);
774 if (CTheme::Instance().IsHighContrastMode() && curlexer
!= SCLEX_NULL
)
776 SendEditor(SCI_CLEARDOCUMENTSTYLE
, 0, 0);
777 SendEditor(SCI_SETILEXER
, 0, reinterpret_cast<sptr_t
>(nullptr));
778 SendEditor(SCI_COLOURISE
, 0, -1);
780 else if (!CTheme::Instance().IsHighContrastMode() && curlexer
!= SCLEX_DIFF
)
782 SendEditor(SCI_CLEARDOCUMENTSTYLE
, 0, 0);
783 SendEditor(SCI_STYLESETBOLD
, SCE_DIFF_COMMENT
, TRUE
);
784 SendEditor(SCI_SETILEXER
, 0, reinterpret_cast<sptr_t
>(CreateLexer("diff")));
785 SendEditor(SCI_STYLESETBOLD
, SCE_DIFF_COMMENT
, TRUE
);
786 SendEditor(SCI_SETKEYWORDS
, 0, reinterpret_cast<LPARAM
>("revision"));
787 SendEditor(SCI_COLOURISE
, 0, -1);
790 HMENU hMenu
= GetMenu(*this);
791 UINT uCheck
= MF_BYCOMMAND
;
792 uCheck
|= CTheme::Instance().IsDarkTheme() ? MF_CHECKED
: MF_UNCHECKED
;
793 CheckMenuItem(hMenu
, ID_VIEW_DARKMODE
, uCheck
);
794 UINT uEnabled
= MF_BYCOMMAND
;
795 uEnabled
|= CTheme::Instance().IsDarkModeAllowed() ? MF_ENABLED
: MF_DISABLED
;
796 EnableMenuItem(hMenu
, ID_VIEW_DARKMODE
, uEnabled
);
798 ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME
| RDW_INVALIDATE
| RDW_ERASE
| RDW_INTERNALPAINT
| RDW_ALLCHILDREN
| RDW_UPDATENOW
);
801 bool CMainWindow::LoadFile(HANDLE hFile
, bool wantStdIn
)
804 char data
[4096] = { 0 };
807 BOOL bRet
= ReadFile(hFile
, data
, sizeof(data
), &dwRead
, nullptr);
808 bool bUTF8
= IsUTF8(data
, dwRead
);
809 while ((dwRead
> 0) && (bRet
))
811 SendEditor(SCI_ADDTEXT
, dwRead
,
812 reinterpret_cast<LPARAM
>(static_cast<char *>(data
)));
813 if (auto sciStatus
= static_cast<int>(SendEditor(SCI_GETSTATUS
)); sciStatus
> SC_STATUS_OK
&& sciStatus
< SC_STATUS_WARN_START
)
815 if (sciStatus
== SC_STATUS_BADALLOC
)
816 SetLastError(static_cast<DWORD
>(E_OUTOFMEMORY
));
818 SetLastError(static_cast<DWORD
>(E_FAIL
));
819 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
822 if (static_cast<Sci_Position
>(SendEditor(SCI_GETTEXT
)) >= 250 * 1024 * 1024) // styling gets really slow and Scintilla requires special initialization for large files
824 MessageBox(*this, static_cast<LPCWSTR
>(ResString(hResource
, IDS_ERR_FILE_TOOBIG
)), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
827 bRet
= ReadFile(hFile
, data
, sizeof(data
), &dwRead
, nullptr);
831 if (hFile
|| wantStdIn
)
833 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
836 SetTitle(L
"Unnamed");
844 bool CMainWindow::LoadFile(LPCWSTR filename
)
846 CAutoFile hfile
= CreateFile(filename
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_DELETE
, nullptr, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr);
849 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
853 if (LARGE_INTEGER fileSize
; !::GetFileSizeEx(hfile
, &fileSize
))
855 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
858 else if (fileSize
.QuadPart
>= 250 * 1024 * 1024) // styling gets really slow and Scintilla requires special initialization for large files
860 MessageBox(*this, static_cast<LPCWSTR
>(ResString(hResource
, IDS_ERR_FILE_TOOBIG
)), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
864 char data
[4096] = { 0 };
866 if (!ReadFile(hfile
, data
, sizeof(data
), &size
, nullptr))
868 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
871 bool bUTF8
= IsUTF8(data
, size
);
876 SendEditor(SCI_ADDTEXT
, size
,
877 reinterpret_cast<LPARAM
>(static_cast<char *>(data
)));
878 if (auto sciStatus
= static_cast<int>(SendEditor(SCI_GETSTATUS
)); sciStatus
> SC_STATUS_OK
&& sciStatus
< SC_STATUS_WARN_START
)
880 if (sciStatus
== SC_STATUS_BADALLOC
)
881 SetLastError(static_cast<DWORD
>(E_OUTOFMEMORY
));
883 SetLastError(static_cast<DWORD
>(E_FAIL
));
887 if (!ReadFile(hfile
, data
, sizeof(data
), &size
, nullptr))
896 MessageBox(*this, static_cast<LPCWSTR
>(CFormatMessageWrapper()), L
"TortoiseGitUDiff", MB_ICONEXCLAMATION
);
899 SetTitle(L
"Unnamed");
903 m_filename
= filename
;
910 void CMainWindow::InitEditor()
913 SendEditor(SCI_SETREADONLY
, FALSE
);
914 SendEditor(SCI_CLEARALL
);
915 SendEditor(SCI_EMPTYUNDOBUFFER
);
916 SendEditor(SCI_SETSAVEPOINT
);
917 SendEditor(SCI_CANCEL
);
918 SendEditor(SCI_SETUNDOCOLLECTION
, 0);
921 void CMainWindow::SetupWindow(bool bUTF8
)
923 SendEditor(SCI_SETCODEPAGE
, bUTF8
? SC_CP_UTF8
: GetACP());
925 SendEditor(SCI_SETUNDOCOLLECTION
, 1);
926 ::SetFocus(m_hWndEdit
);
927 SendEditor(SCI_EMPTYUNDOBUFFER
);
928 SendEditor(SCI_SETSAVEPOINT
);
929 SendEditor(SCI_GOTOPOS
, 0);
931 ::ShowWindow(m_hWndEdit
, SW_SHOW
);
934 bool CMainWindow::SaveFile(LPCWSTR filename
)
937 _wfopen_s(&fp
, filename
, L
"w+b");
940 wchar_t fmt
[1024] = { 0 };
941 LoadString(::hResource
, IDS_ERRORSAVE
, fmt
, _countof(fmt
));
942 wchar_t error
[1024] = { 0 };
943 _snwprintf_s(error
, _countof(error
), fmt
, filename
, static_cast<LPCWSTR
>(CFormatMessageWrapper()));
944 MessageBox(*this, error
, L
"TortoiseGitUDiff", MB_OK
);
948 auto len
= static_cast<int>(SendEditor(SCI_GETTEXT
, 0, 0));
949 auto data
= std::make_unique
<char[]>(len
+ 1);
950 SendEditor(SCI_GETTEXT
, len
, reinterpret_cast<LPARAM
>(static_cast<char *>(data
.get())));
951 fwrite(data
.get(), sizeof(char), len
, fp
);
954 SendEditor(SCI_SETSAVEPOINT
);
955 ::ShowWindow(m_hWndEdit
, SW_SHOW
);
959 void CMainWindow::SetTitle(LPCWSTR title
)
961 size_t len
= wcslen(title
);
962 auto pBuf
= std::make_unique
<wchar_t[]>(len
+ 40);
963 swprintf_s(pBuf
.get(), len
+ 40, L
"%s - TortoiseGitUDiff", title
);
964 SetWindowTitle(std::wstring(pBuf
.get()));
967 void CMainWindow::SetAStyle(int style
, COLORREF fore
, COLORREF back
, int size
, const char *face
)
969 SendEditor(SCI_STYLESETFORE
, style
, fore
);
970 SendEditor(SCI_STYLESETBACK
, style
, back
);
972 SendEditor(SCI_STYLESETSIZE
, style
, size
);
974 SendEditor(SCI_STYLESETFONT
, style
, reinterpret_cast<LPARAM
>(face
));
977 bool CMainWindow::IsUTF8(LPVOID pBuffer
, size_t cb
)
981 auto pVal16
= static_cast<UINT16
*>(pBuffer
);
982 auto pVal8
= reinterpret_cast<UINT8
*>(pVal16
+ 1);
983 // scan the whole buffer for a 0x0000 sequence
984 // if found, we assume a binary file
985 for (size_t i
=0; i
<(cb
-2); i
=i
+2)
987 if (0x0000 == *pVal16
++)
990 pVal16
= static_cast<UINT16
*>(pBuffer
);
991 if (*pVal16
== 0xFEFF)
995 if (*pVal16
== 0xBBEF)
1000 // check for illegal UTF8 chars
1001 pVal8
= static_cast<UINT8
*>(pBuffer
);
1002 for (size_t i
=0; i
<cb
; ++i
)
1004 if ((*pVal8
== 0xC0)||(*pVal8
== 0xC1)||(*pVal8
>= 0xF5))
1008 pVal8
= static_cast<UINT8
*>(pBuffer
);
1010 for (size_t i
=0; i
<(cb
-3); ++i
)
1012 if ((*pVal8
& 0xE0)==0xC0)
1015 if ((*pVal8
& 0xC0)!=0x80)
1019 else if ((*pVal8
& 0xF0)==0xE0)
1022 if ((*pVal8
& 0xC0)!=0x80)
1025 if ((*pVal8
& 0xC0)!=0x80)
1029 else if ((*pVal8
& 0xF8)==0xF0)
1032 if ((*pVal8
& 0xC0)!=0x80)
1035 if ((*pVal8
& 0xC0)!=0x80)
1038 if ((*pVal8
& 0xC0)!=0x80)
1042 else if (*pVal8
>= 0x80)
1052 bool CMainWindow::canCloseWhenModified()
1054 if (SendEditor(SCI_GETMODIFY
) != TRUE
)
1057 wchar_t question
[1024] = { 0 };
1058 LoadString(::hResource
, IDS_MODIFIEDASKSAVE
, question
, _countof(question
));
1059 switch (MessageBox(m_hwnd
, question
, L
"TortoiseGitUDiff", MB_YESNOCANCEL
| MB_ICONQUESTION
))
1064 return loadOrSaveFile(false, m_filename
);
1070 bool CMainWindow::loadOrSaveFile(bool doLoad
, const std::wstring
& filename
/* = L"" */)
1072 OPENFILENAME ofn
= {0}; // common dialog box structure
1073 wchar_t szFile
[MAX_PATH
] = {0}; // buffer for file name
1074 // Initialize OPENFILENAME
1075 ofn
.lStructSize
= sizeof(OPENFILENAME
);
1076 ofn
.hwndOwner
= *this;
1077 ofn
.lpstrFile
= szFile
;
1078 ofn
.nMaxFile
= sizeof(szFile
)/sizeof(wchar_t);
1079 wchar_t filter
[1024] = { 0 };
1080 LoadString(::hResource
, IDS_PATCHFILEFILTER
, filter
, sizeof(filter
) / sizeof(wchar_t));
1081 CStringUtils::PipesToNulls(filter
);
1082 ofn
.lpstrFilter
= filter
;
1083 ofn
.nFilterIndex
= 1;
1084 ofn
.lpstrFileTitle
= nullptr;
1085 ofn
.lpstrDefExt
= L
"diff";
1086 ofn
.nMaxFileTitle
= 0;
1087 ofn
.lpstrInitialDir
= nullptr;
1088 wchar_t fileTitle
[1024] = { 0 };
1089 LoadString(::hResource
, doLoad
? IDS_OPENPATCH
: IDS_SAVEPATCH
, fileTitle
, sizeof(fileTitle
) / sizeof(wchar_t));
1090 ofn
.lpstrTitle
= fileTitle
;
1091 ofn
.Flags
= OFN_ENABLESIZING
| OFN_EXPLORER
;
1093 ofn
.Flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
1095 ofn
.Flags
|= OFN_OVERWRITEPROMPT
;
1096 // Display the Open dialog box.
1099 if (GetOpenFileName(&ofn
)==TRUE
)
1101 return LoadFile(ofn
.lpstrFile
);
1107 if (filename
.empty())
1109 if (GetSaveFileName(&ofn
)==TRUE
)
1111 return SaveFile(ofn
.lpstrFile
);
1116 return SaveFile(filename
.c_str());
1120 void CMainWindow::DoSearch(bool reverse
)
1122 Sci_Position lastcursor
= SendEditor(SCI_GETSELECTIONEND
);
1124 SendEditor(SCI_SETTARGETRANGE
, lastcursor
, SendEditor(SCI_GETLENGTH
));
1127 lastcursor
= SendEditor(SCI_GETSELECTIONSTART
);
1128 SendEditor(SCI_SETTARGETRANGE
, lastcursor
, 0);
1131 auto searchText
= CUnicodeUtils::StdGetUTF8(m_findtext
);
1132 SendEditor(SCI_SETSEARCHFLAGS
, m_bMatchCase
? SCFIND_MATCHCASE
: SCFIND_NONE
);
1133 if (auto pos
= SendEditor(SCI_SEARCHINTARGET
, searchText
.length(), reinterpret_cast<LPARAM
>(static_cast<LPCSTR
>(searchText
.c_str()))); pos
>= 0)
1135 SendEditor(SCI_SETSELECTION
, pos
, pos
+ searchText
.length());
1136 SendEditor(SCI_SCROLLCARET
);
1140 SendEditor(SCI_SETSELECTION
, lastcursor
, lastcursor
);
1142 fwi
.cbSize
= sizeof(FLASHWINFO
);
1144 fwi
.dwTimeout
= 100;
1145 fwi
.dwFlags
= FLASHW_ALL
;
1147 FlashWindowEx(&fwi
);