|
// Copyright (C) 2011 Fog Creek Software
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include "THgShell_h.h"
#include "TortoiseHgKeyboard.h"
#include "TortoiseUtils.h"
#include "StringUtils.h"
#include "RunDialog.h"
#include "GlobalData.h"
// Each Windows Explorer window is launched in a separate thread. Therefore, use thread
// local storage so that each thread can have its own hook.
CTlsPtr<CTortoiseHgKeyboard> CTortoiseHgKeyboard::g_pObject;
STDMETHODIMP CTortoiseHgKeyboard::SetSite(IUnknown* pUnkSite)
{
if (g_pObject == NULL)
{
ATLASSERT(m_hHook == NULL);
g_pObject = this;
m_hHook = ::SetWindowsHookEx(WH_KEYBOARD, HookProc, NULL,
::GetCurrentThreadId());
}
else if (pUnkSite == NULL && g_pObject == this)
{
::UnhookWindowsHookEx(m_hHook);
m_hHook = NULL;
g_pObject = NULL;
}
return IObjectWithSiteImpl<CTortoiseHgKeyboard>::SetSite(pUnkSite);
}
LRESULT CALLBACK CTortoiseHgKeyboard::HookProc(int code, WPARAM wParam, LPARAM lParam)
{
ATLASSERT(g_pObject != NULL);
ATLASSERT(g_pObject->m_hHook != NULL);
// Ignore repeated key messages when the user holds a key down, as well as calls when
// a key is released.
if (!(lParam & 0x80000000L) && !(lParam & 0x40000000L))
{
// Get the state of the Alt, Control, and Shift keys.
WORD wFlags = 0;
if (::GetKeyState(VK_MENU) & 0x8000) wFlags |= KSF_ALT;
if (::GetKeyState(VK_CONTROL) & 0x8000) wFlags |= KSF_CONTROL;
if (::GetKeyState(VK_SHIFT) & 0x8000) wFlags |= KSF_SHIFT;
// Look for a matching keyboard shortcut.
for (int i = 0; i < KeyShortcutListCount; i++)
{
if (KeyShortcutList[i].wKey == wParam && KeyShortcutList[i].wFlags == wFlags)
{
// Get the selected files and the current directory.
CAtlList<CString> listFiles;
g_pObject->GetSelectedFiles(listFiles);
CString strFolder;
g_pObject->GetFolder(strFolder);
// Run the command corresponding to the shortcut.
RunDialog(g_pObject->GetExplorerWindow(), KeyShortcutList[i].strName,
listFiles, strFolder);
}
}
}
return ::CallNextHookEx(g_pObject->m_hHook, code, wParam, lParam);
}
bool CTortoiseHgKeyboard::GetActiveShellView(IShellView** ppSV)
{
CComQIPtr<IWebBrowser2> spWB = m_spUnkSite;
if (spWB == NULL) return false;
CComPtr<IDispatch> spApp;
if (FAILED(spWB->get_Application(&spApp))) return false;
CComQIPtr<IServiceProvider> spSP = spApp;
if (spSP == NULL) return false;
CComPtr<IShellBrowser> spSB;
if (FAILED(spSP->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&spSB)))
{
return false;
}
return SUCCEEDED(spSB->QueryActiveShellView(ppSV));
}
bool CTortoiseHgKeyboard::GetSelectedFiles(CAtlList<CString>& listFiles)
{
// Get the active shell view object.
CComPtr<IShellView> spSV;
if (!GetActiveShellView(&spSV)) return false;
// Obtain the current selection as an IDataObject.
CComPtr<IDataObject> spDO;
if (FAILED(spSV->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (void**)&spDO)))
{
return false;
}
// Obtain a drop handle from the IDataObject.
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg;
stg.tymed = TYMED_HGLOBAL;
if (FAILED(spDO->GetData(&fmt, &stg))) return false;
// Enumerate through the files and add each one to the list.
HDROP hDrop = (HDROP)::GlobalLock(stg.hGlobal);
UINT uNumFiles = ::DragQueryFile(hDrop, (UINT)-1, NULL, 0);
for (UINT i = 0; i < uNumFiles; i++)
{
TCHAR szPath[MAX_PATH];
::DragQueryFile(hDrop, i, szPath, MAX_PATH);
listFiles.AddTail(szPath);
}
::GlobalUnlock(stg.hGlobal);
::ReleaseStgMedium(&stg);
return true;
}
#define FILE_PREFIX "file:///"
#define FILE_PREFIX_LENGTH (sizeof(FILE_PREFIX) / sizeof(TCHAR) - 1)
bool CTortoiseHgKeyboard::GetFolder(CString& strFolder)
{
// Get the URL of the current location.
CComQIPtr<IWebBrowser2> spWB = m_spUnkSite;
if (spWB == NULL) return false;
BSTR bstrURL;
if (FAILED(spWB->get_LocationURL(&bstrURL))) return false;
strFolder = bstrURL;
::SysFreeString(bstrURL);
// Unescape the URL.
LPTSTR lpszFolder = strFolder.LockBuffer();
::UrlUnescape(lpszFolder, NULL, NULL, URL_UNESCAPE_INPLACE);
strFolder.UnlockBuffer();
// Check if it's a file URL and extract the path.
if (StartsWith(strFolder, FILE_PREFIX))
{
strFolder = strFolder.Mid(FILE_PREFIX_LENGTH);
}
return true;
}
HWND CTortoiseHgKeyboard::GetExplorerWindow()
{
CComQIPtr<IWebBrowser2> spWB = m_spUnkSite;
if (spWB == NULL) return NULL;
SHANDLE_PTR hWnd;
if (FAILED(spWB->get_HWND(&hWnd))) return NULL;
return (HWND)hWnd;
}
CTortoiseHgKeyboard::CTortoiseHgKeyboard() :
m_hHook(NULL)
{
}
|
Loading...