int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); SetDllDirectory(L""); SetTaskIDPerUUID(); CRegStdDWORD loc = CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033); long langId = loc; CLangDll langDLL; hResource = langDLL.Init(_T("TortoiseIDiff"), langId); if (hResource == NULL) hResource = hInstance; CCmdLineParser parser(lpCmdLine); if (parser.HasKey(_T("?")) || parser.HasKey(_T("help"))) { TCHAR buf[1024]; LoadString(hResource, IDS_COMMANDLINEHELP, buf, _countof(buf)); MessageBox(NULL, buf, _T("TortoiseIDiff"), MB_ICONINFORMATION); langDLL.Close(); return 0; } MSG msg; HACCEL hAccelTable; hInst = hInstance; INITCOMMONCONTROLSEX used = { sizeof(INITCOMMONCONTROLSEX), ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_WIN95_CLASSES }; InitCommonControlsEx(&used); // load the cursors we need curHand = (HCURSOR)LoadImage(hInst, MAKEINTRESOURCE(IDC_PANCUR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE); curHandDown = (HCURSOR)LoadImage(hInst, MAKEINTRESOURCE(IDC_PANDOWNCUR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE); CMainWindow mainWindow(hResource); mainWindow.SetRegistryPath(_T("Software\\TortoiseGit\\TortoiseIDiffWindowPos")); std::wstring leftfile = parser.HasVal(_T("left")) ? parser.GetVal(_T("left")) : _T(""); if ((leftfile.size() == 0)&&(lpCmdLine[0] != 0)) { leftfile = lpCmdLine; } mainWindow.SetLeft(leftfile.c_str(), parser.HasVal(_T("lefttitle")) ? parser.GetVal(_T("lefttitle")) : _T("")); mainWindow.SetRight(parser.HasVal(_T("right")) ? parser.GetVal(_T("right")) : _T(""), parser.HasVal(_T("righttitle")) ? parser.GetVal(_T("righttitle")) : _T("")); if (mainWindow.RegisterAndCreateWindow()) { hAccelTable = LoadAccelerators(hResource, MAKEINTRESOURCE(IDR_TORTOISEIDIFF)); if (!parser.HasVal(_T("left"))) { PostMessage(mainWindow, WM_COMMAND, ID_FILE_OPEN, 0); } if (parser.HasKey(_T("overlay"))) { PostMessage(mainWindow, WM_COMMAND, ID_VIEW_OVERLAPIMAGES, 0); } if (parser.HasKey(_T("fit"))) { PostMessage(mainWindow, WM_COMMAND, ID_VIEW_FITTOGETHER, 0); } if (parser.HasKey(_T("showinfo"))) { PostMessage(mainWindow, WM_COMMAND, ID_VIEW_IMAGEINFO, 0); } // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(mainWindow, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } langDLL.Close(); DestroyCursor(curHand); DestroyCursor(curHandDown); return 1; }
void CMainWindow::SetupColors(bool recolorize) { SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT)); SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW)); SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT)); SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT)); SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT)); SendEditor(SCI_SETWHITESPACEFORE, true, ::GetSysColor(COLOR_3DSHADOW)); SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0); HIGHCONTRAST highContrast = { 0 }; highContrast.cbSize = sizeof(HIGHCONTRAST); if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrast, 0) == TRUE && (highContrast.dwFlags & HCF_HIGHCONTRASTON)) { SendEditor(SCI_SETLEXER, SCLEX_NULL); return; } //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0)); SetAStyle(SCE_DIFF_COMMAND, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommandColor", UDIFF_COLORFORECOMMAND), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommandColor", UDIFF_COLORBACKCOMMAND)); SetAStyle(SCE_DIFF_POSITION, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForePositionColor", UDIFF_COLORFOREPOSITION), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackPositionColor", UDIFF_COLORBACKPOSITION)); SetAStyle(SCE_DIFF_HEADER, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeHeaderColor", UDIFF_COLORFOREHEADER), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackHeaderColor", UDIFF_COLORBACKHEADER)); SetAStyle(SCE_DIFF_COMMENT, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommentColor", UDIFF_COLORFORECOMMENT), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommentColor", UDIFF_COLORBACKCOMMENT)); SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE); for (int style : { SCE_DIFF_ADDED, SCE_DIFF_PATCH_ADD, SCE_DIFF_PATCH_DELETE }) { SetAStyle(style, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeAddedColor", UDIFF_COLORFOREADDED), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackAddedColor", UDIFF_COLORBACKADDED)); } for (int style : { SCE_DIFF_DELETED, SCE_DIFF_REMOVED_PATCH_ADD, SCE_DIFF_REMOVED_PATCH_DELETE }) { SetAStyle(style, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeRemovedColor", UDIFF_COLORFOREREMOVED), CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackRemovedColor", UDIFF_COLORBACKREMOVED)); } SendEditor(SCI_SETLEXER, SCLEX_DIFF); SendEditor(SCI_SETKEYWORDS, 0, reinterpret_cast<LPARAM>("revision")); if (recolorize) SendEditor(SCI_COLOURISE, 0, -1); }
STDMETHODIMP CShellExt::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) { PreserveChdir preserveChdir; if (!g_ShellCache.IsPathAllowed((TCHAR *)pscd->wszFile)) { return S_FALSE; } LoadLangDll(); ShellCache::CacheType cachetype = g_ShellCache.GetCacheType(); if (pscid->fmtid == CLSID_Tortoisegit_UPTODATE) { stdstring szInfo; const TCHAR * path = (TCHAR *)pscd->wszFile; // reserve for the path + trailing \0 TCHAR buf[MAX_STATUS_STRING_LENGTH+1]; SecureZeroMemory(buf, MAX_STATUS_STRING_LENGTH); switch (pscid->pid) { case 0: // Git Status GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); GitStatus::GetStatusString(g_hResInst, filestatus, buf, _countof(buf), (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))); szInfo = buf; break; case 1: // Git Revision #if 0 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); if (columnrev >= 0) { V_VT(pvarData) = VT_I4; V_I4(pvarData) = columnrev; } #endif return S_OK; break; case 2: // Git Url GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); szInfo = itemurl; break; case 3: // Git Short Url GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); szInfo = itemshorturl; break; case 5: // Git eol-style #if 0 if (cachetype == ShellCache::none) return S_FALSE; if (g_ShellCache.IsPathAllowed(path)) { SVNProperties props = SVNProperties(CTSVNPath(path), false); for (int i=0; i<props.GetCount(); i++) { if (props.GetItemName(i).compare(_T("svn:eol-style"))==0) { szInfo = MultibyteToWide((char *)props.GetItemValue(i).c_str()); } } } #endif break; default: return S_FALSE; } const WCHAR * wsInfo = szInfo.c_str(); V_VT(pvarData) = VT_BSTR; V_BSTR(pvarData) = SysAllocString(wsInfo); return S_OK; } if (pscid->fmtid == FMTID_SummaryInformation) { stdstring szInfo; const TCHAR * path = pscd->wszFile; if (cachetype == ShellCache::none) return S_FALSE; switch (pscid->pid) { case PIDSI_AUTHOR: // Author and Git Author GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); szInfo = columnauthor; break; default: return S_FALSE; } wide_string wsInfo = szInfo; V_VT(pvarData) = VT_BSTR; V_BSTR(pvarData) = SysAllocString(wsInfo.c_str()); return S_OK; } return S_FALSE; }
STDMETHODIMP CDeskBand::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT /*idCmdLast*/, UINT uFlags) { if ((uFlags & CMF_DEFAULTONLY)!=0) return S_OK; //we don't change the default action if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY))) return S_OK; if ((m_ContextDirectory.empty()) && (m_ContextItems.empty())) return S_OK; if (m_ContextDirectory.empty()) { // folder is empty, but maybe files are selected if (m_ContextItems.empty()) return S_OK; // nothing selected - we don't have a menu to show // check whether a selected entry is an UID - those are namespace extensions // which we can't handle for (std::map<tstring, ULONG>::const_iterator it = m_ContextItems.begin(); it != m_ContextItems.end(); ++it) { if (_tcsncmp(it->first.c_str(), _T("::{"), 3)==0) return S_OK; } } else { // ignore namespace extensions if (_tcsncmp(m_ContextDirectory.c_str(), _T("::{"), 3)==0) return S_OK; } if (DWORD(CRegStdDWORD(_T("Software\\StefansTools\\StExBar\\ContextMenu"), TRUE)) == FALSE) return S_OK; //check if we already added our menu entry for a folder. //we check that by iterating through all menu entries and check if //the dwItemData member points to our global ID string. That string is set //by our shell extension when the folder menu is inserted. TCHAR menubuf[MAX_PATH]; int count = GetMenuItemCount(hMenu); for (int i=0; i<count; ++i) { MENUITEMINFO miif; SecureZeroMemory(&miif, sizeof(MENUITEMINFO)); miif.cbSize = sizeof(MENUITEMINFO); miif.fMask = MIIM_DATA; miif.dwTypeData = menubuf; miif.cch = _countof(menubuf); GetMenuItemInfo(hMenu, i, TRUE, &miif); if (miif.dwItemData == (ULONG_PTR)g_MenuIDString) return S_OK; } UINT idCmd = idCmdFirst; //create the sub menu HMENU subMenu = CreateMenu(); int indexSubMenu = 0; m_commands.LoadFromFile(); int index = 0; for (int j = 0; j < m_commands.GetCount(); ++j) { MENUITEMINFO menuiteminfo = {0}; Command cmd = m_commands.GetCommand(j); m_hotkeys[cmd.key] = j; if ((cmd.commandline.compare(INTERNALCOMMANDHIDDEN)==0)&&(cmd.name.compare(_T("Options")) == 0)) { cmd.commandline = INTERNALCOMMAND; // make sure the options button is never hidden. m_commands.SetCommand(j, cmd); } if ((cmd.name.compare(_T("StexBar Internal Edit Box")) == 0)|| (cmd.commandline.compare(INTERNALCOMMANDHIDDEN) == 0)|| (cmd.name.compare(_T("New Folder")) == 0)) { continue; } bool bEnabled = cmd.enabled_viewpath || (cmd.enabled_fileselected && !m_ContextItems.empty()) || (cmd.enabled_folderselected && (!m_ContextItems.empty() || !m_ContextDirectory.empty())) || (cmd.enabled_noselection && (m_ContextItems.empty())) || (cmd.enabled_selectedcount && (cmd.enabled_selectedcount == (int)!m_ContextItems.empty())); HICON hIcon = LoadCommandIcon(cmd); if (hIcon) { menuiteminfo.hbmpItem = IsWindowsVistaOrGreater() ? IconToBitmapPARGB32(hIcon) : HBMMENU_CALLBACK; DestroyIcon(hIcon); } else menuiteminfo.hbmpItem = NULL; if (!cmd.separator) m_tooltips[j] = cmd.name.c_str(); myIDMap[idCmd - idCmdFirst] = j; myIDMap[idCmd] = j; menuiteminfo.cbSize = sizeof(menuiteminfo); menuiteminfo.fMask = cmd.separator ? MIIM_FTYPE : MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_STRING | MIIM_STATE; menuiteminfo.fType = cmd.separator ? MFT_SEPARATOR : MFT_STRING; menuiteminfo.fState = bEnabled ? MFS_ENABLED : MFS_DISABLED; TCHAR menutextbuf[100]; _tcscpy_s(menutextbuf, _countof(menutextbuf), m_commands.GetCommandPtr(j)->name.c_str()); menuiteminfo.dwTypeData = menutextbuf; menuiteminfo.wID = idCmd++; InsertMenuItem(subMenu, indexSubMenu++, TRUE, &menuiteminfo); index++; } //add sub menu to main context menu //don't use InsertMenu because this will lead to multiple menu entries in the explorer file menu. //see http://support.microsoft.com/default.aspx?scid=kb;en-us;214477 for details of that. MENUITEMINFO menuiteminfo = {0}; SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo)); menuiteminfo.cbSize = sizeof(menuiteminfo); menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_STRING; menuiteminfo.fType = MFT_STRING; menuiteminfo.dwTypeData = _T("StEx"); menuiteminfo.hSubMenu = subMenu; menuiteminfo.wID = idCmd++; InsertMenuItem(hMenu, indexMenu++, TRUE, &menuiteminfo); //return number of menu items added return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(idCmd - idCmdFirst))); }
LRESULT CMainWindow::DoCommand(int id) { switch (id) { case ID_FILE_OPEN: loadOrSaveFile(true); break; case ID_FILE_SAVEAS: loadOrSaveFile(false); break; case ID_FILE_SAVE: loadOrSaveFile(false, m_filename); break; case ID_FILE_EXIT: ::PostQuitMessage(0); return 0; case IDM_SHOWFINDBAR: { m_bShowFindBar = true; ::ShowWindow(m_FindBar, SW_SHOW); RECT rect; GetClientRect(*this, &rect); ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top - int(30 * CDPIAware::Instance().ScaleFactorY()), SWP_SHOWWINDOW); ::SetWindowPos(m_FindBar, HWND_TOP, rect.left, rect.bottom - int(30 * CDPIAware::Instance().ScaleFactorY()), rect.right - rect.left, int(30 * CDPIAware::Instance().ScaleFactorY()), SWP_SHOWWINDOW); ::SetFocus(m_FindBar); SendEditor(SCI_SETSELECTIONSTART, 0); SendEditor(SCI_SETSELECTIONEND, 0); SendEditor(SCI_SEARCHANCHOR); } break; case IDM_FINDNEXT: SendEditor(SCI_CHARRIGHT); SendEditor(SCI_SEARCHANCHOR); if (SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, reinterpret_cast<LPARAM>(CUnicodeUtils::StdGetUTF8(m_findtext).c_str())) == -1) { FLASHWINFO fwi; fwi.cbSize = sizeof(FLASHWINFO); fwi.uCount = 3; fwi.dwTimeout = 100; fwi.dwFlags = FLASHW_ALL; fwi.hwnd = m_hwnd; FlashWindowEx(&fwi); } SendEditor(SCI_SCROLLCARET); break; case IDM_FINDPREV: SendEditor(SCI_SEARCHANCHOR); if (SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, reinterpret_cast<LPARAM>(CUnicodeUtils::StdGetUTF8(m_findtext).c_str())) == -1) { FLASHWINFO fwi; fwi.cbSize = sizeof(FLASHWINFO); fwi.uCount = 3; fwi.dwTimeout = 100; fwi.dwFlags = FLASHW_ALL; fwi.hwnd = m_hwnd; FlashWindowEx(&fwi); } SendEditor(SCI_SCROLLCARET); break; case IDM_FINDEXIT: if (IsWindowVisible(m_FindBar)) { RECT rect; GetClientRect(*this, &rect); m_bShowFindBar = false; ::ShowWindow(m_FindBar, SW_HIDE); ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW); } else PostQuitMessage(0); break; case ID_FILE_SETTINGS: { tstring gitCmd = L" /command:settings /page:udiff"; RunCommand(gitCmd); } break; case ID_FILE_APPLYPATCH: { std::wstring command = L" /diff:\""; command += m_filename; command += L'"'; std::wstring tortoiseMergePath = GetAppDirectory() + L"TortoiseGitMerge.exe"; CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath.c_str(), command.c_str()); } break; case ID_FILE_PAGESETUP: { TCHAR localeInfo[3] = { 0 }; GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3); // Metric system. '1' is US System int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000; PAGESETUPDLG pdlg = {0}; pdlg.lStructSize = sizeof(PAGESETUPDLG); pdlg.hwndOwner = *this; pdlg.hInstance = nullptr; pdlg.Flags = PSD_DEFAULTMINMARGINS|PSD_MARGINS|PSD_DISABLEPAPER|PSD_DISABLEORIENTATION; if (localeInfo[0] == '0') pdlg.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS; CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin); CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin); CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin); CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin); pdlg.rtMargin.left = static_cast<long>(m_regMargLeft); pdlg.rtMargin.top = static_cast<long>(m_regMargTop); pdlg.rtMargin.right = static_cast<long>(m_regMargRight); pdlg.rtMargin.bottom = static_cast<long>(m_regMargBottom); if (!PageSetupDlg(&pdlg)) return false; m_regMargLeft = pdlg.rtMargin.left; m_regMargTop = pdlg.rtMargin.top; m_regMargRight = pdlg.rtMargin.right; m_regMargBottom = pdlg.rtMargin.bottom; } break; case ID_FILE_PRINT: { PRINTDLGEX pdlg = {0}; pdlg.lStructSize = sizeof(PRINTDLGEX); pdlg.hwndOwner = *this; pdlg.hInstance = nullptr; pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOPAGENUMS; pdlg.nMinPage = 1; pdlg.nMaxPage = 0xffffU; // We do not know how many pages in the document pdlg.nCopies = 1; pdlg.hDC = 0; pdlg.nStartPage = START_PAGE_GENERAL; // See if a range has been selected auto startPos = static_cast<Sci_Position>(SendEditor(SCI_GETSELECTIONSTART)); auto endPos = static_cast<Sci_Position>(SendEditor(SCI_GETSELECTIONEND)); if (startPos == endPos) pdlg.Flags |= PD_NOSELECTION; else pdlg.Flags |= PD_SELECTION; HRESULT hResult = PrintDlgEx(&pdlg); if ((hResult != S_OK) || (pdlg.dwResultAction != PD_RESULT_PRINT)) return 0; // reset all indicators auto endpos = static_cast<int>(SendEditor(SCI_GETLENGTH)); for (int i = INDIC_CONTAINER; i <= INDIC_MAX; ++i) { SendEditor(SCI_SETINDICATORCURRENT, i); SendEditor(SCI_INDICATORCLEARRANGE, 0, endpos); } // store and reset UI settings auto viewws = static_cast<int>(SendEditor(SCI_GETVIEWWS)); SendEditor(SCI_SETVIEWWS, 0); auto edgemode = static_cast<int>(SendEditor(SCI_GETEDGEMODE)); SendEditor(SCI_SETEDGEMODE, EDGE_NONE); SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END); HDC hdc = pdlg.hDC; RECT rectMargins, rectPhysMargins; POINT ptPage; POINT ptDpi; // Get printer resolution ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction // Start by getting the physical page size (in device units). ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH); // device units ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units // Get the dimensions of the unprintable // part of the page (in device units). rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX); rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY); // To get the right and lower unprintable area, // we take the entire width and height of the paper and // subtract everything else. rectPhysMargins.right = ptPage.x // total paper width - GetDeviceCaps(hdc, HORZRES) // printable width - rectPhysMargins.left; // left unprintable margin rectPhysMargins.bottom = ptPage.y // total paper height - GetDeviceCaps(hdc, VERTRES) // printable height - rectPhysMargins.top; // right unprintable margin TCHAR localeInfo[3] = { 0 }; GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3); // Metric system. '1' is US System int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000; RECT pagesetupMargin; CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin); CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin); CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin); CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin); pagesetupMargin.left = static_cast<long>(m_regMargLeft); pagesetupMargin.top = static_cast<long>(m_regMargTop); pagesetupMargin.right = static_cast<long>(m_regMargRight); pagesetupMargin.bottom = static_cast<long>(m_regMargBottom); if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 || pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0) { RECT rectSetup; // Convert the hundredths of millimeters (HiMetric) or // thousandths of inches (HiEnglish) margin values // from the Page Setup dialog to device units. // (There are 2540 hundredths of a mm in an inch.) if (localeInfo[0] == '0') { // Metric system. '1' is US System rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540); rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540); rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 2540); rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540); } else { rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000); rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000); rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000); rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000); } // Don't reduce margins below the minimum printable area rectMargins.left = max(rectPhysMargins.left, rectSetup.left); rectMargins.top = max(rectPhysMargins.top, rectSetup.top); rectMargins.right = max(rectPhysMargins.right, rectSetup.right); rectMargins.bottom = max(rectPhysMargins.bottom, rectSetup.bottom); } else { rectMargins.left = rectPhysMargins.left; rectMargins.top = rectPhysMargins.top; rectMargins.right = rectPhysMargins.right; rectMargins.bottom = rectPhysMargins.bottom; } // rectMargins now contains the values used to shrink the printable // area of the page. // Convert device coordinates into logical coordinates DPtoLP(hdc, reinterpret_cast<LPPOINT>(&rectMargins), 2); DPtoLP(hdc, reinterpret_cast<LPPOINT>(&rectPhysMargins), 2); // Convert page size to logical units and we're done! DPtoLP(hdc, reinterpret_cast<LPPOINT>(&ptPage), 1); DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0}; di.lpszDocName = m_filename.c_str(); di.lpszOutput = 0; di.lpszDatatype = 0; di.fwType = 0; if (::StartDoc(hdc, &di) < 0) { ::DeleteDC(hdc); return 0; } size_t lengthDoc = static_cast<int>(SendEditor(SCI_GETLENGTH)); size_t lengthDocMax = lengthDoc; size_t lengthPrinted = 0; // Requested to print selection if (pdlg.Flags & PD_SELECTION) { if (startPos > endPos) { lengthPrinted = endPos; lengthDoc = startPos; } else { lengthPrinted = startPos; lengthDoc = endPos; } if (lengthDoc > lengthDocMax) lengthDoc = lengthDocMax; } // We must subtract the physical margins from the printable area Sci_RangeToFormat frPrint; frPrint.hdc = hdc; frPrint.hdcTarget = hdc; frPrint.rc.left = rectMargins.left - rectPhysMargins.left; frPrint.rc.top = rectMargins.top - rectPhysMargins.top; frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left; frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top; frPrint.rcPage.left = 0; frPrint.rcPage.top = 0; frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1; frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1; // Print each page while (lengthPrinted < lengthDoc) { ::StartPage(hdc); frPrint.chrg.cpMin = static_cast<long>(lengthPrinted); frPrint.chrg.cpMax = static_cast<long>(lengthDoc); lengthPrinted = SendEditor(SCI_FORMATRANGE, true, reinterpret_cast<LPARAM>(&frPrint)); ::EndPage(hdc); } SendEditor(SCI_FORMATRANGE, FALSE, 0); ::EndDoc(hdc); ::DeleteDC(hdc); if (pdlg.hDevMode) GlobalFree(pdlg.hDevMode); if (pdlg.hDevNames) GlobalFree(pdlg.hDevNames); if (pdlg.lpPageRanges) GlobalFree(pdlg.lpPageRanges); // reset the UI SendEditor(SCI_SETVIEWWS, viewws); SendEditor(SCI_SETEDGEMODE, edgemode); SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_NONE); } break; default: break; }; return 1; }
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*cmdShow*/) { SetDllDirectory(L""); HANDLE hReloadProtection = ::CreateMutex(NULL, FALSE, GetCacheMutexName()); if (hReloadProtection == 0 || GetLastError() == ERROR_ALREADY_EXISTS) { // An instance of TGitCache is already running ATLTRACE("TGitCache ignoring restart\n"); return 0; } // apr_initialize(); // svn_dso_initialize2(); g_GitAdminDir.Init(); CGitStatusCache::Create(); CGitStatusCache::Instance().Init(); SecureZeroMemory(szCurrentCrawledPath, sizeof(szCurrentCrawledPath)); DWORD dwThreadId; HANDLE hPipeThread; HANDLE hCommandWaitThread; MSG msg; TCHAR szWindowClass[] = {TGIT_CACHE_WINDOW_NAME}; // create a hidden window to receive window messages. WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = 0; wcex.hCursor = 0; wcex.hbrBackground = 0; wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = 0; RegisterClassEx(&wcex); hWnd = CreateWindow(TGIT_CACHE_WINDOW_NAME, TGIT_CACHE_WINDOW_NAME, WS_CAPTION, 0, 0, 800, 300, NULL, 0, hInstance, 0); hTrayWnd = hWnd; if (hWnd == NULL) { return 0; } if (CRegStdDWORD(_T("Software\\TortoiseGit\\CacheTrayIcon"), FALSE)==TRUE) { SecureZeroMemory(&niData,sizeof(NOTIFYICONDATA)); DWORD dwVersion = GetDllVersion(_T("Shell32.dll")); if (dwVersion >= PACKVERSION(6,0)) niData.cbSize = sizeof(NOTIFYICONDATA); else if (dwVersion >= PACKVERSION(5,0)) niData.cbSize = NOTIFYICONDATA_V2_SIZE; else niData.cbSize = NOTIFYICONDATA_V1_SIZE; niData.uID = TRAY_ID; // own tray icon ID niData.hWnd = hWnd; niData.uFlags = NIF_ICON|NIF_MESSAGE; // load the icon niData.hIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_TGITCACHE), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); // set the message to send // note: the message value should be in the // range of WM_APP through 0xBFFF niData.uCallbackMessage = TRAY_CALLBACK; Shell_NotifyIcon(NIM_ADD,&niData); // free icon handle if(niData.hIcon && DestroyIcon(niData.hIcon)) niData.hIcon = NULL; } // Create a thread which waits for incoming pipe connections hPipeThread = CreateThread( NULL, // no security attribute 0, // default stack size PipeThread, (LPVOID) &bRun, // thread parameter 0, // not suspended &dwThreadId); // returns thread ID if (hPipeThread == NULL) { //OutputDebugStringA("TSVNCache: Could not create pipe thread\n"); //DebugOutputLastError(); return 0; } else CloseHandle(hPipeThread); // Create a thread which waits for incoming pipe connections hCommandWaitThread = CreateThread( NULL, // no security attribute 0, // default stack size CommandWaitThread, (LPVOID) &bRun, // thread parameter 0, // not suspended &dwThreadId); // returns thread ID if (hCommandWaitThread == NULL) { //OutputDebugStringA("TSVNCache: Could not create command wait thread\n"); //DebugOutputLastError(); return 0; } else CloseHandle(hCommandWaitThread); // loop to handle window messages. BOOL bLoopRet; while (bRun) { bLoopRet = GetMessage(&msg, NULL, 0, 0); if ((bLoopRet != -1)&&(bLoopRet != 0)) { DispatchMessage(&msg); } } bRun = false; Shell_NotifyIcon(NIM_DELETE,&niData); CGitStatusCache::Destroy(); g_GitAdminDir.Close(); // apr_terminate(); return 0; }
void SetUUIDOverlayIcon( HWND hWnd ) { if (!CRegStdDWORD(L"Software\\TortoiseGit\\GroupTaskbarIconsPerRepo", 3)) return; if (!CRegStdDWORD(L"Software\\TortoiseGit\\GroupTaskbarIconsPerRepoOverlay", TRUE)) return; std::wstring uuid; std::wstring sicon; bool bRemoveicon = false; #ifdef __AFXWIN_H__ uuid = g_sGroupingUUID; sicon = g_sGroupingIcon; bRemoveicon = g_bGroupingRemoveIcon; #else CCmdLineParser parser(GetCommandLine()); if (parser.HasVal(L"groupuuid")) uuid = parser.GetVal(L"groupuuid"); #endif if (uuid.empty()) return; CComPtr<ITaskbarList3> pTaskbarInterface; if (FAILED(pTaskbarInterface.CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER))) return; int foundUUIDIndex = 0; do { wchar_t buf[MAX_PATH] = { 0 }; swprintf_s(buf, L"%s%d", L"Software\\TortoiseGit\\LastUsedUUIDsForGrouping\\", foundUUIDIndex); CRegStdString r = CRegStdString(buf); std::wstring sr = r; if (sr.empty()) { r = uuid + (sicon.empty() ? L"" : (L";" + sicon)); break; } size_t sep = sr.find(L';'); std::wstring olduuid = sep != std::wstring::npos ? sr.substr(0, sep) : sr; if (olduuid.compare(uuid) == 0) { if (bRemoveicon) r = uuid; // reset icon path in registry else if (!sicon.empty()) r = uuid + (sicon.empty() ? L"" : (L";" + sicon)); else sicon = sep != std::wstring::npos ? sr.substr(sep + 1) : L""; break; } foundUUIDIndex++; } while (foundUUIDIndex < 20); if (foundUUIDIndex >= 20) { CRegStdString r = CRegStdString(L"Software\\TortoiseGit\\LastUsedUUIDsForGrouping\\1"); r.removeKey(); } int iconWidth = GetSystemMetrics(SM_CXSMICON); int iconHeight = GetSystemMetrics(SM_CYSMICON); HICON icon = nullptr; if (!sicon.empty()) { if (sicon.size() >= 4 && !_wcsicmp(sicon.substr(sicon.size() - 4).c_str(), L".ico")) icon = (HICON)::LoadImage(nullptr, sicon.c_str(), IMAGE_ICON, iconWidth, iconHeight, LR_LOADFROMFILE | LR_SHARED); else { ULONG_PTR gdiplusToken = 0; Gdiplus::GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); if (gdiplusToken) { Gdiplus::Bitmap* pBitmap = new Gdiplus::Bitmap(sicon.c_str(), FALSE); if (pBitmap->GetLastStatus() == Gdiplus::Status::Ok) pBitmap->GetHICON(&icon); delete pBitmap; Gdiplus::GdiplusShutdown(gdiplusToken); } } } if (!icon) { DWORD colors[6] = { 0x80FF0000, 0x80FFFF00, 0x8000FF00, 0x800000FF, 0x80000000, 0x8000FFFF }; // AND mask - monochrome - determines which pixels get drawn BYTE AND[32]; for (int i = 0; i<32; i++) { AND[i] = 0xFF; } // XOR mask - 32bpp ARGB - determines the pixel values DWORD XOR[256]; for (int i = 0; i<256; i++) { XOR[i] = colors[foundUUIDIndex % 6]; } icon = ::CreateIcon(nullptr, iconWidth, iconHeight, 1, 32, AND, (BYTE*)XOR); } pTaskbarInterface->SetOverlayIcon(hWnd, icon, uuid.c_str()); DestroyIcon(icon); }
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // uncomment the following lines for low-memory tests. // note: process needs to run elevated for this to work. // //auto job = CreateJobObject(NULL, NULL); //JOBOBJECT_EXTENDED_LIMIT_INFORMATION joblimit = { 0 }; //joblimit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_WORKINGSET; //joblimit.JobMemoryLimit = 30 * 1024 * 1024; //joblimit.ProcessMemoryLimit = 30 * 1024 * 1024; //joblimit.PeakProcessMemoryUsed = 30 * 1024 * 1024; //joblimit.BasicLimitInformation.MaximumWorkingSetSize = 30 * 1024 * 1024; //joblimit.BasicLimitInformation.MinimumWorkingSetSize = 30 * 1024; //SetInformationJobObject(job, JobObjectExtendedLimitInformation, &joblimit, sizeof(joblimit)); //AssignProcessToJobObject(job, GetCurrentProcess()); SetDllDirectory(L""); // if multiple items are selected in explorer and grepWin is started for all of them, // explorer starts multiple grepWin instances at once. In case there's already a grepWin instance // running, sleep for a while to give that instance time to fully initialize HANDLE hReloadProtection = ::CreateMutex(NULL, FALSE, L"{6473AA76-0EAE-4C96-8C99-AFDFEFFE42B5}"); bool alreadyRunning = false; if ((!hReloadProtection) || (GetLastError() == ERROR_ALREADY_EXISTS)) { // An instance of grepWin is already running alreadyRunning = true; } g_hInst = hInstance; ::OleInitialize(NULL); ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // we need some of the common controls INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_LINK_CLASS|ICC_LISTVIEW_CLASSES|ICC_PAGESCROLLER_CLASS |ICC_PROGRESS_CLASS|ICC_STANDARD_CLASSES|ICC_TAB_CLASSES|ICC_TREEVIEW_CLASSES |ICC_UPDOWN_CLASS|ICC_USEREX_CLASSES|ICC_WIN95_CLASSES; InitCommonControlsEx(&icex); HMODULE hRichEdt = LoadLibrary(_T("Riched20.dll")); CCmdLineParser parser(lpCmdLine); if (parser.HasKey(L"register")) { RegisterContextMenu(true); return FALSE; } if ((parser.HasKey(L"unregister")) || (parser.HasKey(L"deregister"))) { RegisterContextMenu(false); return FALSE; } bool bQuit = false; HWND hWnd = NULL; int timeout = 20; do { EnumWindows(windowenumerator, (LPARAM)&hWnd); if (alreadyRunning && (hWnd == NULL)) Sleep(100); timeout--; } while ((hWnd == NULL) && alreadyRunning && timeout); auto modulename = CPathUtils::GetFileName(CPathUtils::GetModulePath(0)); bPortable = ((_tcsstr(modulename.c_str(), _T("portable"))) || (parser.HasKey(_T("portable")))); std::wstring iniPath = CPathUtils::GetModuleDir(0); iniPath += L"\\grepwin.ini"; if (parser.HasVal(L"inipath")) iniPath = parser.GetVal(L"inipath"); if (bPortable) g_iniFile.LoadFile(iniPath.c_str()); if (hWnd) { bool bOnlyOne = !!DWORD(CRegStdDWORD(_T("Software\\grepWin\\onlyone"), 0)); if (bPortable) bOnlyOne = !!_wtoi(g_iniFile.GetValue(L"global", L"onlyone", L"0")); UINT GREPWIN_STARTUPMSG = RegisterWindowMessage(_T("grepWin_StartupMessage")); if (SendMessage(hWnd, GREPWIN_STARTUPMSG, 0, 0)) // send the new path { std::wstring spath = parser.GetVal(_T("searchpath")); SearchReplace(spath, L"/", L"\\"); spath = SanitizeSearchPaths(spath); COPYDATASTRUCT CopyData = {0}; CopyData.lpData = (LPVOID)spath.c_str(); CopyData.cbData = (DWORD)spath.size()*sizeof(wchar_t); SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)&CopyData); SetForegroundWindow(hWnd); //set the window to front bQuit = true; } else if (bOnlyOne) { std::wstring spath = parser.HasVal(L"searchpath") ? parser.GetVal(_T("searchpath")) : L""; SearchReplace(spath, L"/", L"\\"); spath = SanitizeSearchPaths(spath); COPYDATASTRUCT CopyData = { 0 }; CopyData.lpData = (LPVOID)spath.c_str(); CopyData.cbData = (DWORD)spath.size()*sizeof(wchar_t); SendMessage(hWnd, WM_COPYDATA, 1, (LPARAM)&CopyData); SetForegroundWindow(hWnd); //set the window to front bQuit = true; } } int ret = 0; if (!bQuit) { CLanguage::Instance().LoadFile(bPortable ? g_iniFile.GetValue(L"global", L"languagefile", L"") : std::wstring(CRegStdString(L"Software\\grepWin\\languagefile"))); if (parser.HasKey(_T("about"))||parser.HasKey(_T("?"))||parser.HasKey(_T("help"))) { CAboutDlg aboutDlg(NULL); ret= (int)aboutDlg.DoModal(hInstance, IDD_ABOUT, NULL, NULL); } else { CSearchDlg searchDlg(NULL); if (parser.HasVal(_T("searchpath"))) { std::wstring spath = parser.GetVal(L"searchpath"); spath = SanitizeSearchPaths(spath); searchDlg.SetSearchPath(spath); } if (parser.HasVal(_T("searchfor"))) searchDlg.SetSearchString(parser.GetVal(_T("searchfor"))); if (parser.HasVal(_T("filemaskregex"))) searchDlg.SetFileMask(parser.GetVal(_T("filemaskregex")), true); if (parser.HasVal(_T("filemask"))) searchDlg.SetFileMask(parser.GetVal(_T("filemask")), false); if (parser.HasVal(_T("filemaskexclude"))) searchDlg.SetExcludeFileMask(parser.GetVal(_T("filemaskexclude"))); if (parser.HasVal(_T("replacewith"))) searchDlg.SetReplaceWith(parser.GetVal(_T("replacewith"))); if (parser.HasVal(_T("i"))) searchDlg.SetCaseSensitive(_tcsicmp(parser.GetVal(_T("i")), _T("yes"))!=0); if (parser.HasVal(_T("n"))) searchDlg.SetMatchesNewline(_tcsicmp(parser.GetVal(_T("n")), _T("yes"))==0); if (parser.HasVal(_T("k"))) searchDlg.SetCreateBackups(_tcsicmp(parser.GetVal(_T("k")), _T("yes"))==0); if (parser.HasVal(_T("utf8"))) searchDlg.SetUTF8(_tcsicmp(parser.GetVal(_T("utf8")), _T("yes"))==0); if (parser.HasVal(_T("size"))) { int cmp = 0; if (parser.HasVal(_T("sizecmp"))) cmp = parser.GetLongVal(_T("sizecmp")); searchDlg.SetSize(parser.GetLongVal(_T("size")), cmp); } if (parser.HasVal(_T("s"))) searchDlg.SetIncludeSystem(_tcsicmp(parser.GetVal(_T("s")), _T("yes"))==0); if (parser.HasVal(_T("h"))) searchDlg.SetIncludeHidden(_tcsicmp(parser.GetVal(_T("h")), _T("yes"))==0); if (parser.HasVal(_T("u"))) searchDlg.SetIncludeSubfolders(_tcsicmp(parser.GetVal(_T("u")), _T("yes"))==0); if (parser.HasVal(_T("b"))) searchDlg.SetIncludeBinary(_tcsicmp(parser.GetVal(_T("b")), _T("yes"))==0); if (parser.HasVal(_T("regex"))) searchDlg.SetUseRegex(_tcsicmp(parser.GetVal(_T("regex")), _T("yes")) == 0); else if(parser.HasVal(_T("searchfor"))) searchDlg.SetUseRegex(true); if (parser.HasKey(L"execute") || parser.HasKey(L"executesearch")) searchDlg.SetExecute(ExecuteAction::Search); if (parser.HasKey(L"executereplace")) searchDlg.SetExecute(ExecuteAction::Replace); if (parser.HasKey(L"closedialog")) searchDlg.SetEndDialog(); ret = (int)searchDlg.DoModal(hInstance, IDD_SEARCHDLG, NULL, IDR_SEARCHDLG); } if (bPortable) { FILE * pFile = NULL; _tfopen_s(&pFile, iniPath.c_str(), _T("wb")); g_iniFile.SaveFile(pFile); fclose(pFile); } } ::CoUninitialize(); ::OleUninitialize(); FreeLibrary(hRichEdt); CloseHandle(hReloadProtection); return ret; }
void SetUUIDOverlayIcon( HWND hWnd ) { if (CRegStdDWORD(_T("Software\\TortoiseGit\\GroupTaskbarIconsPerRepo"), 3)) { if (CRegStdDWORD(_T("Software\\TortoiseGit\\GroupTaskbarIconsPerRepoOverlay"), TRUE)) { std::wstring uuid; #ifdef _MFC_VER uuid = g_sGroupingUUID; #else CCmdLineParser parser(GetCommandLine()); if (parser.HasVal(L"groupuuid")) uuid = parser.GetVal(L"groupuuid"); #endif if (!uuid.empty()) { ITaskbarList3 * pTaskbarInterface = NULL; HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, reinterpret_cast<void**> (&(pTaskbarInterface))); if (SUCCEEDED(hr)) { int foundUUIDIndex = 0; do { wchar_t buf[MAX_PATH]; swprintf_s(buf, _countof(buf), L"%s%d", L"Software\\TortoiseGit\\LastUsedUUIDsForGrouping\\", foundUUIDIndex); CRegStdString r = CRegStdString(buf); std::wstring sr = r; if (sr.empty() || (sr.compare(uuid)==0)) { r = uuid; break; } foundUUIDIndex++; } while (foundUUIDIndex < 20); if (foundUUIDIndex >= 20) { CRegStdString r = CRegStdString(L"Software\\TortoiseGit\\LastUsedUUIDsForGrouping\\1"); r.removeKey(); } DWORD colors[6] = {0x80FF0000, 0x80FFFF00, 0x8000FF00, 0x800000FF, 0x80000000, 0x8000FFFF}; // AND mask - monochrome - determines which pixels get drawn BYTE AND[32]; for( int i=0; i<32; i++ ) { AND[i] = 0xFF; } // XOR mask - 32bpp ARGB - determines the pixel values DWORD XOR[256]; for( int i=0; i<256; i++ ) { XOR[i] = colors[foundUUIDIndex % 6]; } HICON icon = ::CreateIcon(NULL,16,16,1,32,AND,(BYTE*)XOR); pTaskbarInterface->SetOverlayIcon(hWnd, icon, uuid.c_str()); pTaskbarInterface->Release(); DestroyIcon(icon); } } } } }
LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { m_hwnd = hwnd; Initialize(); } break; case WM_COMMAND: { return DoCommand(LOWORD(wParam)); } break; case WM_MOUSEWHEEL: { if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT) { // scroll sideways SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0); } else return DefWindowProc(hwnd, uMsg, wParam, lParam); } break; case WM_SIZE: { RECT rect; GetClientRect(*this, &rect); if (m_bShowFindBar) { ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top-30, SWP_SHOWWINDOW); ::SetWindowPos(m_FindBar, HWND_TOP, rect.left, rect.bottom-30, rect.right-rect.left, 30, SWP_SHOWWINDOW); } else { ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW); ::ShowWindow(m_FindBar, SW_HIDE); } } break; case WM_GETMINMAXINFO: { MINMAXINFO * mmi = (MINMAXINFO*)lParam; mmi->ptMinTrackSize.x = 100; mmi->ptMinTrackSize.y = 100; return 0; } break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CLOSE: { CRegStdDWORD w = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)CW_USEDEFAULT); CRegStdDWORD h = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)CW_USEDEFAULT); CRegStdDWORD p = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0); RECT rect; ::GetWindowRect(*this, &rect); w = rect.right-rect.left; h = rect.bottom-rect.top; p = MAKELONG(rect.left, rect.top); } ::DestroyWindow(m_hwnd); break; case WM_SETFOCUS: SetFocus(m_hWndEdit); break; case COMMITMONITOR_FINDMSGNEXT: { SendEditor(SCI_CHARRIGHT); SendEditor(SCI_SEARCHANCHOR); m_bMatchCase = !!wParam; m_findtext = (LPCTSTR)lParam; SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str()); SendEditor(SCI_SCROLLCARET); } break; case COMMITMONITOR_FINDMSGPREV: { SendEditor(SCI_SEARCHANCHOR); m_bMatchCase = !!wParam; m_findtext = (LPCTSTR)lParam; SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str()); SendEditor(SCI_SCROLLCARET); } break; case COMMITMONITOR_FINDEXIT: { RECT rect; GetClientRect(*this, &rect); m_bShowFindBar = false; ::ShowWindow(m_FindBar, SW_HIDE); ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW); } break; case COMMITMONITOR_FINDRESET: SendEditor(SCI_SETSELECTIONSTART, 0); SendEditor(SCI_SETSELECTIONEND, 0); SendEditor(SCI_SEARCHANCHOR); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; };
BOOL CSetOverlayIcons::OnInitDialog() { ISettingsPropPage::OnInitDialog(); m_cIconList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES); // get the path to our icon sets TCHAR buf[MAX_PATH] = {0}; SHGetSpecialFolderPath(m_hWnd, buf, CSIDL_PROGRAM_FILES_COMMON, true); m_sIconPath = buf; m_sIconPath += _T("\\TortoiseOverlays\\Icons"); // list all the icon sets CDirFileEnum filefinder(m_sIconPath); bool isDir = false; CString item; while (filefinder.NextFile(item, &isDir)) { if (!isDir) continue; m_cIconSet.AddString(CPathUtils::GetFileNameFromPath(item)); } CheckRadioButton(IDC_LISTRADIO, IDC_SYMBOLRADIO, IDC_LISTRADIO); CString sModifiedIcon = m_regModified; if (sModifiedIcon.IsEmpty()) { // no custom icon set, use the default sModifiedIcon = m_sIconPath + _T("\\XPStyle\\ModifiedIcon.ico"); } if (sModifiedIcon.Left(m_sIconPath.GetLength()).CompareNoCase(m_sIconPath)!=0) { // an icon set outside our own installation? We don't support that, // so fall back to the default! sModifiedIcon = m_sIconPath + _T("\\XPStyle\\ModifiedIcon.ico"); } // the name of the icon set is the folder of the icon location m_sOriginalIconSet = sModifiedIcon.Mid(m_sIconPath.GetLength()+1); m_sOriginalIconSet = m_sOriginalIconSet.Left(m_sOriginalIconSet.ReverseFind('\\')); // now we have the name of the icon set. Set the combobox to show // that as selected CString ComboItem; for (int i=0; i<m_cIconSet.GetCount(); ++i) { m_cIconSet.GetLBText(i, ComboItem); if (ComboItem.CompareNoCase(m_sOriginalIconSet)==0) m_cIconSet.SetCurSel(i); } WORD langID = (WORD)(DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID()); TCHAR statustext[MAX_STATUS_STRING_LENGTH] = { 0 }; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_normal, statustext, _countof(statustext), langID); m_sNormal = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_modified, statustext, _countof(statustext), langID); m_sModified = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_conflicted, statustext, _countof(statustext), langID); m_sConflicted = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_deleted, statustext, _countof(statustext), langID); m_sDeleted = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_added, statustext, _countof(statustext), langID); m_sAdded = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_ignored, statustext, _countof(statustext), langID); m_sIgnored = statustext; GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_unversioned, statustext, _countof(statustext), langID); m_sUnversioned = statustext; m_sReadOnly.LoadString(IDS_SETTINGS_READONLYNAME); m_sLocked.LoadString(IDS_SETTINGS_LOCKEDNAME); SetWindowTheme(m_hWnd, L"Explorer", NULL); ShowIconSet(true); return TRUE; }
bool CMainWindow::Initialize() { CRegStdDWORD pos(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0); CRegStdDWORD width(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)640); CRegStdDWORD height(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)480); if (DWORD(pos) && DWORD(width) && DWORD(height)) { RECT rc; rc.left = LOWORD(DWORD(pos)); rc.top = HIWORD(DWORD(pos)); rc.right = rc.left + DWORD(width); rc.bottom = rc.top + DWORD(height); HMONITOR hMon = MonitorFromRect(&rc, MONITOR_DEFAULTTONULL); if (hMon) { // only restore the window position if the monitor is valid MoveWindow(*this, LOWORD(DWORD(pos)), HIWORD(DWORD(pos)), DWORD(width), DWORD(height), FALSE); } } m_hWndEdit = ::CreateWindow( _T("Scintilla"), _T("Source"), WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, *this, 0, hResource, 0); if (m_hWndEdit == NULL) return false; RECT rect; GetClientRect(*this, &rect); ::SetWindowPos(m_hWndEdit, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW); m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0); m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0); // Set up the global default style. These attributes are used wherever no explicit choices are made. SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW), // Reusing TortoiseBlame's setting which already have an user friendly // pane in TortoiseSVN's Settings dialog, while there is no such // pane for TortoiseUDiff. CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10), WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str()); SendEditor(SCI_SETTABWIDTH, 4); SendEditor(SCI_SETREADONLY, TRUE); LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999"); SendEditor(SCI_SETMARGINWIDTHN, 0, pix); SendEditor(SCI_SETMARGINWIDTHN, 1); SendEditor(SCI_SETMARGINWIDTHN, 2); //Set the default windows colors for edit controls SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT)); SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW)); SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT)); SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT)); SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT)); return true; }
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // #include "StdAfx.h" #include ".\statuscacheentry.h" #include "GitStatus.h" #include "CacheInterface.h" #include "registry.h" #define CACHEVERION 7 DWORD cachetimeout = (DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\Cachetimeout"), CACHETIMEOUT); CStatusCacheEntry::CStatusCacheEntry() : m_bSet(false) , m_kind(git_node_unknown) , m_highestPriorityLocalStatus(git_wc_status_none) , m_bAssumeValid(false) , m_bSkipWorktree(false) { SetAsUnversioned(); } CStatusCacheEntry::CStatusCacheEntry(const git_wc_status_kind status) : m_bSet(true) , m_kind(git_node_unknown) , m_highestPriorityLocalStatus(status)
git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */) { // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right // after the call again // apr_hash_t * statushash; // apr_hash_t * exthash; // apr_array_header_t * statusarray; // const sort_item* item; // git_error_clear(m_err); // statushash = apr_hash_make(m_pool); // exthash = apr_hash_make(m_pool); git_revnum_t youngest = GIT_INVALID_REVNUM; // git_opt_revision_t rev; // rev.kind = git_opt_revision_unspecified; CString sProjectRoot; if ( !path.HasAdminDir(&sProjectRoot) ) return youngest; struct hashbaton_t hashbaton; // hashbaton.hash = statushash; // hashbaton.exthash = exthash; hashbaton.pThis = this; bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"), GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull); { LPCTSTR lpszSubPath = NULL; CString sSubPath; CString s = path.GetWinPathString(); if (s.GetLength() > sProjectRoot.GetLength()) { sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength()); lpszSubPath = sSubPath; // skip initial slash if necessary if (*lpszSubPath == _T('\\')) lpszSubPath++; } m_status.prop_status = m_status.text_status = git_wc_status_none; if(path.IsDirectory()) { m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , isfull, false,!noignore, NULL, NULL); } else { m_err = GetFileStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status ,isfull, false,!noignore, NULL,NULL); } } // Error present if function is not under version control if (m_err) /*|| (apr_hash_count(statushash) == 0)*/ { status = NULL; return GIT_INVALID_REVNUM; } // Convert the unordered hash to an ordered, sorted array /*statusarray = sort_hash (statushash, sort_compare_items_as_paths, m_pool);*/ // only the first entry is needed (no recurse) // item = &APR_ARRAY_IDX (statusarray, 0, const sort_item); // status = (git_wc_status2_t *) item->value; status = &m_status; if (update) { // done to match TSVN functionality of this function (not sure if any code uses the reutrn val) // if TGit does not need this, then change the return type of function youngest = g_Git.GetHash(_T("HEAD")); } return youngest; }
void CFolderCrawler::WorkerThread() { HANDLE hWaitHandles[2]; hWaitHandles[0] = m_hTerminationEvent; hWaitHandles[1] = m_hWakeEvent; CTGitPath workingPath; ULONGLONG currentTicks = 0; for(;;) { bool bRecursive = !!(DWORD)CRegStdDWORD(L"Software\\TortoiseGit\\RecursiveOverlay", TRUE); SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); DWORD waitResult = WaitForMultipleObjects(_countof(hWaitHandles), hWaitHandles, FALSE, INFINITE); // exit event/working loop if the first event (m_hTerminationEvent) // has been signaled or if one of the events has been abandoned // (i.e. ~CFolderCrawler() is being executed) if(m_bRun == false || waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED_0 || waitResult == WAIT_ABANDONED_0+1) { // Termination event break; } SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); // If we get here, we've been woken up by something being added to the queue. // However, it's important that we don't do our crawling while // the shell is still asking for items bool bFirstRunAfterWakeup = true; for(;;) { if (!m_bRun) break; // Any locks today? if (CGitStatusCache::Instance().m_bClearMemory) { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().ClearCache(); CGitStatusCache::Instance().m_bClearMemory = false; } if(m_lCrawlInhibitSet > 0) { // We're in crawl hold-off CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Crawl hold-off\n"); Sleep(50); continue; } if (bFirstRunAfterWakeup) { Sleep(20); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Crawl bFirstRunAfterWakeup\n"); bFirstRunAfterWakeup = false; continue; } if ((m_blockReleasesAt < GetTickCount64()) && (!m_blockedPath.IsEmpty())) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Crawl stop blocking path %s\n", m_blockedPath.GetWinPath()); m_blockedPath.Reset(); } CGitStatusCache::Instance().RemoveTimedoutBlocks(); while (!m_pathsToRelease.empty()) { AutoLocker lock(m_critSec); CTGitPath path = m_pathsToRelease.Pop(); GitStatus::ReleasePath(path.GetWinPathString()); } if (m_foldersToUpdate.empty() && m_pathsToUpdate.empty()) { // Nothing left to do break; } currentTicks = GetTickCount64(); if (!m_pathsToUpdate.empty()) { { AutoLocker lock(m_critSec); m_bPathsAddedSinceLastCrawl = false; workingPath = m_pathsToUpdate.Pop(); if ((!m_blockedPath.IsEmpty()) && (m_blockedPath.IsAncestorOf(workingPath))) { // move the path to the end of the list m_pathsToUpdate.Push(workingPath); if (m_pathsToUpdate.size() < 3) Sleep(50); continue; } } // don't crawl paths that are excluded if (!CGitStatusCache::Instance().IsPathAllowed(workingPath)) continue; // check if the changed path is inside an .git folder CString projectroot; if ((workingPath.HasAdminDir(&projectroot)&&workingPath.IsDirectory()) || workingPath.IsAdminDir()) { // we don't crawl for paths changed in a tmp folder inside an .git folder. // Because we also get notifications for those even if we just ask for the status! // And changes there don't affect the file status at all, so it's safe // to ignore notifications on those paths. if (workingPath.IsAdminDir()) { // TODO: add git specific filters here. is there really any change besides index file in .git // that is relevant for overlays? /*CString lowerpath = workingPath.GetWinPathString(); lowerpath.MakeLower(); if (lowerpath.Find(L"\\tmp\\") > 0) continue; if (CStringUtils::EndsWith(lowerpath, L"\\tmp")) continue; if (lowerpath.Find(L"\\log") > 0) continue;*/ // Here's a little problem: // the lock file is also created for fetching the status // and not just when committing. // If we could find out why the lock file was changed // we could decide to crawl the folder again or not. // But for now, we have to crawl the parent folder // no matter what. //if (lowerpath.Find(L"\\lock") > 0) // continue; // only go back to wc root if we are in .git-dir do { workingPath = workingPath.GetContainingDirectory(); } while(workingPath.IsAdminDir()); } else if (!workingPath.Exists()) { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().RemoveCacheForPath(workingPath); continue; } if (!CGitStatusCache::Instance().IsPathGood(workingPath)) { AutoLocker lock(m_critSec); // move the path, the root of the repository, to the end of the list if (projectroot.IsEmpty()) m_pathsToUpdate.Push(workingPath); else m_pathsToUpdate.Push(CTGitPath(projectroot)); if (m_pathsToUpdate.size() < 3) Sleep(50); continue; } CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Invalidating and refreshing folder: %s\n", workingPath.GetWinPath()); { AutoLocker print(critSec); _sntprintf_s(szCurrentCrawledPath[nCurrentCrawledpathIndex], MAX_CRAWLEDPATHSLEN, _TRUNCATE, L"Invalidating and refreshing folder: %s", workingPath.GetWinPath()); ++nCurrentCrawledpathIndex; if (nCurrentCrawledpathIndex >= MAX_CRAWLEDPATHS) nCurrentCrawledpathIndex = 0; } InvalidateRect(hWndHidden, nullptr, FALSE); { CAutoReadLock readLock(CGitStatusCache::Instance().GetGuard()); // Invalidate the cache of this folder, to make sure its status is fetched again. CCachedDirectory * pCachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(workingPath); if (pCachedDir) { git_wc_status_kind status = pCachedDir->GetCurrentFullStatus(); pCachedDir->Invalidate(); if (workingPath.Exists()) { pCachedDir->RefreshStatus(bRecursive); // if the previous status wasn't normal and now it is, then // send a notification too. // We do this here because GetCurrentFullStatus() doesn't send // notifications for 'normal' status - if it would, we'd get tons // of notifications when crawling a working copy not yet in the cache. if ((status != git_wc_status_normal) && (pCachedDir->GetCurrentFullStatus() != status)) { CGitStatusCache::Instance().UpdateShell(workingPath); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": shell update in crawler for %s\n", workingPath.GetWinPath()); } } else { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().RemoveCacheForPath(workingPath); } } } //In case that svn_client_stat() modified a file and we got //a notification about that in the directory watcher, //remove that here again - this is to prevent an endless loop AutoLocker lock(m_critSec); m_pathsToUpdate.erase(workingPath); } else if (workingPath.HasAdminDir()) { if (!workingPath.Exists()) { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().RemoveCacheForPath(workingPath); if (!workingPath.GetContainingDirectory().Exists()) continue; else workingPath = workingPath.GetContainingDirectory(); } CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Updating path: %s\n", workingPath.GetWinPath()); { AutoLocker print(critSec); _sntprintf_s(szCurrentCrawledPath[nCurrentCrawledpathIndex], MAX_CRAWLEDPATHSLEN, _TRUNCATE, L"Updating path: %s", workingPath.GetWinPath()); ++nCurrentCrawledpathIndex; if (nCurrentCrawledpathIndex >= MAX_CRAWLEDPATHS) nCurrentCrawledpathIndex = 0; } InvalidateRect(hWndHidden, nullptr, FALSE); { CAutoReadLock readLock(CGitStatusCache::Instance().GetGuard()); // Invalidate the cache of folders manually. The cache of files is invalidated // automatically if the status is asked for it and the file times don't match // anymore, so we don't need to manually invalidate those. CCachedDirectory* cachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(workingPath.GetDirectory()); if (cachedDir && workingPath.IsDirectory()) cachedDir->Invalidate(); if (cachedDir && cachedDir->GetStatusForMember(workingPath, bRecursive).GetEffectiveStatus() > git_wc_status_unversioned) CGitStatusCache::Instance().UpdateShell(workingPath); } AutoLocker lock(m_critSec); m_pathsToUpdate.erase(workingPath); } else { if (!workingPath.Exists()) { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().RemoveCacheForPath(workingPath); } } } if (!m_foldersToUpdate.empty()) { { AutoLocker lock(m_critSec); m_bItemsAddedSinceLastCrawl = false; // create a new CTGitPath object to make sure the cached flags are requested again. // without this, a missing file/folder is still treated as missing even if it is available // now when crawling. workingPath = CTGitPath(m_foldersToUpdate.Pop().GetWinPath()); if ((!m_blockedPath.IsEmpty())&&(m_blockedPath.IsAncestorOf(workingPath))) { // move the path to the end of the list m_foldersToUpdate.Push(workingPath); if (m_foldersToUpdate.size() < 3) Sleep(50); continue; } } if ((!m_blockedPath.IsEmpty())&&(m_blockedPath.IsAncestorOf(workingPath))) continue; if (!CGitStatusCache::Instance().IsPathAllowed(workingPath)) continue; if (!CGitStatusCache::Instance().IsPathGood(workingPath)) continue; CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Crawling folder: %s\n", workingPath.GetWinPath()); { AutoLocker print(critSec); _sntprintf_s(szCurrentCrawledPath[nCurrentCrawledpathIndex], MAX_CRAWLEDPATHSLEN, _TRUNCATE, L"Crawling folder: %s", workingPath.GetWinPath()); ++nCurrentCrawledpathIndex; if (nCurrentCrawledpathIndex >= MAX_CRAWLEDPATHS) nCurrentCrawledpathIndex = 0; } InvalidateRect(hWndHidden, nullptr, FALSE); { CAutoReadLock readLock(CGitStatusCache::Instance().GetGuard()); // Now, we need to visit this folder, to make sure that we know its 'most important' status CCachedDirectory * cachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(workingPath.GetDirectory()); // check if the path is monitored by the watcher. If it isn't, then we have to invalidate the cache // for that path and add it to the watcher. if (!CGitStatusCache::Instance().IsPathWatched(workingPath)) { if (workingPath.HasAdminDir()) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Add watch path %s\n", workingPath.GetWinPath()); CGitStatusCache::Instance().AddPathToWatch(workingPath); } if (cachedDir) cachedDir->Invalidate(); else { CAutoWriteLock writeLock(CGitStatusCache::Instance().GetGuard()); CGitStatusCache::Instance().RemoveCacheForPath(workingPath); // now cacheDir is invalid because it got deleted in the RemoveCacheForPath() call above. cachedDir = nullptr; } } if (cachedDir) cachedDir->RefreshStatus(bRecursive); } // While refreshing the status, we could get another crawl request for the same folder. // This can happen if the crawled folder has a lower status than one of the child folders // (recursively). To avoid double crawlings, remove such a crawl request here AutoLocker lock(m_critSec); if (m_bItemsAddedSinceLastCrawl) { m_foldersToUpdate.erase(workingPath); } } } } _endthread(); }
void CTortoiseProcApp::CheckUpgrade() { CRegString regVersion = CRegString(_T("Software\\TortoiseGit\\CurrentVersion")); CString sVersion = regVersion; if (sVersion.Compare(_T(STRPRODUCTVER))==0) return; // we're starting the first time with a new version! LONG lVersion = 0; int pos = sVersion.Find(','); if (pos > 0) { lVersion = (_ttol(sVersion.Left(pos))<<24); lVersion |= (_ttol(sVersion.Mid(pos+1))<<16); pos = sVersion.Find(',', pos+1); lVersion |= (_ttol(sVersion.Mid(pos+1))<<8); } CRegDWORD regval = CRegDWORD(_T("Software\\TortoiseGit\\DontConvertBase"), 999); if ((DWORD)regval != 999) { // there's a leftover registry setting we have to convert and then delete it CRegDWORD newregval = CRegDWORD(_T("Software\\TortoiseGit\\ConvertBase")); newregval = !regval; regval.removeValue(); } #if 0 if (lVersion <= 0x01010300) { CSoundUtils::RegisterTSVNSounds(); // remove all saved dialog positions CRegString(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\")).removeKey(); CRegDWORD(_T("Software\\TortoiseGit\\RecursiveOverlay")).removeValue(); // remove the external cache key CRegDWORD(_T("Software\\TortoiseGit\\ExternalCache")).removeValue(); } #endif if (lVersion <= 0x01020200) { // upgrade to > 1.2.3 means the doc diff scripts changed from vbs to js // so remove the diff/merge scripts if they're the defaults CRegString diffreg = CRegString(_T("Software\\TortoiseGit\\DiffTools\\.doc")); CString sDiff = diffreg; CString sCL = _T("wscript.exe \"") + CPathUtils::GetAppParentDirectory()+_T("Diff-Scripts\\diff-doc.vbs\""); if (sDiff.Left(sCL.GetLength()).CompareNoCase(sCL)==0) diffreg = _T(""); CRegString mergereg = CRegString(_T("Software\\TortoiseGit\\MergeTools\\.doc")); sDiff = mergereg; sCL = _T("wscript.exe \"") + CPathUtils::GetAppParentDirectory()+_T("Diff-Scripts\\merge-doc.vbs\""); if (sDiff.Left(sCL.GetLength()).CompareNoCase(sCL)==0) mergereg = _T(""); } if (lVersion <= 0x01040000) { CRegStdDWORD(_T("Software\\TortoiseGit\\OwnerdrawnMenus")).removeValue(); } // set the custom diff scripts for every user CString scriptsdir = CPathUtils::GetAppParentDirectory(); scriptsdir += _T("Diff-Scripts"); CSimpleFileFind files(scriptsdir); while (files.FindNextFileNoDirectories()) { CString file = files.GetFilePath(); CString filename = files.GetFileName(); CString ext = file.Mid(file.ReverseFind('-')+1); ext = _T(".")+ext.Left(ext.ReverseFind('.')); CString kind; if (file.Right(3).CompareNoCase(_T("vbs"))==0) { kind = _T(" //E:vbscript"); } if (file.Right(2).CompareNoCase(_T("js"))==0) { kind = _T(" //E:javascript"); } if (filename.Left(5).CompareNoCase(_T("diff-"))==0) { CRegString diffreg = CRegString(_T("Software\\TortoiseGit\\DiffTools\\")+ext); CString diffregstring = diffreg; if ((diffregstring.IsEmpty()) || (diffregstring.Find(filename)>=0)) diffreg = _T("wscript.exe \"") + file + _T("\" %base %mine") + kind; } if (filename.Left(6).CompareNoCase(_T("merge-"))==0) { CRegString diffreg = CRegString(_T("Software\\TortoiseGit\\MergeTools\\")+ext); CString diffregstring = diffreg; if ((diffregstring.IsEmpty()) || (diffregstring.Find(filename)>=0)) diffreg = _T("wscript.exe \"") + file + _T("\" %merged %theirs %mine %base") + kind; } } // Initialize "Software\\TortoiseGit\\DiffProps" once with the same value as "Software\\TortoiseGit\\Diff" CRegString regDiffPropsPath = CRegString(_T("Software\\TortoiseGit\\DiffProps"),_T("non-existant")); CString strDiffPropsPath = regDiffPropsPath; if ( strDiffPropsPath==_T("non-existant") ) { CString strDiffPath = CRegString(_T("Software\\TortoiseGit\\Diff")); regDiffPropsPath = strDiffPath; } // set the current version so we don't come here again until the next update! regVersion = _T(STRPRODUCTVER); }
void CGitStatusCache::Create() { ATLASSERT(m_pInstance == NULL); m_pInstance = new CGitStatusCache; m_pInstance->watcher.SetFolderCrawler(&m_pInstance->m_folderCrawler); if (!CRegStdDWORD(_T("Software\\TortoiseGit\\CacheSave"), TRUE)) return; #define LOADVALUEFROMFILE(x) if (fread(&x, sizeof(x), 1, pFile)!=1) goto exit; #define LOADVALUEFROMFILE2(x) if (fread(&x, sizeof(x), 1, pFile)!=1) goto error; unsigned int value = (unsigned int)-1; FILE * pFile = NULL; // find the location of the cache CString path = CPathUtils::GetLocalAppDataDirectory(); CString path2; if (!path.IsEmpty()) { path += STATUSCACHEFILENAME; // in case the cache file is corrupt, we could crash while // reading it! To prevent crashing every time once that happens, // we make a copy of the cache file and use that copy to read from. // if that copy is corrupt, the original file won't exist anymore // and the second time we start up and try to read the file, // it's not there anymore and we start from scratch without a crash. path2 = path; path2 += _T("2"); DeleteFile(path2); CopyFile(path, path2, FALSE); DeleteFile(path); pFile = _tfsopen(path2, _T("rb"), _SH_DENYNO); if (pFile) { try { LOADVALUEFROMFILE(value); if (value != CACHEDISKVERSION) { goto error; } int mapsize = 0; LOADVALUEFROMFILE(mapsize); for (int i=0; i<mapsize; ++i) { LOADVALUEFROMFILE2(value); if (value > MAX_PATH) goto error; if (value) { CString sKey; if (fread(sKey.GetBuffer(value+1), sizeof(TCHAR), value, pFile)!=value) { sKey.ReleaseBuffer(0); goto error; } sKey.ReleaseBuffer(value); std::unique_ptr<CCachedDirectory> cacheddir (new CCachedDirectory()); if (!cacheddir.get() || !cacheddir->LoadFromDisk(pFile)) { cacheddir.reset(); goto error; } CTGitPath KeyPath = CTGitPath(sKey); if (m_pInstance->IsPathAllowed(KeyPath)) { // only add the path to the watch list if it is versioned if ((cacheddir->GetCurrentFullStatus() != git_wc_status_unversioned)&&(cacheddir->GetCurrentFullStatus() != git_wc_status_none)) m_pInstance->watcher.AddPath(KeyPath, false); m_pInstance->m_directoryCache[KeyPath] = cacheddir.release(); // do *not* add the paths for crawling! // because crawled paths will trigger a shell // notification, which makes the desktop flash constantly // until the whole first time crawling is over // m_pInstance->AddFolderForCrawling(KeyPath); } } } } catch (CAtlException) { goto error; } } } exit: if (pFile) fclose(pFile); DeleteFile(path2); m_pInstance->watcher.ClearInfoMap(); CTraceToOutputDebugString::Instance()(__FUNCTION__ ": cache loaded from disk successfully!\n"); return; error: if (pFile) fclose(pFile); DeleteFile(path2); m_pInstance->watcher.ClearInfoMap(); Destroy(); m_pInstance = new CGitStatusCache; CTraceToOutputDebugString::Instance()(__FUNCTION__ ": cache not loaded from disk\n"); }
void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu *popmenu) { POSITION pos = GetFirstSelectedItemPosition(); int indexNext = GetNextSelectedItem(pos); if (indexNext < 0) return; GitRevLoglist* pSelLogEntry = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(indexNext)); theApp.DoWaitCursor(1); switch (cmd&0xFFFF) { case ID_COMMIT: { CTGitPathList pathlist; CTGitPathList selectedlist; pathlist.AddPath(this->m_Path); bool bSelectFilesForCommit = !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE)); CString str; CAppUtils::Commit(CString(),false,str, pathlist,selectedlist,bSelectFilesForCommit); //this->Refresh(); this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0); } break; case ID_MERGE_ABORT: { if (CAppUtils::MergeAbort()) this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH, 0); } break; case ID_GNUDIFF1: // compare with WC, unified { GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect)); bool bMerge = false, bCombine = false; CString hash2; if(!r1->m_CommitHash.IsEmpty()) { CString merge; cmd >>= 16; if( (cmd&0xFFFF) == 0xFFFF) bMerge = true; else if((cmd&0xFFFF) == 0xFFFE) bCombine = true; else if ((cmd & 0xFFFF) == 0xFFFD) { CString tempfile = GetTempFile(); CString cmd = _T("git.exe diff-tree --cc ") + r1->m_CommitHash.ToString(); CString lastErr; if (g_Git.RunLogFile(cmd, tempfile, &lastErr)) { MessageBox(lastErr, _T("TortoiseGit"), MB_ICONERROR); break; } try { CStdioFile file(tempfile, CFile::typeText | CFile::modeRead | CFile::shareDenyWrite); CString strLine; bool isHash = file.ReadString(strLine) && r1->m_CommitHash.ToString() == strLine; bool more = isHash && file.ReadString(strLine) && !strLine.IsEmpty(); if (!more) { CMessageBox::Show(nullptr, IDS_NOCHANGEAFTERMERGE, IDS_APPNAME, MB_OK); break; } } catch (CFileException* e) { e->Delete(); } CAppUtils::StartUnifiedDiffViewer(tempfile, _T("dd")); break; } else { if(cmd > r1->m_ParentHash.size()) { CString str; str.Format(IDS_PROC_NOPARENT, cmd); MessageBox(str, _T("TortoiseGit"), MB_OK | MB_ICONERROR); return; } else { if(cmd>0) hash2 = r1->m_ParentHash[cmd-1].ToString(); } } CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), hash2, CTGitPath(), r1->m_CommitHash.ToString(), false, false, false, bMerge, bCombine); } else CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), _T("HEAD"), CTGitPath(), GitRev::GetWorkingCopy(), false, false, false, bMerge, bCombine); }
void CTortoiseProcApp::CheckUpgrade() { CRegString regVersion = CRegString(_T("Software\\TortoiseGit\\CurrentVersion")); CString sVersion = regVersion; if (sVersion.Compare(_T(STRPRODUCTVER))==0) return; // we're starting the first time with a new version! LONG lVersion = 0; int pos = sVersion.Find('.'); if (pos > 0) { lVersion = (_ttol(sVersion.Left(pos))<<24); lVersion |= (_ttol(sVersion.Mid(pos+1))<<16); pos = sVersion.Find('.', pos+1); lVersion |= (_ttol(sVersion.Mid(pos+1))<<8); } else { pos = sVersion.Find(','); if (pos > 0) { lVersion = (_ttol(sVersion.Left(pos))<<24); lVersion |= (_ttol(sVersion.Mid(pos+1))<<16); pos = sVersion.Find(',', pos+1); lVersion |= (_ttol(sVersion.Mid(pos+1))<<8); } } if (lVersion <= 0x01080202) { CSoundUtils::RegisterTGitSounds(); // upgrade to 1.8.3: force recreation of all diff scripts. CAppUtils::SetupDiffScripts(true, CString()); } if (lVersion <= 0x01080100) { if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogTopoOrder"), TRUE) == FALSE) CRegStdDWORD(_T("Software\\TortoiseGit\\LogOrderBy")) = 0; // smoothly migrate broken msysgit path settings CString oldmsysGitSetting = CRegString(REG_MSYSGIT_PATH); oldmsysGitSetting.TrimRight(_T("\\")); CString right = oldmsysGitSetting.Right(4); if (oldmsysGitSetting.GetLength() > 4 && oldmsysGitSetting.Right(4) == _T("\\cmd")) { CString newPath = oldmsysGitSetting.Mid(0, oldmsysGitSetting.GetLength() - 3) + _T("bin"); if (PathFileExists(newPath + _T("\\git.exe"))) { CRegString(REG_MSYSGIT_PATH) = newPath; g_Git.m_bInitialized = FALSE; g_Git.CheckMsysGitDir(); } } } if (lVersion <= 0x01040000) { CRegStdDWORD(_T("Software\\TortoiseGit\\OwnerdrawnMenus")).removeValue(); } if (lVersion <= 0x01070600) { CoInitialize(NULL); EnsureGitLibrary(); CoUninitialize(); CRegStdDWORD(_T("Software\\TortoiseGit\\ConvertBase")).removeValue(); CRegStdDWORD(_T("Software\\TortoiseGit\\DiffProps")).removeValue(); if (CRegStdDWORD(_T("Software\\TortoiseGit\\CheckNewer"), TRUE) == FALSE) CRegStdDWORD(_T("Software\\TortoiseGit\\VersionCheck")) = FALSE; } if (lVersion <= 0x01070E00) { CRegStdDWORD(_T("Software\\TortoiseGit\\CheckNewer")).removeValue(); } CAppUtils::SetupDiffScripts(false, CString()); // set the current version so we don't come here again until the next update! regVersion = _T(STRPRODUCTVER); }
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nCmdShow*/) { SetDllDirectory(L""); SetTaskIDPerUUID(); CRegStdDWORD loc = CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033); long langId = loc; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); CLangDll langDLL; hResource = langDLL.Init(_T("TortoiseIDiff"), langId); if (hResource == NULL) hResource = hInstance; git_libgit2_init(); CCmdLineParser parser(lpCmdLine); if (parser.HasKey(_T("?")) || parser.HasKey(_T("help"))) { TCHAR buf[1024] = { 0 }; LoadString(hResource, IDS_COMMANDLINEHELP, buf, _countof(buf)); MessageBox(NULL, buf, _T("TortoiseIDiff"), MB_ICONINFORMATION); langDLL.Close(); return 0; } MSG msg; hInst = hInstance; INITCOMMONCONTROLSEX used = { sizeof(INITCOMMONCONTROLSEX), ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_WIN95_CLASSES }; InitCommonControlsEx(&used); // load the cursors we need curHand = (HCURSOR)LoadImage(hInst, MAKEINTRESOURCE(IDC_PANCUR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE); curHandDown = (HCURSOR)LoadImage(hInst, MAKEINTRESOURCE(IDC_PANDOWNCUR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE); std::unique_ptr<CMainWindow> mainWindow(new CMainWindow(hResource)); mainWindow->SetRegistryPath(_T("Software\\TortoiseGit\\TortoiseIDiffWindowPos")); std::wstring leftfile = parser.HasVal(_T("left")) ? parser.GetVal(_T("left")) : _T(""); std::wstring rightfile = parser.HasVal(_T("right")) ? parser.GetVal(_T("right")) : _T(""); if ((leftfile.empty()) && (lpCmdLine[0] != 0)) { int nArgs; LPWSTR * szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); if (szArglist) { if (nArgs == 3) { // Four parameters: // [0]: Program name // [1]: left file // [2]: right file if (PathFileExists(szArglist[1]) && PathFileExists(szArglist[2])) { leftfile = szArglist[1]; rightfile = szArglist[2]; } } } // Free memory allocated for CommandLineToArgvW arguments. LocalFree(szArglist); } mainWindow->SetLeft(leftfile.c_str(), parser.HasVal(_T("lefttitle")) ? parser.GetVal(_T("lefttitle")) : _T("")); mainWindow->SetRight(rightfile.c_str(), parser.HasVal(_T("righttitle")) ? parser.GetVal(_T("righttitle")) : _T("")); if (parser.HasVal(L"base")) mainWindow->SetSelectionImage(FileTypeBase, parser.GetVal(L"base"), parser.HasVal(L"basetitle") ? parser.GetVal(L"basetitle") : L""); if (parser.HasVal(L"mine")) mainWindow->SetSelectionImage(FileTypeMine, parser.GetVal(L"mine"), parser.HasVal(L"minetitle") ? parser.GetVal(L"minetitle") : L""); if (parser.HasVal(L"theirs")) mainWindow->SetSelectionImage(FileTypeTheirs, parser.GetVal(L"theirs"), parser.HasVal(L"theirstitle") ? parser.GetVal(L"theirstitle") : L""); if (parser.HasVal(L"result")) mainWindow->SetSelectionResult(parser.GetVal(L"result")); mainWindow->resolveMsgWnd = parser.HasVal(L"resolvemsghwnd") ? (HWND)parser.GetLongLongVal(L"resolvemsghwnd") : 0; mainWindow->resolveMsgWParam = parser.HasVal(L"resolvemsgwparam") ? (WPARAM)parser.GetLongLongVal(L"resolvemsgwparam") : 0; mainWindow->resolveMsgLParam = parser.HasVal(L"resolvemsglparam") ? (LPARAM)parser.GetLongLongVal(L"resolvemsglparam") : 0; if (mainWindow->RegisterAndCreateWindow()) { HACCEL hAccelTable = LoadAccelerators(hResource, MAKEINTRESOURCE(IDR_TORTOISEIDIFF)); if (!parser.HasVal(L"left") && parser.HasVal(L"base") && !parser.HasVal(L"mine") && !parser.HasVal(L"theirs")) { PostMessage(*mainWindow, WM_COMMAND, ID_FILE_OPEN, 0); } if (parser.HasKey(_T("overlay"))) { PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_OVERLAPIMAGES, 0); } if (parser.HasKey(_T("fit"))) { PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_FITIMAGEHEIGHTS, 0); PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_FITIMAGEWIDTHS, 0); } if (parser.HasKey(_T("fitwidth"))) { PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_FITIMAGEWIDTHS, 0); } if (parser.HasKey(_T("fitheight"))) { PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_FITIMAGEHEIGHTS, 0); } if (parser.HasKey(_T("showinfo"))) { PostMessage(*mainWindow, WM_COMMAND, ID_VIEW_IMAGEINFO, 0); } // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(*mainWindow, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } langDLL.Close(); DestroyCursor(curHand); DestroyCursor(curHandDown); CoUninitialize(); git_libgit2_shutdown(); return 1; }
BOOL CTortoiseProcApp::InitInstance() { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": InitInstance\n")); CheckUpgrade(); CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); CMFCButton::EnableWindowsTheming(); CHistoryCombo::m_nGitIconIndex = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_GITCONFIG), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken,&gdiplusStartupInput,NULL); //set the resource dll for the required language CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033); long langId = loc; { CString langStr; langStr.Format(_T("%ld"), langId); CCrashReport::Instance().AddUserInfoToReport(L"LanguageID", langStr); } CString langDll; CStringA langpath = CStringA(CPathUtils::GetAppParentDirectory()); langpath += "Languages"; do { langDll.Format(_T("%sLanguages\\TortoiseProc%ld.dll"), (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId); CString sVer = _T(STRPRODUCTVER); CString sFileVer = CPathUtils::GetVersionFromFile(langDll); if (sFileVer == sVer) { HINSTANCE hInst = LoadLibrary(langDll); if (hInst != NULL) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Load Language DLL %s\n"), langDll); AfxSetResourceHandle(hInst); break; } } { DWORD lid = SUBLANGID(langId); lid--; if (lid > 0) { langId = MAKELANGID(PRIMARYLANGID(langId), lid); } else langId = 0; } } while (langId != 0); TCHAR buf[6] = { 0 }; _tcscpy_s(buf, _T("en")); langId = loc; // MFC uses a help file with the same name as the application by default, // which means we have to change that default to our language specific help files CString sHelppath = CPathUtils::GetAppDirectory() + _T("TortoiseGit_en.chm"); free((void*)m_pszHelpFilePath); m_pszHelpFilePath=_tcsdup(sHelppath); sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseGit_en.chm"); do { CString sLang = _T("_"); if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf))) { sLang += buf; sHelppath.Replace(_T("_en"), sLang); if (PathFileExists(sHelppath)) { free((void*)m_pszHelpFilePath); m_pszHelpFilePath=_tcsdup(sHelppath); break; } } sHelppath.Replace(sLang, _T("_en")); if (GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf))) { sLang += _T("_"); sLang += buf; sHelppath.Replace(_T("_en"), sLang); if (PathFileExists(sHelppath)) { free((void*)m_pszHelpFilePath); m_pszHelpFilePath=_tcsdup(sHelppath); break; } } sHelppath.Replace(sLang, _T("_en")); DWORD lid = SUBLANGID(langId); lid--; if (lid > 0) { langId = MAKELANGID(PRIMARYLANGID(langId), lid); } else langId = 0; } while (langId); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Set Help Filename %s\n"), m_pszHelpFilePath); setlocale(LC_ALL, ""); if (!g_Git.CheckMsysGitDir()) { UINT ret = CMessageBox::Show(NULL, IDS_PROC_NOMSYSGIT, IDS_APPNAME, 3, IDI_HAND, IDS_PROC_SETMSYSGITPATH, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON); if(ret == 2) { ShellExecute(NULL, NULL, _T("http://msysgit.github.io/"), NULL, NULL, SW_SHOW); } else if(ret == 1) { // open settings dialog CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROC_SETTINGS_TITLE)), new CSetMainPage(), this->GetMainWnd()).DoModal(); } return FALSE; } if (CAppUtils::GetMsysgitVersion() < 0x01070a00) { int ret = CMessageBox::ShowCheck(NULL, IDS_PROC_OLDMSYSGIT, IDS_APPNAME, 1, IDI_EXCLAMATION, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON, IDS_IGNOREBUTTON, _T("OldMsysgitVersionWarning"), IDS_PROC_NOTSHOWAGAINIGNORE); if (ret == 1) { CMessageBox::RemoveRegistryKey(_T("OldMsysgitVersionWarning")); // only store answer if it is "Ignore" ShellExecute(NULL, NULL, _T("http://msysgit.github.io/"), NULL, NULL, SW_SHOW); return FALSE; } else if (ret == 2) { CMessageBox::RemoveRegistryKey(_T("OldMsysgitVersionWarning")); // only store answer if it is "Ignore" return FALSE; } } { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Registering Crash Report ...\n")); CCrashReport::Instance().AddUserInfoToReport(L"msysGitDir", CGit::ms_LastMsysGitDir); CString versionString; versionString.Format(_T("%d"), CGit::ms_LastMsysGitVersion); CCrashReport::Instance().AddUserInfoToReport(L"msysGitVersion", versionString); } CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Initializing UI components ...\n")); // InitCommonControls() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX used = { sizeof(INITCOMMONCONTROLSEX), ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES | ICC_DATE_CLASSES | ICC_HOTKEY_CLASS | ICC_INTERNET_CLASSES | ICC_LISTVIEW_CLASSES | ICC_NATIVEFNTCTL_CLASS | ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES }; InitCommonControlsEx(&used); AfxOleInit(); AfxEnableControlContainer(); AfxInitRichEdit5(); CWinAppEx::InitInstance(); SetRegistryKey(_T("TortoiseGit")); AfxGetApp()->m_pszProfileName = _tcsdup(_T("TortoiseProc")); // w/o this ResizableLib will store data under TortoiseGitProc which is not compatible with older versions CCmdLineParser parser(AfxGetApp()->m_lpCmdLine); hWndExplorer = NULL; CString sVal = parser.GetVal(_T("hwnd")); if (!sVal.IsEmpty()) hWndExplorer = (HWND)_wcstoui64(sVal, nullptr, 16); while (GetParent(hWndExplorer)!=NULL) hWndExplorer = GetParent(hWndExplorer); if (!IsWindow(hWndExplorer)) { hWndExplorer = NULL; } // if HKCU\Software\TortoiseGit\Debug is not 0, show our command line // in a message box if (CRegDWORD(_T("Software\\TortoiseGit\\Debug"), FALSE)==TRUE) AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION); if (parser.HasKey(_T("urlhandler"))) { CString url = parser.GetVal(_T("urlhandler")); if (url.Find(_T("tgit://clone/")) == 0) { url = url.Mid(13); // 21 = "tgit://clone/".GetLength() } else if (url.Find(_T("github-windows://openRepo/")) == 0) { url = url.Mid(26); // 26 = "github-windows://openRepo/".GetLength() int questioMark = url.Find('?'); if (questioMark > 0) url = url.Left(questioMark); } else if (url.Find(_T("smartgit://cloneRepo/")) == 0) { url = url.Mid(21); // 21 = "smartgit://cloneRepo/".GetLength() } else { CMessageBox::Show(NULL, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR); return FALSE; } CString newCmd; newCmd.Format(_T("/command:clone /url:\"%s\" /hasurlhandler"), url); parser = CCmdLineParser(newCmd); } if ( parser.HasKey(_T("path")) && parser.HasKey(_T("pathfile"))) { CMessageBox::Show(NULL, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR); return FALSE; } CTGitPath cmdLinePath; CTGitPathList pathList; if (g_sGroupingUUID.IsEmpty()) g_sGroupingUUID = parser.GetVal(L"groupuuid"); if ( parser.HasKey(_T("pathfile")) ) { CString sPathfileArgument = CPathUtils::GetLongPathname(parser.GetVal(_T("pathfile"))); cmdLinePath.SetFromUnknown(sPathfileArgument); if (pathList.LoadFromFile(cmdLinePath)==false) return FALSE; // no path specified! if ( parser.HasKey(_T("deletepathfile")) ) { // We can delete the temporary path file, now that we've loaded it ::DeleteFile(cmdLinePath.GetWinPath()); } // This was a path to a temporary file - it's got no meaning now, and // anybody who uses it again is in for a problem... cmdLinePath.Reset(); } else { CString sPathArgument = CPathUtils::GetLongPathname(parser.GetVal(_T("path"))); if (parser.HasKey(_T("expaths"))) { // an /expaths param means we're started via the buttons in our Win7 library // and that means the value of /expaths is the current directory, and // the selected paths are then added as additional parameters but without a key, only a value // because of the "strange treatment of quotation marks and backslashes by CommandLineToArgvW" // we have to escape the backslashes first. Since we're only dealing with paths here, that's // a save bet. // Without this, a command line like: // /command:commit /expaths:"D:\" "D:\Utils" // would fail because the "D:\" is treated as the backslash being the escape char for the quotation // mark and we'd end up with: // argv[1] = /command:commit // argv[2] = /expaths:D:" D:\Utils // See here for more details: http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx CString cmdLine = GetCommandLineW(); cmdLine.Replace(L"\\", L"\\\\"); int nArgs = 0; LPWSTR *szArglist = CommandLineToArgvW(cmdLine, &nArgs); if (szArglist) { // argument 0 is the process path, so start with 1 for (int i = 1; i < nArgs; ++i) { if (szArglist[i][0] != '/') { if (!sPathArgument.IsEmpty()) sPathArgument += '*'; sPathArgument += szArglist[i]; } } sPathArgument.Replace(L"\\\\", L"\\"); } LocalFree(szArglist); } if (sPathArgument.IsEmpty() && parser.HasKey(L"path")) { CMessageBox::Show(hWndExplorer, IDS_ERR_INVALIDPATH, IDS_APPNAME, MB_ICONERROR); return FALSE; } int asterisk = sPathArgument.Find('*'); cmdLinePath.SetFromUnknown(asterisk >= 0 ? sPathArgument.Left(asterisk) : sPathArgument); pathList.LoadFromAsteriskSeparatedString(sPathArgument); } if (pathList.IsEmpty()) { pathList.AddPath(CTGitPath::CTGitPath(g_Git.m_CurrentDir)); } // Set CWD to temporary dir, and restore it later { DWORD len = GetCurrentDirectory(0, NULL); if (len) { std::unique_ptr<TCHAR[]> originalCurrentDirectory(new TCHAR[len]); if (GetCurrentDirectory(len, originalCurrentDirectory.get())) { sOrigCWD = originalCurrentDirectory.get(); sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD); } } TCHAR pathbuf[MAX_PATH] = {0}; GetTortoiseGitTempPath(MAX_PATH, pathbuf); SetCurrentDirectory(pathbuf); } CheckForNewerVersion(); CAutoGeneralHandle TGitMutex = ::CreateMutex(NULL, FALSE, _T("TortoiseGitProc.exe")); if (!g_Git.SetCurrentDir(cmdLinePath.GetWinPathString(), parser.HasKey(_T("submodule")) == TRUE)) { for (int i = 0; i < pathList.GetCount(); ++i) if(g_Git.SetCurrentDir(pathList[i].GetWinPath())) break; } if(!g_Git.m_CurrentDir.IsEmpty()) { sOrigCWD = g_Git.m_CurrentDir; SetCurrentDirectory(g_Git.m_CurrentDir); } if (g_sGroupingUUID.IsEmpty()) { CRegStdDWORD groupSetting = CRegStdDWORD(_T("Software\\TortoiseGit\\GroupTaskbarIconsPerRepo"), 3); switch (DWORD(groupSetting)) { case 1: case 2: // implemented differently to TortoiseSVN atm break; case 3: case 4: { CString wcroot; if (g_GitAdminDir.HasAdminDir(g_Git.m_CurrentDir, true, &wcroot)) { git_oid oid; CStringA wcRootA(wcroot + CPathUtils::GetAppDirectory()); if (!git_odb_hash(&oid, wcRootA, wcRootA.GetLength(), GIT_OBJ_BLOB)) { CStringA hash; git_oid_tostr(hash.GetBufferSetLength(GIT_OID_HEXSZ + 1), GIT_OID_HEXSZ + 1, &oid); hash.ReleaseBuffer(); g_sGroupingUUID = hash; } ProjectProperties pp; pp.ReadProps(); CString icon = pp.sIcon; icon.Replace('/', '\\'); if (icon.IsEmpty()) g_bGroupingRemoveIcon = true; g_sGroupingIcon = icon; } } } } CString sAppID = GetTaskIDPerUUID(g_sGroupingUUID).c_str(); InitializeJumpList(sAppID); EnsureGitLibrary(false); { CString err; try { // requires CWD to be set CGit::m_LogEncode = CAppUtils::GetLogOutputEncode(); // make sure all config files are read in order to check that none contains an error g_Git.GetConfigValue(_T("doesnot.exist")); } catch (char* msg) { err = CString(msg); } if (!err.IsEmpty()) { UINT choice = CMessageBox::Show(hWndExplorer, err, _T("TortoiseGit"), 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_PROC_EDITLOCALGITCONFIG)), CString(MAKEINTRESOURCE(IDS_PROC_EDITGLOBALGITCONFIG)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))); if (choice == 1) { // open the config file with alternative editor CAppUtils::LaunchAlternativeEditor(g_Git.GetGitLocalConfig()); } else if (choice == 2) { // open the global config file with alternative editor CAppUtils::LaunchAlternativeEditor(g_Git.GetGitGlobalConfig()); } return FALSE; } } // execute the requested command CommandServer server; Command * cmd = server.GetCommand(parser.GetVal(_T("command"))); if (cmd) { cmd->SetExplorerHwnd(hWndExplorer); cmd->SetParser(parser); cmd->SetPaths(pathList, cmdLinePath); retSuccess = cmd->Execute(); delete cmd; } // Look for temporary files left around by TortoiseSVN and // remove them. But only delete 'old' files because some // apps might still be needing the recent ones. { DWORD len = GetTortoiseGitTempPath(0, NULL); std::unique_ptr<TCHAR[]> path(new TCHAR[len + 100]); len = GetTortoiseGitTempPath (len + 100, path.get()); if (len != 0) { CDirFileEnum finder(path.get()); FILETIME systime_; ::GetSystemTimeAsFileTime(&systime_); __int64 systime = (((_int64)systime_.dwHighDateTime)<<32) | ((__int64)systime_.dwLowDateTime); bool isDir; CString filepath; while (finder.NextFile(filepath, &isDir)) { HANDLE hFile = ::CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, isDir ? FILE_FLAG_BACKUP_SEMANTICS : NULL, NULL); if (hFile != INVALID_HANDLE_VALUE) { FILETIME createtime_; if (::GetFileTime(hFile, &createtime_, NULL, NULL)) { ::CloseHandle(hFile); __int64 createtime = (((_int64)createtime_.dwHighDateTime)<<32) | ((__int64)createtime_.dwLowDateTime); if ((createtime + 864000000000) < systime) //only delete files older than a day { ::SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL); if (isDir) ::RemoveDirectory(filepath); else ::DeleteFile(filepath); } } else ::CloseHandle(hFile); } } } } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; }
ShellCache::ShellCache() { cachetype = CRegStdDWORD(L"Software\\TortoiseSVN\\CacheType", GetSystemMetrics(SM_REMOTESESSION) ? dll : exe); showrecursive = CRegStdDWORD(L"Software\\TortoiseSVN\\RecursiveOverlay", TRUE); folderoverlay = CRegStdDWORD(L"Software\\TortoiseSVN\\FolderOverlay", TRUE); driveremote = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskRemote"); drivefixed = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskFixed", TRUE); drivecdrom = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskCDROM"); driveremove = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskRemovable"); drivefloppy = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskFloppy"); driveram = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskRAM"); driveunknown = CRegStdDWORD(L"Software\\TortoiseSVN\\DriveMaskUnknown"); shellmenuaccelerators = CRegStdDWORD(L"Software\\TortoiseSVN\\ShellMenuAccelerators", TRUE); unversionedasmodified = CRegStdDWORD(L"Software\\TortoiseSVN\\UnversionedAsModified", FALSE); ignoreoncommitignored = CRegStdDWORD(L"Software\\TortoiseSVN\\IgnoreOnCommitIgnored", TRUE); hidemenusforunversioneditems = CRegStdDWORD(L"Software\\TortoiseSVN\\HideMenusForUnversionedItems", FALSE); getlocktop = CRegStdDWORD(L"Software\\TortoiseSVN\\GetLockTop", TRUE); excludedasnormal = CRegStdDWORD(L"Software\\TortoiseSVN\\ShowExcludedFoldersAsNormal", FALSE); alwaysextended = CRegStdDWORD(L"Software\\TortoiseSVN\\AlwaysExtendedMenu", FALSE); cachetypeticker = GetTickCount64(); recursiveticker = cachetypeticker; folderoverlayticker = cachetypeticker; driveticker = cachetypeticker; drivetypeticker = 0; langticker = cachetypeticker; columnrevformatticker = cachetypeticker; pathfilterticker = 0; shellmenuacceleratorsticker = cachetypeticker; unversionedasmodifiedticker = cachetypeticker; ignoreoncommitignoredticker = cachetypeticker; columnseverywhereticker = cachetypeticker; getlocktopticker = cachetypeticker; excludedasnormalticker = cachetypeticker; alwaysextendedticker = cachetypeticker; hidemenusforunversioneditemsticker = cachetypeticker; layoutticker = cachetypeticker; menumaskticker = cachetypeticker; blockstatusticker = cachetypeticker; excontextticker = 0; menulayoutlow = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntries", MENUCHECKOUT | MENUUPDATE | MENUCOMMIT); menulayouthigh = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntrieshigh", 0); menumasklow_lm = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntriesMaskLow", 0, FALSE, HKEY_LOCAL_MACHINE); menumaskhigh_lm = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntriesMaskHigh", 0, FALSE, HKEY_LOCAL_MACHINE); menumasklow_cu = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntriesMaskLow", 0); menumaskhigh_cu = CRegStdDWORD(L"Software\\TortoiseSVN\\ContextMenuEntriesMaskHigh", 0); langid = CRegStdDWORD(L"Software\\TortoiseSVN\\LanguageID", 1033); blockstatus = CRegStdDWORD(L"Software\\TortoiseSVN\\BlockStatus", 0); columnseverywhere = CRegStdDWORD(L"Software\\TortoiseSVN\\ColumnsEveryWhere", FALSE); std::fill_n(drivetypecache, 27, (UINT)-1); if (DWORD(drivefloppy) == 0) { // A: and B: are floppy disks drivetypecache[0] = DRIVE_REMOVABLE; drivetypecache[1] = DRIVE_REMOVABLE; } TCHAR szBuffer[5] = { 0 }; columnrevformatticker = GetTickCount64(); SecureZeroMemory(&columnrevformat, sizeof(NUMBERFMT)); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, &szDecSep[0], _countof(szDecSep)); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, &szThousandsSep[0], _countof(szThousandsSep)); columnrevformat.lpDecimalSep = szDecSep; columnrevformat.lpThousandSep = szThousandsSep; GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, &szBuffer[0], _countof(szBuffer)); columnrevformat.Grouping = _wtoi(szBuffer); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, &szBuffer[0], _countof(szBuffer)); columnrevformat.NegativeOrder = _wtoi(szBuffer); nocontextpaths = CRegStdString(L"Software\\TortoiseSVN\\NoContextPaths", L""); drivetypepathcache[0] = 0; m_critSec.Init(); }