OSDN Git Service

add Utils
authorFrank Li <Frank.li@freescale.com>
Wed, 5 Nov 2008 14:08:23 +0000 (22:08 +0800)
committerFrank Li <Frank.li@freescale.com>
Wed, 5 Nov 2008 14:08:23 +0000 (22:08 +0800)
130 files changed:
Utils/BugTraqAssociations.cpp [new file with mode: 0644]
Utils/BugTraqAssociations.h [new file with mode: 0644]
Utils/CmdLineParser.cpp [new file with mode: 0644]
Utils/CmdLineParser.h [new file with mode: 0644]
Utils/Debug.c [new file with mode: 0644]
Utils/Debug.h [new file with mode: 0644]
Utils/DebugHelpers.cpp [new file with mode: 0644]
Utils/DebugHelpers.h [new file with mode: 0644]
Utils/DirFileEnum.cpp [new file with mode: 0644]
Utils/DirFileEnum.h [new file with mode: 0644]
Utils/DragDropImpl.cpp [new file with mode: 0644]
Utils/DragDropImpl.h [new file with mode: 0644]
Utils/DropFiles.cpp [new file with mode: 0644]
Utils/DropFiles.h [new file with mode: 0644]
Utils/HighResClock.h [new file with mode: 0644]
Utils/Hooks.cpp [new file with mode: 0644]
Utils/Hooks.h [new file with mode: 0644]
Utils/LangDll.cpp [new file with mode: 0644]
Utils/LangDll.h [new file with mode: 0644]
Utils/MiscUI/Balloon.cpp [new file with mode: 0644]
Utils/MiscUI/Balloon.h [new file with mode: 0644]
Utils/MiscUI/BaseDialog.cpp [new file with mode: 0644]
Utils/MiscUI/BaseDialog.h [new file with mode: 0644]
Utils/MiscUI/BaseWindow.cpp [new file with mode: 0644]
Utils/MiscUI/BaseWindow.h [new file with mode: 0644]
Utils/MiscUI/BrowseFolder.cpp [new file with mode: 0644]
Utils/MiscUI/BrowseFolder.h [new file with mode: 0644]
Utils/MiscUI/BufferDC.cpp [new file with mode: 0644]
Utils/MiscUI/BufferDC.h [new file with mode: 0644]
Utils/MiscUI/CMessageBox_1.jpg [new file with mode: 0644]
Utils/MiscUI/CMessageBox_2.jpg [new file with mode: 0644]
Utils/MiscUI/CMessageBox_3.jpg [new file with mode: 0644]
Utils/MiscUI/CMessageBox_4.jpg [new file with mode: 0644]
Utils/MiscUI/CMessageBox_5.jpg [new file with mode: 0644]
Utils/MiscUI/Cursor.h [new file with mode: 0644]
Utils/MiscUI/DIB.cpp [new file with mode: 0644]
Utils/MiscUI/DIB.h [new file with mode: 0644]
Utils/MiscUI/DlgTemplate.h [new file with mode: 0644]
Utils/MiscUI/ExtTextOutFL.h [new file with mode: 0644]
Utils/MiscUI/FileDropEdit.cpp [new file with mode: 0644]
Utils/MiscUI/FileDropEdit.h [new file with mode: 0644]
Utils/MiscUI/FilterEdit.cpp [new file with mode: 0644]
Utils/MiscUI/FilterEdit.h [new file with mode: 0644]
Utils/MiscUI/Gradient.cpp [new file with mode: 0644]
Utils/MiscUI/Gradient.h [new file with mode: 0644]
Utils/MiscUI/HTMLFormatter.cpp [new file with mode: 0644]
Utils/MiscUI/HTMLFormatter.h [new file with mode: 0644]
Utils/MiscUI/HighColorTab.hpp [new file with mode: 0644]
Utils/MiscUI/HintListCtrl.cpp [new file with mode: 0644]
Utils/MiscUI/HintListCtrl.h [new file with mode: 0644]
Utils/MiscUI/HistoryCombo.cpp [new file with mode: 0644]
Utils/MiscUI/HistoryCombo.h [new file with mode: 0644]
Utils/MiscUI/HyperLink.cpp [new file with mode: 0644]
Utils/MiscUI/HyperLink.h [new file with mode: 0644]
Utils/MiscUI/IconMenu.cpp [new file with mode: 0644]
Utils/MiscUI/IconMenu.h [new file with mode: 0644]
Utils/MiscUI/MenuButton.cpp [new file with mode: 0644]
Utils/MiscUI/MenuButton.h [new file with mode: 0644]
Utils/MiscUI/MessageBox.cpp [new file with mode: 0644]
Utils/MiscUI/MessageBox.h [new file with mode: 0644]
Utils/MiscUI/MyGraph.cpp [new file with mode: 0644]
Utils/MiscUI/MyGraph.h [new file with mode: 0644]
Utils/MiscUI/MyMemDC.h [new file with mode: 0644]
Utils/MiscUI/OddButton.cpp [new file with mode: 0644]
Utils/MiscUI/OddButton.h [new file with mode: 0644]
Utils/MiscUI/Picture.cpp [new file with mode: 0644]
Utils/MiscUI/Picture.h [new file with mode: 0644]
Utils/MiscUI/ProgressDlg.cpp [new file with mode: 0644]
Utils/MiscUI/ProgressDlg.h [new file with mode: 0644]
Utils/MiscUI/SciEdit.cpp [new file with mode: 0644]
Utils/MiscUI/SciEdit.h [new file with mode: 0644]
Utils/MiscUI/ScrollTool.cpp [new file with mode: 0644]
Utils/MiscUI/ScrollTool.h [new file with mode: 0644]
Utils/MiscUI/SplitterControl.cpp [new file with mode: 0644]
Utils/MiscUI/SplitterControl.h [new file with mode: 0644]
Utils/MiscUI/StandAloneDlg.cpp [new file with mode: 0644]
Utils/MiscUI/StandAloneDlg.h [new file with mode: 0644]
Utils/MiscUI/Tooltip.cpp [new file with mode: 0644]
Utils/MiscUI/Tooltip.h [new file with mode: 0644]
Utils/MiscUI/WaitCursorEx.cpp [new file with mode: 0644]
Utils/MiscUI/WaitCursorEx.h [new file with mode: 0644]
Utils/MiscUI/WaterEffect.cpp [new file with mode: 0644]
Utils/MiscUI/WaterEffect.h [new file with mode: 0644]
Utils/MiscUI/XPImageButton.cpp [new file with mode: 0644]
Utils/MiscUI/XPImageButton.h [new file with mode: 0644]
Utils/MiscUI/XPTheme.cpp [new file with mode: 0644]
Utils/MiscUI/XPTheme.h [new file with mode: 0644]
Utils/MiscUI/balloon_box.jpg [new file with mode: 0644]
Utils/MiscUI/balloon_tooltip.jpg [new file with mode: 0644]
Utils/MiscUI/filterEdit.jpg [new file with mode: 0644]
Utils/MiscUI/htmlformatter.png [new file with mode: 0644]
Utils/MiscUI/hyperlink_base.cpp [new file with mode: 0644]
Utils/MiscUI/hyperlink_base.h [new file with mode: 0644]
Utils/PathUtils.cpp [new file with mode: 0644]
Utils/PathUtils.h [new file with mode: 0644]
Utils/PathWatcher.cpp [new file with mode: 0644]
Utils/PathWatcher.h [new file with mode: 0644]
Utils/PersonalDictionary.cpp [new file with mode: 0644]
Utils/PersonalDictionary.h [new file with mode: 0644]
Utils/ProfilingInfo.cpp [new file with mode: 0644]
Utils/ProfilingInfo.h [new file with mode: 0644]
Utils/QuickHash.h [new file with mode: 0644]
Utils/QuickHashMap.h [new file with mode: 0644]
Utils/RWSection.cpp [new file with mode: 0644]
Utils/RWSection.h [new file with mode: 0644]
Utils/RegHistory.cpp [new file with mode: 0644]
Utils/RegHistory.h [new file with mode: 0644]
Utils/Registry.cpp [new file with mode: 0644]
Utils/ShellUpdater.cpp [new file with mode: 0644]
Utils/ShellUpdater.h [new file with mode: 0644]
Utils/SoundUtils.cpp [new file with mode: 0644]
Utils/SoundUtils.h [new file with mode: 0644]
Utils/StdioFileT.cpp [new file with mode: 0644]
Utils/StdioFileT.h [new file with mode: 0644]
Utils/StringUtils.cpp [new file with mode: 0644]
Utils/StringUtils.h [new file with mode: 0644]
Utils/SysImageList.cpp [new file with mode: 0644]
Utils/SysImageList.h [new file with mode: 0644]
Utils/TempFile.cpp [new file with mode: 0644]
Utils/TempFile.h [new file with mode: 0644]
Utils/TreePropSheet/PropPageFrame.cpp [new file with mode: 0644]
Utils/TreePropSheet/PropPageFrame.h [new file with mode: 0644]
Utils/TreePropSheet/PropPageFrameDefault.cpp [new file with mode: 0644]
Utils/TreePropSheet/PropPageFrameDefault.h [new file with mode: 0644]
Utils/TreePropSheet/TreePropSheet.cpp [new file with mode: 0644]
Utils/TreePropSheet/TreePropSheet.h [new file with mode: 0644]
Utils/UnicodeUtils.cpp [new file with mode: 0644]
Utils/UnicodeUtils.h [new file with mode: 0644]
Utils/registry.h [new file with mode: 0644]
Utils/stdex_vector.h [new file with mode: 0644]

diff --git a/Utils/BugTraqAssociations.cpp b/Utils/BugTraqAssociations.cpp
new file mode 100644 (file)
index 0000000..faffabd
--- /dev/null
@@ -0,0 +1,242 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "BugTraqAssociations.h"\r
+\r
+#include <initguid.h>\r
+\r
+// {3494FA92-B139-4730-9591-01135D5E7831}\r
+DEFINE_GUID(CATID_BugTraqProvider, \r
+                       0x3494fa92, 0xb139, 0x4730, 0x95, 0x91, 0x1, 0x13, 0x5d, 0x5e, 0x78, 0x31);\r
+\r
+#define BUGTRAQ_ASSOCIATIONS_REGPATH _T("Software\\TortoiseSVN\\BugTraq Associations")\r
+\r
+CBugTraqAssociations::~CBugTraqAssociations()\r
+{\r
+       for (inner_t::iterator it = m_inner.begin(); it != m_inner.end(); ++it)\r
+               delete *it;\r
+}\r
+\r
+void CBugTraqAssociations::Load()\r
+{\r
+       HKEY hk;\r
+       if (RegOpenKeyEx(HKEY_CURRENT_USER, BUGTRAQ_ASSOCIATIONS_REGPATH, 0, KEY_READ, &hk) != ERROR_SUCCESS)\r
+               return;\r
+\r
+       for (DWORD dwIndex = 0; /* nothing */; ++dwIndex)\r
+       {\r
+               TCHAR szSubKey[MAX_PATH];\r
+               DWORD cchSubKey = MAX_PATH;\r
+               LSTATUS status = RegEnumKeyEx(hk, dwIndex, szSubKey, &cchSubKey, NULL, NULL, NULL, NULL);\r
+               if (status != ERROR_SUCCESS)\r
+                       break;\r
+\r
+               HKEY hk2;\r
+               if (RegOpenKeyEx(hk, szSubKey, 0, KEY_READ, &hk2) == ERROR_SUCCESS)\r
+               {\r
+                       TCHAR szWorkingCopy[MAX_PATH];\r
+                       DWORD cbWorkingCopy = sizeof(szWorkingCopy);\r
+                       RegQueryValueEx(hk2, _T("WorkingCopy"), NULL, NULL, (LPBYTE)szWorkingCopy, &cbWorkingCopy);\r
+\r
+                       TCHAR szClsid[MAX_PATH];\r
+                       DWORD cbClsid = sizeof(szClsid);\r
+                       RegQueryValueEx(hk2, _T("Provider"), NULL, NULL, (LPBYTE)szClsid, &cbClsid);\r
+\r
+                       CLSID provider_clsid;\r
+                       CLSIDFromString(szClsid, &provider_clsid);\r
+\r
+                       DWORD cbParameters = 0;\r
+                       RegQueryValueEx(hk2, _T("Parameters"), NULL, NULL, (LPBYTE)NULL, &cbParameters);\r
+                       TCHAR * szParameters = new TCHAR[cbParameters+1];\r
+                       RegQueryValueEx(hk2, _T("Parameters"), NULL, NULL, (LPBYTE)szParameters, &cbParameters);\r
+                       szParameters[cbParameters] = 0;\r
+                       m_inner.push_back(new CBugTraqAssociation(szWorkingCopy, provider_clsid, LookupProviderName(provider_clsid), szParameters));\r
+                       delete [] szParameters;\r
+\r
+                       RegCloseKey(hk2);\r
+               }\r
+       }\r
+\r
+       RegCloseKey(hk);\r
+}\r
+\r
+void CBugTraqAssociations::Add(const CBugTraqAssociation &assoc)\r
+{\r
+       m_inner.push_back(new CBugTraqAssociation(assoc));\r
+}\r
+\r
+bool CBugTraqAssociations::FindProvider(const CTSVNPathList &pathList, CBugTraqAssociation *assoc) const\r
+{\r
+       return FindProviderForPathList(pathList, assoc);\r
+}\r
+\r
+bool CBugTraqAssociations::FindProviderForPathList(const CTSVNPathList &pathList, CBugTraqAssociation *assoc) const\r
+{\r
+       for (int i = 0; i < pathList.GetCount(); ++i)\r
+       {\r
+               CTSVNPath path = pathList[i];\r
+               if (FindProviderForPath(path, assoc))\r
+                       return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+bool CBugTraqAssociations::FindProviderForPath(CTSVNPath path, CBugTraqAssociation *assoc) const\r
+{\r
+       do\r
+       {\r
+               inner_t::const_iterator it = std::find_if(m_inner.begin(), m_inner.end(), FindByPathPred(path));\r
+               if (it != m_inner.end())\r
+               {\r
+                       *assoc = *(*it);\r
+                       return true;\r
+               }\r
+               \r
+               path = path.GetContainingDirectory();\r
+       } while(!path.IsEmpty());\r
+\r
+       return false;\r
+}\r
+\r
+/* static */\r
+CString CBugTraqAssociations::LookupProviderName(const CLSID &provider_clsid)\r
+{\r
+       OLECHAR szClsid[40];\r
+       StringFromGUID2(provider_clsid, szClsid, ARRAYSIZE(szClsid));\r
+\r
+       TCHAR szSubKey[MAX_PATH];\r
+       _stprintf_s(szSubKey, _T("CLSID\\%ls"), szClsid);\r
+       \r
+       CString provider_name = CString(szClsid);\r
+\r
+       HKEY hk;\r
+       if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szSubKey, 0, KEY_READ, &hk) == ERROR_SUCCESS)\r
+       {\r
+               TCHAR szClassName[MAX_PATH];\r
+               DWORD cbClassName = sizeof(szClassName);\r
+\r
+               if (RegQueryValueEx(hk, NULL, NULL, NULL, (LPBYTE)szClassName, &cbClassName) == ERROR_SUCCESS)\r
+                       provider_name = CString(szClassName);\r
+\r
+               RegCloseKey(hk);\r
+       }\r
+\r
+       return provider_name;\r
+}\r
+\r
+LSTATUS RegSetValueFromCString(HKEY hKey, LPCTSTR lpValueName, CString str)\r
+{\r
+       LPCTSTR lpsz = str;\r
+       DWORD cb = (str.GetLength() + 1) * sizeof(TCHAR);\r
+       return RegSetValueEx(hKey, lpValueName, 0, REG_SZ, (const BYTE *)lpsz, cb);\r
+}\r
+\r
+void CBugTraqAssociations::Save() const\r
+{\r
+       SHDeleteKey(HKEY_CURRENT_USER, BUGTRAQ_ASSOCIATIONS_REGPATH);\r
+\r
+       HKEY hk;\r
+       if (RegCreateKeyEx(HKEY_CURRENT_USER, BUGTRAQ_ASSOCIATIONS_REGPATH, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hk, NULL) != ERROR_SUCCESS)\r
+               return;\r
+\r
+       DWORD dwIndex = 0;\r
+       for (const_iterator it = begin(); it != end(); ++it)\r
+       {\r
+               TCHAR szSubKey[MAX_PATH];\r
+               _stprintf_s(szSubKey, _T("%d"), dwIndex);\r
+\r
+               HKEY hk2;\r
+               if (RegCreateKeyEx(hk, szSubKey, 0, NULL, 0, KEY_WRITE, NULL, &hk2, NULL) == ERROR_SUCCESS)\r
+               {\r
+                       RegSetValueFromCString(hk2, _T("Provider"), (*it)->GetProviderClassAsString());\r
+                       RegSetValueFromCString(hk2, _T("WorkingCopy"), (*it)->GetPath().GetWinPath());\r
+                       RegSetValueFromCString(hk2, _T("Parameters"), (*it)->GetParameters());\r
+                       \r
+                       RegCloseKey(hk2);\r
+               }\r
+\r
+               ++dwIndex;\r
+       }\r
+\r
+       RegCloseKey(hk);\r
+}\r
+\r
+void CBugTraqAssociations::RemoveByPath(const CTSVNPath &path)\r
+{\r
+       inner_t::iterator it = std::find_if(m_inner.begin(), m_inner.end(), FindByPathPred(path));\r
+       if (it != m_inner.end())\r
+       {\r
+               delete *it;\r
+               m_inner.erase(it);\r
+       }\r
+}\r
+\r
+CString CBugTraqAssociation::GetProviderClassAsString() const\r
+{\r
+       OLECHAR szTemp[40];\r
+       StringFromGUID2(m_provider.clsid, szTemp, ARRAYSIZE(szTemp));\r
+\r
+       return CString(szTemp);\r
+}\r
+\r
+/* static */\r
+std::vector<CBugTraqProvider> CBugTraqAssociations::GetAvailableProviders()\r
+{\r
+       std::vector<CBugTraqProvider> results;\r
+\r
+       ICatInformation *pCatInformation = NULL;\r
+\r
+       HRESULT hr;\r
+       if (SUCCEEDED(hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL, IID_ICatInformation, (void **)&pCatInformation)))\r
+       {\r
+               IEnumGUID *pEnum = NULL;\r
+               if (SUCCEEDED(hr = pCatInformation->EnumClassesOfCategories(1, &CATID_BugTraqProvider, 0, NULL, &pEnum)))\r
+               {\r
+                       HRESULT hrEnum;\r
+                       do\r
+                       {\r
+                               CLSID clsids[5];\r
+                               ULONG cClsids;\r
+\r
+                               hrEnum = pEnum->Next(ARRAYSIZE(clsids), clsids, &cClsids);\r
+                               if (SUCCEEDED(hrEnum))\r
+                               {\r
+                                       for (ULONG i = 0; i < cClsids; ++i)\r
+                                       {\r
+                                               CBugTraqProvider provider;\r
+                                               provider.clsid = clsids[i];\r
+                                               provider.name = LookupProviderName(clsids[i]);\r
+                                               results.push_back(provider);\r
+                                       }\r
+                               }\r
+                       } while (hrEnum == S_OK);\r
+               }\r
+\r
+               if (pEnum)\r
+                       pEnum->Release();\r
+               pEnum = NULL;\r
+       }\r
+\r
+       if (pCatInformation)\r
+               pCatInformation->Release();\r
+       pCatInformation = NULL;\r
+\r
+       return results;\r
+}\r
diff --git a/Utils/BugTraqAssociations.h b/Utils/BugTraqAssociations.h
new file mode 100644 (file)
index 0000000..15795c6
--- /dev/null
@@ -0,0 +1,95 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include "TSVNPath.h"\r
+\r
+struct CBugTraqProvider\r
+{\r
+       CLSID clsid;\r
+       CString name;\r
+};\r
+\r
+/* TODO: It's less of an association and more of a "token" or "memento".\r
+ */\r
+class CBugTraqAssociation\r
+{\r
+       CTSVNPath m_path;\r
+       CBugTraqProvider m_provider;\r
+       CString m_parameters;\r
+\r
+public:\r
+       CBugTraqAssociation()\r
+       {\r
+                m_provider.clsid = GUID_NULL;\r
+       }\r
+\r
+       CBugTraqAssociation(LPCTSTR szWorkingCopy, const CLSID &provider_clsid, LPCTSTR szProviderName, LPCTSTR szParameters)\r
+               : m_path(szWorkingCopy), m_parameters(szParameters)\r
+       {\r
+               m_provider.clsid = provider_clsid;\r
+               m_provider.name = szProviderName;\r
+       }\r
+\r
+       const CTSVNPath &GetPath() const { return m_path; }\r
+       CString GetProviderName() const { return m_provider.name; }\r
+       CLSID GetProviderClass() const { return m_provider.clsid; }\r
+       CString GetProviderClassAsString() const;\r
+       CString GetParameters() const { return m_parameters; }\r
+};\r
+\r
+class CBugTraqAssociations\r
+{\r
+       typedef std::vector< CBugTraqAssociation * > inner_t;\r
+       inner_t m_inner;\r
+\r
+public:\r
+       ~CBugTraqAssociations();\r
+\r
+       void Load();\r
+       void Save() const;\r
+\r
+       void Add(const CBugTraqAssociation &assoc);\r
+       void RemoveByPath(const CTSVNPath &path);\r
+\r
+       bool FindProvider(const CTSVNPathList &pathList, CBugTraqAssociation *assoc) const;\r
+\r
+       typedef inner_t::const_iterator const_iterator;\r
+       const_iterator begin() const { return m_inner.begin(); }\r
+       const_iterator end() const { return m_inner.end(); }\r
+\r
+       static std::vector<CBugTraqProvider> GetAvailableProviders();\r
+       static CString LookupProviderName(const CLSID &provider_clsid);\r
+\r
+private:\r
+       bool FindProviderForPathList(const CTSVNPathList &pathList, CBugTraqAssociation *assoc) const;\r
+       bool FindProviderForPath(CTSVNPath path, CBugTraqAssociation *assoc) const;\r
+\r
+       struct FindByPathPred\r
+       {\r
+               const CTSVNPath &m_path;\r
+\r
+               FindByPathPred(const CTSVNPath &path)\r
+                       : m_path(path) { }\r
+\r
+               bool operator() (const CBugTraqAssociation *assoc) const\r
+               {\r
+                       return (assoc->GetPath().IsEquivalentToWithoutCase(m_path));\r
+               }\r
+       };\r
+};\r
diff --git a/Utils/CmdLineParser.cpp b/Utils/CmdLineParser.cpp
new file mode 100644 (file)
index 0000000..a0adac6
--- /dev/null
@@ -0,0 +1,205 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "CmdLineParser.h"\r
+#include <locale>\r
+#include <algorithm>\r
+\r
+const TCHAR CCmdLineParser::m_sDelims[] = _T("-/");\r
+const TCHAR CCmdLineParser::m_sQuotes[] = _T("\"");\r
+const TCHAR CCmdLineParser::m_sValueSep[] = _T(" :"); // don't forget space!!\r
+\r
+\r
+CCmdLineParser::CCmdLineParser(LPCTSTR sCmdLine)\r
+{\r
+       if(sCmdLine) \r
+       {\r
+               Parse(sCmdLine);\r
+       }\r
+}\r
+\r
+CCmdLineParser::~CCmdLineParser()\r
+{\r
+       m_valueMap.clear();\r
+}\r
+\r
+BOOL CCmdLineParser::Parse(LPCTSTR sCmdLine) \r
+{\r
+       const stdstring sEmpty = _T("");                        //use this as a value if no actual value is given in commandline\r
+       int nArgs = 0;\r
+\r
+       if(!sCmdLine) \r
+               return false;\r
+       \r
+       m_valueMap.clear();\r
+       m_sCmdLine = sCmdLine;\r
+\r
+       LPCTSTR sCurrent = sCmdLine;\r
+\r
+       for(;;)\r
+       {\r
+               //format is  -Key:"arg"\r
+               \r
+               if (_tcslen(sCurrent) == 0)\r
+                       break;          // no more data, leave loop\r
+\r
+               LPCTSTR sArg = _tcspbrk(sCurrent, m_sDelims);\r
+               if(!sArg) \r
+                       break; // no (more) delimiters found\r
+               sArg =  _tcsinc(sArg);\r
+\r
+               if(_tcslen(sArg) == 0) \r
+                       break; // ends with delim\r
+                       \r
+               LPCTSTR sVal = _tcspbrk(sArg, m_sValueSep);\r
+               if(sVal == NULL) \r
+               { \r
+                       stdstring Key(sArg);\r
+                       std::transform(Key.begin(), Key.end(), Key.begin(), ::tolower);\r
+                       m_valueMap.insert(CValsMap::value_type(Key, sEmpty));\r
+                       break;\r
+               } \r
+               else if (sVal[0] == _T(' ') || _tcslen(sVal) == 1 ) \r
+               { \r
+                       // cmdline ends with /Key: or a key with no value \r
+                       stdstring Key(sArg, (int)(sVal - sArg));\r
+                       if(!Key.empty()) \r
+                       { \r
+                               std::transform(Key.begin(), Key.end(), Key.begin(), ::tolower);\r
+                               m_valueMap.insert(CValsMap::value_type(Key, sEmpty));\r
+                       }\r
+                       sCurrent = _tcsinc(sVal);\r
+                       continue;\r
+               }\r
+               else \r
+               { \r
+                       // key has value\r
+                       stdstring Key(sArg, (int)(sVal - sArg));\r
+                       std::transform(Key.begin(), Key.end(), Key.begin(), ::tolower);\r
+\r
+                       sVal = _tcsinc(sVal);\r
+\r
+                       LPCTSTR sQuote = _tcspbrk(sVal, m_sQuotes), sEndQuote(NULL);\r
+                       if(sQuote == sVal) \r
+                       { \r
+                               // string with quotes (defined in m_sQuotes, e.g. '")\r
+                               sQuote = _tcsinc(sVal);\r
+                               sEndQuote = _tcspbrk(sQuote, m_sQuotes);\r
+                       } \r
+                       else \r
+                       {\r
+                               sQuote = sVal;\r
+                               sEndQuote = _tcschr(sQuote, _T(' '));\r
+                       }\r
+\r
+                       if(sEndQuote == NULL) \r
+                       { \r
+                               // no end quotes or terminating space, take the rest of the string to its end\r
+                               stdstring csVal(sQuote);\r
+                               if(!Key.empty()) \r
+                               { \r
+                                       m_valueMap.insert(CValsMap::value_type(Key, csVal));\r
+                               }\r
+                               break;\r
+                       } \r
+                       else \r
+                       { \r
+                               // end quote\r
+                               if(!Key.empty()) \r
+                               {       \r
+                                       stdstring csVal(sQuote, (int)(sEndQuote - sQuote));\r
+                                       m_valueMap.insert(CValsMap::value_type(Key, csVal));\r
+                               }\r
+                               sCurrent = _tcsinc(sEndQuote);\r
+                               continue;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       return (nArgs > 0);             //TRUE if arguments were found\r
+}\r
+\r
+CCmdLineParser::CValsMap::const_iterator CCmdLineParser::findKey(LPCTSTR sKey) const \r
+{\r
+       stdstring s(sKey);\r
+       std::transform(s.begin(), s.end(), s.begin(), ::tolower);\r
+       return m_valueMap.find(s);\r
+}\r
+\r
+BOOL CCmdLineParser::HasKey(LPCTSTR sKey) const \r
+{\r
+       CValsMap::const_iterator it = findKey(sKey);\r
+       if(it == m_valueMap.end()) \r
+               return false;\r
+       return true;\r
+}\r
+\r
+\r
+BOOL CCmdLineParser::HasVal(LPCTSTR sKey) const \r
+{\r
+       CValsMap::const_iterator it = findKey(sKey);\r
+       if(it == m_valueMap.end()) \r
+               return false;\r
+       if(it->second.empty()) \r
+               return false;\r
+       return true;\r
+}\r
+\r
+LPCTSTR CCmdLineParser::GetVal(LPCTSTR sKey) const \r
+{\r
+       CValsMap::const_iterator it = findKey(sKey);\r
+       if (it == m_valueMap.end()) \r
+               return 0;\r
+       return it->second.c_str();\r
+}\r
+\r
+LONG CCmdLineParser::GetLongVal(LPCTSTR sKey) const\r
+{\r
+       CValsMap::const_iterator it = findKey(sKey);\r
+       if (it == m_valueMap.end())\r
+               return 0;\r
+       return _tstol(it->second.c_str());\r
+}\r
+\r
+\r
+CCmdLineParser::ITERPOS CCmdLineParser::begin() const \r
+{\r
+       return m_valueMap.begin();\r
+}\r
+\r
+CCmdLineParser::ITERPOS CCmdLineParser::getNext(ITERPOS& pos, stdstring& sKey, stdstring& sValue) const \r
+{\r
+       if (m_valueMap.end() == pos)\r
+       {\r
+               sKey.clear();\r
+               return pos;\r
+       } \r
+       else \r
+       {\r
+               sKey = pos->first;\r
+               sValue = pos->second;\r
+               pos ++;\r
+               return pos;\r
+       }\r
+}\r
+\r
+BOOL CCmdLineParser::isLast(const ITERPOS& pos) const \r
+{\r
+       return (pos == m_valueMap.end());\r
+}\r
diff --git a/Utils/CmdLineParser.h b/Utils/CmdLineParser.h
new file mode 100644 (file)
index 0000000..15649c5
--- /dev/null
@@ -0,0 +1,148 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include <map>\r
+#include <string>\r
+\r
+using std::map;\r
+\r
+#pragma warning (push,1)\r
+typedef std::wstring wide_string;\r
+#ifndef stdstring\r
+#ifdef UNICODE\r
+#      define stdstring wide_string\r
+#else\r
+#      define stdstring std::string\r
+#endif\r
+#endif\r
+#pragma warning (pop)\r
+\r
+/**\r
+ * \ingroup Utils\r
+ *\r
+ * A helper class for parsing command lines.\r
+ * It provides methods to extract 'key' and 'value'\r
+ * pairs of the form -keyname:value or /keyname:value.\r
+ * Parameter examples:\n\r
+ * \code\r
+ * "/key1 /key2:myvalue -key3:anothervalue -key4:"this could be a path with spaces"\r
+ * \endcode\r
+ * /key is the same as -key\n\r
+ * all keys and values are case-insensitive.\r
+ * Please note that both keys and values are strings although the class\r
+ * provides a method to get a long as a value.\r
+ * Example:\n\r
+ * \code\r
+ * CCmdLineParser parser(::GetCommandLine());\r
+ * if (parser.HasKey("r"))\r
+ * {\r
+ *     // the key -r or /r is there (could mean e.g. 'recurse')\r
+ * }\r
+ * //now assume the command line is /open:"c:\test.txt" /wait:30\r
+ * CString file = parser.GetVal("open");\r
+ * //file contains now c:\test.txt\r
+ * long number = parser.GetLongVal("seconds");\r
+ * //number has now the value 30\r
+ * \endcode\r
+ */\r
+class CCmdLineParser \r
+{\r
+public:\r
+       typedef map<stdstring, stdstring> CValsMap;\r
+       typedef CValsMap::const_iterator ITERPOS;\r
+public:\r
+       /**\r
+        * Creates a CCmdLineParser object and parses the parameters in.\r
+        * \param sCmdLine the command line\r
+        */\r
+       CCmdLineParser(LPCTSTR sCmdLine = NULL);\r
+       virtual ~CCmdLineParser();\r
+\r
+       /**\r
+        * returns the command line string this object was created on.\r
+        * \return the command line\r
+        */\r
+       LPCTSTR getCmdLine() const { return m_sCmdLine.c_str(); }\r
+\r
+       /**\r
+        * Starts an iteration over all command line parameters.\r
+        * \return the first position\r
+        */\r
+       ITERPOS begin() const;  \r
+\r
+       /**\r
+        * Get the next key/value pair. If no more keys are available then\r
+        * an empty key is returned.\r
+        * \param the position from where to get. To get the first pair use the\r
+        * begin() method. \a pos is incremented by 1 on return.\r
+        * \param sKey returns the key\r
+        * \param sValue returns the value\r
+        * \return the next position\r
+        */\r
+       ITERPOS getNext(ITERPOS& pos, stdstring& sKey, stdstring& sValue) const;\r
+               \r
+       /**\r
+        * Checks if the position is the last or if there are more key/value pairs in the command line.\r
+        * \param pos the position to check\r
+        * \return TRUE if no more key/value pairs are available\r
+        */\r
+       BOOL isLast(const ITERPOS& pos) const;\r
+\r
+       /**\r
+        * Checks if the given key is in the command line.\r
+        * \param sKey the key to check for\r
+        * \return TRUE if the key exists, FALSE if the key is not in command line\r
+        */\r
+       BOOL HasKey(LPCTSTR sKey) const;\r
+\r
+       /**\r
+        * Checks if a key also has a value or not.\r
+        * \param sKey the key to check for a value\r
+        * \return TRUE if the key has a value, FALSE if no value (or no key) was found\r
+        */\r
+       BOOL HasVal(LPCTSTR sKey) const;\r
+\r
+       /**\r
+        * Reads the value for a key. If the key has no value then NULL is returned.\r
+        * \param sKey the key to get the value from\r
+        * \return the value string of the key\r
+        */\r
+       LPCTSTR GetVal(LPCTSTR sKey) const;\r
+       \r
+       /**\r
+        * Reads the value for a key as a long. If the value is a string which can't be\r
+        * converted to a number then 0 is returned.\r
+        * \param the key to get the value from\r
+        * \return the value converted to a long\r
+        */\r
+       LONG GetLongVal(LPCTSTR sKey) const;\r
+\r
+private:\r
+       BOOL Parse(LPCTSTR sCmdLine);\r
+       CValsMap::const_iterator findKey(LPCTSTR sKey) const;\r
+       const CValsMap& getVals() const { return m_valueMap; }\r
+private:\r
+       stdstring       m_sCmdLine;\r
+       CValsMap        m_valueMap;\r
+\r
+       static const TCHAR m_sDelims[];\r
+       static const TCHAR m_sValueSep[];\r
+       static const TCHAR m_sQuotes[];\r
+};\r
+\r
diff --git a/Utils/Debug.c b/Utils/Debug.c
new file mode 100644 (file)
index 0000000..6e05980
--- /dev/null
@@ -0,0 +1,39 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "Debug.h"\r
+#include <tchar.h>\r
+\r
+#if defined(_DEBUG) || defined(DEBUG)\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+void TRACE(LPCTSTR str, ...)\r
+{\r
+       static TCHAR buf[20*1024];\r
+\r
+       va_list ap;\r
+       va_start(ap, str);\r
+\r
+       _vstprintf_s(buf, 20*1024, str, ap);\r
+       OutputDebugString(buf);\r
+       va_end(ap);\r
+\r
+};\r
+#else\r
+void TRACE(LPCTSTR str, ...) {UNREFERENCED_PARAMETER(str);}\r
+#endif
\ No newline at end of file
diff --git a/Utils/Debug.h b/Utils/Debug.h
new file mode 100644 (file)
index 0000000..7cd93e7
--- /dev/null
@@ -0,0 +1,27 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include <Windows.h>\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Trace macro for win32 applications where the MFC or ATL trace macro is\r
+ * not available.\r
+ */\r
+void TRACE(LPCTSTR str, ...);\r
diff --git a/Utils/DebugHelpers.cpp b/Utils/DebugHelpers.cpp
new file mode 100644 (file)
index 0000000..8d5bae4
--- /dev/null
@@ -0,0 +1,87 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "StdAfx.h"\r
+#include "debughelpers.h"\r
+\r
+CString GetLastErrorMessageString(int err)\r
+{\r
+       LPVOID lpMsgBuf;\r
+       FormatMessage( \r
+               FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+               FORMAT_MESSAGE_FROM_SYSTEM | \r
+               FORMAT_MESSAGE_IGNORE_INSERTS,\r
+               NULL,\r
+               err,\r
+               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
+               (LPTSTR) &lpMsgBuf,\r
+               0,\r
+               NULL \r
+       );\r
+       CString temp = CString((LPCTSTR)lpMsgBuf);\r
+       LocalFree(lpMsgBuf);\r
+       return temp;\r
+};\r
+\r
+CString GetLastErrorMessageString()\r
+{\r
+       LPVOID lpMsgBuf;\r
+       FormatMessage( \r
+               FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+               FORMAT_MESSAGE_FROM_SYSTEM | \r
+               FORMAT_MESSAGE_IGNORE_INSERTS,\r
+               NULL,\r
+               GetLastError(),\r
+               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
+               (LPTSTR) &lpMsgBuf,\r
+               0,\r
+               NULL \r
+       );\r
+       CString temp = CString((LPCTSTR)lpMsgBuf);\r
+       LocalFree(lpMsgBuf);\r
+       return temp;\r
+};\r
+\r
+void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName)\r
+{\r
+#ifdef _UNICODE\r
+       char narrow[_MAX_PATH * 3];\r
+       BOOL defaultCharUsed;\r
+       int ret = WideCharToMultiByte(CP_ACP, 0, szThreadName, (int)_tcslen(szThreadName), narrow, _MAX_PATH*3 - 1, ".", &defaultCharUsed);\r
+       narrow[ret] = 0;\r
+#endif\r
+       THREADNAME_INFO info;\r
+       info.dwType = 0x1000;\r
+#ifdef _UNICODE\r
+       info.szName = narrow;\r
+#else\r
+       info.szName = szThreadName;\r
+#endif\r
+       info.dwThreadID = dwThreadID;\r
+       info.dwFlags = 0;\r
+\r
+       __try\r
+       {\r
+               RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD),\r
+                       (DWORD *)&info);\r
+       }\r
+       __except (EXCEPTION_CONTINUE_EXECUTION)\r
+       {\r
+       }\r
+}\r
+\r
diff --git a/Utils/DebugHelpers.h b/Utils/DebugHelpers.h
new file mode 100644 (file)
index 0000000..de0b9bc
--- /dev/null
@@ -0,0 +1,57 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+#ifdef _DEBUG\r
+#      define BEGIN_TICK   { DWORD dwTickMeasureBegin = ::GetTickCount();\r
+#      define END_TICK(s) DWORD dwTickMeasureEnd = ::GetTickCount(); TRACE("%s: tick count = %d\n", s, dwTickMeasureEnd-dwTickMeasureBegin); }\r
+#else\r
+#      define BEGIN_TICK\r
+#      define END_TICK(s)\r
+#endif\r
+\r
+/**\r
+ * \ingroup CommonClasses\r
+ * returns the string to the given error number.\r
+ * \param err the error number, obtained with GetLastError() or WSAGetLastError() or ...\r
+ */\r
+CString GetLastErrorMessageString(int err);\r
+/*\r
+ * \ingroup CommonClasses\r
+ * returns the string to the GetLastError() function.\r
+ */\r
+CString GetLastErrorMessageString();\r
+\r
+#define MS_VC_EXCEPTION 0x406d1388\r
+\r
+typedef struct tagTHREADNAME_INFO\r
+{\r
+       DWORD dwType;        // must be 0x1000\r
+       LPCSTR szName;       // pointer to name (in same addr space)\r
+       DWORD dwThreadID;    // thread ID (-1 caller thread)\r
+       DWORD dwFlags;       // reserved for future use, most be zero\r
+} THREADNAME_INFO;\r
+\r
+/**\r
+ * Sets a name for a thread. The Thread name must not exceed 9 characters!\r
+ * Inside the current thread you can use -1 for dwThreadID.\r
+ * \param dwThreadID The Thread ID\r
+ * \param szThreadName A name for the thread.\r
+ */   \r
+void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName);\r
diff --git a/Utils/DirFileEnum.cpp b/Utils/DirFileEnum.cpp
new file mode 100644 (file)
index 0000000..79d1a43
--- /dev/null
@@ -0,0 +1,177 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2005 - 2006 - Jon Foster\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+#include "StdAfx.h"\r
+#include "DirFileEnum.h"\r
+\r
+\r
+CSimpleFileFind::CSimpleFileFind(const CString &sPath, LPCTSTR pPattern) :\r
+   m_dError(ERROR_SUCCESS),\r
+   m_bFirst(TRUE),\r
+   m_sPathPrefix(sPath)\r
+{\r
+   // Add a trailing \ to m_sPathPrefix if it is missing.\r
+   // Do not add one to "C:" since "C:" and "C:\" are different.\r
+   {\r
+      int len = m_sPathPrefix.GetLength();\r
+      if (len != 0) {\r
+         TCHAR ch = sPath[len-1];\r
+         if (ch != '\\' && (ch != ':' || len != 2)) {\r
+            m_sPathPrefix += "\\";\r
+         }\r
+      }\r
+   }\r
+\r
+   m_hFindFile = ::FindFirstFile((LPCTSTR)(m_sPathPrefix + pPattern), &m_FindFileData); \r
+   if (m_hFindFile == INVALID_HANDLE_VALUE) {\r
+      m_dError = ::GetLastError();\r
+   }\r
+}\r
+\r
+CSimpleFileFind::~CSimpleFileFind()\r
+{\r
+   if (m_hFindFile != INVALID_HANDLE_VALUE) {\r
+      ::FindClose(m_hFindFile);\r
+   }\r
+}\r
+\r
+BOOL CSimpleFileFind::FindNextFile()\r
+{\r
+   if (m_dError) {\r
+      return FALSE;\r
+   }\r
+\r
+   if (m_bFirst) {\r
+      m_bFirst = FALSE;\r
+      return TRUE;\r
+   }\r
+\r
+   if (!::FindNextFile(m_hFindFile, &m_FindFileData)) {\r
+      m_dError = ::GetLastError();\r
+      return FALSE;\r
+   }\r
+\r
+   return TRUE;\r
+}\r
+\r
+BOOL CSimpleFileFind::FindNextFileNoDots()\r
+{\r
+   BOOL result;\r
+   do {\r
+      result = FindNextFile();\r
+   } while (result && IsDots());\r
+\r
+   return result;\r
+}\r
+\r
+BOOL CSimpleFileFind::FindNextFileNoDirectories()\r
+{\r
+   BOOL result;\r
+   do {\r
+      result = FindNextFile();\r
+   } while (result && IsDirectory());\r
+\r
+   return result;\r
+}\r
+\r
+\r
+/*\r
+ * Implementation notes:\r
+ *\r
+ * This is a depth-first traversal.  Directories are visited before\r
+ * their contents.\r
+ *\r
+ * We keep a stack of directories.  The deepest directory is at the top\r
+ * of the stack, the originally-requested directory is at the bottom.\r
+ * If we come across a directory, we first return it to the user, then\r
+ * recurse into it.  The finder at the bottom of the stack always points\r
+ * to the file or directory last returned to the user (except immediately\r
+ * after creation, when the finder points to the first valid thing we need\r
+ * to return, but we haven't actually returned anything yet - hence the\r
+ * m_bIsNew variable).\r
+ *\r
+ * Errors reading a directory are assumed to be end-of-directory, and\r
+ * are otherwise ignored.\r
+ *\r
+ * The "." and ".." psedo-directories are ignored for obvious reasons.\r
+ */\r
+\r
+\r
+CDirFileEnum::CDirStackEntry::CDirStackEntry(CDirStackEntry * seNext,\r
+                                             const CString& sDirName)\r
+                                             : CSimpleFileFind(sDirName),\r
+                                             m_seNext(seNext)\r
+{\r
+}\r
+\r
+CDirFileEnum::CDirStackEntry::~CDirStackEntry()\r
+{\r
+}\r
+\r
+inline void CDirFileEnum::PopStack()\r
+{\r
+   CDirStackEntry * seToDelete = m_seStack;\r
+   m_seStack = seToDelete->m_seNext;\r
+   delete seToDelete;\r
+}\r
+\r
+inline void CDirFileEnum::PushStack(const CString& sDirName)\r
+{\r
+   m_seStack = new CDirStackEntry(m_seStack,sDirName);\r
+}\r
+\r
+CDirFileEnum::CDirFileEnum(const CString& sDirName) :\r
+   m_seStack(NULL),\r
+   m_bIsNew(TRUE)\r
+{\r
+   PushStack(sDirName);\r
+}\r
+\r
+CDirFileEnum::~CDirFileEnum()\r
+{\r
+   while (m_seStack != NULL) {\r
+      PopStack();\r
+   }\r
+}\r
+\r
+BOOL CDirFileEnum::NextFile(CString &sResult, bool* pbIsDirectory)\r
+{\r
+   if (m_bIsNew) {\r
+      // Special-case first time - haven't found anything yet,\r
+      // so don't do recurse-into-directory check.\r
+      m_bIsNew = FALSE;\r
+   } else if (m_seStack && m_seStack->IsDirectory()) {\r
+      PushStack(m_seStack->GetFilePath());\r
+   }\r
+\r
+   while (m_seStack && !m_seStack->FindNextFileNoDots()) {\r
+      // No more files in this directory, try parent.\r
+      PopStack();\r
+   }\r
+\r
+   if (m_seStack) \r
+   {\r
+      sResult = m_seStack->GetFilePath();\r
+         if(pbIsDirectory != NULL)\r
+         {\r
+                 *pbIsDirectory = m_seStack->IsDirectory();\r
+         }\r
+      return TRUE;\r
+   } else {\r
+      return FALSE;\r
+   }\r
+}\r
diff --git a/Utils/DirFileEnum.h b/Utils/DirFileEnum.h
new file mode 100644 (file)
index 0000000..4b1fcf9
--- /dev/null
@@ -0,0 +1,273 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2005 - 2006 - Jon Foster\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Enumerates over a directory tree, non-recursively.\r
+ * Advantages over CFileFind:\r
+ * 1) Return values are not broken.  An error return from\r
+ *    CFileFind::FindNext() indicates that the *next*\r
+ *    call to CFileFind::FindNext() will fail.\r
+ *    A failure from CSimpleFileFind means *that* call\r
+ *    failed, which is what I'd expect.\r
+ * 2) Error handling.  If you use CFileFind, you are\r
+ *    required to call ::GetLastError() yourself, there\r
+ *    is no abstraction.\r
+ * 3) Support for ignoring the "." and ".." directories\r
+ *    automatically.\r
+ * 4) No dynamic memory allocation.\r
+ */\r
+class CSimpleFileFind {\r
+private:\r
+   /**\r
+    * Windows FindFirstFile() handle.\r
+    */\r
+   HANDLE m_hFindFile;\r
+\r
+   /**\r
+    * Windows error code - if all is well, ERROR_SUCCESS.\r
+    * At end of directory, ERROR_NO_MORE_FILES.\r
+    */\r
+   DWORD m_dError;\r
+\r
+   /**\r
+    * Flag indicating that FindNextFile() has not yet been\r
+    * called.\r
+    */\r
+   BOOL m_bFirst;\r
+\r
+protected:\r
+   /**\r
+    * The prefix for files in this directory.\r
+    * Ends with a "\", unless it's a drive letter only\r
+    * ("C:" is different from "C:\", and "C:filename" is\r
+    * legal anyway.)\r
+    */\r
+   CString m_sPathPrefix;\r
+\r
+   /**\r
+    * The file data returned by FindFirstFile()/FindNextFile().\r
+    */\r
+   WIN32_FIND_DATA m_FindFileData;\r
+\r
+public:\r
+\r
+   /**\r
+    * Constructor.\r
+    *\r
+    * \param sPath    The path to search in.\r
+    * \param sPattern The filename pattern - default all files.\r
+    */\r
+   CSimpleFileFind(const CString &sPath, LPCTSTR pPattern = _T("*.*"));\r
+   ~CSimpleFileFind();\r
+\r
+   /**\r
+    * Advance to the next file.\r
+    * Note that the state of this object is undefined until\r
+    * this method is called the first time.\r
+    *\r
+    * \return TRUE if a file was found, FALSE on error or\r
+    * end-of-directory (use IsError() and IsPastEnd() to\r
+    * disambiguate).\r
+    */\r
+   BOOL FindNextFile();\r
+\r
+   /**\r
+    * Advance to the next file, ignoring the "." and ".."\r
+    * pseudo-directories (if seen).\r
+    *\r
+    * Behaves like FindNextFile(), apart from ignoring "."\r
+    * and "..".\r
+    *\r
+    * \return TRUE if a file was found, FALSE on error or\r
+    * end-of-directory.\r
+    */\r
+   BOOL FindNextFileNoDots();\r
+\r
+   /**\r
+    * Advance to the next file, ignoring all directories.\r
+    *\r
+    * Behaves like FindNextFile(), apart from ignoring\r
+    * directories.\r
+    *\r
+    * \return TRUE if a file was found, FALSE on error or\r
+    * end-of-directory.\r
+    */\r
+   BOOL FindNextFileNoDirectories();\r
+\r
+   /**\r
+    * Get the Windows error code.\r
+    * Only useful when IsError() returns true.\r
+    *\r
+    * \return Windows error code.\r
+    */\r
+   inline DWORD GetError() const\r
+   {\r
+      return m_dError;\r
+   }\r
+\r
+   /**\r
+    * Check if the current file data is valid.\r
+    * (I.e. there has not been an error and we are not past\r
+    * the end of the directory).\r
+    *\r
+    * \return TRUE iff the current file data is valid.\r
+    */\r
+   inline BOOL IsValid() const\r
+   {\r
+      return (m_dError == ERROR_SUCCESS);\r
+   }\r
+\r
+   /**\r
+    * Check if we have passed the end of the directory.\r
+    *\r
+    * \return TRUE iff we have passed the end of the directory.\r
+    */\r
+   inline BOOL IsPastEnd() const\r
+   {\r
+      return (m_dError == ERROR_NO_MORE_FILES);\r
+   }\r
+\r
+   /**\r
+    * Check if there has been an unexpected error - i.e.\r
+    * any error other than passing the end of the directory.\r
+    *\r
+    * \return TRUE iff there has been an unexpected error.\r
+    */\r
+   inline BOOL IsError() const\r
+   {\r
+      return (m_dError != ERROR_SUCCESS)\r
+          && (m_dError != ERROR_NO_MORE_FILES);\r
+   }\r
+\r
+   /**\r
+    * Check if the current file is a directory.\r
+    *\r
+    * \return TRUE iff the current file is a directory.\r
+    */\r
+   inline bool IsDirectory() const\r
+   {\r
+      return !!(m_FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+   }\r
+\r
+   /**\r
+    * Get the current file name (excluding the path).\r
+    *\r
+    * \return the current file name.\r
+    */\r
+   inline CString GetFileName() const\r
+   {\r
+      return m_FindFileData.cFileName;\r
+   }\r
+\r
+   /*\r
+    * Get the current file name, including the path.\r
+    *\r
+    * \return the current file path.\r
+    */\r
+   inline CString GetFilePath() const\r
+   {\r
+      return m_sPathPrefix + m_FindFileData.cFileName;\r
+   }\r
+\r
+   /**\r
+    * Check if the current file is the "." or ".."\r
+    * pseudo-directory.\r
+    *\r
+    * \return TRUE iff the current file is the "." or ".."\r
+    * pseudo-directory.\r
+    */\r
+   inline BOOL IsDots() const \r
+   {\r
+      return IsDirectory()\r
+          && m_FindFileData.cFileName[0] == _T('.')\r
+          && ( (m_FindFileData.cFileName[1] == 0)\r
+            || (m_FindFileData.cFileName[1] == _T('.')\r
+             && m_FindFileData.cFileName[2] == 0) );\r
+   }\r
+};\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Enumerates over a directory tree, recursively.\r
+ *\r
+ * \par requirements\r
+ * win95 or later\r
+ * winNT4 or later\r
+ * MFC\r
+ *\r
+ * \version 1.0\r
+ * first version\r
+ *\r
+ * \date 18-Feb-2004\r
+ *\r
+ * \author Jon Foster\r
+ *\r
+ * \par license\r
+ * This code is GPL'd.\r
+ */\r
+class CDirFileEnum\r
+{\r
+private:\r
+\r
+   class CDirStackEntry : public CSimpleFileFind {\r
+   public:\r
+      CDirStackEntry(CDirStackEntry * seNext, const CString& sDirName);\r
+      ~CDirStackEntry();\r
+\r
+      CDirStackEntry * m_seNext;\r
+   };\r
+\r
+   CDirStackEntry * m_seStack;\r
+   BOOL m_bIsNew;\r
+\r
+   inline void PopStack();\r
+   inline void PushStack(const CString& sDirName);\r
+\r
+public:\r
+   /**\r
+    * Iterate through the specified directory and all subdirectories.\r
+    * It does not matter whether or not the specified directory ends\r
+    * with a slash.  Both relative and absolute paths are allowed,\r
+    * the results of this iterator will be consistent with the style\r
+    * passed to this constructor.\r
+    *\r
+    * @param dirName The directory to search in.\r
+    */\r
+       CDirFileEnum(const CString& dirName);\r
+\r
+   /**\r
+    * Destructor.  Frees all resources.\r
+    */\r
+   ~CDirFileEnum();\r
+\r
+   /**\r
+    * Get the next file from this iterator.\r
+    *\r
+    * \param  result On successful return, holds the full path to the found\r
+    *                file. (If this function returns FALSE, the value of\r
+    *                result is unspecified).\r
+       * \param  pbIsDirectory Pointer to a bool variable which will hold\r
+       *                TRUE if the \c result path is a directory, FALSE\r
+       *                                if it's a file. Pass NULL if you don't need that information.\r
+    * \return TRUE iff a file was found, false at end of the iteration.\r
+    */\r
+   BOOL NextFile(CString &result, bool* pbIsDirectory);\r
+};\r
+\r
diff --git a/Utils/DragDropImpl.cpp b/Utils/DragDropImpl.cpp
new file mode 100644 (file)
index 0000000..a47258c
--- /dev/null
@@ -0,0 +1,572 @@
+/**************************************************************************\r
+   THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF\r
+   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO\r
+   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A\r
+   PARTICULAR PURPOSE.\r
+   Author: Leon Finker  1/2001\r
+**************************************************************************/\r
+// IDataObjectImpl.cpp: implementation of the CIDataObjectImpl class.\r
+//////////////////////////////////////////////////////////////////////\r
+#include <shlobj.h>\r
+#include <atlbase.h>\r
+#include "DragDropImpl.h"\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CIDataObject Class\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+CIDataObject::CIDataObject(CIDropSource* pDropSource):\r
+m_cRefCount(0), m_pDropSource(pDropSource)\r
+{\r
+}\r
+\r
+CIDataObject::~CIDataObject()\r
+{\r
+       for(int i = 0; i < m_StgMedium.GetSize(); ++i)\r
+       {\r
+               ReleaseStgMedium(m_StgMedium[i]);\r
+               delete m_StgMedium[i];\r
+       }\r
+       for(int j = 0; j < m_ArrFormatEtc.GetSize(); ++j)\r
+               delete m_ArrFormatEtc[j];\r
+}\r
+\r
+STDMETHODIMP CIDataObject::QueryInterface(/* [in] */ REFIID riid,\r
+/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)\r
+{\r
+       *ppvObject = NULL;\r
+       if (IID_IUnknown==riid || IID_IDataObject==riid)\r
+             *ppvObject=this;\r
+       /*if(riid == IID_IAsyncOperation)\r
+               *ppvObject=(IAsyncOperation*)this;*/\r
+    if (NULL!=*ppvObject)\r
+    {\r
+        ((LPUNKNOWN)*ppvObject)->AddRef();\r
+        return S_OK;\r
+    }\r
+    return E_NOINTERFACE;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CIDataObject::AddRef( void)\r
+{\r
+       return ++m_cRefCount;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CIDataObject::Release( void)\r
+{\r
+   long nTemp;\r
+   nTemp = --m_cRefCount;\r
+   if(nTemp==0)\r
+      delete this;\r
+   return nTemp;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::GetData( \r
+    /* [unique][in] */ FORMATETC __RPC_FAR *pformatetcIn,\r
+    /* [out] */ STGMEDIUM __RPC_FAR *pmedium)\r
+{ \r
+       if(pformatetcIn == NULL || pmedium == NULL)\r
+               return E_INVALIDARG;\r
+       pmedium->hGlobal = NULL;\r
+\r
+       ATLASSERT(m_StgMedium.GetSize() == m_ArrFormatEtc.GetSize());\r
+       for(int i=0; i < m_ArrFormatEtc.GetSize(); ++i)\r
+       {\r
+               if(pformatetcIn->tymed & m_ArrFormatEtc[i]->tymed &&\r
+                  pformatetcIn->dwAspect == m_ArrFormatEtc[i]->dwAspect &&\r
+                  pformatetcIn->cfFormat == m_ArrFormatEtc[i]->cfFormat)\r
+               {\r
+                       CopyMedium(pmedium, m_StgMedium[i], m_ArrFormatEtc[i]);\r
+                       return S_OK;\r
+               }\r
+       }\r
+       return DV_E_FORMATETC;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::GetDataHere( \r
+    /* [unique][in] */ FORMATETC __RPC_FAR * /*pformatetc*/,\r
+    /* [out][in] */ STGMEDIUM __RPC_FAR * /*pmedium*/)\r
+{\r
+       return E_NOTIMPL;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::QueryGetData( \r
+   /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc)\r
+{ \r
+       if(pformatetc == NULL)\r
+               return E_INVALIDARG;\r
+\r
+       //support others if needed DVASPECT_THUMBNAIL  //DVASPECT_ICON   //DVASPECT_DOCPRINT\r
+       if (!(DVASPECT_CONTENT & pformatetc->dwAspect))\r
+               return (DV_E_DVASPECT);\r
+       HRESULT  hr = DV_E_TYMED;\r
+       for(int i = 0; i < m_ArrFormatEtc.GetSize(); ++i)\r
+       {\r
+          if(pformatetc->tymed & m_ArrFormatEtc[i]->tymed)\r
+          {\r
+                 if(pformatetc->cfFormat == m_ArrFormatEtc[i]->cfFormat)\r
+                        return S_OK;\r
+                 else\r
+                        hr = DV_E_CLIPFORMAT;\r
+          }\r
+          else\r
+                 hr = DV_E_TYMED;\r
+       }\r
+       return hr;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::GetCanonicalFormatEtc( \r
+    /* [unique][in] */ FORMATETC __RPC_FAR * /*pformatectIn*/,\r
+    /* [out] */ FORMATETC __RPC_FAR *pformatetcOut)\r
+{ \r
+       if (pformatetcOut == NULL)\r
+               return E_INVALIDARG;\r
+       return DATA_S_SAMEFORMATETC;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::SetData( \r
+    /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,\r
+    /* [unique][in] */ STGMEDIUM __RPC_FAR *pmedium,\r
+    /* [in] */ BOOL fRelease)\r
+{ \r
+       if(pformatetc == NULL || pmedium == NULL)\r
+      return E_INVALIDARG;\r
+\r
+       ATLASSERT(pformatetc->tymed == pmedium->tymed);\r
+       FORMATETC* fetc=new FORMATETC;\r
+       STGMEDIUM* pStgMed = new STGMEDIUM;\r
+\r
+       if(fetc == NULL || pStgMed == NULL)\r
+               return E_OUTOFMEMORY;\r
+\r
+       SecureZeroMemory(fetc,sizeof(FORMATETC));\r
+       SecureZeroMemory(pStgMed,sizeof(STGMEDIUM));\r
+\r
+       *fetc = *pformatetc;\r
+       m_ArrFormatEtc.Add(fetc);\r
+\r
+    if(fRelease)\r
+      *pStgMed = *pmedium;\r
+    else\r
+    {\r
+               CopyMedium(pStgMed, pmedium, pformatetc);\r
+       }\r
+       m_StgMedium.Add(pStgMed);\r
+\r
+    return S_OK;\r
+}\r
+void CIDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)\r
+{\r
+               switch(pMedSrc->tymed)\r
+               {\r
+               case TYMED_HGLOBAL:\r
+                       pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal,pFmtSrc->cfFormat, NULL);\r
+                       break;\r
+               case TYMED_GDI:\r
+                       pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap,pFmtSrc->cfFormat, NULL);\r
+                       break;\r
+               case TYMED_MFPICT:\r
+                       pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict,pFmtSrc->cfFormat, NULL);\r
+                       break;\r
+               case TYMED_ENHMF:\r
+                       pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile,pFmtSrc->cfFormat, NULL);\r
+                       break;\r
+               case TYMED_FILE:\r
+                       pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName,pFmtSrc->cfFormat, NULL);\r
+                       break;\r
+               case TYMED_ISTREAM:\r
+                       pMedDest->pstm = pMedSrc->pstm;\r
+                       pMedSrc->pstm->AddRef();\r
+                       break;\r
+               case TYMED_ISTORAGE:\r
+                       pMedDest->pstg = pMedSrc->pstg;\r
+                       pMedSrc->pstg->AddRef();\r
+                       break;\r
+               case TYMED_NULL:\r
+               default:\r
+                       break;\r
+               }\r
+               pMedDest->tymed = pMedSrc->tymed;\r
+               pMedDest->pUnkForRelease = NULL;\r
+               if(pMedSrc->pUnkForRelease != NULL)\r
+               {\r
+                       pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;\r
+                       pMedSrc->pUnkForRelease->AddRef();\r
+               }\r
+}\r
+STDMETHODIMP CIDataObject::EnumFormatEtc(\r
+   /* [in] */ DWORD dwDirection,\r
+   /* [out] */ IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc)\r
+{ \r
+       if(ppenumFormatEtc == NULL)\r
+      return E_POINTER;\r
+\r
+       *ppenumFormatEtc=NULL;\r
+       switch (dwDirection)\r
+    {\r
+      case DATADIR_GET:\r
+         *ppenumFormatEtc= new CEnumFormatEtc(m_ArrFormatEtc);\r
+                if(*ppenumFormatEtc == NULL)\r
+                        return E_OUTOFMEMORY;\r
+         (*ppenumFormatEtc)->AddRef(); \r
+         break;\r
+      \r
+         case DATADIR_SET:\r
+      default:\r
+                return E_NOTIMPL;\r
+         break;\r
+    }\r
+\r
+   return S_OK;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::DAdvise( \r
+   /* [in] */ FORMATETC __RPC_FAR * /*pformatetc*/,\r
+   /* [in] */ DWORD /*advf*/,\r
+   /* [unique][in] */ IAdviseSink __RPC_FAR * /*pAdvSink*/,\r
+   /* [out] */ DWORD __RPC_FAR * /*pdwConnection*/)\r
+{ \r
+       return OLE_E_ADVISENOTSUPPORTED;\r
+}\r
+\r
+STDMETHODIMP CIDataObject::DUnadvise( \r
+   /* [in] */ DWORD /*dwConnection*/)\r
+{\r
+       return E_NOTIMPL;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE CIDataObject::EnumDAdvise( \r
+   /* [out] */ IEnumSTATDATA __RPC_FAR *__RPC_FAR * /*ppenumAdvise*/)\r
+{\r
+       return OLE_E_ADVISENOTSUPPORTED;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CIDropSource Class\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+STDMETHODIMP CIDropSource::QueryInterface(/* [in] */ REFIID riid,\r
+                                                                                /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)\r
+{\r
+   *ppvObject = NULL;\r
+   if (IID_IUnknown==riid || IID_IDropSource==riid)\r
+       *ppvObject=this;\r
+\r
+    if (*ppvObject != NULL)\r
+    {\r
+       ((LPUNKNOWN)*ppvObject)->AddRef();\r
+        return S_OK;\r
+    }\r
+    return E_NOINTERFACE;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CIDropSource::AddRef( void)\r
+{\r
+       return ++m_cRefCount;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CIDropSource::Release( void)\r
+{\r
+   long nTemp;\r
+   nTemp = --m_cRefCount;\r
+   ATLASSERT(nTemp >= 0);\r
+   if(nTemp==0)\r
+      delete this;\r
+   return nTemp;\r
+}\r
+\r
+STDMETHODIMP CIDropSource::QueryContinueDrag( \r
+    /* [in] */ BOOL fEscapePressed,\r
+    /* [in] */ DWORD grfKeyState)\r
+{\r
+   if(fEscapePressed)\r
+      return DRAGDROP_S_CANCEL;\r
+   if(!(grfKeyState & (MK_LBUTTON|MK_RBUTTON)))\r
+   {\r
+         m_bDropped = true;\r
+      return DRAGDROP_S_DROP;\r
+   }\r
+\r
+   return S_OK;\r
+\r
+}\r
+\r
+STDMETHODIMP CIDropSource::GiveFeedback(\r
+    /* [in] */ DWORD /*dwEffect*/)\r
+{\r
+       return DRAGDROP_S_USEDEFAULTCURSORS;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CEnumFormatEtc Class\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+CEnumFormatEtc::CEnumFormatEtc(const CSimpleArray<FORMATETC>& ArrFE):\r
+m_cRefCount(0),m_iCur(0)\r
+{\r
+   ATLTRACE("CEnumFormatEtc::CEnumFormatEtc()\n");\r
+   for(int i = 0; i < ArrFE.GetSize(); ++i)\r
+               m_pFmtEtc.Add(ArrFE[i]);\r
+}\r
+\r
+CEnumFormatEtc::CEnumFormatEtc(const CSimpleArray<FORMATETC*>& ArrFE):\r
+m_cRefCount(0),m_iCur(0)\r
+{\r
+   for(int i = 0; i < ArrFE.GetSize(); ++i)\r
+               m_pFmtEtc.Add(*ArrFE[i]);\r
+}\r
+\r
+STDMETHODIMP  CEnumFormatEtc::QueryInterface(REFIID refiid, void FAR* FAR* ppv)\r
+{\r
+   *ppv = NULL;\r
+   if (IID_IUnknown==refiid || IID_IEnumFORMATETC==refiid)\r
+             *ppv=this;\r
+\r
+    if (*ppv != NULL)\r
+    {\r
+        ((LPUNKNOWN)*ppv)->AddRef();\r
+        return S_OK;\r
+    }\r
+    return E_NOINTERFACE;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CEnumFormatEtc::AddRef(void)\r
+{\r
+   return ++m_cRefCount;\r
+}\r
+\r
+STDMETHODIMP_(ULONG) CEnumFormatEtc::Release(void)\r
+{\r
+   long nTemp = --m_cRefCount;\r
+   ATLASSERT(nTemp >= 0);\r
+   if(nTemp == 0)\r
+     delete this;\r
+\r
+   return nTemp; \r
+}\r
+\r
+STDMETHODIMP CEnumFormatEtc::Next( ULONG celt,LPFORMATETC lpFormatEtc, ULONG FAR *pceltFetched)\r
+{\r
+   if(pceltFetched != NULL)\r
+          *pceltFetched=0;\r
+       \r
+   ULONG cReturn = celt;\r
+\r
+   if(celt <= 0 || lpFormatEtc == NULL || m_iCur >= m_pFmtEtc.GetSize())\r
+      return S_FALSE;\r
+\r
+   if(pceltFetched == NULL && celt != 1) // pceltFetched can be NULL only for 1 item request\r
+      return S_FALSE;\r
+\r
+       while (m_iCur < m_pFmtEtc.GetSize() && cReturn > 0)\r
+       {\r
+               *lpFormatEtc++ = m_pFmtEtc[m_iCur++];\r
+               --cReturn;\r
+       }\r
+       if (pceltFetched != NULL)\r
+               *pceltFetched = celt - cReturn;\r
+\r
+    return (cReturn == 0) ? S_OK : S_FALSE;\r
+}\r
+   \r
+STDMETHODIMP CEnumFormatEtc::Skip(ULONG celt)\r
+{\r
+       if((m_iCur + int(celt)) >= m_pFmtEtc.GetSize())\r
+               return S_FALSE;\r
+       m_iCur += celt;\r
+       return S_OK;\r
+}\r
+\r
+STDMETHODIMP CEnumFormatEtc::Reset(void)\r
+{\r
+   m_iCur = 0;\r
+   return S_OK;\r
+}\r
+               \r
+STDMETHODIMP CEnumFormatEtc::Clone(IEnumFORMATETC FAR * FAR*ppCloneEnumFormatEtc)\r
+{\r
+  if(ppCloneEnumFormatEtc == NULL)\r
+      return E_POINTER;\r
+      \r
+  CEnumFormatEtc *newEnum = new CEnumFormatEtc(m_pFmtEtc);\r
+  if(newEnum ==NULL)\r
+               return E_OUTOFMEMORY;   \r
+  newEnum->AddRef();\r
+  newEnum->m_iCur = m_iCur;\r
+  *ppCloneEnumFormatEtc = newEnum;\r
+  return S_OK;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CIDropTarget Class\r
+//////////////////////////////////////////////////////////////////////\r
+CIDropTarget::CIDropTarget(HWND hTargetWnd): \r
+       m_hTargetWnd(hTargetWnd),\r
+       m_cRefCount(0), m_bAllowDrop(false),\r
+       m_pDropTargetHelper(NULL), m_pSupportedFrmt(NULL)\r
+{\r
+       if(FAILED(CoCreateInstance(CLSID_DragDropHelper,NULL,CLSCTX_INPROC_SERVER,\r
+                     IID_IDropTargetHelper,(LPVOID*)&m_pDropTargetHelper)))\r
+               m_pDropTargetHelper = NULL;\r
+}\r
+\r
+CIDropTarget::~CIDropTarget()\r
+{\r
+       if(m_pDropTargetHelper != NULL)\r
+       {\r
+               m_pDropTargetHelper->Release();\r
+               m_pDropTargetHelper = NULL;\r
+       }\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE CIDropTarget::QueryInterface( /* [in] */ REFIID riid,\r
+                                               /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)\r
+{\r
+   *ppvObject = NULL;\r
+   if (IID_IUnknown==riid || IID_IDropTarget==riid)\r
+                        *ppvObject=this;\r
+\r
+       if (*ppvObject != NULL)\r
+       {\r
+               ((LPUNKNOWN)*ppvObject)->AddRef();\r
+               return S_OK;\r
+       }\r
+       return E_NOINTERFACE;\r
+}\r
+\r
+ULONG STDMETHODCALLTYPE CIDropTarget::Release( void)\r
+{\r
+   long nTemp;\r
+   nTemp = --m_cRefCount;\r
+   ATLASSERT(nTemp >= 0);\r
+   if(nTemp==0)\r
+         delete this;\r
+   return nTemp;\r
+}\r
+\r
+bool CIDropTarget::QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect)\r
+{  \r
+       DWORD dwOKEffects = *pdwEffect; \r
+\r
+       if(!m_bAllowDrop)\r
+       {\r
+          *pdwEffect = DROPEFFECT_NONE;\r
+          return false;\r
+       }\r
+       //CTRL+SHIFT  -- DROPEFFECT_LINK\r
+       //CTRL        -- DROPEFFECT_COPY\r
+       //SHIFT       -- DROPEFFECT_MOVE\r
+       //no modifier -- DROPEFFECT_MOVE or whatever is allowed by src\r
+       *pdwEffect = (grfKeyState & MK_CONTROL) ?\r
+                                ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY ):\r
+                                ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : 0 );\r
+       if(*pdwEffect == 0) \r
+       {\r
+          // No modifier keys used by user while dragging. \r
+          if (DROPEFFECT_MOVE & dwOKEffects)\r
+                 *pdwEffect = DROPEFFECT_MOVE;\r
+          else if (DROPEFFECT_COPY & dwOKEffects)\r
+                 *pdwEffect = DROPEFFECT_COPY; \r
+          else if (DROPEFFECT_LINK & dwOKEffects)\r
+                 *pdwEffect = DROPEFFECT_LINK; \r
+          else \r
+          {\r
+                 *pdwEffect = DROPEFFECT_NONE;\r
+          }\r
+       } \r
+       else\r
+       {\r
+          // Check if the drag source application allows the drop effect desired by user.\r
+          // The drag source specifies this in DoDragDrop\r
+          if(!(*pdwEffect & dwOKEffects))\r
+                 *pdwEffect = DROPEFFECT_NONE;\r
+       }  \r
+\r
+       return (DROPEFFECT_NONE == *pdwEffect)?false:true;\r
+}   \r
+\r
+HRESULT STDMETHODCALLTYPE CIDropTarget::DragEnter(\r
+    /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,\r
+    /* [in] */ DWORD grfKeyState,\r
+    /* [in] */ POINTL pt,\r
+    /* [out][in] */ DWORD __RPC_FAR *pdwEffect)\r
+{\r
+       if(pDataObj == NULL)\r
+               return E_INVALIDARG;\r
+\r
+       if(m_pDropTargetHelper)\r
+               m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, (LPPOINT)&pt, *pdwEffect);\r
+       //IEnumFORMATETC* pEnum;\r
+       //pDataObj->EnumFormatEtc(DATADIR_GET,&pEnum);\r
+       //FORMATETC ftm;\r
+       //for()\r
+       //pEnum->Next(1,&ftm,0);\r
+       //pEnum->Release();\r
+       m_pSupportedFrmt = NULL;\r
+       for(int i =0; i<m_formatetc.GetSize(); ++i)\r
+       {\r
+               m_bAllowDrop = (pDataObj->QueryGetData(&m_formatetc[i]) == S_OK)?true:false;\r
+               if(m_bAllowDrop)\r
+               {\r
+                       m_pSupportedFrmt = &m_formatetc[i];\r
+                       break;\r
+               }\r
+       }\r
+\r
+       QueryDrop(grfKeyState, pdwEffect);\r
+       return S_OK;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE CIDropTarget::DragOver( \r
+        /* [in] */ DWORD grfKeyState,\r
+        /* [in] */ POINTL pt,\r
+        /* [out][in] */ DWORD __RPC_FAR *pdwEffect)\r
+{\r
+       if(m_pDropTargetHelper)\r
+               m_pDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);\r
+       QueryDrop(grfKeyState, pdwEffect);\r
+       return S_OK;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE CIDropTarget::DragLeave( void)\r
+{\r
+       if(m_pDropTargetHelper)\r
+               m_pDropTargetHelper->DragLeave();\r
+       \r
+       m_bAllowDrop = false;\r
+       m_pSupportedFrmt = NULL;\r
+       return S_OK;\r
+}\r
+\r
+HRESULT STDMETHODCALLTYPE CIDropTarget::Drop(\r
+       /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,\r
+    /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, \r
+       /* [out][in] */ DWORD __RPC_FAR *pdwEffect)\r
+{\r
+       if (pDataObj == NULL)\r
+               return E_INVALIDARG;    \r
+\r
+       if(m_pDropTargetHelper)\r
+               m_pDropTargetHelper->Drop(pDataObj, (LPPOINT)&pt, *pdwEffect);\r
+\r
+       if(QueryDrop(grfKeyState, pdwEffect))\r
+       {\r
+               if(m_bAllowDrop && m_pSupportedFrmt != NULL)\r
+               {\r
+                       STGMEDIUM medium;\r
+                       if(pDataObj->GetData(m_pSupportedFrmt, &medium) == S_OK)\r
+                       {\r
+                               if(OnDrop(m_pSupportedFrmt, medium, pdwEffect, pt)) //does derive class wants us to free medium?\r
+                                       ReleaseStgMedium(&medium);\r
+                       }\r
+               }\r
+       }\r
+       m_bAllowDrop=false;\r
+       *pdwEffect = DROPEFFECT_NONE;\r
+       m_pSupportedFrmt = NULL;\r
+       return S_OK;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CIDragSourceHelper Class\r
+//////////////////////////////////////////////////////////////////////\r
diff --git a/Utils/DragDropImpl.h b/Utils/DragDropImpl.h
new file mode 100644 (file)
index 0000000..638c131
--- /dev/null
@@ -0,0 +1,242 @@
+// IDataObjectImpl.h: interface for the CIDataObjectImpl class.\r
+/**************************************************************************\r
+   THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF\r
+   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO\r
+   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A\r
+   PARTICULAR PURPOSE.\r
+   Author: Leon Finker  1/2001\r
+**************************************************************************/\r
+#ifndef __DRAGDROPIMPL_H__\r
+#define __DRAGDROPIMPL_H__\r
+//#include <ShlDisp.h>\r
+///////////////////////////////////////////////////////////////////////////////////////////////\r
+class CEnumFormatEtc : public IEnumFORMATETC\r
+{\r
+   private:\r
+     ULONG           m_cRefCount;\r
+     CSimpleArray<FORMATETC>  m_pFmtEtc;\r
+     int           m_iCur;\r
+\r
+   public:\r
+     CEnumFormatEtc(const CSimpleArray<FORMATETC>& ArrFE);\r
+        CEnumFormatEtc(const CSimpleArray<FORMATETC*>& ArrFE);\r
+     //IUnknown members\r
+     STDMETHOD(QueryInterface)(REFIID, void FAR* FAR*);\r
+     STDMETHOD_(ULONG, AddRef)(void);\r
+     STDMETHOD_(ULONG, Release)(void);\r
+\r
+     //IEnumFORMATETC members\r
+     STDMETHOD(Next)(ULONG, LPFORMATETC, ULONG FAR *);\r
+     STDMETHOD(Skip)(ULONG);\r
+     STDMETHOD(Reset)(void);\r
+     STDMETHOD(Clone)(IEnumFORMATETC FAR * FAR*);\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////////////////////////\r
+class CIDropSource : public IDropSource\r
+{\r
+       long m_cRefCount;\r
+public:\r
+       bool m_bDropped;\r
+\r
+       CIDropSource::CIDropSource():m_cRefCount(0),m_bDropped(false) {}\r
+       //IUnknown\r
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(\r
+            /* [in] */ REFIID riid,\r
+            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);        \r
+    virtual ULONG STDMETHODCALLTYPE AddRef( void);\r
+    virtual ULONG STDMETHODCALLTYPE Release( void);\r
+       //IDropSource\r
+    virtual HRESULT STDMETHODCALLTYPE QueryContinueDrag( \r
+        /* [in] */ BOOL fEscapePressed,\r
+        /* [in] */ DWORD grfKeyState);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE GiveFeedback( \r
+        /* [in] */ DWORD dwEffect);\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////////////////////////\r
+class CIDataObject : public IDataObject//,public IAsyncOperation\r
+{\r
+       CIDropSource* m_pDropSource;\r
+       long m_cRefCount;\r
+       CSimpleArray<FORMATETC*> m_ArrFormatEtc;\r
+    CSimpleArray<STGMEDIUM*> m_StgMedium;\r
+public:\r
+       CIDataObject(CIDropSource* pDropSource);\r
+       ~CIDataObject();\r
+       void CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc);\r
+    //IUnknown\r
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(\r
+            /* [in] */ REFIID riid,\r
+            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);        \r
+    virtual ULONG STDMETHODCALLTYPE AddRef( void);\r
+    virtual ULONG STDMETHODCALLTYPE Release( void);\r
+\r
+       //IDataObject\r
+       virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetData( \r
+        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetcIn,\r
+        /* [out] */ STGMEDIUM __RPC_FAR *pmedium);\r
+    \r
+    virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDataHere( \r
+        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,\r
+        /* [out][in] */ STGMEDIUM __RPC_FAR *pmedium);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE QueryGetData( \r
+        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc( \r
+        /* [unique][in] */ FORMATETC __RPC_FAR *pformatectIn,\r
+        /* [out] */ FORMATETC __RPC_FAR *pformatetcOut);\r
+    \r
+    virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetData( \r
+        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,\r
+        /* [unique][in] */ STGMEDIUM __RPC_FAR *pmedium,\r
+        /* [in] */ BOOL fRelease);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc( \r
+        /* [in] */ DWORD dwDirection,\r
+        /* [out] */ IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE DAdvise( \r
+        /* [in] */ FORMATETC __RPC_FAR *pformatetc,\r
+        /* [in] */ DWORD advf,\r
+        /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink,\r
+        /* [out] */ DWORD __RPC_FAR *pdwConnection);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE DUnadvise( \r
+        /* [in] */ DWORD dwConnection);\r
+    \r
+    virtual HRESULT STDMETHODCALLTYPE EnumDAdvise( \r
+        /* [out] */ IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise);\r
+\r
+       //IAsyncOperation\r
+    //virtual HRESULT STDMETHODCALLTYPE SetAsyncMode( \r
+    //    /* [in] */ BOOL fDoOpAsync)\r
+       //{\r
+       //      return E_NOTIMPL;\r
+       //}\r
+    //\r
+    //virtual HRESULT STDMETHODCALLTYPE GetAsyncMode( \r
+    //    /* [out] */ BOOL __RPC_FAR *pfIsOpAsync)\r
+       //{\r
+       //      return E_NOTIMPL;\r
+       //}\r
+    //\r
+    //virtual HRESULT STDMETHODCALLTYPE StartOperation( \r
+    //    /* [optional][unique][in] */ IBindCtx __RPC_FAR *pbcReserved)\r
+       //{\r
+       //      return E_NOTIMPL;\r
+       //}\r
+    //\r
+    //virtual HRESULT STDMETHODCALLTYPE InOperation( \r
+    //    /* [out] */ BOOL __RPC_FAR *pfInAsyncOp)\r
+       //{\r
+       //      return E_NOTIMPL;\r
+       //}\r
+    //\r
+    //virtual HRESULT STDMETHODCALLTYPE EndOperation( \r
+    //    /* [in] */ HRESULT hResult,\r
+    //    /* [unique][in] */ IBindCtx __RPC_FAR *pbcReserved,\r
+    //    /* [in] */ DWORD dwEffects)\r
+       //{\r
+       //      return E_NOTIMPL;\r
+       //}\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////////////////////////\r
+class CIDropTarget : public IDropTarget\r
+{\r
+       DWORD m_cRefCount;\r
+       bool m_bAllowDrop;\r
+       struct IDropTargetHelper *m_pDropTargetHelper;\r
+       CSimpleArray<FORMATETC> m_formatetc;\r
+       FORMATETC* m_pSupportedFrmt;\r
+protected:\r
+       HWND m_hTargetWnd;\r
+public:\r
+       \r
+       CIDropTarget(HWND m_hTargetWnd);\r
+       virtual ~CIDropTarget();\r
+       void AddSuportedFormat(FORMATETC& ftetc) { m_formatetc.Add(ftetc); }\r
+       \r
+       //return values: true - release the medium. false - don't release the medium \r
+       virtual bool OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium,DWORD *pdwEffect, POINTL pt) = 0;\r
+\r
+       virtual HRESULT STDMETHODCALLTYPE QueryInterface( \r
+               /* [in] */ REFIID riid,\r
+               /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);\r
+       virtual ULONG STDMETHODCALLTYPE AddRef( void) { ATLTRACE("CIDropTarget::AddRef\n"); return ++m_cRefCount; }\r
+       virtual ULONG STDMETHODCALLTYPE Release( void);\r
+\r
+    bool QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect);\r
+       virtual HRESULT STDMETHODCALLTYPE DragEnter(\r
+        /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,\r
+        /* [in] */ DWORD grfKeyState,\r
+        /* [in] */ POINTL pt,\r
+        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);\r
+    virtual HRESULT STDMETHODCALLTYPE DragOver( \r
+        /* [in] */ DWORD grfKeyState,\r
+        /* [in] */ POINTL pt,\r
+        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);\r
+    virtual HRESULT STDMETHODCALLTYPE DragLeave( void);    \r
+    virtual HRESULT STDMETHODCALLTYPE Drop(\r
+        /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,\r
+        /* [in] */ DWORD grfKeyState,\r
+        /* [in] */ POINTL pt,\r
+        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);\r
+};\r
+\r
+class CDragSourceHelper\r
+{\r
+       IDragSourceHelper* pDragSourceHelper;\r
+public:\r
+       CDragSourceHelper()\r
+       {\r
+               pDragSourceHelper = NULL;\r
+               if(FAILED(CoCreateInstance(CLSID_DragDropHelper,\r
+                        NULL,\r
+                        CLSCTX_INPROC_SERVER,\r
+                        IID_IDragSourceHelper,\r
+                        (void**)&pDragSourceHelper)))\r
+                       pDragSourceHelper = NULL;\r
+       }\r
+       virtual ~CDragSourceHelper()\r
+       {\r
+               if( pDragSourceHelper!= NULL )\r
+               {\r
+                       pDragSourceHelper->Release();\r
+                       pDragSourceHelper=NULL;\r
+               }\r
+       }\r
+    \r
+       // IDragSourceHelper\r
+    HRESULT InitializeFromBitmap(HBITMAP hBitmap, \r
+               POINT& pt,      // cursor position in client coords of the window\r
+               RECT& rc,       // selected item's bounding rect\r
+               IDataObject* pDataObject,\r
+               COLORREF crColorKey=GetSysColor(COLOR_WINDOW)// color of the window used for transparent effect.\r
+               )\r
+       {\r
+               if(pDragSourceHelper == NULL)\r
+                       return E_FAIL;\r
+\r
+                   SHDRAGIMAGE di;\r
+            BITMAP      bm;\r
+            GetObject(hBitmap, sizeof(bm), &bm);\r
+            di.sizeDragImage.cx = bm.bmWidth;\r
+            di.sizeDragImage.cy = bm.bmHeight;\r
+            di.hbmpDragImage = hBitmap;\r
+            di.crColorKey = crColorKey; \r
+            di.ptOffset.x = pt.x - rc.left;\r
+            di.ptOffset.y = pt.y - rc.top;\r
+        return pDragSourceHelper->InitializeFromBitmap(&di, pDataObject);\r
+       }\r
+    HRESULT InitializeFromWindow(HWND hwnd, POINT& pt,IDataObject* pDataObject)\r
+       {               \r
+               if(pDragSourceHelper == NULL)\r
+                       return E_FAIL;\r
+               return pDragSourceHelper->InitializeFromWindow(hwnd, &pt, pDataObject);\r
+       }\r
+};\r
+#endif //__DRAGDROPIMPL_H__
\ No newline at end of file
diff --git a/Utils/DropFiles.cpp b/Utils/DropFiles.cpp
new file mode 100644 (file)
index 0000000..0257b37
--- /dev/null
@@ -0,0 +1,100 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2006, 2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "DropFiles.h"\r
+\r
+CDropFiles::CDropFiles()\r
+{\r
+       m_pBuffer = NULL;\r
+       m_nBufferSize = 0;\r
+}\r
+\r
+CDropFiles::~CDropFiles()\r
+{\r
+       delete [] m_pBuffer;\r
+}\r
+\r
+void CDropFiles::AddFile(const CString &sFile)\r
+{\r
+       m_arFiles.Add(sFile);\r
+}\r
+\r
+INT_PTR CDropFiles::GetCount()\r
+{\r
+       return m_arFiles.GetCount();\r
+}\r
+\r
+void CDropFiles::CreateBuffer()\r
+{\r
+       ASSERT(m_pBuffer == NULL);\r
+       ASSERT(m_nBufferSize == 0);\r
+       ASSERT(m_arFiles.GetCount()>0);\r
+\r
+       int nLength = 0;\r
+\r
+       for(int i=0;i<m_arFiles.GetSize();i++)\r
+       {\r
+               nLength += m_arFiles[i].GetLength();\r
+               nLength += 1; // '\0' separator\r
+       }\r
+\r
+       m_nBufferSize = sizeof(DROPFILES) + (nLength+1)*sizeof(TCHAR);\r
+       m_pBuffer = new char[m_nBufferSize];\r
+       \r
+       SecureZeroMemory(m_pBuffer, m_nBufferSize);\r
+\r
+       DROPFILES* df = (DROPFILES*)m_pBuffer;\r
+       df->pFiles = sizeof(DROPFILES);\r
+       df->fWide = 1;\r
+\r
+       TCHAR* pFilenames = (TCHAR*)(m_pBuffer + sizeof(DROPFILES));\r
+       TCHAR* pCurrentFilename = pFilenames;\r
+\r
+       for(int i=0;i<m_arFiles.GetSize();i++)\r
+       {\r
+               CString str = m_arFiles[i];\r
+               wcscpy_s(pCurrentFilename,str.GetLength()+1,str.GetBuffer());\r
+               pCurrentFilename += str.GetLength(); \r
+               *pCurrentFilename = '\0'; // separator between file names\r
+               pCurrentFilename++;\r
+       }\r
+       *pCurrentFilename = '\0'; // terminate array\r
+}\r
+\r
+void* CDropFiles::GetBuffer() const\r
+{\r
+       return (void*)m_pBuffer;\r
+}\r
+\r
+int    CDropFiles::GetBufferSize() const\r
+{\r
+       return m_nBufferSize;\r
+}\r
+\r
+void CDropFiles::CreateStructure()\r
+{\r
+       CreateBuffer();\r
+       \r
+       COleDataSource dropData;\r
+       HGLOBAL hMem = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE, GetBufferSize()); \r
+       memcpy( ::GlobalLock(hMem), GetBuffer(), GetBufferSize() );\r
+       ::GlobalUnlock(hMem);\r
+       dropData.CacheGlobalData( CF_HDROP, hMem );\r
+       dropData.DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK,NULL);\r
+}\r
diff --git a/Utils/DropFiles.h b/Utils/DropFiles.h
new file mode 100644 (file)
index 0000000..5d9baf4
--- /dev/null
@@ -0,0 +1,81 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2006-2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+#ifndef __DROPFILES_H__\r
+#define __DROPFILES_H__\r
+\r
+#include <shlobj.h>\r
+#include <afxcoll.h>\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Use this class to create the DROPFILES structure which is needed to\r
+ * support drag and drop of file names to other applications.\r
+ * Based on an example by Thomas Blenkers.\r
+ */\r
+class CDropFiles\r
+{\r
+public:\r
+       CDropFiles();\r
+       ~CDropFiles();\r
+\r
+       /**\r
+        * Add a file with an absolute file name. This file will later be\r
+        * included the DROPFILES structure.\r
+        */\r
+       void AddFile(const CString &sFile);\r
+\r
+       /**\r
+        * Returns the number of files which have been added\r
+        */\r
+       INT_PTR GetCount();\r
+\r
+       /**\r
+        * Call this method when dragging begins. It will fill\r
+        * the DROPFILES structure with the files previously\r
+        * added with AddFile(...)\r
+        */\r
+       void CreateStructure();\r
+\r
+protected:\r
+       /**\r
+        * CreateBuffer must be called once when all files have been added\r
+        */\r
+       void CreateBuffer();\r
+\r
+       /**\r
+        * Returns a pointer to the buffer containing the DROPFILES\r
+        * structure\r
+        */\r
+       void* GetBuffer() const;\r
+\r
+       /**\r
+        * Returns the size of the buffer in bytes\r
+        */\r
+       int GetBufferSize() const;\r
+\r
+protected:\r
+       CStringArray m_arFiles;\r
+       \r
+       char* m_pBuffer;\r
+       int      m_nBufferSize;\r
+};\r
+\r
+#endif\r
diff --git a/Utils/HighResClock.h b/Utils/HighResClock.h
new file mode 100644 (file)
index 0000000..317a841
--- /dev/null
@@ -0,0 +1,77 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2007-2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+///////////////////////////////////////////////////////////////\r
+//\r
+// CHighResClock\r
+//\r
+//             high resolution clock for performance measurement.\r
+//             Depending on the hardware it will provide Âµsec \r
+//             resolution and accuracy.\r
+//\r
+//             May not be available on all machines.\r
+//\r
+///////////////////////////////////////////////////////////////\r
+\r
+class CHighResClock\r
+{\r
+private:\r
+\r
+       LARGE_INTEGER start;\r
+       LARGE_INTEGER taken;\r
+\r
+public:\r
+\r
+       // construction (starts measurement) / destruction\r
+\r
+       CHighResClock() \r
+       {\r
+               taken.QuadPart = 0;\r
+               Start();\r
+       }\r
+\r
+       ~CHighResClock()\r
+       {\r
+       }\r
+\r
+       // (re-start)\r
+\r
+       void Start()\r
+       {\r
+               QueryPerformanceCounter(&start);\r
+       }\r
+\r
+       // set "taken" to time since last Start()\r
+\r
+       void Stop()\r
+       {\r
+               QueryPerformanceCounter(&taken);\r
+               taken.QuadPart -= start.QuadPart;\r
+       }\r
+\r
+       // time in microseconds between last Start() and last Stop()\r
+\r
+       DWORD GetMusecsTaken() const\r
+       {\r
+               LARGE_INTEGER frequency;\r
+               QueryPerformanceFrequency(&frequency);\r
+               return (DWORD)((taken.QuadPart * 1000000) / frequency.QuadPart);\r
+       }\r
+};\r
diff --git a/Utils/Hooks.cpp b/Utils/Hooks.cpp
new file mode 100644 (file)
index 0000000..859c30b
--- /dev/null
@@ -0,0 +1,481 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2007-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "Hooks.h"\r
+#include "registry.h"\r
+#include "StringUtils.h"\r
+#include "TempFile.h"\r
+\r
+CHooks* CHooks::m_pInstance;\r
+\r
+CHooks::CHooks()\r
+{\r
+}\r
+\r
+CHooks::~CHooks()\r
+{\r
+};\r
+\r
+bool CHooks::Create()\r
+{\r
+       if (m_pInstance == NULL)\r
+               m_pInstance = new CHooks();\r
+       CRegString reghooks = CRegString(_T("Software\\TortoiseSVN\\hooks"));\r
+       CString strhooks = reghooks;\r
+       // now fill the map with all the hooks defined in the string\r
+       // the string consists of multiple lines, where one hook script is defined\r
+       // as four lines:\r
+       // line 1: the hook type\r
+       // line 2: path to working copy where to apply the hook script\r
+       // line 3: command line to execute\r
+       // line 4: 'true' or 'false' for waiting for the script to finish\r
+       // line 5: 'show' or 'hide' on how to start the hook script\r
+       hookkey key;\r
+       int pos = 0;\r
+       hookcmd cmd;\r
+       while ((pos = strhooks.Find('\n')) >= 0)\r
+       {\r
+               // line 1\r
+               key.htype = GetHookType(strhooks.Mid(0, pos));\r
+               if (pos+1 < strhooks.GetLength())\r
+                       strhooks = strhooks.Mid(pos+1);\r
+               else\r
+                       strhooks.Empty();\r
+               bool bComplete = false;\r
+               if ((pos = strhooks.Find('\n')) >= 0)\r
+               {\r
+                       // line 2\r
+                       key.path = CTSVNPath(strhooks.Mid(0, pos));\r
+                       if (pos+1 < strhooks.GetLength())\r
+                               strhooks = strhooks.Mid(pos+1);\r
+                       else\r
+                               strhooks.Empty();\r
+                       if ((pos = strhooks.Find('\n')) >= 0)\r
+                       {\r
+                               // line 3\r
+                               cmd.commandline = strhooks.Mid(0, pos);\r
+                               if (pos+1 < strhooks.GetLength())\r
+                                       strhooks = strhooks.Mid(pos+1);\r
+                               else\r
+                                       strhooks.Empty();\r
+                               if ((pos = strhooks.Find('\n')) >= 0)\r
+                               {\r
+                                       // line 4\r
+                                       cmd.bWait = (strhooks.Mid(0, pos).CompareNoCase(_T("true"))==0);\r
+                                       if (pos+1 < strhooks.GetLength())\r
+                                               strhooks = strhooks.Mid(pos+1);\r
+                                       else\r
+                                               strhooks.Empty();\r
+                                       if ((pos = strhooks.Find('\n')) >= 0)\r
+                                       {\r
+                                               // line 5\r
+                                               cmd.bShow = (strhooks.Mid(0, pos).CompareNoCase(_T("show"))==0);\r
+                                               if (pos+1 < strhooks.GetLength())\r
+                                                       strhooks = strhooks.Mid(pos+1);\r
+                                               else\r
+                                                       strhooks.Empty();\r
+                                               bComplete = true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               if (bComplete)\r
+               {\r
+                       m_pInstance->insert(std::pair<hookkey, hookcmd>(key, cmd));\r
+               }\r
+       } \r
+       return true;\r
+}\r
+\r
+CHooks& CHooks::Instance()\r
+{\r
+       return *m_pInstance;\r
+}\r
+\r
+void CHooks::Destroy()\r
+{\r
+       delete m_pInstance;\r
+}\r
+\r
+bool CHooks::Save()\r
+{\r
+       CString strhooks;\r
+       for (hookiterator it = begin(); it != end(); ++it)\r
+       {\r
+               strhooks += GetHookTypeString(it->first.htype);\r
+               strhooks += '\n';\r
+               strhooks += it->first.path.GetWinPathString();\r
+               strhooks += '\n';\r
+               strhooks += it->second.commandline;\r
+               strhooks += '\n';\r
+               strhooks += (it->second.bWait ? _T("true") : _T("false"));\r
+               strhooks += '\n';\r
+               strhooks += (it->second.bShow ? _T("show") : _T("hide"));\r
+               strhooks += '\n';\r
+       }\r
+       CRegString reghooks = CRegString(_T("Software\\TortoiseSVN\\hooks"));\r
+       reghooks = strhooks;\r
+       if (reghooks.LastError)\r
+               return false;\r
+       return true;\r
+}\r
+\r
+bool CHooks::Remove(hookkey key)\r
+{\r
+       return (erase(key) > 0);\r
+}\r
+\r
+void CHooks::Add(hooktype ht, const CTSVNPath& Path, LPCTSTR szCmd, bool bWait, bool bShow)\r
+{\r
+       hookkey key;\r
+       key.htype = ht;\r
+       key.path = Path;\r
+       hookiterator it = find(key);\r
+       if (it!=end())\r
+               erase(it);\r
+\r
+       hookcmd cmd;\r
+       cmd.commandline = szCmd;\r
+       cmd.bWait = bWait;\r
+       cmd.bShow = bShow;\r
+       insert(std::pair<hookkey, hookcmd>(key, cmd));\r
+}\r
+\r
+CString CHooks::GetHookTypeString(hooktype t)\r
+{\r
+       switch (t)\r
+       {\r
+       case start_commit_hook:\r
+               return _T("start_commit_hook");\r
+       case pre_commit_hook:\r
+               return _T("pre_commit_hook");\r
+       case post_commit_hook:\r
+               return _T("post_commit_hook");\r
+       case start_update_hook:\r
+               return _T("start_update_hook");\r
+       case pre_update_hook:\r
+               return _T("pre_update_hook");\r
+       case post_update_hook:\r
+               return _T("post_update_hook");\r
+       }\r
+       return _T("");\r
+}\r
+\r
+hooktype CHooks::GetHookType(const CString& s)\r
+{\r
+       if (s.Compare(_T("start_commit_hook"))==0)\r
+               return start_commit_hook;\r
+       if (s.Compare(_T("pre_commit_hook"))==0)\r
+               return pre_commit_hook;\r
+       if (s.Compare(_T("post_commit_hook"))==0)\r
+               return post_commit_hook;\r
+       if (s.Compare(_T("start_update_hook"))==0)\r
+               return start_update_hook;\r
+       if (s.Compare(_T("pre_update_hook"))==0)\r
+               return pre_update_hook;\r
+       if (s.Compare(_T("post_update_hook"))==0)\r
+               return post_update_hook;\r
+       return unknown_hook;\r
+}\r
+\r
+void CHooks::AddParam(CString& sCmd, const CString& param)\r
+{\r
+       sCmd += _T(" \"");\r
+       sCmd += param;\r
+       sCmd += _T("\"");\r
+}\r
+\r
+void CHooks::AddPathParam(CString& sCmd, const CTSVNPathList& pathList)\r
+{\r
+       CTSVNPath temppath = CTempFiles::Instance().GetTempFilePath(true);\r
+       pathList.WriteToFile(temppath.GetWinPathString(), true);\r
+       AddParam(sCmd, temppath.GetWinPathString());\r
+}\r
+\r
+void CHooks::AddCWDParam(CString& sCmd, const CTSVNPathList& pathList)\r
+{\r
+       AddParam(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPathString());\r
+}\r
+\r
+void CHooks::AddDepthParam(CString& sCmd, svn_depth_t depth)\r
+{\r
+       CString sTemp;\r
+       sTemp.Format(_T("%d"), depth);\r
+       AddParam(sCmd, sTemp);\r
+}\r
+\r
+void CHooks::AddErrorParam(CString& sCmd, const CString& error)\r
+{\r
+       CTSVNPath tempPath;\r
+       tempPath = CTempFiles::Instance().GetTempFilePath(true);\r
+       CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)error);\r
+       AddParam(sCmd, tempPath.GetWinPathString());\r
+}\r
+\r
+CTSVNPath CHooks::AddMessageFileParam(CString& sCmd, const CString& message)\r
+{\r
+       CTSVNPath tempPath;\r
+       tempPath = CTempFiles::Instance().GetTempFilePath(true);\r
+       CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)message);\r
+       AddParam(sCmd, tempPath.GetWinPathString());\r
+       return tempPath;\r
+}\r
+\r
+bool CHooks::StartCommit(const CTSVNPathList& pathList, CString& message, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(start_commit_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       CTSVNPath temppath = AddMessageFileParam(sCmd, message);\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       if (!exitcode && !temppath.IsEmpty())\r
+       {\r
+               CStringUtils::ReadStringFromTextFile(temppath.GetWinPathString(), message);\r
+       }\r
+       return true;\r
+}\r
+\r
+bool CHooks::PreCommit(const CTSVNPathList& pathList, svn_depth_t depth, const CString& message, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(pre_commit_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       AddDepthParam(sCmd, depth);\r
+       AddMessageFileParam(sCmd, message);\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       return true;\r
+}\r
+\r
+bool CHooks::PostCommit(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, const CString& message, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(post_commit_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       AddDepthParam(sCmd, depth);\r
+       AddMessageFileParam(sCmd, message);\r
+       AddParam(sCmd, rev.ToString());\r
+       AddErrorParam(sCmd, error);\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       return true;\r
+}\r
+\r
+bool CHooks::StartUpdate(const CTSVNPathList& pathList, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(start_update_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       return true;\r
+}\r
+\r
+bool CHooks::PreUpdate(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(pre_update_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       AddDepthParam(sCmd, depth);\r
+       AddParam(sCmd, rev.ToString());\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       return true;\r
+}\r
+\r
+bool CHooks::PostUpdate(const CTSVNPathList& pathList, svn_depth_t depth, SVNRev rev, DWORD& exitcode, CString& error)\r
+{\r
+       hookiterator it = FindItem(post_update_hook, pathList);\r
+       if (it == end())\r
+               return false;\r
+       CString sCmd = it->second.commandline;\r
+       AddPathParam(sCmd, pathList);\r
+       AddDepthParam(sCmd, depth);\r
+       AddParam(sCmd, rev.ToString());\r
+       AddErrorParam(sCmd, error);\r
+       AddCWDParam(sCmd, pathList);\r
+       exitcode = RunScript(sCmd, pathList.GetCommonRoot().GetDirectory().GetWinPath(), error, it->second.bWait, it->second.bShow);\r
+       return true;\r
+}\r
+\r
+hookiterator CHooks::FindItem(hooktype t, const CTSVNPathList& pathList)\r
+{\r
+       hookkey key;\r
+       for (int i=0; i<pathList.GetCount(); ++i)\r
+       {\r
+               CTSVNPath path = pathList[i];\r
+               do \r
+               {\r
+                       key.htype = t;\r
+                       key.path = path;\r
+                       hookiterator it = find(key);\r
+                       if (it != end())\r
+                       {\r
+                               return it;\r
+                       }\r
+                       path = path.GetContainingDirectory();\r
+               } while(!path.IsEmpty());\r
+       }\r
+       // look for a script with a path as '*'\r
+       key.htype = t;\r
+       key.path = CTSVNPath(_T("*"));\r
+       hookiterator it = find(key);\r
+       if (it != end())\r
+       {\r
+               return it;\r
+       }\r
+\r
+       return end();\r
+}\r
+\r
+DWORD CHooks::RunScript(CString cmd, LPCTSTR currentDir, CString& error, bool bWait, bool bShow)\r
+{\r
+       DWORD exitcode = 0;\r
+       SECURITY_ATTRIBUTES sa;\r
+       SecureZeroMemory(&sa, sizeof(sa));\r
+       sa.nLength = sizeof(sa);\r
+       sa.bInheritHandle = TRUE;\r
+\r
+       HANDLE hOut   = INVALID_HANDLE_VALUE;\r
+       HANDLE hRedir = INVALID_HANDLE_VALUE;\r
+       HANDLE hErr   = INVALID_HANDLE_VALUE;\r
+\r
+       // clear the error string\r
+       error.Empty();\r
+\r
+       // Create Temp File for redirection\r
+       TCHAR szTempPath[MAX_PATH];\r
+       TCHAR szOutput[MAX_PATH];\r
+       TCHAR szErr[MAX_PATH];\r
+       GetTempPath(sizeof(szTempPath)/sizeof(TCHAR),szTempPath);\r
+       GetTempFileName(szTempPath, _T("svn"), 0, szErr);\r
+\r
+       // setup redirection handles\r
+       // output handle must be WRITE mode, share READ\r
+       // redirect handle must be READ mode, share WRITE\r
+       hErr   = CreateFile(szErr, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY,        0);\r
+\r
+       if (hErr  == INVALID_HANDLE_VALUE) \r
+       {\r
+               return (DWORD)-1;\r
+       }\r
+\r
+       hRedir = CreateFile(szErr, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);\r
+\r
+       if (hRedir  == INVALID_HANDLE_VALUE) \r
+       {\r
+               CloseHandle(hErr);\r
+               return (DWORD)-1;\r
+       }\r
+\r
+       GetTempFileName(szTempPath, _T("svn"), 0, szOutput);\r
+       hOut   = CreateFile(szOutput, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY,     0);\r
+\r
+       if (hOut  == INVALID_HANDLE_VALUE) \r
+       {\r
+               CloseHandle(hErr);\r
+               CloseHandle(hRedir);\r
+               return (DWORD)-1;\r
+       }\r
+\r
+       // setup startup info, set std out/err handles\r
+       // hide window\r
+       STARTUPINFO si;\r
+       SecureZeroMemory(&si, sizeof(si));\r
+       si.cb = sizeof(si);\r
+       if (hOut  != INVALID_HANDLE_VALUE) \r
+       {\r
+               si.dwFlags     = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;\r
+               si.hStdOutput  = hOut;\r
+               si.hStdError   = hErr;\r
+               si.wShowWindow = bShow ? SW_SHOW : SW_HIDE;\r
+       }\r
+\r
+       PROCESS_INFORMATION pi;\r
+       SecureZeroMemory(&pi, sizeof(pi));\r
+\r
+       DWORD dwFlags = 0;\r
+\r
+       if (!CreateProcess(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, dwFlags, NULL, currentDir, &si, &pi)) \r
+       {\r
+                       int err = GetLastError();  // preserve the CreateProcess error\r
+                       if (hErr != INVALID_HANDLE_VALUE) \r
+                       {\r
+                               CloseHandle(hErr);\r
+                               CloseHandle(hRedir);\r
+                       }\r
+                       SetLastError(err);\r
+                       cmd.ReleaseBuffer();\r
+                       return (DWORD)-1;\r
+       }\r
+       cmd.ReleaseBuffer();\r
+\r
+       CloseHandle(pi.hThread);\r
+\r
+       // wait for process to finish, capture redirection and\r
+       // send it to the parent window/console\r
+       if (bWait)\r
+       {\r
+               DWORD dw;\r
+               char buf[256];\r
+               do \r
+               {\r
+                       SecureZeroMemory(&buf,sizeof(buf));\r
+                       while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) \r
+                       {\r
+                               if (dw == 0) \r
+                                       break;\r
+                               error += CString(CStringA(buf,dw));\r
+                               SecureZeroMemory(&buf,sizeof(buf));\r
+                       }\r
+               } while (WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0);\r
+\r
+               // perform any final flushing\r
+               while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) \r
+               {\r
+                       if (dw == 0) \r
+                               break;\r
+\r
+                       error += CString(CStringA(buf, dw));\r
+                       SecureZeroMemory(&buf,sizeof(buf));\r
+               }\r
+               WaitForSingleObject(pi.hProcess, INFINITE);\r
+               GetExitCodeProcess(pi.hProcess, &exitcode);\r
+       }\r
+       CloseHandle(pi.hProcess);\r
+       CloseHandle(hErr);\r
+       CloseHandle(hOut);\r
+       CloseHandle(hRedir);\r
+       DeleteFile(szOutput);\r
+       DeleteFile(szErr);\r
+\r
+       return exitcode;\r
+}\r
+\r
diff --git a/Utils/Hooks.h b/Utils/Hooks.h
new file mode 100644 (file)
index 0000000..5aef06a
--- /dev/null
@@ -0,0 +1,240 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2006-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include <map>\r
+#include "registry.h"\r
+#include "TSVNPath.h"\r
+#include "SVNRev.h"\r
+\r
+/**\r
+ * \ingroup TortoiseProc\r
+ * enumeration of all client hook types\r
+ */\r
+typedef enum hooktype\r
+{\r
+       unknown_hook,\r
+       start_commit_hook,\r
+       pre_commit_hook,\r
+       post_commit_hook,\r
+       start_update_hook,\r
+       pre_update_hook,\r
+       post_update_hook,\r
+       issue_tracker_hook\r
+} hooktype;\r
+\r
+/**\r
+ * \ingroup TortoiseProc\r
+ * helper class, used as the key to the std::map we store\r
+ * the data for the client hook scripts in.\r
+ */\r
+class hookkey\r
+{\r
+public:\r
+       hooktype                htype;\r
+       CTSVNPath               path;\r
+\r
+       bool operator < (const hookkey& hk) const \r
+       {\r
+               if (htype == hk.htype) \r
+                       return (path < hk.path); \r
+               else \r
+                       return htype < hk.htype;\r
+       }\r
+};\r
+\r
+/**\r
+ * \ingroup TortoiseProc\r
+ * helper struct, used as the value to the std::map we\r
+ * store the data for the client hook scripts in.\r
+ */\r
+typedef struct hookcmd\r
+{\r
+       CString                 commandline;\r
+       bool                    bWait;\r
+       bool                    bShow;\r
+} hookcmd;\r
+\r
+typedef std::map<hookkey, hookcmd>::iterator hookiterator;\r
+\r
+/**\r
+ * \ingroup TortoiseProc\r
+ * Singleton class which deals with the client hook scripts.\r
+ */\r
+class CHooks : public std::map<hookkey, hookcmd>\r
+{\r
+private:\r
+       CHooks();\r
+       ~CHooks();\r
+       void AddPathParam(CString& sCmd, const CTSVNPathList& pathList);\r
+       void AddDepthParam(CString& sCmd, svn_depth_t depth);\r
+       void AddCWDParam(CString& sCmd, const CTSVNPathList& pathList);\r
+       void AddErrorParam(CString& sCmd, const CString& error);\r
+       void AddParam(CString& sCmd, const CString& param);\r
+       CTSVNPath AddMessageFileParam(CString& sCmd, const CString& message);\r
+public:\r
+       /// Create the singleton. Call this at the start of the program.\r
+       static bool                     Create();\r
+       /// Returns the singleton instance\r
+       static CHooks&          Instance();\r
+       /// Destroys the singleton object. Call this at the end of the program.\r
+       static void                     Destroy();\r
+\r
+public:\r
+       /// Saves the hook script information to the registry.\r
+       bool                            Save();\r
+       /**\r
+        * Removes the hook script identified by \c key. To make the change persistent\r
+        * call Save().\r
+        */\r
+       bool                            Remove(hookkey key);\r
+       /**\r
+        * Adds a new hook script. To make the change persistent, call Save().\r
+        */\r
+       void                            Add(hooktype ht, const CTSVNPath& Path, LPCTSTR szCmd, \r
+                                                       bool bWait, bool bShow);\r
+\r
+       /// returns the string representation of the hook type.\r
+       static CString          GetHookTypeString(hooktype t);\r
+       /// returns the hooktype from a string representation of the same.\r
+       static hooktype         GetHookType(const CString& s);\r
+\r
+       /**\r
+        * Executes the Start-Update-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the update is about to be done on.\r
+        */\r
+       bool                            StartUpdate(const CTSVNPathList& pathList, DWORD& exitcode, \r
+                                                                       CString& error);\r
+       /**\r
+        * Executes the Pre-Update-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param depth the depth of the commit\r
+        * \param rev the revision the update is done to\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the update is about to be done on.\r
+        * The string "%RECURSIVE%" is replaced with either "recursive" or "nonrecursive" according\r
+        * to the \c bRecursive parameter. And the string "%REVISION%" is replaced with\r
+        * the string representation of \c rev.\r
+        */\r
+       bool                            PreUpdate(const CTSVNPathList& pathList, svn_depth_t depth, \r
+                                                                       SVNRev rev, DWORD& exitcode, CString& error);\r
+       /**\r
+        * Executes the Post-Update-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param depth the depth of the commit\r
+        * \param rev the revision the update was done to\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the update is about to be done on.\r
+        * The string "%RECURSIVE%" is replaced with either "recursive" or "nonrecursive" according\r
+        * to the \c bRecursive parameter. And the string "%REVISION%" is replaced with\r
+        * the string representation of \c rev.\r
+        */\r
+       bool                            PostUpdate(const CTSVNPathList& pathList, svn_depth_t depth, \r
+                                                                       SVNRev rev, DWORD& exitcode, CString& error);\r
+\r
+       /**\r
+        * Executes the Start-Commit-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param message a commit message\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the commit is about to be done on.\r
+        * The string %MESSAGEFILE% is replaced with path to temporary file containing\r
+        * \c message. If the script finishes successfully, contents of this file\r
+        * is read back into \c message parameter.\r
+        */\r
+       bool                            StartCommit(const CTSVNPathList& pathList, CString& message,\r
+                                                                       DWORD& exitcode, CString& error);\r
+       /**\r
+        * Executes the Pre-Commit-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param depth the depth of the commit\r
+        * \param message the commit message\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the update is about to be done on.\r
+        * The string "%DEPTH%" is replaced with the numerical value (string) of the\r
+        * svn_depth_t parameter. See the Subversion source documentation about the\r
+        * values.\r
+        */\r
+       bool                            PreCommit(const CTSVNPathList& pathList, svn_depth_t depth, \r
+                                                                       const CString& message, DWORD& exitcode, \r
+                                                                       CString& error);\r
+       /**\r
+        * Executes the Post-Commit-Hook that first matches one of the paths in\r
+        * \c pathList.\r
+        * \param pathList a list of paths to look for the hook scripts\r
+        * \param depth the depth of the commit\r
+        * \param message the commit message\r
+        * \param rev the revision the commit was done to\r
+        * \param exitcode on return, contains the exit code of the hook script\r
+        * \param error the data the hook script outputs to stderr\r
+        * \remark the string "%PATHS% in the command line of the hook script is \r
+        * replaced with the path to a temporary file which contains a list of files\r
+        * in \c pathList, separated by newlines. The hook script can parse this\r
+        * file to get all the paths the commit is about to be done on.\r
+        * The string "%DEPTH%" is replaced with the numerical value (string) of the\r
+        * svn_depth_t parameter. See the Subversion source documentation about the\r
+        * values.\r
+        */\r
+       bool                            PostCommit(const CTSVNPathList& pathList, svn_depth_t depth, \r
+                                                                       SVNRev rev, const CString& message, \r
+                                                                       DWORD& exitcode, CString& error);\r
+\r
+private:\r
+       /**\r
+        * Starts a new process, specified in \c cmd.\r
+        * \param error the data the process writes to stderr\r
+        * \param bWait if true, then this method waits until the created process has finished. If false, then the return\r
+        * value will always be 0 and \c error will be an empty string.\r
+        * \param bShow set to true if the process should be started visible.\r
+        * \return the exit code of the process if \c bWait is true, zero otherwise.\r
+        */\r
+       DWORD                           RunScript(CString cmd, LPCTSTR currentDir, CString& error, bool bWait, bool bShow);\r
+       /**\r
+        * Find the hook script information for the hook type \c t which matches a\r
+        * path in \c pathList.\r
+        */\r
+       hookiterator            FindItem(hooktype t, const CTSVNPathList& pathList);\r
+       static CHooks *         m_pInstance;\r
+};\r
diff --git a/Utils/LangDll.cpp b/Utils/LangDll.cpp
new file mode 100644 (file)
index 0000000..7036af0
--- /dev/null
@@ -0,0 +1,143 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006,2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include <assert.h>\r
+#include "LangDll.h"\r
+#include "..\version.h"\r
+\r
+#pragma comment(lib, "Version.lib")\r
+\r
+CLangDll::CLangDll()\r
+{\r
+       m_hInstance = NULL;\r
+}\r
+\r
+CLangDll::~CLangDll()\r
+{\r
+\r
+}\r
+\r
+HINSTANCE CLangDll::Init(LPCTSTR appname, unsigned long langID)\r
+{\r
+       TCHAR langpath[MAX_PATH];\r
+       TCHAR langdllpath[MAX_PATH];\r
+       TCHAR sVer[MAX_PATH];\r
+       _tcscpy_s(sVer, MAX_PATH, _T(STRPRODUCTVER));\r
+       GetModuleFileName(NULL, langpath, MAX_PATH);\r
+       TCHAR * pSlash = _tcsrchr(langpath, '\\');\r
+       if (pSlash)\r
+       {\r
+               *pSlash = 0;\r
+               pSlash = _tcsrchr(langpath, '\\');\r
+               if (pSlash)\r
+               {\r
+                       *pSlash = 0;\r
+                       _tcscat_s(langpath, MAX_PATH, _T("\\Languages\\"));\r
+                       assert(m_hInstance == NULL);\r
+                       do\r
+                       {\r
+                               _stprintf_s(langdllpath, MAX_PATH, _T("%s%s%d.dll"), langpath, appname, langID);\r
+\r
+                               m_hInstance = LoadLibrary(langdllpath);\r
+\r
+                               if (!DoVersionStringsMatch(sVer, langdllpath))\r
+                               {\r
+                                       FreeLibrary(m_hInstance);\r
+                                       m_hInstance = NULL;\r
+                               }\r
+                               if (m_hInstance == NULL)\r
+                               {\r
+                                       DWORD lid = SUBLANGID(langID);\r
+                                       lid--;\r
+                                       if (lid > 0)\r
+                                       {\r
+                                               langID = MAKELANGID(PRIMARYLANGID(langID), lid);\r
+                                       }\r
+                                       else\r
+                                               langID = 0;\r
+                               }\r
+                       } while ((m_hInstance == NULL) && (langID != 0));\r
+               }\r
+       }\r
+       return m_hInstance;\r
+}\r
+\r
+void CLangDll::Close()\r
+{\r
+       if (m_hInstance)\r
+       {\r
+               FreeLibrary(m_hInstance);\r
+               m_hInstance = NULL;\r
+       }\r
+}\r
+\r
+bool CLangDll::DoVersionStringsMatch(LPCTSTR sVer, LPCTSTR langDll)\r
+{\r
+       struct TRANSARRAY\r
+       {\r
+               WORD wLanguageID;\r
+               WORD wCharacterSet;\r
+       };\r
+\r
+       bool bReturn = false;\r
+       DWORD dwReserved,dwBufferSize;\r
+       dwBufferSize = GetFileVersionInfoSize((LPTSTR)langDll,&dwReserved);\r
+\r
+       if (dwBufferSize > 0)\r
+       {\r
+               LPVOID pBuffer = (void*) malloc(dwBufferSize);\r
+\r
+               if (pBuffer != (void*) NULL)\r
+               {\r
+                       UINT        nInfoSize = 0,\r
+                               nFixedLength = 0;\r
+                       LPSTR       lpVersion = NULL;\r
+                       VOID*       lpFixedPointer;\r
+                       TRANSARRAY* lpTransArray;\r
+                       TCHAR       strLangProduktVersion[MAX_PATH];\r
+\r
+                       GetFileVersionInfo((LPTSTR)langDll,\r
+                               dwReserved,\r
+                               dwBufferSize,\r
+                               pBuffer);\r
+\r
+                       VerQueryValue(  pBuffer,\r
+                               _T("\\VarFileInfo\\Translation"),\r
+                               &lpFixedPointer,\r
+                               &nFixedLength);\r
+                       lpTransArray = (TRANSARRAY*) lpFixedPointer;\r
+\r
+                       _stprintf_s(strLangProduktVersion, MAX_PATH, \r
+                                               _T("\\StringFileInfo\\%04x%04x\\ProductVersion"),\r
+                                               lpTransArray[0].wLanguageID,\r
+                                               lpTransArray[0].wCharacterSet);\r
+\r
+                       VerQueryValue(pBuffer,\r
+                               (LPTSTR)strLangProduktVersion,\r
+                               (LPVOID *)&lpVersion,\r
+                               &nInfoSize);\r
+\r
+                       bReturn = (_tcscmp(sVer, (LPCTSTR)lpVersion)==0);\r
+                       free(pBuffer);\r
+               }\r
+       } \r
+\r
+       return bReturn;\r
+}\r
+\r
diff --git a/Utils/LangDll.h b/Utils/LangDll.h
new file mode 100644 (file)
index 0000000..a33ecae
--- /dev/null
@@ -0,0 +1,36 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Helper class to load language dependent resource dlls.\r
+ */\r
+class CLangDll\r
+{\r
+public:\r
+       CLangDll();\r
+       ~CLangDll();\r
+\r
+       HINSTANCE       Init(LPCTSTR appname, unsigned long langID);\r
+       void            Close();\r
+private:\r
+       bool            DoVersionStringsMatch(LPCTSTR sVer, LPCTSTR langDll);\r
+       HINSTANCE       m_hInstance;\r
+};\r
diff --git a/Utils/MiscUI/Balloon.cpp b/Utils/MiscUI/Balloon.cpp
new file mode 100644 (file)
index 0000000..95501b7
--- /dev/null
@@ -0,0 +1,1491 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "Balloon.h"\r
+\r
+tagBALLOON_INFO::tagBALLOON_INFO()\r
+    : hIcon(NULL),                     \r
+         sBalloonTip(),\r
+         nMask(0),\r
+         nStyles(0),\r
+         nDirection(0),\r
+         nEffect(0),\r
+         nBehaviour(0),\r
+         crBegin(0),\r
+         crMid(0),\r
+         crEnd(0)\r
+{\r
+}\r
+\r
+CBalloon::CBalloon()\r
+    : m_nStyles (0)\r
+{\r
+       m_pParentWnd = NULL;\r
+       m_hCurrentWnd = NULL;\r
+       m_hDisplayedWnd = NULL;\r
+\r
+       m_rgnShadow.CreateRectRgn(0, 0, 1, 1);\r
+       m_rgnBalloon.CreateRectRgn(0, 0, 1, 1);\r
+\r
+       m_ptOriginal.x = -1;\r
+       m_ptOriginal.y = -1;\r
+\r
+       SetDelayTime(TTDT_INITIAL, 500);\r
+       SetDelayTime(TTDT_AUTOPOP, 30000);\r
+       SetNotify(FALSE);\r
+       SetDirection();\r
+       SetBehaviour();\r
+       SetDefaultStyles();\r
+       SetDefaultColors();\r
+       SetDefaultSizes();\r
+       SetEffectBk(BALLOON_EFFECT_SOLID);\r
+       RemoveAllTools();\r
+       m_bButtonPushed = FALSE;\r
+\r
+       // Register the window class if it has not already been registered.\r
+       WNDCLASS wndcls;\r
+       HINSTANCE hInst = AfxGetInstanceHandle();\r
+       if(!(::GetClassInfo(hInst, BALLOON_CLASSNAME, &wndcls)))\r
+       {\r
+               // otherwise we need to register a new class\r
+               wndcls.style                    = CS_SAVEBITS;\r
+               wndcls.lpfnWndProc              = ::DefWindowProc;\r
+               wndcls.cbClsExtra               = wndcls.cbWndExtra = 0;\r
+               wndcls.hInstance                = hInst;\r
+               wndcls.hIcon                    = NULL;\r
+               wndcls.hCursor                  = LoadCursor(hInst, IDC_ARROW );\r
+               wndcls.hbrBackground    = NULL;\r
+               wndcls.lpszMenuName             = NULL;\r
+               wndcls.lpszClassName    = BALLOON_CLASSNAME;\r
+\r
+               if (!AfxRegisterClass(&wndcls))\r
+                       AfxThrowResourceException();\r
+       }\r
+}\r
+\r
+CBalloon::~CBalloon()\r
+{\r
+       RemoveAllTools();\r
+\r
+       m_rgnBalloon.DeleteObject();\r
+       m_rgnShadow.DeleteObject();\r
+\r
+       if (IsWindow(m_hWnd))\r
+        DestroyWindow();\r
+}\r
+\r
+\r
+BEGIN_MESSAGE_MAP(CBalloon, CWnd)\r
+       //{{AFX_MSG_MAP(CBalloon)\r
+       ON_WM_PAINT()\r
+       ON_WM_TIMER()\r
+       ON_WM_DESTROY()\r
+       ON_WM_KILLFOCUS()\r
+       //}}AFX_MSG_MAP\r
+       ON_WM_MOUSEMOVE()\r
+       ON_WM_LBUTTONDOWN()\r
+       ON_WM_LBUTTONUP()\r
+END_MESSAGE_MAP()\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+// CBalloon message handlers\r
+\r
+BOOL CBalloon::Create(CWnd* pParentWnd) \r
+{\r
+       DWORD dwStyle = WS_POPUP; \r
+       DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;\r
+\r
+       m_pParentWnd = pParentWnd;\r
+\r
+       if (!CreateEx(dwExStyle, BALLOON_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, m_pParentWnd->GetSafeHwnd(), NULL, NULL))\r
+       {\r
+               return FALSE;\r
+       }\r
+       SetDefaultFont();\r
+       \r
+       return TRUE;\r
+}\r
+\r
+void CBalloon::OnDestroy() \r
+{\r
+       TRACE("OnDestroy()\n");\r
+       KillTimers();\r
+       \r
+       CWnd::OnDestroy();\r
+}\r
+\r
+\r
+\r
+void CBalloon::OnKillFocus(CWnd* pNewWnd) \r
+{\r
+       CWnd::OnKillFocus(pNewWnd);\r
+       Pop();\r
+}\r
+\r
+\r
+BOOL CBalloon::PreTranslateMessage(MSG* pMsg) \r
+{\r
+       RelayEvent(pMsg);\r
+\r
+       return CWnd::PreTranslateMessage(pMsg);\r
+}\r
+\r
+LRESULT CBalloon::SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi)\r
+{\r
+       //make sure this is a valid window\r
+       if (!IsWindow(GetSafeHwnd()))\r
+               return 0L;\r
+\r
+       //see if the user wants to be notified\r
+       if (!GetNotify())\r
+               return 0L;\r
+\r
+       NM_BALLOON_DISPLAY lpnm;\r
+       \r
+       lpnm.pWnd                 = pWnd;\r
+       lpnm.pt                   = pt;\r
+       lpnm.bi                   = &bi;\r
+       lpnm.hdr.hwndFrom = m_hWnd;\r
+    lpnm.hdr.idFrom   = GetDlgCtrlID();\r
+    lpnm.hdr.code     = UDM_TOOLTIP_DISPLAY;\r
+       \r
+       ::SendMessage(m_hNotifyWnd, WM_NOTIFY, lpnm.hdr.idFrom, (LPARAM)&lpnm);\r
+\r
+       return 0L;\r
+}\r
+\r
+void CBalloon::OnPaint() \r
+{\r
+       //if (!m_pCurrentWnd)\r
+       //      return;\r
+\r
+       m_hDisplayedWnd = m_hCurrentWnd;\r
+\r
+       CPaintDC dc(this); // device context for painting\r
+\r
+       CRect rect;\r
+       GetClientRect(&rect);\r
+       rect.DeflateRect(0, 0, 1, 1);\r
+\r
+       //create a memory device-context. This is done to help reduce\r
+       //screen flicker, since we will paint the entire control to the\r
+       //off screen device context first.CDC memDC;\r
+       CDC memDC;\r
+       CBitmap bitmap;\r
+       memDC.CreateCompatibleDC(&dc);\r
+       bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());\r
+       CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); \r
+       \r
+       memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);\r
+       \r
+       //draw the tooltip\r
+       OnDraw(&memDC, rect);\r
+\r
+       //Copy the memory device context back into the original DC.\r
+       dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY);\r
+       \r
+       //Cleanup resources.\r
+       memDC.SelectObject(pOldBitmap);\r
+       memDC.DeleteDC();\r
+       bitmap.DeleteObject(); \r
+}\r
+\r
+void CBalloon::OnDraw(CDC * pDC, CRect rect)\r
+{\r
+       CBrush brBackground(m_crColor [BALLOON_COLOR_BK_BEGIN]);\r
+       CBrush brShadow(m_crColor [BALLOON_COLOR_SHADOW]);\r
+       CBrush brBorder(m_crColor [BALLOON_COLOR_BORDER]);\r
+       \r
+       pDC->SetBkMode(TRANSPARENT); \r
+       pDC->SetTextColor(m_crColor [BALLOON_COLOR_FG]);\r
+       //set clip region of the tooltip and draw the shadow if needed\r
+       if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
+       {\r
+               //draw the shadow for the tooltip\r
+               int nRop2Mode = pDC->SetROP2(R2_MASKPEN);\r
+               pDC->FillRgn(&m_rgnShadow, &brShadow);\r
+               pDC->SetROP2(nRop2Mode);\r
+               rect.DeflateRect(0, 0, m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
+       }\r
+       pDC->SelectClipRgn(&m_rgnBalloon);\r
+\r
+       OnDrawBackground(pDC, &rect);\r
+\r
+       //draw the main region's border of the tooltip\r
+       pDC->FrameRgn(&m_rgnBalloon, &brBorder, m_nSizes[XBLSZ_BORDER_CX], m_nSizes[XBLSZ_BORDER_CY]);\r
+\r
+       if ((m_nLastDirection == BALLOON_RIGHT_BOTTOM) || (m_nLastDirection == BALLOON_LEFT_BOTTOM))\r
+               rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
+       else\r
+               rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
+\r
+       if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
+       {\r
+               m_rtCloseButton = CRect(\r
+                       rect.right - m_szCloseButton.cx - m_nSizes[XBLSZ_BUTTON_MARGIN_CX] , rect.top + m_nSizes[XBLSZ_BUTTON_MARGIN_CY], \r
+                       rect.right - m_nSizes[XBLSZ_BUTTON_MARGIN_CX], rect.top + m_szCloseButton.cy + m_nSizes[XBLSZ_BUTTON_MARGIN_CY]);\r
+               pDC->DrawFrameControl(m_rtCloseButton, DFC_CAPTION, DFCS_CAPTIONCLOSE|DFCS_FLAT|DFCS_TRANSPARENT);\r
+               rect.right -= (m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX]);\r
+       }\r
+\r
+       //get the rectangle to draw the tooltip text\r
+       rect.DeflateRect(m_nSizes[XBLSZ_MARGIN_CX], m_nSizes[XBLSZ_MARGIN_CY]);\r
+\r
+       //draw the icon\r
+       if (m_pToolInfo.hIcon != NULL)\r
+       {\r
+               DrawIconEx(pDC->m_hDC, m_nSizes[XBLSZ_MARGIN_CX], rect.top + (rect.Height() - m_szBalloonIcon.cy) / 2, \r
+                       m_pToolInfo.hIcon, m_szBalloonIcon.cx, m_szBalloonIcon.cy, 0, NULL, DI_NORMAL);\r
+\r
+               rect.left += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX]; \r
+       }\r
+\r
+\r
+       //aligns tool tip's text\r
+       if (m_pToolInfo.nStyles & BALLOON_BOTTOM_ALIGN)\r
+               rect.top = rect.bottom - m_szBalloonText.cy;\r
+       else if (m_pToolInfo.nStyles & BALLOON_VCENTER_ALIGN)\r
+               rect.top += (rect.Height() - m_szBalloonText.cy) / 2;\r
+\r
+       //prints the tool tip's text\r
+       DrawHTML(pDC, rect, m_pToolInfo.sBalloonTip, m_LogFont, FALSE);\r
+\r
+       //free resources\r
+       brBackground.DeleteObject();\r
+       brShadow.DeleteObject();\r
+       brBorder.DeleteObject();\r
+}\r
+\r
+void CBalloon::OnDrawBackground(CDC * pDC, CRect * pRect)\r
+{\r
+#ifdef USE_GDI_GRADIENT\r
+       #define DRAW CGradient::DrawGDI\r
+#else\r
+       #define DRAW CGradient::Draw\r
+#endif\r
+       switch (m_pToolInfo.nEffect)\r
+       {\r
+       case BALLOON_EFFECT_HGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END]);\r
+               break;\r
+       case BALLOON_EFFECT_VGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], FALSE);\r
+               break;\r
+       case BALLOON_EFFECT_HCGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN]);\r
+               break;\r
+       case BALLOON_EFFECT_VCGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN], FALSE);\r
+               break;\r
+       case BALLOON_EFFECT_3HGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END]);\r
+               break;\r
+       case BALLOON_EFFECT_3VGRADIENT:\r
+               DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END], FALSE);\r
+               break;\r
+#undef DRAW\r
+       default:\r
+               pDC->FillSolidRect(pRect, m_crColor[BALLOON_COLOR_BK_BEGIN]);\r
+               break;\r
+       }\r
+}\r
+\r
+CRect CBalloon::GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const\r
+{\r
+       CRect rect;\r
+       rect.SetRect(0, 0, sz.cx, sz.cy);\r
+       CRgn rgnRect;\r
+       CRgn rgnAnchor;\r
+       CPoint ptAnchor [3];\r
+       ptAnchor [0] = pt;\r
+       ScreenToClient(&ptAnchor [0]);\r
+\r
+       switch (m_nLastDirection)\r
+       {\r
+       case BALLOON_LEFT_TOP:\r
+       case BALLOON_RIGHT_TOP:\r
+               rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
+               ptAnchor [1].y = ptAnchor [2].y = rect.bottom;\r
+               break;\r
+       case BALLOON_LEFT_BOTTOM:\r
+       case BALLOON_RIGHT_BOTTOM:\r
+               rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
+               ptAnchor [1].y = ptAnchor [2].y = rect.top;\r
+               break;\r
+       }\r
+\r
+       //get the region for rectangle with the text\r
+       if (m_pToolInfo.nStyles & BALLOON_ROUNDED)\r
+               rgnRect.CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1, \r
+                       m_nSizes[XBLSZ_ROUNDED_CX], m_nSizes[XBLSZ_ROUNDED_CY]);\r
+       else rgnRect.CreateRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1);\r
+\r
+       //gets the region for the anchor\r
+       if (m_pToolInfo.nStyles & BALLOON_ANCHOR)\r
+       {\r
+               switch (m_nLastDirection)\r
+               {\r
+               case BALLOON_LEFT_TOP:\r
+               case BALLOON_LEFT_BOTTOM:\r
+                       ptAnchor [1].x = rect.right - m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
+                       ptAnchor [2].x = ptAnchor [1].x - m_nSizes[XBLSZ_WIDTH_ANCHOR];\r
+                       break;\r
+               case BALLOON_RIGHT_TOP:\r
+               case BALLOON_RIGHT_BOTTOM:\r
+                       ptAnchor [1].x = rect.left + m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
+                       ptAnchor [2].x = ptAnchor [1].x + m_nSizes[XBLSZ_WIDTH_ANCHOR];\r
+                       break;\r
+               }\r
+               rgnAnchor.CreatePolygonRgn(ptAnchor, 3, ALTERNATE);\r
+       }\r
+       else\r
+               rgnAnchor.CreateRectRgn(0, 0, 0, 0);\r
+       \r
+       rgn->CreateRectRgn(0, 0, 1, 1);\r
+       rgn->CombineRgn(&rgnRect, &rgnAnchor, RGN_OR);\r
+       \r
+       rgnAnchor.DeleteObject();\r
+       rgnRect.DeleteObject();\r
+\r
+       return rect;\r
+}\r
+\r
+void CBalloon::RelayEvent(MSG* pMsg)\r
+{\r
+       HWND hWnd = NULL;\r
+       CPoint pt;\r
+       CString str;\r
+       CRect rect;\r
+\r
+       BALLOON_INFO  Info;\r
+               \r
+       switch(pMsg->message)\r
+       {\r
+       case WM_MOUSEMOVE:\r
+               if (m_ptOriginal == pMsg->pt)\r
+                       return;\r
+\r
+               m_ptOriginal = pMsg->pt; \r
+               \r
+               //get the real window under the mouse pointer\r
+               pt = pMsg->pt;\r
+               if (m_pParentWnd)\r
+                       m_pParentWnd->ScreenToClient(&pt);\r
+               hWnd = GetChildWindowFromPoint(pt);\r
+\r
+               if (!hWnd)\r
+               {\r
+                       if (!(GetBehaviour() & BALLOON_DIALOG))\r
+                       {\r
+                               Pop();\r
+                               m_hCurrentWnd = NULL;\r
+                               m_hDisplayedWnd = NULL;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       UINT behaviour = GetBehaviour(CWnd::FromHandle(hWnd));\r
+                       if (hWnd == m_hDisplayedWnd)\r
+                       {\r
+                               if (IsWindowVisible())\r
+                               {\r
+                                       if ((behaviour & BALLOON_TRACK_MOUSE)&&(!(behaviour & BALLOON_DIALOG)))\r
+                                       {\r
+                                               //mouse moved, so move the tooltip too\r
+                                               CRect rect;\r
+                                               GetWindowRect(rect);\r
+                                               CalculateInfoBoxRect(&m_ptOriginal, &rect);\r
+                                               SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);\r
+                                       }\r
+                                       else\r
+                                               return;\r
+                               }\r
+                               else if ((behaviour & BALLOON_MULTIPLE_SHOW)&&(!(behaviour & BALLOON_DIALOG)))\r
+                               {\r
+                                       SetNewToolTip(CWnd::FromHandle(hWnd));\r
+                               }\r
+                               else\r
+                                       Pop();\r
+                       }\r
+                       else\r
+                       {\r
+                               SetNewToolTip(CWnd::FromHandle(hWnd));\r
+                       }\r
+               }\r
+               break;\r
+       }\r
+}\r
+\r
+void CBalloon::SetNewToolTip(CWnd * pWnd)\r
+{\r
+       m_hDisplayedWnd = NULL;\r
+       Pop();\r
+\r
+       if (!pWnd->IsWindowEnabled())\r
+               return;\r
+\r
+       if (!GetTool(pWnd, m_pToolInfo))\r
+               return;\r
+\r
+       m_hCurrentWnd = pWnd->GetSafeHwnd();\r
+\r
+       SetTimer(BALLOON_SHOW, m_nTimeInitial, NULL);\r
+}\r
+\r
+void CBalloon::OnTimer(UINT_PTR nIDEvent) \r
+{\r
+       CPoint pt, point;\r
+       CString str;\r
+       HWND hWnd;\r
+\r
+       switch (nIDEvent)\r
+       {\r
+       case BALLOON_SHOW:\r
+               KillTimers(BALLOON_SHOW);\r
+               //check if mouse pointer is still over the right window\r
+               GetCursorPos(&pt);\r
+               point = pt;\r
+               if (m_pParentWnd)\r
+                       m_pParentWnd->ScreenToClient(&point);\r
+               hWnd = GetChildWindowFromPoint(point);\r
+               if (hWnd == m_hCurrentWnd)\r
+               {\r
+                       DisplayToolTip(&pt);\r
+                       SetTimer(BALLOON_HIDE, m_nTimeAutoPop, NULL);\r
+               }\r
+               break;\r
+       case BALLOON_HIDE:\r
+               KillTimers(BALLOON_HIDE);\r
+               Pop();\r
+               if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
+               {\r
+                       CWnd::OnTimer(nIDEvent);\r
+                       DestroyWindow();\r
+                       return;\r
+               }\r
+               break;\r
+       }\r
+       \r
+       CWnd::OnTimer(nIDEvent);\r
+}\r
+\r
+HWND CBalloon::GetChildWindowFromPoint(CPoint & point) const\r
+{\r
+       if (!m_pParentWnd)\r
+               return NULL;\r
+    CPoint pt = point;\r
+    m_pParentWnd->ClientToScreen(&pt);\r
+    HWND hWnd = ::WindowFromPoint(pt);\r
+\r
+    //::WindowFromPoint misses disabled windows and such - go for a more\r
+    //comprehensive search in this case.\r
+    if (hWnd == m_pParentWnd->GetSafeHwnd())\r
+        hWnd = m_pParentWnd->ChildWindowFromPoint(point, CWP_ALL)->GetSafeHwnd();\r
+\r
+    //check that we aren't over the parent or out of client area\r
+    if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd())\r
+        return NULL;\r
+\r
+    //if it's not part of the main parent window hierarchy, then we are\r
+    //not interested\r
+    if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd))\r
+        return NULL;\r
+\r
+    return hWnd;\r
+}\r
+\r
+BOOL CBalloon::IsCursorInToolTip() const\r
+{\r
+    if (!IsVisible() || !IsWindow(m_hWnd))\r
+          return FALSE;\r
+\r
+    CPoint pt;\r
+    GetCursorPos(&pt);\r
+\r
+       CBalloon * pWnd = (CBalloon*)WindowFromPoint(pt);\r
+\r
+       return (pWnd == this);\r
+}\r
+\r
+void CBalloon::KillTimers(UINT nIDTimer /* = NULL */)\r
+{\r
+       if (nIDTimer == NULL)\r
+       {\r
+               KillTimer(BALLOON_SHOW);\r
+               KillTimer(BALLOON_HIDE);\r
+       }\r
+       else if (nIDTimer == BALLOON_SHOW)\r
+               KillTimer(BALLOON_SHOW);\r
+       else if (nIDTimer == BALLOON_HIDE)\r
+               KillTimer(BALLOON_HIDE);\r
+}\r
+\r
+void CBalloon::DisplayToolTip(CPoint * pt /* = NULL */)\r
+{\r
+       if(!GetTool(CWnd::FromHandle(m_hCurrentWnd), m_pToolInfo) || m_pToolInfo.sBalloonTip.IsEmpty())\r
+               return;\r
+       //if a mask is set then use the default values for the tooltip\r
+       if (!(m_pToolInfo.nMask & BALLOON_MASK_STYLES))\r
+               m_pToolInfo.nStyles = m_nStyles;\r
+       if (!(m_pToolInfo.nMask & BALLOON_MASK_EFFECT))\r
+       {\r
+               m_pToolInfo.nEffect = m_nEffect;\r
+       }\r
+       if (!(m_pToolInfo.nMask & BALLOON_MASK_COLORS))\r
+       {\r
+               m_pToolInfo.crBegin = m_crColor[BALLOON_COLOR_BK_BEGIN];\r
+               m_pToolInfo.crMid = m_crColor[BALLOON_COLOR_BK_MID];\r
+               m_pToolInfo.crEnd = m_crColor[BALLOON_COLOR_BK_END];\r
+       }\r
+       if (!(m_pToolInfo.nMask & BALLOON_MASK_DIRECTION))\r
+               m_pToolInfo.nDirection = m_nDirection;\r
+\r
+       //send notification\r
+       SendNotify(CWnd::FromHandle(m_hCurrentWnd), pt, m_pToolInfo);\r
+\r
+       //calculate the width and height of the box dynamically\r
+    CSize sz = GetTooltipSize(m_pToolInfo.sBalloonTip);\r
+       m_szBalloonText = sz;\r
+       \r
+       //get the size of the current icon\r
+       m_szBalloonIcon = GetSizeIcon(m_pToolInfo.hIcon);\r
+       if (m_szBalloonIcon.cx || m_szBalloonIcon.cy)\r
+       {\r
+               sz.cx += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX];\r
+               sz.cy = max(m_szBalloonIcon.cy, sz.cy);\r
+       }\r
+\r
+       //get the size of the close button\r
+       m_szCloseButton = CSize(::GetSystemMetrics(SM_CXMENUSIZE), ::GetSystemMetrics(SM_CYMENUSIZE));\r
+       if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
+       {\r
+               sz.cx += m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX];\r
+       }\r
+\r
+       //get size of the tooltip with margins\r
+       sz.cx += m_nSizes[XBLSZ_MARGIN_CX] * 2;\r
+       sz.cy += m_nSizes[XBLSZ_MARGIN_CY] * 2 + m_nSizes[XBLSZ_HEIGHT_ANCHOR];\r
+       if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
+       {\r
+               sz.cx += m_nSizes[XBLSZ_SHADOW_CX];\r
+               sz.cy += m_nSizes[XBLSZ_SHADOW_CY];\r
+       }\r
+       \r
+       CRect rect (0, 0, sz.cx, sz.cy);\r
+       \r
+       DisplayToolTip(pt, &rect);\r
+}\r
+\r
+void CBalloon::DisplayToolTip(CPoint * pt, CRect * rect)\r
+{\r
+       CalculateInfoBoxRect(pt, rect);\r
+\r
+       SetWindowPos(\r
+               NULL, rect->left, rect->top, rect->Width() + 2, rect->Height() + 2,\r
+               SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER);\r
+\r
+       CRgn rgn;\r
+       rgn.CreateRectRgn(0, 0, 1, 1);\r
+       if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
+       {\r
+               rect->right -= m_nSizes[XBLSZ_SHADOW_CX];\r
+               rect->bottom -= m_nSizes[XBLSZ_SHADOW_CY];\r
+       }\r
+\r
+       m_rgnBalloon.DeleteObject();\r
+       GetWindowRegion(&m_rgnBalloon, CSize (rect->Width(), rect->Height()), *pt);\r
+       rgn.CopyRgn(&m_rgnBalloon);\r
+       if (m_pToolInfo.nStyles & BALLOON_SHADOW)\r
+       {\r
+               m_rgnShadow.DeleteObject();\r
+               m_rgnShadow.CreateRectRgn(0, 0, 1, 1);\r
+               m_rgnShadow.CopyRgn(&m_rgnBalloon);\r
+               m_rgnShadow.OffsetRgn(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
+               rgn.CombineRgn(&rgn, &m_rgnShadow, RGN_OR);\r
+       }\r
+       SetWindowRgn(rgn, FALSE);\r
+\r
+       rgn.DeleteObject();\r
+}\r
+\r
+void CBalloon::Pop()\r
+{\r
+       KillTimers();   \r
+       ShowWindow(SW_HIDE);\r
+       m_bButtonPushed = FALSE;\r
+}\r
+\r
+CSize CBalloon::GetTooltipSize(const CString& str)\r
+{\r
+       CRect rect;\r
+       GetWindowRect(&rect);\r
+\r
+       CDC * pDC = GetDC();\r
+\r
+       CDC memDC;\r
+       CBitmap bitmap;\r
+       memDC.CreateCompatibleDC(pDC);\r
+       bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());\r
+       CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);\r
+\r
+       //get the minimum size of the rectangle of the tooltip\r
+       CSize sz = DrawHTML(&memDC, rect, str, m_LogFont, TRUE);\r
+\r
+       memDC.SelectObject(pOldBitmap);\r
+       memDC.DeleteDC();\r
+       bitmap.DeleteObject();\r
+\r
+       ReleaseDC(pDC);\r
+\r
+       return sz;\r
+}\r
+\r
+CSize CBalloon::GetSizeIcon(HICON hIcon) const\r
+{\r
+       ICONINFO ii;\r
+       CSize sz (0, 0);\r
+\r
+       if (hIcon != NULL)\r
+       {\r
+               //get icon dimensions\r
+               ::SecureZeroMemory(&ii, sizeof(ICONINFO));\r
+               if (::GetIconInfo(hIcon, &ii))\r
+               {\r
+                       sz.cx = (DWORD)(ii.xHotspot * 2);\r
+                       sz.cy = (DWORD)(ii.yHotspot * 2);\r
+                       //release icon mask bitmaps\r
+                       if(ii.hbmMask)\r
+                               ::DeleteObject(ii.hbmMask);\r
+                       if(ii.hbmColor)\r
+                               ::DeleteObject(ii.hbmColor);\r
+               }\r
+       }\r
+       return sz;\r
+}\r
+\r
+void CBalloon::CalculateInfoBoxRect(CPoint * pt, CRect * rect)\r
+{\r
+       CRect monitorRect;\r
+       GetMonitorWorkArea(*pt, monitorRect);\r
+\r
+       CPoint ptEnd;\r
+       m_nLastDirection = m_pToolInfo.nDirection;\r
+       BOOL horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);\r
+       if (!horzAdjusted)\r
+       {\r
+               m_nLastDirection = GetNextHorizDirection(m_nLastDirection);\r
+               horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect);\r
+       }\r
+       BOOL vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);\r
+       if (!vertAdjusted)\r
+       {\r
+               m_nLastDirection = GetNextVertDirection(m_nLastDirection);\r
+               vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect);\r
+       }\r
+       // in case the rectangle wasn't adjusted which can happen if the tooltip is\r
+       // larger than half the monitor size, we center the tooltip around the mouse pointer\r
+       if (!horzAdjusted)\r
+       {\r
+               int cx = rect->Width() / 2;\r
+               rect->right = pt->x + cx;\r
+               rect->left = pt->x - cx;\r
+       }\r
+       if (!vertAdjusted)\r
+       {\r
+               int cy = rect->Height() / 2;\r
+               rect->bottom = pt->y + cy;\r
+               rect->top = pt->y - cy;\r
+       }\r
+       if ((m_pToolInfo.nStyles & BALLOON_SHADOW) && \r
+               ((m_nLastDirection == BALLOON_LEFT_TOP) || (m_nLastDirection == BALLOON_LEFT_BOTTOM)))\r
+               rect->OffsetRect(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]);\r
+}\r
+\r
+\r
+int CBalloon::GetNextHorizDirection(int nDirection) const\r
+{\r
+       switch (nDirection)\r
+       {\r
+       case BALLOON_LEFT_TOP:\r
+               nDirection = BALLOON_RIGHT_TOP;\r
+               break;\r
+       case BALLOON_RIGHT_TOP:\r
+               nDirection = BALLOON_LEFT_TOP;\r
+               break;\r
+       case BALLOON_LEFT_BOTTOM:\r
+               nDirection = BALLOON_RIGHT_BOTTOM;\r
+               break;\r
+       case BALLOON_RIGHT_BOTTOM:\r
+               nDirection = BALLOON_LEFT_BOTTOM;\r
+               break;\r
+       }\r
+       return nDirection;\r
+}\r
+\r
+int CBalloon::GetNextVertDirection(int nDirection) const\r
+{\r
+       switch (nDirection)\r
+       {\r
+       case BALLOON_LEFT_TOP:\r
+               nDirection = BALLOON_LEFT_BOTTOM;\r
+               break;\r
+       case BALLOON_LEFT_BOTTOM:\r
+               nDirection = BALLOON_LEFT_TOP;\r
+               break;\r
+       case BALLOON_RIGHT_TOP:\r
+               nDirection = BALLOON_RIGHT_BOTTOM;\r
+               break;\r
+       case BALLOON_RIGHT_BOTTOM:\r
+               nDirection = BALLOON_RIGHT_TOP;\r
+               break;\r
+       }\r
+       return nDirection;\r
+}\r
+\r
+BOOL CBalloon::TestHorizDirection(int x, int cx, const CRect& monitorRect,\r
+                                                                 int nDirection, LPRECT rect)\r
+{\r
+       int left = 0;\r
+       int right = 0;\r
+       int anchorMarginSize = (int)m_nSizes[XBLSZ_MARGIN_ANCHOR];\r
+\r
+       switch (nDirection)\r
+       {\r
+       case BALLOON_LEFT_TOP:\r
+       case BALLOON_LEFT_BOTTOM:\r
+               right = ((x + anchorMarginSize) > monitorRect.right) ? monitorRect.right : (x + anchorMarginSize);\r
+               left = right - cx;\r
+               break;\r
+       case BALLOON_RIGHT_TOP:\r
+       case BALLOON_RIGHT_BOTTOM:\r
+               left = (x - anchorMarginSize)<monitorRect.left ? monitorRect.left : (x - anchorMarginSize);\r
+               right = left + cx;\r
+               break;\r
+       }\r
+\r
+       BOOL bTestOk = ((left >= monitorRect.left) && (right <= monitorRect.right)) ? TRUE : FALSE;\r
+       if (bTestOk)\r
+       {\r
+               rect->left = left;\r
+               rect->right = right;\r
+       }\r
+\r
+       return bTestOk;\r
+}\r
+\r
+BOOL CBalloon::TestVertDirection(int y, int cy, const CRect& monitorRect,\r
+                                                                int nDirection, LPRECT rect)\r
+{\r
+       int top = 0;\r
+       int bottom = 0;\r
+\r
+       switch (nDirection)\r
+       {\r
+       case BALLOON_LEFT_TOP:\r
+       case BALLOON_RIGHT_TOP:\r
+               bottom = y;\r
+               top = bottom - cy;\r
+               break;\r
+       case BALLOON_LEFT_BOTTOM:\r
+       case BALLOON_RIGHT_BOTTOM:\r
+               top = y;\r
+               bottom = top + cy;\r
+               break;\r
+       }\r
+\r
+       BOOL bTestOk = ((top >= monitorRect.top) && (bottom <= monitorRect.bottom)) ? TRUE : FALSE;\r
+       if (bTestOk)\r
+       {\r
+               rect->top = top;\r
+               rect->bottom = bottom;\r
+       }\r
+\r
+       return bTestOk;\r
+}\r
+\r
+LPLOGFONT CBalloon::GetSystemToolTipFont() const\r
+{\r
+    static LOGFONT LogFont;\r
+\r
+    NONCLIENTMETRICS ncm;\r
+    ncm.cbSize = sizeof(NONCLIENTMETRICS);\r
+    if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))\r
+        return FALSE;\r
+\r
+    memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT));\r
+\r
+    return &LogFont; \r
+}\r
+\r
+\r
+void CBalloon::Redraw(BOOL /*bRedraw*/ /* = TRUE */)\r
+{\r
+}\r
+\r
+void CBalloon::SetStyles(DWORD nStyles, CWnd * pWnd /* = NULL */)\r
+{\r
+       ModifyStyles(nStyles, (DWORD)-1, pWnd);\r
+}\r
+\r
+void CBalloon::ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd /* = NULL */)\r
+{\r
+       if (!pWnd)\r
+       {\r
+               m_nStyles &= ~nRemoveStyles;\r
+               m_nStyles |= nAddStyles;\r
+       }\r
+       else\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (!(bi.nMask & BALLOON_MASK_STYLES))\r
+                               bi.nStyles = m_nStyles;\r
+                       bi.nStyles &= ~nRemoveStyles;\r
+                       bi.nStyles |= nAddStyles;\r
+                       bi.nMask |= BALLOON_MASK_STYLES;\r
+                       AddTool(pWnd, bi);\r
+               }\r
+       }\r
+} \r
+\r
+DWORD CBalloon::GetStyles(CWnd * pWnd /* = NULL */) const\r
+{\r
+       if (pWnd)\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (bi.nMask & BALLOON_MASK_STYLES)\r
+                               return bi.nStyles;\r
+               }\r
+       }\r
+       return m_nStyles;\r
+} \r
+\r
+void CBalloon::SetDefaultStyles(CWnd * pWnd /* = NULL */)\r
+{\r
+       SetStyles(BALLOON_RSA, pWnd);\r
+}\r
+\r
+void CBalloon::SetColor(int nIndex, COLORREF crColor)\r
+{\r
+       if (nIndex >= BALLOON_MAX_COLORS)\r
+               return;\r
+\r
+       m_crColor [nIndex] = crColor;\r
+}\r
+\r
+COLORREF CBalloon::GetColor(int nIndex) const\r
+{\r
+       if (nIndex >= BALLOON_MAX_COLORS)\r
+               nIndex = BALLOON_COLOR_FG;\r
+\r
+       return m_crColor [nIndex];\r
+}\r
+\r
+void CBalloon::SetDefaultColors()\r
+{\r
+       SetColor(BALLOON_COLOR_FG, ::GetSysColor(COLOR_INFOTEXT));\r
+       SetColor(BALLOON_COLOR_BK_BEGIN, ::GetSysColor(COLOR_INFOBK));\r
+       SetColor(BALLOON_COLOR_BK_MID, ::GetSysColor(COLOR_INFOBK));\r
+       SetColor(BALLOON_COLOR_BK_END, ::GetSysColor(COLOR_INFOBK));\r
+       SetColor(BALLOON_COLOR_SHADOW, ::GetSysColor(COLOR_3DSHADOW));\r
+       SetColor(BALLOON_COLOR_BORDER, ::GetSysColor(COLOR_WINDOWFRAME));\r
+}\r
+\r
+void CBalloon::SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd /* = NULL */)\r
+{\r
+       if (!pWnd)\r
+       {\r
+               SetColor(BALLOON_COLOR_BK_BEGIN, crBegin);\r
+               SetColor(BALLOON_COLOR_BK_MID, crMid);\r
+               SetColor(BALLOON_COLOR_BK_END, crEnd);\r
+       }\r
+       else\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       bi.crBegin = crBegin;\r
+                       bi.crMid = crMid;\r
+                       bi.crEnd = crEnd;\r
+                       bi.nMask |= BALLOON_MASK_COLORS;\r
+                       AddTool(pWnd, bi);\r
+               }\r
+       }\r
+}\r
+\r
+void CBalloon::GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd /* = NULL */) const\r
+{\r
+       if (pWnd)\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (bi.nMask & BALLOON_MASK_COLORS)\r
+                       {\r
+                               crBegin = bi.crBegin;\r
+                               crMid = bi.crMid;\r
+                               crEnd = bi.crEnd;\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       crBegin = GetColor(BALLOON_COLOR_BK_BEGIN);\r
+       crMid = GetColor(BALLOON_COLOR_BK_MID);\r
+       crEnd = GetColor(BALLOON_COLOR_BK_END);\r
+} \r
+\r
+void CBalloon::ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon)\r
+{\r
+       CString str;\r
+       str.LoadString(nIdText);\r
+       HICON hIcon = (HICON)::LoadImage(NULL, szIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);\r
+       ShowBalloon(pWnd, pt, str, showCloseButton, hIcon);\r
+}\r
+\r
+void CBalloon::ShowBalloon(\r
+       CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon, \r
+       UINT nDirection, UINT nEffect, COLORREF crStart, COLORREF crMid, COLORREF crEnd)\r
+{\r
+       BALLOON_INFO Info;\r
+       Info.hIcon = hIcon;\r
+       Info.sBalloonTip = sText;\r
+       Info.nMask = 0;\r
+\r
+       CBalloon * pSB = new CBalloon();\r
+       if (pWnd == NULL)\r
+               pWnd = GetDesktopWindow();\r
+       pSB->Create(pWnd);\r
+       pSB->AddTool(pWnd, Info);\r
+       pSB->m_hCurrentWnd = pWnd->GetSafeHwnd();\r
+       pSB->SetDirection(nDirection);\r
+       pSB->SetEffectBk(nEffect);\r
+       if (crStart == NULL)\r
+               crStart = ::GetSysColor(COLOR_INFOBK);\r
+       if (crMid == NULL)\r
+               crMid = ::GetSysColor(COLOR_INFOBK);\r
+       if (crEnd == NULL)\r
+               crEnd = ::GetSysColor(COLOR_INFOBK);\r
+       pSB->SetGradientColors(crStart, crMid, crEnd);\r
+       pSB->SetBehaviour(BALLOON_DIALOG | BALLOON_DIALOG_DESTROY);\r
+       if (showCloseButton)\r
+       {\r
+               pSB->ModifyStyles(BALLOON_CLOSEBUTTON, 0);\r
+       }\r
+       pSB->DisplayToolTip(&pt);\r
+       if (!showCloseButton)\r
+               pSB->SetTimer(BALLOON_HIDE, 5000, NULL); //auto close the dialog if no close button is shown\r
+}\r
+\r
+void CBalloon::AddTool(int nIdWnd, UINT nIdText, HICON hIcon/* = NULL*/)\r
+{\r
+       AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, hIcon);\r
+}\r
+void CBalloon::AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon)\r
+{\r
+       AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, nIdIcon);\r
+}\r
+void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon/* = NULL*/)\r
+{\r
+       AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, hIcon);\r
+}\r
+void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon)\r
+{\r
+       AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, nIdIcon);\r
+}\r
+\r
+void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon /* = NULL */)\r
+{\r
+       CString str;\r
+    str.LoadString(nIdText);\r
+       AddTool(pWnd, str, hIcon);\r
+}\r
+\r
+void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon)\r
+{\r
+       CString str;\r
+    str.LoadString(nIdText);\r
+       AddTool(pWnd, str, nIdIcon);\r
+} \r
+\r
+void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon)\r
+{\r
+       HICON hIcon     = NULL;\r
+       HINSTANCE hInstResource = NULL;\r
+\r
+       if (nIdIcon >= 0)\r
+       {\r
+               hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIdIcon), RT_GROUP_ICON);\r
+               \r
+               hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIdIcon), IMAGE_ICON, 0, 0, 0);\r
+       }\r
+\r
+       AddTool(pWnd, sBalloonTipText, hIcon);\r
+} \r
+\r
+void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon /* = NULL */)\r
+{\r
+       //store the tool information\r
+       BALLOON_INFO Info;\r
+       Info.hIcon = hIcon;\r
+       Info.sBalloonTip = sBalloonTipText;\r
+       Info.nMask = 0;\r
+\r
+       AddTool(pWnd, Info);\r
+}\r
+\r
+void CBalloon::AddTool(CWnd * pWnd, BALLOON_INFO & bi)\r
+{\r
+       if (pWnd)\r
+               m_ToolMap.SetAt(pWnd->m_hWnd, bi);\r
+       else\r
+               m_ToolMap.SetAt(NULL, bi);\r
+} \r
+\r
+BOOL CBalloon::GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const\r
+{\r
+       BALLOON_INFO bi;\r
+       BOOL bFound = GetTool(pWnd, bi);\r
+       if (bFound)\r
+       {\r
+               sBalloonTipText = bi.sBalloonTip;\r
+               hIcon = bi.hIcon;\r
+       }\r
+\r
+       return bFound;\r
+}\r
+\r
+BOOL CBalloon::GetTool(CWnd * pWnd, BALLOON_INFO & bi) const\r
+{\r
+       if (pWnd)\r
+               return m_ToolMap.Lookup(pWnd->m_hWnd, bi);\r
+       return m_ToolMap.Lookup(NULL, bi);\r
+}\r
+\r
+void CBalloon::RemoveTool(CWnd * pWnd)\r
+{\r
+       if (pWnd)\r
+               m_ToolMap.RemoveKey(pWnd->m_hWnd);\r
+       m_ToolMap.RemoveKey(NULL);\r
+}\r
+\r
+void CBalloon::RemoveAllTools()\r
+{\r
+       m_ToolMap.RemoveAll();\r
+}\r
+\r
+void CBalloon::SetMaskTool(CWnd * pWnd, UINT nMask /* = 0 */)\r
+{\r
+       ModifyMaskTool(pWnd, nMask, (UINT)-1);\r
+}\r
+\r
+void CBalloon::ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask)\r
+{\r
+       ASSERT(pWnd);\r
+\r
+       BALLOON_INFO bi;\r
+       if (GetTool(pWnd, bi))\r
+       {\r
+               bi.nMask &= ~nRemoveMask;\r
+               bi.nMask |= nAddMask;\r
+               AddTool(pWnd, bi);\r
+       }\r
+}\r
+\r
+UINT CBalloon::GetMaskTool(CWnd * pWnd) const\r
+{\r
+       ASSERT(pWnd);\r
+\r
+       UINT nMask = 0;\r
+       BALLOON_INFO bi;\r
+       if (GetTool(pWnd, bi))\r
+               nMask = bi.nMask;\r
+       return nMask;\r
+}\r
+\r
+void CBalloon::SetEffectBk(UINT nEffect, CWnd * pWnd /* = NULL */)\r
+{\r
+       if (!pWnd)\r
+       {\r
+               m_nEffect = nEffect;\r
+       }\r
+       else\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       bi.nEffect = nEffect;\r
+                       bi.nMask |= BALLOON_MASK_EFFECT;\r
+                       AddTool(pWnd, bi);\r
+               }\r
+       }\r
+}\r
+\r
+UINT CBalloon::GetEffectBk(CWnd * pWnd /* = NULL */) const\r
+{\r
+       if (pWnd)\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (bi.nMask & BALLOON_MASK_EFFECT)\r
+                       {\r
+                               return bi.nEffect;\r
+                       }\r
+               }\r
+       }\r
+       return m_nEffect;\r
+}\r
+\r
+void CBalloon::SetNotify(BOOL bParentNotify /* = TRUE */)\r
+{\r
+       HWND hWnd = NULL;\r
+\r
+       if (bParentNotify)\r
+               hWnd = m_pParentWnd->GetSafeHwnd();\r
+\r
+       SetNotify(hWnd);\r
+}\r
+\r
+void CBalloon::SetNotify(HWND hWnd)\r
+{\r
+       m_hNotifyWnd = hWnd;\r
+}\r
+\r
+BOOL CBalloon::GetNotify() const\r
+{\r
+       return (m_hNotifyWnd != NULL);\r
+} \r
+\r
+void CBalloon::SetDelayTime(DWORD dwDuration, UINT nTime)\r
+{\r
+       switch (dwDuration)\r
+       {\r
+       case TTDT_AUTOPOP:\r
+               m_nTimeAutoPop = nTime;\r
+               break;\r
+       case TTDT_INITIAL :\r
+               m_nTimeInitial = nTime;\r
+               break;\r
+       }\r
+}\r
+\r
+UINT CBalloon::GetDelayTime(DWORD dwDuration) const\r
+{\r
+       UINT nTime = 0;\r
+       switch (dwDuration)\r
+       {\r
+       case TTDT_AUTOPOP:\r
+               nTime = m_nTimeAutoPop;\r
+               break;\r
+       case TTDT_INITIAL:\r
+               nTime = m_nTimeInitial;\r
+               break;\r
+       }\r
+\r
+       return nTime;\r
+} \r
+\r
+void CBalloon::SetSize(int nSizeIndex, UINT nValue)\r
+{\r
+       if (nSizeIndex >= XBLSZ_MAX_SIZES)\r
+               return;\r
+\r
+       m_nSizes [nSizeIndex] = nValue;\r
+}\r
+\r
+UINT CBalloon::GetSize(int nSizeIndex) const\r
+{\r
+       if (nSizeIndex >= XBLSZ_MAX_SIZES)\r
+               return 0;\r
+\r
+       return m_nSizes [nSizeIndex];\r
+}\r
+\r
+void CBalloon::SetDefaultSizes()\r
+{\r
+       SetSize(XBLSZ_ROUNDED_CX, 16);\r
+       SetSize(XBLSZ_ROUNDED_CY, 16);\r
+       SetSize(XBLSZ_MARGIN_CX, 12);\r
+       SetSize(XBLSZ_MARGIN_CY, 12);\r
+       SetSize(XBLSZ_SHADOW_CX, 4);\r
+       SetSize(XBLSZ_SHADOW_CY, 4);\r
+       SetSize(XBLSZ_WIDTH_ANCHOR, 12);\r
+       SetSize(XBLSZ_HEIGHT_ANCHOR, 16);\r
+       SetSize(XBLSZ_MARGIN_ANCHOR, 16);\r
+       SetSize(XBLSZ_BORDER_CX, 1);\r
+       SetSize(XBLSZ_BORDER_CY, 1);\r
+       SetSize(XBLSZ_BUTTON_MARGIN_CX, 5);\r
+       SetSize(XBLSZ_BUTTON_MARGIN_CY, 5);\r
+}\r
+\r
+void CBalloon::SetDirection(UINT nDirection /* = BALLOON_RIGHT_TOP */, CWnd * pWnd /* = NULL */)\r
+{\r
+       if (nDirection >= BALLOON_MAX_DIRECTIONS)\r
+               return;\r
+\r
+       if (!pWnd)\r
+       {\r
+               m_nDirection = nDirection;\r
+       }\r
+       else\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       bi.nDirection = nDirection;\r
+                       bi.nMask |= BALLOON_MASK_DIRECTION;\r
+                       AddTool(pWnd, bi);\r
+               }\r
+       }\r
+}\r
+\r
+UINT CBalloon::GetDirection(CWnd * pWnd /* = NULL */) const\r
+{\r
+       if (pWnd)\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (bi.nMask & BALLOON_MASK_DIRECTION)\r
+                               return bi.nDirection;\r
+               }\r
+       }\r
+       return m_nDirection;\r
+}\r
+\r
+void CBalloon::SetBehaviour(UINT nBehaviour /* = 0 */, CWnd * pWnd /* = NULL */)\r
+{\r
+       if (!pWnd)\r
+       {\r
+               m_nBehaviour = nBehaviour;\r
+       }\r
+       else\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       bi.nBehaviour = nBehaviour;\r
+                       bi.nMask |= BALLOON_MASK_BEHAVIOUR;\r
+                       AddTool(pWnd, bi);\r
+               }\r
+       }\r
+}\r
+\r
+UINT CBalloon::GetBehaviour(CWnd * pWnd /* = NULL */) const\r
+{\r
+       if (pWnd)\r
+       {\r
+               BALLOON_INFO bi;\r
+               if (GetTool(pWnd, bi))\r
+               {\r
+                       if (bi.nMask & BALLOON_MASK_BEHAVIOUR)\r
+                               return bi.nBehaviour;\r
+               }\r
+       }\r
+       return m_nBehaviour;\r
+}\r
+\r
+BOOL CBalloon::SetFont(CFont & font)\r
+{\r
+       LOGFONT lf;\r
+       font.GetLogFont (&lf);\r
+\r
+       return SetFont(&lf);\r
+}\r
+\r
+BOOL CBalloon::SetFont(LPLOGFONT lf)\r
+{\r
+    memcpy(&m_LogFont, lf, sizeof(LOGFONT));\r
+\r
+       return TRUE; \r
+}\r
+\r
+BOOL CBalloon::SetFont(LPCTSTR lpszFaceName, int nSizePoints /* = 8 */,\r
+                                                                       BOOL bUnderline /* = FALSE */, BOOL bBold /* = FALSE */,\r
+                                                                       BOOL bStrikeOut /* = FALSE */, BOOL bItalic /* = FALSE */)\r
+{\r
+       CDC* pDC = GetDC();\r
+       LOGFONT lf;\r
+       memset (&lf, 0, sizeof(LOGFONT));\r
+\r
+       _tcscpy_s (lf.lfFaceName, 32, lpszFaceName);\r
+       lf.lfHeight = -MulDiv (nSizePoints, GetDeviceCaps (pDC->m_hDC, LOGPIXELSY), 72);\r
+       lf.lfUnderline = (BYTE)bUnderline;\r
+       lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;\r
+       lf.lfStrikeOut = (BYTE)bStrikeOut;\r
+       lf.lfItalic = (BYTE)bItalic;\r
+\r
+       if (pDC)\r
+               ReleaseDC(pDC);\r
+\r
+       return SetFont(&lf);\r
+}\r
+\r
+void CBalloon::SetDefaultFont()\r
+{\r
+       LPLOGFONT lpSysFont = GetSystemToolTipFont();\r
+\r
+       SetFont(lpSysFont);\r
+} \r
+\r
+void CBalloon::GetFont(CFont & font) const\r
+{\r
+       font.CreateFontIndirect(&m_LogFont);\r
+}\r
+\r
+void CBalloon::GetFont(LPLOGFONT lf) const\r
+{\r
+       memcpy(lf, &m_LogFont, sizeof(LOGFONT));\r
+}\r
+\r
+void CBalloon::OnMouseMove(UINT nFlags, CPoint point)\r
+{\r
+       if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON)\r
+       {\r
+               UINT nState = DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_TRANSPARENT;\r
+               if (m_rtCloseButton.PtInRect(point))\r
+               {\r
+                       nState |= DFCS_HOT;\r
+                       if (m_bButtonPushed)\r
+                               nState |= DFCS_PUSHED;\r
+               }\r
+               CClientDC dc(this);\r
+               dc.DrawFrameControl(m_rtCloseButton, DFC_CAPTION, nState);\r
+\r
+               if (IsPointOverALink(point))\r
+                       m_Cursor.SetCursor(IDC_HAND);\r
+               else\r
+                       m_Cursor.Restore();     \r
+       }\r
+\r
+       CWnd::OnMouseMove(nFlags, point);\r
+}\r
+\r
+void CBalloon::OnLButtonDown(UINT nFlags, CPoint point)\r
+{\r
+       if ((m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && m_rtCloseButton.PtInRect(point))\r
+       {\r
+               m_bButtonPushed = TRUE;\r
+               OnMouseMove(0, point);\r
+       }\r
+\r
+       CWnd::OnLButtonDown(nFlags, point);\r
+}\r
+\r
+void CBalloon::OnLButtonUp(UINT nFlags, CPoint point)\r
+{\r
+       if (IsPointOverALink(point))\r
+       {\r
+               CString url = GetLinkForPoint(point);\r
+               ShellExecute(NULL, _T("open"), url, NULL,NULL, 0);\r
+       }\r
+       else if (\r
+               // Dialog has close button, but user has clicked somewhere else.\r
+               (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) &&\r
+               (!m_rtCloseButton.PtInRect(point) || !m_bButtonPushed))\r
+       {\r
+               m_bButtonPushed =  FALSE;\r
+       }\r
+       else\r
+       {\r
+               Pop();\r
+               if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
+               {\r
+                       CWnd::OnLButtonUp(nFlags, point);\r
+                       DestroyWindow();\r
+                       return;\r
+               }\r
+       }\r
+       CWnd::OnLButtonUp(nFlags, point);\r
+}\r
+\r
+void CBalloon::PostNcDestroy()\r
+{\r
+       CWnd::PostNcDestroy();\r
+       TRACE("CBalloon: PostNcDestroy()\n");\r
+\r
+       if (GetBehaviour() & BALLOON_DIALOG_DESTROY)\r
+       {\r
+               TRACE("CBalloon: object deleted\n");\r
+               delete this;\r
+       }\r
+}\r
+\r
+void CBalloon::GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const\r
+{\r
+       // identify the monitor that contains the sourcePoint\r
+       // and return the work area (the portion of the screen \r
+       // not obscured by the system task bar or by application \r
+       // desktop tool bars) of that monitor\r
+       OSVERSIONINFOEX VersionInformation;\r
+       SecureZeroMemory(&VersionInformation, sizeof(OSVERSIONINFOEX));\r
+       VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
+       GetVersionEx((OSVERSIONINFO *)&VersionInformation);\r
+\r
+       ::GetWindowRect(GetDesktopWindow()->m_hWnd, &monitorRect);\r
+       \r
+       if (VersionInformation.dwMajorVersion >= 5)\r
+       {\r
+               MONITORINFO mi;\r
+\r
+               //\r
+               // get the work area\r
+               //\r
+               mi.cbSize = sizeof(mi);\r
+               HMODULE hUser32 = ::GetModuleHandle (_T("USER32.DLL"));\r
+               if (hUser32 != NULL)\r
+               {\r
+                       typedef HMONITOR (WINAPI *FN_MonitorFromPoint) (POINT pt, DWORD dwFlags);\r
+                       typedef BOOL (WINAPI *FN_GetMonitorInfo) (HMONITOR hMonitor, LPMONITORINFO lpmi);\r
+                       FN_MonitorFromPoint pfnMonitorFromPoint = (FN_MonitorFromPoint)\r
+                               ::GetProcAddress (hUser32, "MonitorFromPoint");\r
+                       FN_GetMonitorInfo pfnGetMonitorInfo = (FN_GetMonitorInfo)\r
+                               ::GetProcAddress (hUser32, "GetMonitorInfoW");\r
+                       if (pfnMonitorFromPoint != NULL && pfnGetMonitorInfo != NULL)\r
+                       {\r
+                               MONITORINFO mi;\r
+                               HMONITOR hMonitor = pfnMonitorFromPoint (sourcePoint, \r
+                                       MONITOR_DEFAULTTONEAREST);\r
+                               mi.cbSize = sizeof (mi);\r
+                               pfnGetMonitorInfo (hMonitor, &mi);\r
+                               monitorRect = mi.rcWork;\r
+                       }\r
+               }\r
+       }\r
+\r
+}\r
+\r
+CPoint \r
+CBalloon::GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId)\r
+{\r
+       CWnd* pCtrl = pDlgWnd->GetDlgItem(ctrlId);\r
+       if(pCtrl == NULL)\r
+       {\r
+               ASSERT(FALSE);\r
+               return CPoint(200,200);\r
+       }\r
+       CRect ctrlRect;\r
+       pCtrl->GetWindowRect(ctrlRect);\r
+       return ctrlRect.CenterPoint();\r
+}\r
diff --git a/Utils/MiscUI/Balloon.h b/Utils/MiscUI/Balloon.h
new file mode 100644 (file)
index 0000000..74f2a23
--- /dev/null
@@ -0,0 +1,624 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+#include "gradient.h"\r
+#include "htmlformatter.h"\r
+#include "cursor.h"\r
+\r
+//The styles\r
+#define BALLOON_ANCHOR                                 0x0001\r
+#define BALLOON_SHADOW                                 0x0002\r
+#define BALLOON_ROUNDED                                        0x0004\r
+#define BALLOON_RSA                                            0x0007\r
+#define BALLOON_VCENTER_ALIGN                  0x0008\r
+#define BALLOON_BOTTOM_ALIGN                   0x0010\r
+#define BALLOON_CLOSEBUTTON                            0x0020\r
+\r
+//The behaviors\r
+#define BALLOON_MULTIPLE_SHOW                  0x0001  //Multiple show for single control\r
+#define        BALLOON_TRACK_MOUSE                             0x0002  //ToolTip follows the mouse cursor\r
+#define BALLOON_DIALOG                                 0x0004  //Shown as a dialog instead of a tooltip\r
+#define BALLOON_DIALOG_DESTROY                 0x0008  //delete the object after window is destroyed. Use carefully!\r
+\r
+//The masks\r
+#define BALLOON_MASK_STYLES                            0x0001  //The styles for the tooltip gets from the structures\r
+#define BALLOON_MASK_EFFECT                            0x0002  //The background's type for the tooltip gets from the structures\r
+#define BALLOON_MASK_COLORS                            0x0004  //The background's colors for the tooltip gets from the structures\r
+#define BALLOON_MASK_DIRECTION                 0x0008  //The align for the tooltip gets from the structures\r
+#define BALLOON_MASK_BEHAVIOUR                 0x0010  //The behavior for the tooltip gets from the structures\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * BALLOON_INFO structure.\r
+ */\r
+typedef struct tagBALLOON_INFO\r
+{\r
+       HICON           hIcon;                  ///<The icon of the tooltip\r
+       CString         sBalloonTip;    ///<The string of the tooltip\r
+       UINT        nMask;                      ///<The mask \r
+       UINT            nStyles;                ///<The tool tip's styles\r
+       UINT        nDirection;         ///<Direction display the tooltip relate cursor point\r
+       UINT            nEffect;                ///<The color's type or effects\r
+       UINT        nBehaviour;         ///<The tool tip's behavior\r
+       COLORREF        crBegin;                ///<Begin Color\r
+       COLORREF    crMid;                      ///<Mid Color\r
+       COLORREF        crEnd;                  ///<End Color\r
+\r
+    tagBALLOON_INFO();          ///<proper initialization of all members\r
+} BALLOON_INFO;\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * This structure is sent to with the notify messages.\r
+ */\r
+typedef struct tagNM_BALLOON_DISPLAY {\r
+    NMHDR hdr;\r
+       CPoint * pt;\r
+       CWnd * pWnd;\r
+       BALLOON_INFO * bi;\r
+} NM_BALLOON_DISPLAY;\r
+\r
+#define BALLOON_CLASSNAME    _T("CBalloon")  // Window class name\r
+#define UDM_TOOLTIP_FIRST                 (WM_USER + 100)\r
+#define UDM_TOOLTIP_DISPLAY               (UDM_TOOLTIP_FIRST) //User has changed the data\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Shows Balloons with info text in it. Either as Tooltips or as modeless dialog boxes.\r
+ * Several options are available to customize the look and behavior of the balloons.\r
+ * Since this class inherits CHTMLFormatter you can use all the tags CHTMLFormatter\r
+ * provides to format the text.\r
+ * Please refer to the documentation of the methods for details.\n\r
+ * \image html "balloon_box.jpg"\r
+ * \image html "balloon_tooltip.jpg"\r
+ * \r
+ * To use the dialog balloons just call the static methods:\r
+ * \code\r
+ * CWnd* ctrl = GetDlgItem(IDC_EDITBOX);\r
+ * CRect rt;\r
+ * ctrl->GetWindowRect(rt);\r
+ * CPoint point = CPoint((rt.left+rt.right)/2, (rt.top+rt.bottom)/2);\r
+ * CBalloon::ShowBalloon(NULL, point, \r
+ *                 "this is a <b>Message Balloon</b>\n<hr=100%>\n<ct=0x0000FF>Warning! Warning!</ct>\nSomething unexpected happened",\r
+ *                 TRUE, IDI_EXCLAMATION);\r
+ * \endcode\r
+ * \r
+ * To use the tooltips, declare an object of CBalloon as a member of your dialog class:\r
+ * \code\r
+ * CBalloon m_tooltips;\r
+ * \code\r
+ * In your OnInitDialog() method add the tooltips and modify them as you like:\r
+ * \code\r
+ * m_tooltips.Create(this);            //initializes the tooltips\r
+ * m_tooltips.AddTool(IDC_BUTTON, "this button does nothing");\r
+ * m_tooltips.AddTool(IDC_EDITBOX, "enter a value here", IDI_ICON);\r
+ * m_tooltips.SetEffectBk(GetDlgItem(IDC_EDITBOX), CBalloon::BALLOON_EFFECT_HGRADIENT);                //only affects the edit box tooltip\r
+ * m_tooltips.SetGradientColors(0x80ffff, 0x000000, 0xffff80);\r
+ * \endcode\r
+ * and last you have to override the PreTranslateMessage() method of your dialog box:\r
+ * \code\r
+ * BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)\r
+ * {\r
+ *  m_tooltips.RelayEvent(pMsg);\r
+ *  return CDialog::PreTranslateMessage(pMsg);\r
+ * }\r
+ * \endcode\r
+ */\r
+class CBalloon : public CWnd, public CHTMLFormatter\r
+{\r
+// Construction\r
+public:\r
+       virtual BOOL Create(CWnd* pParentWnd);\r
+       CBalloon();\r
+       virtual ~CBalloon();\r
+\r
+// Attributes\r
+public:\r
+       enum {  XBLSZ_ROUNDED_CX = 0,\r
+                       XBLSZ_ROUNDED_CY,\r
+                       XBLSZ_MARGIN_CX,\r
+                       XBLSZ_MARGIN_CY,\r
+                       XBLSZ_SHADOW_CX,\r
+                       XBLSZ_SHADOW_CY,\r
+                       XBLSZ_WIDTH_ANCHOR,\r
+                       XBLSZ_HEIGHT_ANCHOR,\r
+                       XBLSZ_MARGIN_ANCHOR,\r
+                       XBLSZ_BORDER_CX,\r
+                       XBLSZ_BORDER_CY,\r
+                       XBLSZ_BUTTON_MARGIN_CX,\r
+                       XBLSZ_BUTTON_MARGIN_CY,\r
+\r
+                       XBLSZ_MAX_SIZES\r
+               };\r
+\r
+       enum {  BALLOON_COLOR_FG = 0,\r
+                       BALLOON_COLOR_BK_BEGIN,\r
+                       BALLOON_COLOR_BK_MID,\r
+                       BALLOON_COLOR_BK_END,\r
+                       BALLOON_COLOR_SHADOW,           // Color for the shadow\r
+                       BALLOON_COLOR_BORDER,           // Color for border of the tooltip\r
+\r
+                       BALLOON_MAX_COLORS\r
+               };\r
+\r
+       enum {  BALLOON_LEFT_TOP = 0,\r
+                       BALLOON_RIGHT_TOP,\r
+                       BALLOON_LEFT_BOTTOM,\r
+                       BALLOON_RIGHT_BOTTOM,\r
+\r
+                       BALLOON_MAX_DIRECTIONS\r
+               };\r
+\r
+       enum {  BALLOON_EFFECT_SOLID = 0,\r
+                       BALLOON_EFFECT_HGRADIENT,\r
+                       BALLOON_EFFECT_VGRADIENT,\r
+                       BALLOON_EFFECT_HCGRADIENT,\r
+                       BALLOON_EFFECT_VCGRADIENT,\r
+                       BALLOON_EFFECT_3HGRADIENT,\r
+                       BALLOON_EFFECT_3VGRADIENT,\r
+\r
+                       BALLOON_MAX_EFFECTS\r
+               };\r
+\r
+\r
+// Operations\r
+public:\r
+\r
+// Overrides\r
+       // ClassWizard generated virtual function overrides\r
+       //{{AFX_VIRTUAL(XToolTip)\r
+       public:\r
+       virtual BOOL PreTranslateMessage(MSG* pMsg);\r
+       //}}AFX_VIRTUAL\r
+\r
+// Implementation\r
+public:\r
+       /** \name Balloon Dialogs \r
+        * static methods to show a balloon like a modeless dialog\r
+        */\r
+       //@{\r
+       /**\r
+        * Pops up a balloon like a modeless dialog to inform the user in\r
+        * a non disturbing way like with a normal MessageBox().\r
+        * \image html "balloon_box.jpg"\r
+        * An example of when to use such balloons is in a dialog box where\r
+        * the user can enter values. If one or several values are outside\r
+        * of valid ranges then just pop up a balloon. That way the user \r
+        * knows exactly \b where the wrong value is (if the balloon is\r
+        * placed so that the anchor points to the edit box) and also\r
+        * doesn't have to press "OK" to close the box.\r
+        *\r
+        * \param pWnd the parent window. Or NULL if no parent window is available.\r
+        * \param pt the point where the anchor should point to. For example if you\r
+        * want to point to an edit box the point would be:\r
+        * \code\r
+        * CWnd* ctrl = GetDlgItem(IDC_EDITBOX);\r
+        * CRect rt;\r
+        * ctrl->GetWindowRect(rt);\r
+        * CPoint point = CPoint((rt.left+rt.right)/2, (rt.top+rt.bottom)/2);\r
+        * \endcode\r
+        * \param nIdText the string ID of the text to show. The ID has to be in your resources.\r
+        * \param sText the string to show.\r
+        * \param showCloseButton If TRUE, then the balloon has a close button in the upper right corner. That also\r
+        * makes the balloon to show up until the user presses the close button.\n\r
+        * If FALSE, then the balloon will be closed after a timeout of 5 seconds or as soon as the user clicks anywhere\r
+        * on the balloon.\r
+        * \param hIcon a handle of an icon.\r
+        * \param nIdIcon an ID of an icon which has to be in your resources.\r
+        * \param szIcon a name of an icon. Either a path to an icon file or one of\r
+        * the following system icons:\n\r
+        * - IDI_APPLICATION\r
+        * - IDI_ERROR\r
+        * - IDI_HAND\r
+        * - IDI_EXCLAMATION\r
+        * - IDI_WARNING\r
+        * - IDI_QUESTION\r
+        * - IDI_WINLOGO\r
+        * - IDI_INFORMATION\r
+        * - IDI_ASTERISK\r
+        * - IDI_QUESTION\r
+        * \r
+        * \param nDirection the direction to where the dialog should be drawn. Defaults to BALLOON_RIGHT_TOP.\r
+        * - BALLOON_LEFT_TOP\r
+        * - BALLOON_RIGHT_TOP\r
+        * - BALLOON_LEFT_BOTTOM\r
+        * - BALLOON_RIGHT_BOTTOM\r
+        *\r
+        * \param nEffect specifies how to draw the background. Defaults to BALLOON_EFFECT_SOLID.\r
+        * - BALLOON_EFFECT_SOLID               one color for the background. The default is the standard windows color for tooltip backgrounds.\r
+        * - BALLOON_EFFECT_HGRADIENT   draws a horizontal gradient from crStart to crEnd\r
+        * - BALLOON_EFFECT_VGRADIENT   draws a vertical gradient from crStart to crEnd\r
+        * - BALLOON_EFFECT_HCGRADIENT  draws a horizontal gradient from crStart to crEnd to crStart\r
+        * - BALLOON_EFFECT_VCGRADIENT  draws a vertical gradient from crStart to crEnd to crStart\r
+        * - BALLOON_EFFECT_3HGRADIENT  draws a horizontal gradient from crStart to crMid to crEnd\r
+        * - BALLOON_EFFECT_3VGRADIENT  draws a vertical gradient from crStart to crMid to crEnd\r
+        *\r
+        * \param crStart the starting color for gradients\r
+        * \param crMid the middle color for three colored gradients\r
+        * \param crEnd the end color for gradients\r
+        */\r
+       /**\r
+        * \overload ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, UINT nIdIcon, UINT nDirection = BALLOON_RIGHT_TOP, UINT nEffect = BALLOON_EFFECT_SOLID, COLORREF crStart = NULL, COLORREF crMid = NULL, COLORREF crEnd = NULL);\r
+        */\r
+       static void ShowBalloon(\r
+               CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon,\r
+               UINT nDirection = BALLOON_RIGHT_TOP, UINT nEffect = BALLOON_EFFECT_SOLID,\r
+               COLORREF crStart = NULL, COLORREF crMid = NULL, COLORREF crEnd = NULL);\r
+       /**\r
+        * \overload ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon);\r
+        */\r
+       static void ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon);\r
+       //@}\r
+\r
+       /** \r
+        * Helper function to return the center point of a dialog control \r
+        * Useful for passing to ShowBalloon\r
+        */\r
+       static CPoint GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId);\r
+\r
+       /** \name ToolTips \r
+        * handling of tooltips.\r
+        */\r
+       //@{\r
+       //@{\r
+       /**\r
+        * Adds a tooltip for a windows element to the internal list.\r
+        * \param pWnd pointer to a windows element.\r
+        * \param nIdWnd an ID of a dialog resource.\r
+        * \param nIdText an ID of a string dialog resource to use as the tooltip text.\r
+        * \param sBalloontipText string for the tooltip.\r
+        * \param hIcon handle for an icon to show on the tooltip.\r
+        * \param nIdIcon a resource ID for an icon to show on the tooltip.\r
+        * \param bi pointer to a BALLOON_IFNO structure.\r
+        */\r
+       void    AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon = NULL); //Adds tool\r
+       void    AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon); //Adds tool\r
+       void    AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon = NULL); //Adds tool\r
+       void    AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon); //Adds tool\r
+       void    AddTool(int nIdWnd, UINT nIdText, HICON hIcon = NULL); //Adds tool\r
+       void    AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon); //Adds tool\r
+       void    AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon = NULL); //Adds tool\r
+       void    AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon); //Adds tool\r
+       void    AddTool(CWnd * pWnd, BALLOON_INFO & bi); //Adds tool\r
+       //@}\r
+\r
+       /**\r
+        * Gets the text and the icon handle of a specific tooltip.\r
+        * \param pWnd pointer to the tooltip window\r
+        * \param sBalloonTipText the returned tooltip text\r
+        * \param hIcon the returned icon handle\r
+        * \param bi pointer to the returned BALLOON_INFO structure.\r
+        * \return TRUE if the tooltip exists.\r
+        */\r
+       BOOL    GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const; //Gets the tool tip's text\r
+       BOOL    GetTool(CWnd * pWnd, BALLOON_INFO & bi) const; //Gets tool\r
+\r
+       /**\r
+        * Removes a specific tooltip from the internal list.\r
+        * \param pWnd pointer to the tooltip window\r
+        */\r
+       void    RemoveTool(CWnd * pWnd);  //Removes specified tool\r
+\r
+       /**\r
+        * Removes all tooltips from the internal list. \r
+        */\r
+       void    RemoveAllTools(); // Removes all tools\r
+       //@}\r
+\r
+       /** \name Styles \r
+        * handling of tooltip appearance styles.\r
+        * The following styles are available:\r
+        * - BALLOON_ANCHOR                     the balloon is drawn with an anchor\r
+        * - BALLOON_SHADOW                     the balloon is drawn with a SE shadow\r
+        * - BALLOON_ROUNDED            the balloon has round corners. For tooltips like the standard windows ones disable this style.\r
+        * - BALLOON_RSA                        combines BALLOON_ANCHOR, BALLOON_SHADOW and BALLOON_ROUNDED. This is the default.\r
+        * - BALLOON_VCENTER_ALIGN\r
+        * - BALLOON_BOTTOM_ALIGN\r
+        * - BALLOON_CLOSEBUTTON        the balloon has a close button in the upper right corner.\r
+        */\r
+       //@{\r
+       /**\r
+        * sets styles for either all tooltips or specific ones.\r
+        * \param nStyles the styles to set.\r
+        * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips.\r
+        */\r
+       void    SetStyles(DWORD nStyles, CWnd * pWnd = NULL); //Sets New Style\r
+       /**\r
+        * Modifies existing styles.\r
+        * \param nAddStyles the styles to add.\r
+        * \param nRemoveStyles the styles to remove\r
+        * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips.\r
+        */\r
+       void    ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd = NULL); //Modifies styles\r
+       /**\r
+        * returns the current styles for the tooltip.\r
+        * \param pWnd pointer to the tooltip window or NULL if the global styles are needed.\r
+        */\r
+       DWORD   GetStyles(CWnd * pWnd = NULL) const; //Gets current Styles\r
+       /**\r
+        * Resets the styles to the default values.\r
+        * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips.\r
+        */\r
+       void    SetDefaultStyles(CWnd * pWnd = NULL); //Sets default styles\r
+       //@}\r
+\r
+       /** \name Colors\r
+        * different color settings. The following elements have colors:\r
+        * - BALLOON_COLOR_FG                           the foreground text color. Default is black.\r
+        * - BALLOON_COLOR_BK_BEGIN                     the background color and the first color in gradients.\r
+        * - BALLOON_COLOR_BK_MID                       the middle color for gradients.\r
+        * - BALLOON_COLOR_BK_END                       the end color for gradients.\r
+        * - BALLOON_COLOR_SHADOW                       the color of the shadow\r
+        * - BALLOON_COLOR_BORDER                       the color for the balloon border\r
+        */\r
+       //@{\r
+       /**\r
+        * Sets the color for a balloon element.\r
+        * \param nIndex the element to set the color.\r
+        * \param crColor the color.\r
+        */\r
+       void    SetColor(int nIndex, COLORREF crColor); //Sets the color\r
+       /**\r
+        * Returns the color of a balloon element.\r
+        */\r
+       COLORREF        GetColor(int nIndex) const; //Gets the color\r
+       /**\r
+        * Resets all colors to default values.\r
+        */\r
+       void    SetDefaultColors(); //Sets default colors\r
+       /**\r
+        * Sets the colors used in the background gradients.\r
+        * \param crBegin first color\r
+        * \param crMid middle color\r
+        * \param crEnd end color\r
+        * \param pWnd pointer to the tooltip window or NULL if the settings are global.\r
+        */\r
+       void    SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd = NULL); //Sets the gradient's colors\r
+       /**\r
+        * Returns the colors used in the background gradients.\r
+        * \param pWnd pointer to the tooltip window or NULL if the global settings are needed.\r
+        */\r
+       void    GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd = NULL) const; //Gets the gradient's colors\r
+       //@}\r
+\r
+\r
+       /** \name Masks \r
+        * Manipulate masks of tooltips. Masks are used to define styles, effects, colors and the like for single\r
+        * tooltips and not only for all tooltips.\r
+        * Whatever mask is set for a specific tooltip means that this tooltip has its own version of those settings\r
+        * and ignores the global settings.\n\r
+        * The following masks are available:\r
+        * - BALLOON_MASK_STYLES                masks out the styles\r
+        * - BALLOON_MASK_EFFECT                masks out the effects\r
+        * - BALLOON_MASK_COLORS                masks out the colors\r
+        * - BALLOON_MASK_DIRECTION             masks out the direction\r
+        * - BALLOON_MASK_BEHAVIOUR             masks out the behavior\r
+        * \r
+        * The functions either set, modify or read out the masks for specific tooltip windows.\r
+        */\r
+       //@{\r
+       void    SetMaskTool(CWnd * pWnd, UINT nMask = 0);\r
+       void    ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask);\r
+       UINT    GetMaskTool(CWnd * pWnd) const;\r
+       //@}\r
+\r
+       /** \name Effects\r
+        * Use these methods to manipulate background effects of the tooltip. The following\r
+        * effects are available.\r
+        * - BALLOON_EFFECT_SOLID               one color for the background. The default is the standard windows color for tooltip backgrounds.\r
+        * - BALLOON_EFFECT_HGRADIENT   draws a horizontal gradient from crStart to crEnd\r
+        * - BALLOON_EFFECT_VGRADIENT   draws a vertical gradient from crStart to crEnd\r
+        * - BALLOON_EFFECT_HCGRADIENT  draws a horizontal gradient from crStart to crEnd to crStart\r
+        * - BALLOON_EFFECT_VCGRADIENT  draws a vertical gradient from crStart to crEnd to crStart\r
+        * - BALLOON_EFFECT_3HGRADIENT  draws a horizontal gradient from crStart to crMid to crEnd\r
+        * - BALLOON_EFFECT_3VGRADIENT  draws a vertical gradient from crStart to crMid to crEnd\r
+        */\r
+       //@{\r
+       void    SetEffectBk(UINT nEffect, CWnd * pWnd = NULL);\r
+       UINT    GetEffectBk(CWnd * pWnd = NULL) const;\r
+       //@}\r
+\r
+       /** \name Notification \r
+        * Gets or sets if the parent or any other window should get notification messages from\r
+        * the tooltips.\r
+        */\r
+       //@{\r
+       void    SetNotify(HWND hWnd);\r
+       void    SetNotify(BOOL bParentNotify = TRUE);\r
+       BOOL    GetNotify() const; //Is enabled notification\r
+       //@}\r
+\r
+       /** \name Delaytimes\r
+        * Gets or sets the delay times for the tooltips.\r
+        * - TTDT_AUTOPOP time in milliseconds until the tooltip automatically closes.\r
+        * - TTDT_INITIAL time in milliseconds until the tooltip appears when the mouse pointer is over a control.\r
+        */\r
+       //@{\r
+       void    SetDelayTime(DWORD dwDuration, UINT nTime);\r
+       UINT    GetDelayTime(DWORD dwDuration) const;\r
+       //@}\r
+\r
+\r
+       /** \name Direction \r
+        * Gets or sets the direction of the balloons.\r
+        * - BALLOON_LEFT_TOP\r
+        * - BALLOON_RIGHT_TOP\r
+        * - BALLOON_LEFT_BOTTOM\r
+        * - BALLOON_RIGHT_BOTTOM\r
+        */\r
+       //@{\r
+       void    SetDirection(UINT nDirection = BALLOON_RIGHT_TOP, CWnd * pWnd = NULL);\r
+       UINT    GetDirection(CWnd * pWnd = NULL) const;\r
+       //@}\r
+\r
+       /** \name Behavior\r
+        * Gets or sets the behavior of the balloons.\r
+        * - BALLOON_MULTIPLE_SHOW              if this is set then the tooltip will appear again if the mouse pointer is still over the same control.\r
+        * - BALLOON_TRACK_MOUSE                if set then the tooltip will follow the mouse pointer\r
+        * - BALLOON_DIALOG                             the balloon is shown as a dialog instead of a tooltip, i.e. it won't close when the mouse pointer leaves the control.\r
+        * - BALLOON_DIALOG_DESTROY             the object itself is destroyed when the balloon is closed. Use this \b very carefully!\r
+        */\r
+       //@{\r
+       void    SetBehaviour(UINT nBehaviour = 0, CWnd * pWnd = NULL);\r
+       UINT    GetBehaviour(CWnd * pWnd = NULL) const;\r
+       //@}\r
+\r
+       /** \name Fonts \r
+        * Font settings for the balloon text.\r
+        */\r
+       //@{\r
+       BOOL    SetFont(CFont & font); //set font\r
+       BOOL    SetFont(LPLOGFONT lf); //set font\r
+       BOOL    SetFont(LPCTSTR lpszFaceName, int nSizePoints = 8,\r
+                                                                       BOOL bUnderline = FALSE, BOOL bBold = FALSE,\r
+                                                                       BOOL bStrikeOut = FALSE, BOOL bItalic = FALSE); //set font\r
+       void    SetDefaultFont(); //set default fonts\r
+       void    GetFont(CFont & font) const;\r
+       void    GetFont(LPLOGFONT lf) const;\r
+       //@}\r
+\r
+       /**\r
+        * Call this method from CDialog::PreTranslateMessage(pMsg).\r
+        */\r
+       void    RelayEvent(MSG* pMsg);\r
+       \r
+       /**\r
+        * Hide tooltip immediately.\r
+        */\r
+       void    Pop();\r
+\r
+       /**\r
+        * Shows a tooltip immediately.\r
+        */\r
+       void    DisplayToolTip(CPoint * pt = NULL);\r
+       void    DisplayToolTip(CPoint * pt, CRect * rect);\r
+\r
+       // Generated message map functions\r
+protected:\r
+       void    SetSize(int nSizeIndex, UINT nValue);\r
+       UINT    GetSize(int nSizeIndex) const;\r
+       void    SetDefaultSizes();\r
+\r
+       void    Redraw(BOOL bRedraw = TRUE);\r
+       void    KillTimers(UINT nIDTimer = NULL);\r
+               \r
+       void    SetNewToolTip(CWnd * pWnd);\r
+       void    GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const;\r
+\r
+       /**\r
+        * Finds the child window to which the point belongs\r
+        * \param point the point to look for the child window\r
+        * \return the pointer to the child window, or NULL if there is now window\r
+        */\r
+       HWND    GetChildWindowFromPoint(CPoint & point) const;\r
+       BOOL    IsCursorInToolTip() const;\r
+    inline     BOOL IsVisible() const { return ((GetStyle() & WS_VISIBLE) == WS_VISIBLE); }\r
+\r
+       CSize   GetTooltipSize(const CString& str); //Gets max rectangle for display tooltip text\r
+       CSize   GetSizeIcon(HICON hIcon) const;\r
+       void    CalculateInfoBoxRect(CPoint * pt, CRect * rect);\r
+\r
+       LPLOGFONT       GetSystemToolTipFont() const;\r
+\r
+       int             GetNextHorizDirection(int nDirection) const;\r
+       int             GetNextVertDirection(int nDirection) const;\r
+       BOOL    TestHorizDirection(int x, int cx, const CRect& monitorRect, int nDirection, LPRECT rect);\r
+       BOOL    TestVertDirection(int y, int cy, const CRect& monitorRect, int nDirection, LPRECT rect);\r
+\r
+       CRect   GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const;\r
+\r
+       LRESULT SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi);\r
+\r
+       void    OnDraw(CDC * pDC, CRect rect);\r
+       void    OnDrawBackground(CDC * pDC, CRect * pRect);\r
+\r
+       virtual void PostNcDestroy();\r
+       afx_msg void OnPaint();\r
+       afx_msg void OnTimer(UINT_PTR nIDEvent);\r
+       afx_msg void OnDestroy();\r
+       afx_msg void OnKillFocus(CWnd* pNewWnd);\r
+       afx_msg void OnMouseMove(UINT nFlags, CPoint point);\r
+       afx_msg void OnLButtonDown(UINT nFlags, CPoint point);\r
+       afx_msg void OnLButtonUp(UINT nFlags, CPoint point);\r
+       DECLARE_MESSAGE_MAP()\r
+protected:\r
+       enum {  BALLOON_SHOW = 0x100, //the identifier of the timer for show the tooltip\r
+                       BALLOON_HIDE = 0x101  //the identifier of the timer for hide the tooltip\r
+               };\r
+\r
+       CMap<HWND, HWND, BALLOON_INFO, BALLOON_INFO> m_ToolMap; //Tool Maps\r
+\r
+       HWND    m_hNotifyWnd; // Handle to window for notification about change data\r
+       CWnd *  m_pParentWnd; // The pointer to the parent window\r
+       HWND    m_hCurrentWnd;\r
+       HWND    m_hDisplayedWnd;\r
+       UINT    m_nLastDirection;\r
+       \r
+\r
+    LOGFONT    m_LogFont;                  // Current font in use\r
+       \r
+       //Default setting\r
+       COLORREF        m_crColor [BALLOON_MAX_COLORS]; //The indexing colors\r
+       UINT    m_nSizes [XBLSZ_MAX_SIZES]; //All sizes \r
+       UINT    m_nStyles;\r
+       UINT    m_nDirection;\r
+       UINT    m_nEffect;\r
+       UINT    m_nBehaviour;    //The tool tip's behavior \r
+\r
+       UINT    m_nTimeAutoPop; \r
+       UINT    m_nTimeInitial;\r
+\r
+       //The properties of the current tooltip\r
+       CPoint  m_ptOriginal;\r
+\r
+       CRgn    m_rgnBalloon;\r
+       CRgn    m_rgnShadow;\r
+\r
+       CSize   m_szBalloonIcon; //the size of the current icon\r
+       CSize   m_szBalloonText; //the size of the tool tip's text\r
+       CSize   m_szCloseButton;\r
+\r
+       CRect   m_rtCloseButton;        //the rect for the close button\r
+       BOOL    m_bButtonPushed;\r
+\r
+       CCursor m_Cursor;\r
+\r
+       BALLOON_INFO    m_pToolInfo; //info of the current tooltip\r
+\r
+\r
+};\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
diff --git a/Utils/MiscUI/BaseDialog.cpp b/Utils/MiscUI/BaseDialog.cpp
new file mode 100644 (file)
index 0000000..2577676
--- /dev/null
@@ -0,0 +1,82 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "BaseDialog.h"\r
+\r
+\r
+INT_PTR CDialog::DoModal(HINSTANCE hInstance, int resID, HWND hWndParent)\r
+{\r
+       hResource = hInstance;\r
+       return DialogBoxParam(hInstance, MAKEINTRESOURCE(resID), hWndParent, &CDialog::stDlgFunc, (LPARAM)this);\r
+}\r
+\r
+HWND CDialog::Create(HINSTANCE hInstance, int resID, HWND hWndParent)\r
+{\r
+       hResource = hInstance;\r
+    m_hwnd = CreateDialogParam(hInstance, MAKEINTRESOURCE(resID), hWndParent, &CDialog::stDlgFunc, (LPARAM)this);\r
+    return m_hwnd;\r
+}\r
+\r
+void CDialog::InitDialog(HWND hwndDlg, UINT iconID)\r
+{\r
+       HWND hwndOwner; \r
+       RECT rc, rcDlg, rcOwner;\r
+\r
+       hwndOwner = ::GetParent(hwndDlg);\r
+       if (hwndOwner == NULL)\r
+               hwndOwner = ::GetDesktopWindow();\r
+\r
+       GetWindowRect(hwndOwner, &rcOwner); \r
+       GetWindowRect(hwndDlg, &rcDlg); \r
+       CopyRect(&rc, &rcOwner); \r
+\r
+       OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); \r
+       OffsetRect(&rc, -rc.left, -rc.top); \r
+       OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); \r
+\r
+       SetWindowPos(hwndDlg, HWND_TOP, rcOwner.left + (rc.right / 2), rcOwner.top + (rc.bottom / 2), 0, 0,     SWP_NOSIZE); \r
+       HICON hIcon = (HICON)::LoadImage(hResource, MAKEINTRESOURCE(iconID), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_SHARED);\r
+       ::SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);\r
+       ::SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);\r
+}\r
+\r
+INT_PTR CALLBACK CDialog::stDlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+       CDialog* pWnd;\r
+       if (uMsg == WM_INITDIALOG)\r
+       {\r
+               // get the pointer to the window from lpCreateParams\r
+               SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);\r
+               pWnd = (CDialog*)lParam;\r
+               pWnd->m_hwnd = hwndDlg;\r
+       }\r
+       // get the pointer to the window\r
+       pWnd = GetObjectFromWindow(hwndDlg);\r
+\r
+       // if we have the pointer, go to the message handler of the window\r
+       // else, use DefWindowProc\r
+       if (pWnd)\r
+       {\r
+               LRESULT lRes = pWnd->DlgFunc(hwndDlg, uMsg, wParam, lParam);\r
+               SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, lRes);\r
+               return lRes;\r
+       }\r
+       else\r
+               return DefWindowProc(hwndDlg, uMsg, wParam, lParam);\r
+}\r
diff --git a/Utils/MiscUI/BaseDialog.h b/Utils/MiscUI/BaseDialog.h
new file mode 100644 (file)
index 0000000..bc03d90
--- /dev/null
@@ -0,0 +1,53 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include <string>\r
+\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * A base window class.\r
+ * Provides separate window message handlers for every window object based on\r
+ * this class.\r
+ */\r
+class CDialog\r
+{\r
+public:\r
+       INT_PTR DoModal(HINSTANCE hInstance, int resID, HWND hWndParent);\r
+    HWND    Create(HINSTANCE hInstance, int resID, HWND hWndParent);\r
+\r
+       virtual LRESULT CALLBACK DlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;\r
+\r
+       operator HWND() {return m_hwnd;}\r
+protected:\r
+       HINSTANCE hResource;\r
+       HWND m_hwnd;\r
+\r
+       void InitDialog(HWND hwndDlg, UINT iconID);\r
+\r
+       // the real message handler\r
+       static INT_PTR CALLBACK stDlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+\r
+       // returns a pointer the dialog (stored as the WindowLong)\r
+       inline static CDialog * GetObjectFromWindow(HWND hWnd)\r
+       {\r
+               return (CDialog *)GetWindowLongPtr(hWnd, GWLP_USERDATA);\r
+       }\r
+};\r
+\r
diff --git a/Utils/MiscUI/BaseWindow.cpp b/Utils/MiscUI/BaseWindow.cpp
new file mode 100644 (file)
index 0000000..491429f
--- /dev/null
@@ -0,0 +1,146 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "BaseWindow.h"\r
+\r
+\r
+ResString::ResString (HINSTANCE hInst, int resId)\r
+{\r
+       if (!::LoadString (hInst, resId, _buf, MAX_RESSTRING + 1))\r
+       {\r
+               SecureZeroMemory(_buf, sizeof(_buf));\r
+       }\r
+}\r
+\r
+\r
+bool CWindow::RegisterWindow(UINT style, HICON hIcon, HCURSOR hCursor, HBRUSH hbrBackground, \r
+                                                                       LPCTSTR lpszMenuName, LPCTSTR lpszClassName, HICON hIconSm)\r
+{\r
+       WNDCLASSEX wcx; \r
\r
+       // Fill in the window class structure with default parameters \r
\r
+       wcx.cbSize = sizeof(WNDCLASSEX);                                // size of structure \r
+       wcx.style = style;                                                              // redraw if size changes \r
+       wcx.lpfnWndProc = CWindow::stWinMsgHandler;             // points to window procedure \r
+       wcx.cbClsExtra = 0;                                                             // no extra class memory \r
+       wcx.cbWndExtra = 0;                                                             // no extra window memory \r
+       wcx.hInstance = hResource;                                              // handle to instance \r
+       wcx.hIcon = hIcon;                                                              // predefined app. icon \r
+       wcx.hCursor = hCursor;                                                  // predefined arrow \r
+       wcx.hbrBackground = hbrBackground;                              // white background brush \r
+       wcx.lpszMenuName = lpszMenuName;                                // name of menu resource \r
+       wcx.lpszClassName = lpszClassName;                              // name of window class \r
+       wcx.hIconSm = hIconSm;                                                  // small class icon \r
\r
+       // Register the window class. \r
+       return RegisterWindow(&wcx); \r
+}\r
+\r
+bool CWindow::RegisterWindow(CONST WNDCLASSEX* wcx)\r
+{\r
+       // Register the window class. \r
+       sClassName = std::wstring(wcx->lpszClassName);\r
+\r
+       if (RegisterClassEx(wcx) == 0)\r
+               return FALSE;\r
+       else\r
+               return TRUE;\r
+}\r
+\r
+LRESULT CALLBACK CWindow::stWinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+       CWindow* pWnd;\r
+\r
+       if (uMsg == WM_NCCREATE)\r
+       {\r
+               // get the pointer to the window from lpCreateParams which was set in CreateWindow\r
+               SetWindowLongPtr(hwnd, GWLP_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));\r
+       }\r
+\r
+       // get the pointer to the window\r
+       pWnd = GetObjectFromWindow(hwnd);\r
+\r
+       // if we have the pointer, go to the message handler of the window\r
+       // else, use DefWindowProc\r
+       if (pWnd)\r
+               return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);\r
+       else\r
+               return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
+}\r
+\r
+bool CWindow::Create()\r
+{ \r
+       // Create the window\r
+       RECT rect;\r
+\r
+       rect.top = 0;\r
+       rect.left = 0;\r
+       rect.right = 600;\r
+       rect.bottom = 400;\r
+\r
+       return Create(WS_OVERLAPPEDWINDOW | WS_VISIBLE, NULL, &rect);\r
+}\r
+\r
+bool CWindow::Create(DWORD dwStyles, HWND hParent /* = NULL */, RECT* rect /* = NULL */)\r
+{ \r
+       return CreateEx(0, dwStyles, hParent, rect);    \r
+}\r
+\r
+bool CWindow::CreateEx(DWORD dwExStyles, DWORD dwStyles, HWND hParent /* = NULL */, RECT* rect /* = NULL */)\r
+{\r
+       // send the this pointer as the window creation parameter\r
+       if (rect == NULL)\r
+               m_hwnd = CreateWindowEx(dwExStyles, sClassName.c_str(), sWindowTitle.c_str(), dwStyles, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hParent, NULL, hResource, (void *)this);\r
+       else\r
+       {\r
+               m_hwnd = CreateWindowEx(dwExStyles, sClassName.c_str(), sWindowTitle.c_str(), dwStyles, rect->left, rect->top, \r
+                       rect->right - rect->left, rect->bottom - rect->top, hParent, NULL, hResource, \r
+                       (void *)this);\r
+       }\r
+       return (m_hwnd != NULL);\r
+}\r
+\r
+void CWindow::SetTransparency(BYTE alpha, COLORREF color /* = 0xFF000000 */)\r
+{\r
+       if (alpha == 255)\r
+       {\r
+               LONG_PTR exstyle = GetWindowLongPtr(*this, GWL_EXSTYLE);\r
+               exstyle &= ~WS_EX_LAYERED;\r
+               SetWindowLongPtr(*this, GWL_EXSTYLE, exstyle);\r
+       }\r
+       else\r
+       {\r
+               LONG_PTR exstyle = GetWindowLongPtr(*this, GWL_EXSTYLE);\r
+               exstyle |= WS_EX_LAYERED;\r
+               SetWindowLongPtr(*this, GWL_EXSTYLE, exstyle);\r
+       }\r
+       COLORREF col = color;\r
+       DWORD flags = LWA_ALPHA;\r
+       if (col & 0xFF000000)\r
+       {\r
+               col = RGB(255, 255, 255);\r
+               flags = LWA_ALPHA;\r
+       }\r
+       else\r
+       {\r
+               flags = LWA_COLORKEY;\r
+       }\r
+       SetLayeredWindowAttributes(*this, col, alpha, flags);\r
+}\r
diff --git a/Utils/MiscUI/BaseWindow.h b/Utils/MiscUI/BaseWindow.h
new file mode 100644 (file)
index 0000000..17a6a03
--- /dev/null
@@ -0,0 +1,101 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+#include <string>\r
+\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Loads a string from the application resources.\r
+ */\r
+class ResString\r
+{\r
+       enum { MAX_RESSTRING = 1024 };\r
+public:\r
+       ResString (HINSTANCE hInst, int resId);\r
+       operator TCHAR const * () const { return _buf; }\r
+private:\r
+       TCHAR _buf [MAX_RESSTRING + 1];\r
+};\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * A base window class.\r
+ * Provides separate window message handlers for every window object based on\r
+ * this class.\r
+ */\r
+class CWindow\r
+{\r
+public:\r
+       virtual bool RegisterWindow(UINT style, HICON hIcon, HCURSOR hCursor, HBRUSH hbrBackground, \r
+               LPCTSTR lpszMenuName, LPCTSTR lpszClassName, HICON hIconSm);\r
+       virtual bool RegisterWindow(CONST WNDCLASSEX* wcx);\r
+\r
+       /// static message handler to put in WNDCLASSEX structure\r
+       static LRESULT CALLBACK stWinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+\r
+       /**\r
+        * Sets the window title. \r
+        */\r
+       void SetWindowTitle(const std::wstring& sTitle) \r
+       {\r
+               sWindowTitle = sTitle;\r
+       };\r
+\r
+       /**\r
+        * Sets the transparency of the window.\r
+        * \remark note that this also sets the WS_EX_LAYERED style!\r
+        */\r
+       void SetTransparency(BYTE alpha, COLORREF color = 0xFF000000);\r
+\r
+       virtual bool Create();\r
+       virtual bool Create(DWORD dwStyles, HWND hParent = NULL, RECT* rect = NULL);\r
+       virtual bool CreateEx(DWORD dwExStyles, DWORD dwStyles, HWND hParent = NULL, RECT* rect = NULL);\r
+\r
+       //void MsgLoop();\r
+       bool IsWindowClosed() { return bWindowClosed; };\r
+\r
+       operator HWND() {return m_hwnd;}\r
+protected:\r
+       HINSTANCE hResource;\r
+       HWND m_hwnd;\r
+       bool bWindowClosed;\r
+       std::wstring sClassName;\r
+       std::wstring sWindowTitle;\r
+\r
+       //constructor \r
+       CWindow(HINSTANCE hInst, CONST WNDCLASSEX* wcx = NULL) : m_hwnd(NULL)\r
+               , hResource(NULL)\r
+               , bWindowClosed(FALSE)\r
+       {\r
+               hResource = hInst; \r
+               if (wcx != NULL)\r
+                       RegisterWindow(wcx);\r
+       };\r
+\r
+       // the real message handler\r
+       virtual LRESULT CALLBACK WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;\r
+\r
+       // returns a pointer the window (stored as the WindowLong)\r
+       inline static CWindow * GetObjectFromWindow(HWND hWnd)\r
+       {\r
+               return (CWindow *)GetWindowLongPtr(hWnd, GWLP_USERDATA);\r
+       }\r
+};\r
+\r
diff --git a/Utils/MiscUI/BrowseFolder.cpp b/Utils/MiscUI/BrowseFolder.cpp
new file mode 100644 (file)
index 0000000..80480ee
--- /dev/null
@@ -0,0 +1,311 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "StdAfx.h"\r
+#include <windowsx.h>\r
+#include "BrowseFolder.h"\r
+\r
+BOOL CBrowseFolder::m_bCheck = FALSE;\r
+BOOL CBrowseFolder::m_bCheck2 = FALSE;\r
+WNDPROC CBrowseFolder::CBProc = NULL;\r
+HWND CBrowseFolder::checkbox = NULL;\r
+HWND CBrowseFolder::checkbox2 = NULL;\r
+HWND CBrowseFolder::ListView = NULL;\r
+TCHAR CBrowseFolder::m_CheckText[200];         \r
+TCHAR CBrowseFolder::m_CheckText2[200];                \r
+CString CBrowseFolder::m_sDefaultPath;\r
+bool CBrowseFolder::m_DisableCheckbox2WhenCheckbox1IsChecked = false;\r
+\r
+\r
+CBrowseFolder::CBrowseFolder(void)\r
+:      m_style(0),\r
+       m_root(NULL)\r
+{\r
+       memset(m_displayName, 0, sizeof(m_displayName));\r
+       memset(m_title, 0, sizeof(m_title));\r
+       memset(m_CheckText, 0, sizeof(m_CheckText));\r
+}\r
+\r
+CBrowseFolder::~CBrowseFolder(void)\r
+{\r
+}\r
+\r
+//show the dialog\r
+CBrowseFolder::retVal CBrowseFolder::Show(HWND parent, LPTSTR path, size_t pathlen, LPCTSTR szDefaultPath /* = NULL */)\r
+{\r
+       CString temp;\r
+       temp = path;\r
+       CString sDefault;\r
+       if (szDefaultPath)\r
+               sDefault = szDefaultPath;\r
+       CBrowseFolder::retVal ret = Show(parent, temp, sDefault);\r
+       _tcscpy_s(path, pathlen, temp);\r
+       return ret;\r
+}\r
+CBrowseFolder::retVal CBrowseFolder::Show(HWND parent, CString& path, const CString& sDefaultPath /* = CString() */)\r
+{\r
+       retVal ret = OK;                //assume OK\r
+       m_sDefaultPath = sDefaultPath;\r
+       if (m_sDefaultPath.IsEmpty() && !path.IsEmpty())\r
+       {\r
+               // if the result path already contains a path, use that as the default path\r
+               m_sDefaultPath = path;\r
+       }\r
+       LPITEMIDLIST itemIDList;\r
+\r
+       BROWSEINFO browseInfo;\r
+\r
+       browseInfo.hwndOwner            = parent;\r
+       browseInfo.pidlRoot                     = m_root;\r
+       browseInfo.pszDisplayName       = m_displayName;\r
+       browseInfo.lpszTitle            = m_title;\r
+       browseInfo.ulFlags                      = m_style;\r
+       browseInfo.lpfn                         = NULL;\r
+       browseInfo.lParam                       = (LPARAM)this;\r
+       \r
+       if ((_tcslen(m_CheckText) > 0)||(!m_sDefaultPath.IsEmpty()))\r
+       {\r
+               browseInfo.lpfn = BrowseCallBackProc;\r
+       }\r
+       \r
+       itemIDList = SHBrowseForFolder(&browseInfo);\r
+\r
+       //is the dialog canceled?\r
+       if (!itemIDList)\r
+               ret = CANCEL;\r
+\r
+       if (ret != CANCEL) \r
+       {\r
+               if (!SHGetPathFromIDList(itemIDList, path.GetBuffer(MAX_PATH)))         // MAX_PATH ok. Explorer can't handle paths longer than MAX_PATH.\r
+                       ret = NOPATH;\r
+\r
+               path.ReleaseBuffer();\r
+       \r
+               LPMALLOC        shellMalloc;\r
+               HRESULT         hr;\r
+\r
+               hr = SHGetMalloc(&shellMalloc);\r
+\r
+               if (SUCCEEDED(hr)) \r
+               {\r
+                       //free memory\r
+                       shellMalloc->Free(itemIDList);\r
+                       //release interface\r
+                       shellMalloc->Release();\r
+               }\r
+       }\r
+       return ret;\r
+}\r
+\r
+void CBrowseFolder::SetInfo(LPCTSTR title)\r
+{\r
+       ASSERT(title);\r
+       \r
+       if (title)\r
+               _tcscpy_s(m_title, 200, title);\r
+}\r
+\r
+void CBrowseFolder::SetCheckBoxText(LPCTSTR checktext)\r
+{\r
+       ASSERT(checktext);\r
+\r
+       if (checktext)\r
+               _tcscpy_s(m_CheckText, 200, checktext);\r
+}\r
+\r
+void CBrowseFolder::SetCheckBoxText2(LPCTSTR checktext)\r
+{\r
+       ASSERT(checktext);\r
+\r
+       if (checktext)\r
+               _tcscpy_s(m_CheckText2, 200, checktext);\r
+}\r
+\r
+void CBrowseFolder::SetFont(HWND hwnd,LPTSTR FontName,int FontSize)\r
+{\r
+\r
+       HFONT hf;\r
+       LOGFONT lf={0};\r
+       HDC hdc=GetDC(hwnd);\r
+\r
+       GetObject(GetWindowFont(hwnd),sizeof(lf),&lf);\r
+       lf.lfWeight = FW_REGULAR;\r
+       lf.lfHeight = (LONG)FontSize;\r
+       lstrcpy( lf.lfFaceName, FontName );\r
+       hf=CreateFontIndirect(&lf);\r
+       SetBkMode(hdc,OPAQUE);\r
+       SendMessage(hwnd,WM_SETFONT,(WPARAM)hf,TRUE);\r
+       ReleaseDC(hwnd,hdc);\r
+\r
+}\r
+\r
+int CBrowseFolder::BrowseCallBackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM /*lpData*/)\r
+{\r
+       RECT ListViewRect,Dialog;\r
+       //Initialization callback message\r
+       if (uMsg == BFFM_INITIALIZED)\r
+       {\r
+               if (_tcslen(m_CheckText) > 0)\r
+               {\r
+                       bool bSecondCheckbox = (_tcslen(m_CheckText2)!=0);\r
+                       //Rectangles for getting the positions\r
+                       checkbox = CreateWindowEx(      0,\r
+                               _T("BUTTON"),\r
+                               m_CheckText,\r
+                               WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|BS_AUTOCHECKBOX,\r
+                               0,100,100,50,\r
+                               hwnd,\r
+                               0,\r
+                               NULL,\r
+                               NULL);\r
+                       if (checkbox == NULL)\r
+                               return 0;\r
+\r
+                       if (bSecondCheckbox)\r
+                       {\r
+                               //Rectangles for getting the positions\r
+                               checkbox2 = CreateWindowEx(     0,\r
+                                       _T("BUTTON"),\r
+                                       m_CheckText2,\r
+                                       WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|BS_AUTOCHECKBOX,\r
+                                       0,100,100,50,\r
+                                       hwnd,\r
+                                       0,\r
+                                       NULL,\r
+                                       NULL);\r
+                               if (checkbox2 == NULL)\r
+                                       return 0;\r
+                       }\r
+\r
+                       ListView = FindWindowEx(hwnd,NULL,_T("SysTreeView32"),NULL);\r
+                       if (ListView == NULL)\r
+                               ListView = FindWindowEx(hwnd,NULL,_T("SHBrowseForFolder ShellNameSpace Control"),NULL);\r
+\r
+                       if (ListView == NULL)\r
+                               return 0;\r
+\r
+                       //Gets the dimensions of the windows\r
+                       GetWindowRect(hwnd,&Dialog);\r
+                       GetWindowRect(ListView,&ListViewRect);\r
+                       POINT pt;\r
+                       pt.x = ListViewRect.left;\r
+                       pt.y = ListViewRect.top;\r
+                       ScreenToClient(hwnd, &pt);\r
+                       ListViewRect.top = pt.y;\r
+                       ListViewRect.left = pt.x;\r
+                       pt.x = ListViewRect.right;\r
+                       pt.y = ListViewRect.bottom;\r
+                       ScreenToClient(hwnd, &pt);\r
+                       ListViewRect.bottom = pt.y;\r
+                       ListViewRect.right = pt.x;\r
+                       //Sets the list view controls dimensions\r
+                       SetWindowPos(ListView,0,ListViewRect.left,\r
+                               bSecondCheckbox ? ListViewRect.top+40 : ListViewRect.top+20,\r
+                               (ListViewRect.right-ListViewRect.left),\r
+                               bSecondCheckbox ? (ListViewRect.bottom - ListViewRect.top)-40 : (ListViewRect.bottom - ListViewRect.top)-20,\r
+                               SWP_NOZORDER);\r
+                       //Sets the window positions of checkbox and dialog controls\r
+                       SetWindowPos(checkbox,HWND_BOTTOM,ListViewRect.left,\r
+                               ListViewRect.top,\r
+                               (ListViewRect.right-ListViewRect.left),\r
+                               14,\r
+                               SWP_NOZORDER);\r
+                       if (bSecondCheckbox)\r
+                       {\r
+                               SetWindowPos(checkbox2,HWND_BOTTOM,ListViewRect.left,\r
+                                       ListViewRect.top+20,\r
+                                       (ListViewRect.right-ListViewRect.left),\r
+                                       14,\r
+                                       SWP_NOZORDER);\r
+                       }\r
+                       HWND label = FindWindowEx(hwnd, NULL, _T("STATIC"), NULL);\r
+                       if (label)\r
+                       {\r
+                               HFONT hFont = (HFONT)::SendMessage(label, WM_GETFONT, 0, 0);\r
+                               LOGFONT lf = {0};\r
+                               GetObject(hFont, sizeof(lf), &lf);\r
+                               HFONT hf2 = CreateFontIndirect(&lf);\r
+                               ::SendMessage(checkbox, WM_SETFONT, (WPARAM)hf2, TRUE);\r
+                               if (bSecondCheckbox)\r
+                                       ::SendMessage(checkbox2, WM_SETFONT, (WPARAM)hf2, TRUE);\r
+                       }\r
+                       else\r
+                       {\r
+                               //Sets the fonts of static controls\r
+                               SetFont(checkbox,_T("MS Sans Serif"),12);\r
+                               if (bSecondCheckbox)\r
+                                       SetFont(checkbox2,_T("MS Sans Serif"),12);\r
+                       }\r
+\r
+                       // Subclass the checkbox control. \r
+                       CBProc = (WNDPROC) SetWindowLongPtr(checkbox,GWLP_WNDPROC, (LONG_PTR) CheckBoxSubclassProc); \r
+                       //Sets the checkbox to checked position\r
+                       SendMessage(checkbox,BM_SETCHECK,(WPARAM)m_bCheck,0);\r
+                       if (bSecondCheckbox)\r
+                       {\r
+                               CBProc = (WNDPROC) SetWindowLongPtr(checkbox2,GWLP_WNDPROC, (LONG_PTR) CheckBoxSubclassProc2); \r
+                               SendMessage(checkbox2,BM_SETCHECK,(WPARAM)m_bCheck,0);\r
+                       }\r
+                       // send a resize message to the resized list view control. Otherwise it won't show\r
+                       // up properly until the user resizes the window!\r
+                       SendMessage(ListView, WM_SIZE, SIZE_RESTORED, MAKELONG(ListViewRect.right-ListViewRect.left, bSecondCheckbox ? (ListViewRect.bottom - ListViewRect.top)-40 : (ListViewRect.bottom - ListViewRect.top)-20));\r
+               }\r
+               \r
+               // now set the default directory\r
+               SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)(LPCTSTR)m_sDefaultPath);\r
+       }\r
+       if (uMsg == BFFM_SELCHANGED)\r
+       {\r
+               // Set the status window to the currently selected path.\r
+               TCHAR szDir[MAX_PATH];\r
+               if (SHGetPathFromIDList((LPITEMIDLIST)lParam, szDir))\r
+               {\r
+                       SendMessage(hwnd,BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);\r
+               }\r
+       }\r
+       \r
+       return 0;\r
+}\r
+\r
+LRESULT CBrowseFolder::CheckBoxSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)\r
+{\r
+       if (uMsg == WM_LBUTTONUP)\r
+       {\r
+               m_bCheck = (SendMessage(hwnd,BM_GETCHECK,0,0)==BST_UNCHECKED);\r
+               if (m_bCheck && m_DisableCheckbox2WhenCheckbox1IsChecked)\r
+               {\r
+                       ::EnableWindow(checkbox2, !m_bCheck);\r
+               }\r
+               else\r
+                       ::EnableWindow(checkbox2, true);\r
+       }\r
+\r
+       return CallWindowProc(CBProc, hwnd, uMsg, \r
+               wParam, lParam); \r
+} \r
+\r
+LRESULT CBrowseFolder::CheckBoxSubclassProc2(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)\r
+{\r
+       if (uMsg == WM_LBUTTONUP)\r
+       {\r
+               m_bCheck2 = (SendMessage(hwnd,BM_GETCHECK,0,0)==BST_UNCHECKED);\r
+       }\r
+\r
+       return CallWindowProc(CBProc, hwnd, uMsg, \r
+               wParam, lParam); \r
+} \r
diff --git a/Utils/MiscUI/BrowseFolder.h b/Utils/MiscUI/BrowseFolder.h
new file mode 100644 (file)
index 0000000..f34562d
--- /dev/null
@@ -0,0 +1,87 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * A simple wrapper class for the SHBrowseForFolder API.\r
+ * Help-Link: ms-help://MS.VSCC/MS.MSDNVS/shellcc/platform/Shell/Functions/SHBrowseForFolder.htm\r
+ */\r
+class CBrowseFolder\r
+{\r
+public:\r
+       enum retVal\r
+       {\r
+               CANCEL = 0,             ///< the user has pressed cancel\r
+               NOPATH,                 ///< no folder was selected\r
+               OK                              ///< everything went just fine\r
+       };\r
+public:\r
+       //constructor / deconstructor\r
+       CBrowseFolder(void);\r
+       ~CBrowseFolder(void);\r
+public:\r
+       DWORD m_style;          ///< styles of the dialog.\r
+       /**\r
+        * Sets the info text of the dialog. Call this method before calling Show().\r
+        */\r
+       void SetInfo(LPCTSTR title);\r
+       /*\r
+        * Sets the text to show for the checkbox. If this method is not called,\r
+        * then no checkbox is added.\r
+        */\r
+       void SetCheckBoxText(LPCTSTR checktext);\r
+       void SetCheckBoxText2(LPCTSTR checktext);\r
+       /**\r
+        * Shows the Dialog. \r
+        * \param parent [in] window handle of the parent window.\r
+        * \param path [out] the path to the folder which the user has selected \r
+        * \return one of CANCEL, NOPATH or OK\r
+        */\r
+       CBrowseFolder::retVal Show(HWND parent, CString& path, const CString& sDefaultPath = CString());\r
+       CBrowseFolder::retVal Show(HWND parent, LPTSTR path, size_t pathlen, LPCTSTR szDefaultPath = NULL);\r
+\r
+       /**\r
+        * If this is set to true, then the second checkbox gets disabled as soon as the first\r
+        * checkbox is checked. If the first checkbox is unchecked, then the second checkbox is enabled\r
+        * again.\r
+        */\r
+       void DisableCheckBox2WhenCheckbox1IsEnabled(bool bSet = true) {m_DisableCheckbox2WhenCheckbox1IsChecked = bSet;}\r
+\r
+       static BOOL m_bCheck;           ///< state of the checkbox on closing the dialog\r
+       static BOOL m_bCheck2;\r
+       TCHAR m_title[200];\r
+protected:\r
+       static void SetFont(HWND hwnd,LPTSTR FontName,int FontSize);\r
+\r
+       static int CALLBACK BrowseCallBackProc(HWND  hwnd,UINT  uMsg,LPARAM  lParam,LPARAM  lpData);\r
+       static LRESULT APIENTRY CheckBoxSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);\r
+       static LRESULT APIENTRY CheckBoxSubclassProc2(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);\r
+               \r
+       static WNDPROC CBProc;\r
+       static HWND checkbox;\r
+       static HWND checkbox2;\r
+       static HWND ListView;\r
+       static CString m_sDefaultPath;\r
+       TCHAR m_displayName[200];\r
+       LPITEMIDLIST m_root;\r
+       static TCHAR m_CheckText[200];\r
+       static TCHAR m_CheckText2[200];\r
+       static bool m_DisableCheckbox2WhenCheckbox1IsChecked;\r
+};\r
diff --git a/Utils/MiscUI/BufferDC.cpp b/Utils/MiscUI/BufferDC.cpp
new file mode 100644 (file)
index 0000000..1566d9a
--- /dev/null
@@ -0,0 +1,120 @@
+#include "StdAfx.h"\r
+#include "BufferDC.h"\r
+\r
+IMPLEMENT_DYNAMIC(CBufferDC, CPaintDC)\r
+\r
+CBufferDC::CBufferDC(CWnd* pWnd) : CPaintDC(pWnd)\r
+{\r
+       if (pWnd != NULL && CPaintDC::m_hDC != NULL)\r
+       {\r
+               m_hOutputDC    = CPaintDC::m_hDC;\r
+               m_hAttributeDC = CPaintDC::m_hAttribDC;\r
+\r
+               pWnd->GetClientRect(&m_ClientRect);\r
+\r
+               m_hMemoryDC = ::CreateCompatibleDC(m_hOutputDC);\r
+\r
+               m_hPaintBitmap =\r
+                       ::CreateCompatibleBitmap(\r
+                                       m_hOutputDC,\r
+                                       m_ClientRect.right  - m_ClientRect.left,\r
+                                       m_ClientRect.bottom - m_ClientRect.top);\r
+\r
+               m_hOldBitmap = (HBITMAP)::SelectObject(m_hMemoryDC, m_hPaintBitmap);\r
+\r
+               CPaintDC::m_hDC       = m_hMemoryDC;\r
+               CPaintDC::m_hAttribDC = m_hMemoryDC;\r
+       }\r
+\r
+       m_bBoundsUpdated = FALSE;\r
+}\r
+\r
+CBufferDC::~CBufferDC(void)\r
+{\r
+       Flush();\r
+\r
+       ::SelectObject(m_hMemoryDC, m_hOldBitmap);\r
+       ::DeleteObject(m_hPaintBitmap);\r
+\r
+       CPaintDC::m_hDC           = m_hOutputDC;\r
+       CPaintDC::m_hAttribDC = m_hAttributeDC;\r
+\r
+       ::DeleteDC(m_hMemoryDC);\r
+}\r
+\r
+void CBufferDC::Flush()\r
+{\r
+       ::BitBlt(\r
+               m_hOutputDC,\r
+               m_ClientRect.left, m_ClientRect.top,\r
+               m_ClientRect.right  - m_ClientRect.left, \r
+               m_ClientRect.bottom - m_ClientRect.top,\r
+               m_hMemoryDC,\r
+               0, 0,\r
+               SRCCOPY);\r
+}\r
+\r
+UINT CBufferDC::SetBoundsRect( LPCRECT lpRectBounds, UINT flags )\r
+{\r
+       if (lpRectBounds != NULL)\r
+       {\r
+               if (m_ClientRect.right  - m_ClientRect.left > lpRectBounds->right  - lpRectBounds->left ||\r
+                       m_ClientRect.bottom - m_ClientRect.top  > lpRectBounds->bottom - lpRectBounds->top)\r
+               {\r
+                       lpRectBounds = &m_ClientRect;\r
+               }\r
+\r
+               HBITMAP bmp =\r
+                       ::CreateCompatibleBitmap(\r
+                                       m_hOutputDC, \r
+                                       lpRectBounds->right - lpRectBounds->left, \r
+                                       lpRectBounds->bottom - lpRectBounds->top);\r
+\r
+               HDC tmpDC  = ::CreateCompatibleDC(m_hOutputDC);\r
+               \r
+               HBITMAP oldBmp = (HBITMAP)::SelectObject(tmpDC, bmp);\r
+\r
+               ::BitBlt(\r
+                       tmpDC,\r
+                       m_ClientRect.left, m_ClientRect.top,\r
+                       m_ClientRect.right  - m_ClientRect.left, \r
+                       m_ClientRect.bottom - m_ClientRect.top,\r
+                       m_hMemoryDC,\r
+                       0, 0,\r
+                       SRCCOPY);\r
+\r
+               ::SelectObject(tmpDC, oldBmp);\r
+               ::DeleteDC(tmpDC);\r
+\r
+               HBITMAP old = (HBITMAP)::SelectObject(m_hMemoryDC, bmp);\r
+\r
+               if (old != NULL && old != m_hPaintBitmap)\r
+               {\r
+                       ::DeleteObject(old);\r
+               }\r
+\r
+               if (m_hPaintBitmap != NULL)\r
+               {\r
+                       ::DeleteObject(m_hPaintBitmap);\r
+               }\r
+\r
+               m_hPaintBitmap = bmp;\r
+\r
+               m_ClientRect = *lpRectBounds;\r
+               m_bBoundsUpdated = TRUE;\r
+       }\r
+\r
+       return CPaintDC::SetBoundsRect(lpRectBounds, flags);\r
+}\r
+\r
+BOOL CBufferDC::RestoreDC( int nSavedDC )\r
+{\r
+       BOOL ret = CPaintDC::RestoreDC(nSavedDC);\r
+\r
+       if (m_bBoundsUpdated)\r
+       {\r
+               SelectObject(m_hPaintBitmap);\r
+       }\r
+\r
+       return ret;\r
+}
\ No newline at end of file
diff --git a/Utils/MiscUI/BufferDC.h b/Utils/MiscUI/BufferDC.h
new file mode 100644 (file)
index 0000000..b172eff
--- /dev/null
@@ -0,0 +1,31 @@
+#pragma once\r
+#include "afxwin.h"\r
+\r
+class CBufferDC :\r
+       public CPaintDC\r
+{\r
+       DECLARE_DYNAMIC(CBufferDC)\r
+\r
+private:\r
+       HDC m_hOutputDC;\r
+       HDC m_hAttributeDC;\r
+       HDC m_hMemoryDC;\r
+\r
+       HBITMAP  m_hPaintBitmap;\r
+       HBITMAP  m_hOldBitmap;\r
+\r
+       RECT m_ClientRect;\r
+\r
+       BOOL m_bBoundsUpdated;\r
+\r
+public:\r
+       CBufferDC(CWnd* pWnd);\r
+       ~CBufferDC(void);\r
+\r
+private:\r
+       void Flush();\r
+\r
+public:\r
+       UINT SetBoundsRect(LPCRECT lpRectBounds, UINT flags);\r
+       virtual BOOL RestoreDC(int nSavedDC);\r
+};\r
diff --git a/Utils/MiscUI/CMessageBox_1.jpg b/Utils/MiscUI/CMessageBox_1.jpg
new file mode 100644 (file)
index 0000000..b8881cf
Binary files /dev/null and b/Utils/MiscUI/CMessageBox_1.jpg differ
diff --git a/Utils/MiscUI/CMessageBox_2.jpg b/Utils/MiscUI/CMessageBox_2.jpg
new file mode 100644 (file)
index 0000000..341b1e6
Binary files /dev/null and b/Utils/MiscUI/CMessageBox_2.jpg differ
diff --git a/Utils/MiscUI/CMessageBox_3.jpg b/Utils/MiscUI/CMessageBox_3.jpg
new file mode 100644 (file)
index 0000000..0af3839
Binary files /dev/null and b/Utils/MiscUI/CMessageBox_3.jpg differ
diff --git a/Utils/MiscUI/CMessageBox_4.jpg b/Utils/MiscUI/CMessageBox_4.jpg
new file mode 100644 (file)
index 0000000..2dd25ae
Binary files /dev/null and b/Utils/MiscUI/CMessageBox_4.jpg differ
diff --git a/Utils/MiscUI/CMessageBox_5.jpg b/Utils/MiscUI/CMessageBox_5.jpg
new file mode 100644 (file)
index 0000000..01b09f3
Binary files /dev/null and b/Utils/MiscUI/CMessageBox_5.jpg differ
diff --git a/Utils/MiscUI/Cursor.h b/Utils/MiscUI/Cursor.h
new file mode 100644 (file)
index 0000000..c540fed
--- /dev/null
@@ -0,0 +1,109 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup Utils\r
+ * Helper class for setting mouse cursors.\n\r
+ * There are two ways of using this class:\r
+ * -# Just declare a CCursor object with the\r
+ *    required cursor. As soon as the object\r
+ *    goes out of scope the previous cursor\r
+ *    is restored.\r
+ *    \code\r
+ *    someMethod()\r
+ *    {\r
+ *      CCursor(IDC_WAIT);\r
+ *      //do something here\r
+ *    }\r
+ *    //now CCursor is out of scope and the default cursor is restored\r
+ *    \endcode\r
+ * -# use the object the usual way. Declare a CCursor object\r
+ *    and use the methods to set the cursors.\r
+ *\r
+ * \remark the class can be used on Win95 and NT4 too, but the\r
+ * hand cursor won't be available.\r
+ */\r
+class CCursor\r
+{\r
+public:\r
+       /**\r
+        * Constructs a CCursor object.\r
+        */\r
+       CCursor(LPCT