/** * Copyright (c) 2015 Daniel Molkentin . All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ #include "stdafx.h" #include "OCContextMenu.h" #include "OCClientInterface.h" #include #include #include #include extern HINSTANCE g_hInst; extern long g_cDllRef; #define IDM_SHARE 0 OCContextMenu::OCContextMenu(void) : m_cRef(1) , m_pszMenuText(L"&Share") , m_pszVerb("ocshare") , m_pwszVerb(L"ocshare") , m_pszVerbCanonicalName("OCShareViaOC") , m_pwszVerbCanonicalName(L"OCShareViaOC") , m_pszVerbHelpText("Share via ownCloud") , m_pwszVerbHelpText(L"Share via ownCloud") { InterlockedIncrement(&g_cDllRef); } OCContextMenu::~OCContextMenu(void) { InterlockedDecrement(&g_cDllRef); } void OCContextMenu::OnVerbDisplayFileName(HWND hWnd) { OCClientInterface::ShareObject(std::wstring(m_szSelectedFile)); } #pragma region IUnknown // Query to the interface the component supported. IFACEMETHODIMP OCContextMenu::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(OCContextMenu, IContextMenu), QITABENT(OCContextMenu, IShellExtInit), { 0 }, }; return QISearch(this, qit, riid, ppv); } // Increase the reference count for an interface on an object. IFACEMETHODIMP_(ULONG) OCContextMenu::AddRef() { return InterlockedIncrement(&m_cRef); } // Decrease the reference count for an interface on an object. IFACEMETHODIMP_(ULONG) OCContextMenu::Release() { ULONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; } return cRef; } #pragma endregion #pragma region IShellExtInit // Initialize the context menu handler. IFACEMETHODIMP OCContextMenu::Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID) { if (!pDataObj) { return E_INVALIDARG; } HRESULT hr = E_FAIL; FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stm; if (SUCCEEDED(pDataObj->GetData(&fe, &stm))) { // Get an HDROP handle. HDROP hDrop = static_cast(GlobalLock(stm.hGlobal)); if (hDrop) { // Ignore multi-selections UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); if (nFiles == 1) { // Get the path of the file. if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile))) { hr = S_OK; } } GlobalUnlock(stm.hGlobal); } ReleaseStgMedium(&stm); } // If any value other than S_OK is returned from the method, the context // menu item is not displayed. return hr; } #pragma endregion #pragma region IContextMenu void InsertSeperator(HMENU hMenu, UINT indexMenu) { // Add a separator. MENUITEMINFO sep = { sizeof(sep) }; sep.fMask = MIIM_TYPE; sep.fType = MFT_SEPARATOR; InsertMenuItem(hMenu, indexMenu, TRUE, &sep); } IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { // If uFlags include CMF_DEFAULTONLY then we should not do anything. if (CMF_DEFAULTONLY & uFlags) { return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0)); } OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo(); bool skip = true; size_t selectedFileLength = wcslen(m_szSelectedFile); for (const std::wstring path : info.watchedDirectories) { if (StringUtil::isDescendantOf(m_szSelectedFile, selectedFileLength, path)) { skip = false; break; } } if (skip) { return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0)); } InsertSeperator(hMenu, indexMenu); indexMenu++; assert(!info.shareMenuTitle.empty()); MENUITEMINFO mii = { sizeof(mii) }; mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE; mii.wID = idCmdFirst + IDM_SHARE; mii.fType = MFT_STRING; mii.dwTypeData = &info.shareMenuTitle[0]; mii.fState = MFS_ENABLED; if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii)) { return HRESULT_FROM_WIN32(GetLastError()); } indexMenu++; InsertSeperator(hMenu, indexMenu); // Return an HRESULT value with the severity set to SEVERITY_SUCCESS. // Set the code value to the offset of the largest command identifier // that was assigned, plus one (1). return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1)); } IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { // For the Unicode case, if the high-order word is not zero, the // command's verb string is in lpcmi->lpVerbW. if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) { // Is the verb supported by this context menu extension? if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0) { OnVerbDisplayFileName(pici->hwnd); } else { // If the verb is not recognized by the context menu handler, it // must return E_FAIL to allow it to be passed on to the other // context menu handlers that might implement that verb. return E_FAIL; } } // If the command cannot be identified through the verb string, then // check the identifier offset. else { // Is the command identifier offset supported by this context menu // extension? if (LOWORD(pici->lpVerb) == IDM_SHARE) { OnVerbDisplayFileName(pici->hwnd); } else { // If the verb is not recognized by the context menu handler, it // must return E_FAIL to allow it to be passed on to the other // context menu handlers that might implement that verb. return E_FAIL; } } return S_OK; } IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax) { HRESULT hr = E_INVALIDARG; if (idCommand == IDM_SHARE) { switch (uFlags) { case GCS_HELPTEXTW: // Only useful for pre-Vista versions of Windows that have a // Status bar. hr = StringCchCopy(reinterpret_cast(pszName), cchMax, m_pwszVerbHelpText); break; case GCS_VERBW: // GCS_VERBW is an optional feature that enables a caller to // discover the canonical name for the verb passed in through // idCommand. hr = StringCchCopy(reinterpret_cast(pszName), cchMax, m_pwszVerbCanonicalName); break; default: hr = S_OK; } } // If the command (idCommand) is not supported by this context menu // extension handler, return E_INVALIDARG. return hr; } #pragma endregion