1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "Shellupdater.h"
\r
21 #include "../TGitCache/CacheInterface.h"
\r
22 #include "Registry.h"
\r
25 CShellUpdater::CShellUpdater(void)
\r
27 m_hInvalidationEvent = CreateEvent(NULL, FALSE, FALSE, _T("TortoiseGitCacheInvalidationEvent"));
\r
30 CShellUpdater::~CShellUpdater(void)
\r
34 CloseHandle(m_hInvalidationEvent);
\r
37 CShellUpdater& CShellUpdater::Instance()
\r
39 static CShellUpdater instance;
\r
44 * Add a single path for updating.
\r
45 * The update will happen at some suitable time in the future
\r
47 void CShellUpdater::AddPathForUpdate(const CTGitPath& path)
\r
49 // Tell the shell extension to purge its cache - we'll redo this when
\r
50 // we actually do the shell-updates, but sometimes there's an earlier update, which
\r
51 // might benefit from cache invalidation
\r
52 SetEvent(m_hInvalidationEvent);
\r
54 m_pathsForUpdating.AddPath(path);
\r
57 * Add a list of paths for updating.
\r
58 * The update will happen when the list is destroyed, at the end of execution
\r
60 void CShellUpdater::AddPathsForUpdate(const CTGitPathList& pathList)
\r
62 for(int nPath=0; nPath < pathList.GetCount(); nPath++)
\r
64 AddPathForUpdate(pathList[nPath]);
\r
68 void CShellUpdater::Flush()
\r
70 if(m_pathsForUpdating.GetCount() > 0)
\r
72 ATLTRACE("Flushing shell update list\n");
\r
75 m_pathsForUpdating.Clear();
\r
79 void CShellUpdater::UpdateShell()
\r
81 // Tell the shell extension to purge its cache
\r
82 ATLTRACE("Setting cache invalidation event %d\n", GetTickCount());
\r
83 SetEvent(m_hInvalidationEvent);
\r
85 // We use the SVN 'notify' call-back to add items to the list
\r
86 // Because this might call-back more than once per file (for example, when committing)
\r
87 // it's possible that there may be duplicates in the list.
\r
88 // There's no point asking the shell to do more than it has to, so we remove the duplicates before
\r
89 // passing the list on
\r
90 m_pathsForUpdating.RemoveDuplicates();
\r
92 // if we use the external cache, we tell the cache directly that something
\r
93 // has changed, without the detour via the shell.
\r
94 HANDLE hPipe = CreateFile(
\r
95 GetCacheCommandPipeName(), // pipe name
\r
96 GENERIC_READ | // read and write access
\r
99 NULL, // default security attributes
\r
100 OPEN_EXISTING, // opens existing pipe
\r
101 FILE_FLAG_OVERLAPPED, // default attributes
\r
102 NULL); // no template file
\r
105 if (hPipe != INVALID_HANDLE_VALUE)
\r
107 // The pipe connected; change to message-read mode.
\r
110 dwMode = PIPE_READMODE_MESSAGE;
\r
111 if(SetNamedPipeHandleState(
\r
112 hPipe, // pipe handle
\r
113 &dwMode, // new pipe mode
\r
114 NULL, // don't set maximum bytes
\r
115 NULL)) // don't set maximum time
\r
118 for(int nPath = 0; nPath < m_pathsForUpdating.GetCount(); nPath++)
\r
120 path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_pathsForUpdating[nPath].GetWinPathString());
\r
121 ATLTRACE(_T("Cache Item Update for %s (%d)\n"), path.GetWinPathString(), GetTickCount());
\r
122 if (!path.IsDirectory())
\r
124 // send notifications to the shell for changed files - folders are updated by the cache itself.
\r
125 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.GetWinPath(), NULL);
\r
128 TSVNCacheCommand cmd;
\r
129 cmd.command = TSVNCACHECOMMAND_CRAWL;
\r
130 wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH);
\r
131 BOOL fSuccess = WriteFile(
\r
132 hPipe, // handle to pipe
\r
133 &cmd, // buffer to write from
\r
134 sizeof(cmd), // number of bytes to write
\r
135 &cbWritten, // number of bytes written
\r
136 NULL); // not overlapped I/O
\r
138 if (! fSuccess || sizeof(cmd) != cbWritten)
\r
140 DisconnectNamedPipe(hPipe);
\r
141 CloseHandle(hPipe);
\r
142 hPipe = INVALID_HANDLE_VALUE;
\r
146 if (hPipe != INVALID_HANDLE_VALUE)
\r
148 // now tell the cache we don't need it's command thread anymore
\r
150 TSVNCacheCommand cmd;
\r
151 cmd.command = TSVNCACHECOMMAND_END;
\r
153 hPipe, // handle to pipe
\r
154 &cmd, // buffer to write from
\r
155 sizeof(cmd), // number of bytes to write
\r
156 &cbWritten, // number of bytes written
\r
157 NULL); // not overlapped I/O
\r
158 DisconnectNamedPipe(hPipe);
\r
159 CloseHandle(hPipe);
\r
160 hPipe = INVALID_HANDLE_VALUE;
\r
165 ATLTRACE("SetNamedPipeHandleState failed");
\r
166 CloseHandle(hPipe);
\r
171 bool CShellUpdater::RebuildIcons()
\r
173 const int BUFFER_SIZE = 1024;
\r
177 DWORD dwRegValueTemp;
\r
179 DWORD_PTR dwResult;
\r
181 std::wstring sRegValueName;
\r
182 std::wstring sDefaultIconSize;
\r
183 int iDefaultIconSize;
\r
184 bool bResult = false;
\r
186 lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop\\WindowMetrics"),
\r
187 0, KEY_READ | KEY_WRITE, &hRegKey);
\r
188 if (lRegResult != ERROR_SUCCESS)
\r
191 buf = new TCHAR[BUFFER_SIZE];
\r
195 // we're going to change the Shell Icon Size value
\r
196 sRegValueName = _T("Shell Icon Size");
\r
198 // Read registry value
\r
199 dwSize = BUFFER_SIZE;
\r
200 lRegResult = RegQueryValueEx(hRegKey, sRegValueName.c_str(), NULL, NULL,
\r
201 (LPBYTE) buf, &dwSize);
\r
202 if (lRegResult != ERROR_FILE_NOT_FOUND)
\r
204 // If registry key doesn't exist create it using system current setting
\r
205 iDefaultIconSize = ::GetSystemMetrics(SM_CXICON);
\r
206 if (0 == iDefaultIconSize)
\r
207 iDefaultIconSize = 32;
\r
208 _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), iDefaultIconSize);
\r
210 else if (lRegResult != ERROR_SUCCESS)
\r
213 // Change registry value
\r
214 dwRegValue = _ttoi(buf);
\r
215 dwRegValueTemp = dwRegValue-1;
\r
217 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValueTemp) + sizeof(TCHAR);
\r
218 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
\r
219 (LPBYTE) buf, dwSize);
\r
220 if (lRegResult != ERROR_SUCCESS)
\r
224 // Update all windows
\r
225 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
\r
226 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
\r
228 // Reset registry value
\r
229 dwSize = _sntprintf_s(buf, BUFFER_SIZE, BUFFER_SIZE, _T("%d"), dwRegValue) + sizeof(TCHAR);
\r
230 lRegResult = RegSetValueEx(hRegKey, sRegValueName.c_str(), 0, REG_SZ,
\r
231 (LPBYTE) buf, dwSize);
\r
232 if(lRegResult != ERROR_SUCCESS)
\r
235 // Update all windows
\r
236 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS,
\r
237 0, SMTO_ABORTIFHUNG, 5000, &dwResult);
\r
244 RegCloseKey(hRegKey);
\r