shell/windows: Use a submenu to include private link actions
authorJocelyn Turcotte <jturcotte@woboq.com>
Tue, 4 Jul 2017 14:58:55 +0000 (16:58 +0200)
committerChristian Kamm <mail@ckamm.de>
Fri, 7 Jul 2017 08:49:51 +0000 (10:49 +0200)
Refactor things a bit to be able to reuse some code and
clean things up.

shell_integration/windows/OCContextMenu/OCClientInterface.cpp
shell_integration/windows/OCContextMenu/OCClientInterface.h
shell_integration/windows/OCContextMenu/OCContextMenu.cpp
shell_integration/windows/OCContextMenu/OCContextMenu.h
shell_integration/windows/OCUtil/RemotePathChecker.cpp
shell_integration/windows/OCUtil/StringUtil.h

index 05b23ec09aa875ec37687cf7e88b970620f5443b..56facf60064cca7e6d823424005b4ef2a0c5df64 100644 (file)
@@ -45,7 +45,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
     if (!socket.Connect(pipename)) {
         return {};
     }
-    socket.SendMsg(L"SHARE_MENU_TITLE\n");
+    socket.SendMsg(L"GET_STRINGS\n");
 
     ContextMenuInfo info;
     std::wstring response;
@@ -56,9 +56,21 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
                 wstring responsePath = response.substr(14); // length of REGISTER_PATH
                 info.watchedDirectories.push_back(responsePath);
             }
-            else if (StringUtil::begins_with(response, wstring(L"SHARE_MENU_TITLE:"))) {
-                info.shareMenuTitle = response.substr(17); // length of SHARE_MENU_TITLE:
-                break; // Stop once we received the last sent request
+            else if (StringUtil::begins_with(response, wstring(L"STRING:"))) {
+                wstring stringName, stringValue;
+                if (!StringUtil::extractChunks(response, stringName, stringValue))
+                    continue;
+                if (stringName == L"SHARE_MENU_TITLE")
+                    info.shareMenuTitle = move(stringValue);
+                else if (stringName == L"CONTEXT_MENU_TITLE")
+                    info.contextMenuTitle = move(stringValue);
+                else if (stringName == L"COPY_PRIVATE_LINK_TITLE")
+                    info.copyLinkMenuTitle = move(stringValue);
+                else if (stringName == L"EMAIL_PRIVATE_LINK_TITLE")
+                    info.emailLinkMenuTitle = move(stringValue);
+            }
+            else if (StringUtil::begins_with(response, wstring(L"GET_STRINGS:END"))) {
+                break; // Stop once we completely received the last sent request
             }
         }
         else {
@@ -69,7 +81,22 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
     return info;
 }
 
-void OCClientInterface::ShareObject(const std::wstring &path)
+void OCClientInterface::RequestShare(const std::wstring &path)
+{
+    SendRequest(L"SHARE", path);
+}
+
+void OCClientInterface::RequestCopyLink(const std::wstring &path)
+{
+    SendRequest(L"COPY_PRIVATE_LINK", path);
+}
+
+void OCClientInterface::RequestEmailLink(const std::wstring &path)
+{
+    SendRequest(L"EMAIL_PRIVATE_LINK", path);
+}
+
+void OCClientInterface::SendRequest(wchar_t *verb, const std::wstring &path)
 {
     auto pipename = CommunicationSocket::DefaultPipePath();
 
@@ -82,7 +109,7 @@ void OCClientInterface::ShareObject(const std::wstring &path)
     }
 
     wchar_t msg[SOCK_BUFFER] = { 0 };
-    if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"SHARE:%s\n", path.c_str())))
+    if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"%s:%s\n", verb, path.c_str())))
     {
         socket.SendMsg(msg);
     }
index 00d4470eafd0e1563fd32ef7cbd6e3ebb45a61a2..74e6364fec839eb675e4efcbdef55a26a75e7e43 100644 (file)
@@ -45,10 +45,19 @@ class OCClientInterface
 public:
     struct ContextMenuInfo {
         std::vector<std::wstring> watchedDirectories;
+        std::wstring contextMenuTitle;
         std::wstring shareMenuTitle;
+        std::wstring copyLinkMenuTitle;
+        std::wstring emailLinkMenuTitle;
     };
     static ContextMenuInfo FetchInfo();
-    static void ShareObject(const std::wstring &path);
+
+    static void RequestShare(const std::wstring &path);
+    static void RequestCopyLink(const std::wstring &path);
+    static void RequestEmailLink(const std::wstring &path);
+
+private:
+    static void SendRequest(wchar_t *verb, const std::wstring &path);
 };
 
 #endif //ABSTRACTSOCKETHANDLER_H
index a5558fc0a3e83e714009685a909c437fdb3ed69c..bda86b86d8308fe528d5e12fa5f31a445ff20253 100644 (file)
 extern HINSTANCE g_hInst;
 extern long g_cDllRef;
 
-#define IDM_SHARE             0
-
-
+#define IDM_SHARE 0
+#define IDM_COPYLINK 1
+#define IDM_EMAILLINK 2
 
 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);
 }
@@ -48,9 +41,19 @@ OCContextMenu::~OCContextMenu(void)
 }
 
 
-void OCContextMenu::OnVerbDisplayFileName(HWND hWnd)
+void OCContextMenu::OnVerbShare(HWND hWnd)
+{
+    OCClientInterface::RequestShare(std::wstring(m_szSelectedFile));
+}
+
+void OCContextMenu::OnVerbCopyLink(HWND hWnd)
+{
+    OCClientInterface::RequestCopyLink(std::wstring(m_szSelectedFile));
+}
+
+void OCContextMenu::OnVerbEmailLink(HWND hWnd)
 {
-    OCClientInterface::ShareObject(std::wstring(m_szSelectedFile));
+    OCClientInterface::RequestEmailLink(std::wstring(m_szSelectedFile));
 }
 
 
@@ -164,29 +167,61 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
         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))
+    InsertSeperator(hMenu, indexMenu++);
+
+    HMENU hSubmenu = CreateMenu();
     {
-        return HRESULT_FROM_WIN32(GetLastError());
+        MENUITEMINFO mii = { sizeof(mii) };
+        mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
+        mii.hSubMenu = hSubmenu;
+        mii.fType = MFT_STRING;
+        mii.dwTypeData = &info.contextMenuTitle[0];
+
+        if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii))
+            return HRESULT_FROM_WIN32(GetLastError());
     }
+    InsertSeperator(hMenu, indexMenu++);
 
-    indexMenu++;
-    InsertSeperator(hMenu, indexMenu);
+    UINT indexSubMenu = 0;
+    {
+        assert(!info.shareMenuTitle.empty());
+        MENUITEMINFO mii = { sizeof(mii) };
+        mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
+        mii.wID = idCmdFirst + IDM_SHARE;
+        mii.fType = MFT_STRING;
+        mii.dwTypeData = &info.shareMenuTitle[0];
+
+        if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
+            return HRESULT_FROM_WIN32(GetLastError());
+    }
+    {
+        assert(!info.copyLinkMenuTitle.empty());
+        MENUITEMINFO mii = { sizeof(mii) };
+        mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
+        mii.wID = idCmdFirst + IDM_COPYLINK;
+        mii.fType = MFT_STRING;
+        mii.dwTypeData = &info.copyLinkMenuTitle[0];
+
+        if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
+            return HRESULT_FROM_WIN32(GetLastError());
+    }
+    {
+        assert(!info.emailLinkMenuTitle.empty());
+        MENUITEMINFO mii = { sizeof(mii) };
+        mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
+        mii.wID = idCmdFirst + IDM_EMAILLINK;
+        mii.fType = MFT_STRING;
+        mii.dwTypeData = &info.emailLinkMenuTitle[0];
+
+        if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
+            return HRESULT_FROM_WIN32(GetLastError());
+    }
 
 
     // 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));
+    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_EMAILLINK + 1));
 }
 
 IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
@@ -197,12 +232,16 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
     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);
+        if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocshare") == 0) {
+            OnVerbShare(pici->hwnd);
         }
-        else
-        {
+        else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"occopylink") == 0) {
+            OnVerbCopyLink(pici->hwnd);
+        }
+        else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocemaillink") == 0) {
+            OnVerbEmailLink(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.
@@ -216,12 +255,16 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
     {
         // Is the command identifier offset supported by this context menu 
         // extension?
-        if (LOWORD(pici->lpVerb) == IDM_SHARE)
-        {
-            OnVerbDisplayFileName(pici->hwnd);
+        if (LOWORD(pici->lpVerb) == IDM_SHARE) {
+            OnVerbShare(pici->hwnd);
+        }
+        else if (LOWORD(pici->lpVerb) == IDM_COPYLINK) {
+            OnVerbCopyLink(pici->hwnd);
+        }
+        else if (LOWORD(pici->lpVerb) == IDM_EMAILLINK) {
+            OnVerbEmailLink(pici->hwnd);
         }
-        else
-        {
+        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.
@@ -237,31 +280,33 @@ IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand,
 {
     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<PWSTR>(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 
+    switch (idCommand) {
+    case IDM_SHARE:
+        if (uFlags == 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<PWSTR>(pszName), cchMax,
-                m_pwszVerbCanonicalName);
-            break;
-
-        default:
-            hr = S_OK;
+                L"OCShareViaOC");
+        }
+        break;
+    case IDM_COPYLINK:
+        if (uFlags == GCS_VERBW) {
+            hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
+                L"OCCopyLink");
+        }
+        break;
+    case IDM_EMAILLINK:
+        if (uFlags == GCS_VERBW) {
+            hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
+                L"OCEmailLink");
         }
+        break;
+    default:
+        break;
     }
 
-    // If the command (idCommand) is not supported by this context menu 
+    // If the idCommand or uFlags is not supported by this context menu
     // extension handler, return E_INVALIDARG.
 
     return hr;
index 49834fce05f94c7c02ba99200dbf7aa6209feec4..7fe3e2adea819b0c8ad7fe7013a0b9bb714bb4f0 100644 (file)
@@ -46,8 +46,10 @@ private:
        // The name of the selected file.
        wchar_t m_szSelectedFile[MAX_PATH];
 
-       // The method that handles the "display" verb.
-       void OnVerbDisplayFileName(HWND hWnd);
+    // The method that handles the "ocshare" verb.
+    void OnVerbShare(HWND hWnd);
+    void OnVerbCopyLink(HWND hWnd);
+    void OnVerbEmailLink(HWND hWnd);
 
        PWSTR m_pszMenuText;
        PCSTR m_pszVerb;
index 4a13c1cd56323ec6a5a80cae319086e806d07300..37caee18240bf4ef62295cd19cc0b3c62a94abe5 100644 (file)
@@ -111,17 +111,10 @@ void RemotePathChecker::workerThreadLoop()
             } else if (StringUtil::begins_with(response, wstring(L"STATUS:")) ||
                     StringUtil::begins_with(response, wstring(L"BROADCAST:"))) {
 
-                auto statusBegin = response.find(L':', 0);
-                assert(statusBegin != std::wstring::npos);
-
-                auto statusEnd = response.find(L':', statusBegin + 1);
-                if (statusEnd == std::wstring::npos) {
-                    // the command do not contains two colon?
+                wstring responseStatus, responsePath;
+                if (!StringUtil::extractChunks(response, responseStatus, responsePath))
                     continue;
-                }
 
-                auto responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1);
-                auto responsePath = response.substr(statusEnd+1);
                 auto state = _StrToFileState(responseStatus);
                 bool wasAsked = asked.erase(responsePath) > 0;
 
index 0f52d51dd6bc86181929bdda86d27ef0f62ed8ac..8ec325aa94d7acf962a7e3b37c610fc9672fce22 100644 (file)
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <string>
+#include <cassert>
 
 class __declspec(dllexport) StringUtil {
 public:
@@ -44,6 +45,22 @@ public:
         return (childLength == parentLength || childLength > parentLength && (child[parentLength] == L'\\' || child[parentLength - 1] == L'\\'))
             && wcsncmp(child, parent, parentLength) == 0;
     }
+
+    static bool extractChunks(const std::wstring &source, std::wstring &secondChunk, std::wstring &thirdChunk) {
+        auto statusBegin = source.find(L':', 0);
+        assert(statusBegin != std::wstring::npos);
+
+        auto statusEnd = source.find(L':', statusBegin + 1);
+        if (statusEnd == std::wstring::npos) {
+            // the command do not contains two colon?
+            return false;
+        }
+
+        // Assume the caller extracted the chunk before the first colon.
+        secondChunk = source.substr(statusBegin + 1, statusEnd - statusBegin - 1);
+        thirdChunk = source.substr(statusEnd + 1);
+        return true;
+    }
 };
 
 #endif // STRINGUTIL_H