[PATCH 5/5] shell32: Add a navigation pane to the ExplorerBrowser control. (resend)

David Hedberg david.hedberg at gmail.com
Thu Aug 26 06:58:20 CDT 2010


---
 dlls/shell32/ebrowser.c       |  354 ++++++++++++++++++++++++++++++++++++++++-
 dlls/shell32/tests/ebrowser.c |    2 +-
 2 files changed, 352 insertions(+), 4 deletions(-)

diff --git a/dlls/shell32/ebrowser.c b/dlls/shell32/ebrowser.c
index 77dec20..80a9df2 100644
--- a/dlls/shell32/ebrowser.c
+++ b/dlls/shell32/ebrowser.c
@@ -37,6 +37,10 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+#define SPLITTER_WIDTH 2
+#define NP_MIN_WIDTH 60
+#define SV_MIN_WIDTH 150
+
 typedef struct _event_client {
     struct list entry;
     IExplorerBrowserEvents *pebe;
@@ -60,6 +64,16 @@ typedef struct _ExplorerBrowserImpl {
     HWND hwnd_main;
     HWND hwnd_sv;
 
+    RECT splitter_rc;
+    struct {
+        INameSpaceTreeControl2 *pnstc2;
+        HWND hwnd_splitter, hwnd_nstc;
+        DWORD nstc_cookie;
+        UINT width;
+        BOOL show;
+        RECT rc;
+    } navpane;
+
     EXPLORER_BROWSER_OPTIONS eb_options;
     FOLDERSETTINGS fs;
 
@@ -80,6 +94,8 @@ typedef struct _ExplorerBrowserImpl {
     IExplorerPaneVisibility *pepv_site;
 } ExplorerBrowserImpl;
 
+static void initialize_navpane(ExplorerBrowserImpl *This, HWND hwnd_parent, RECT *rc);
+
 /**************************************************************************
  * Event functions.
  */
@@ -255,14 +271,66 @@ static LPCITEMIDLIST travellog_go_forward(ExplorerBrowserImpl *This)
 static void update_layout(ExplorerBrowserImpl *This)
 {
     RECT rc;
-    TRACE("%p\n", This);
+    INT navpane_width_actual;
+    INT shellview_width_actual;
+    TRACE("%p (navpane: %d, EBO_SHOWFRAMES: %d)\n",
+          This, This->navpane.show, This->eb_options & EBO_SHOWFRAMES);
 
     GetClientRect(This->hwnd_main, &rc);
-    CopyRect(&This->sv_rc, &rc);
+
+    if((This->eb_options & EBO_SHOWFRAMES) && This->navpane.show)
+        navpane_width_actual = This->navpane.width;
+    else
+        navpane_width_actual = 0;
+
+    shellview_width_actual = rc.right - navpane_width_actual;
+    if(shellview_width_actual < SV_MIN_WIDTH && navpane_width_actual)
+    {
+        INT missing_width = SV_MIN_WIDTH - shellview_width_actual;
+        if(missing_width < (navpane_width_actual - NP_MIN_WIDTH))
+        {
+            /* Shrink the navpane */
+            navpane_width_actual -= missing_width;
+            shellview_width_actual += missing_width;
+        }
+        else
+        {
+            /* Hide the navpane */
+            shellview_width_actual += navpane_width_actual;
+            navpane_width_actual = 0;
+        }
+    }
+
+    /**************************************************************
+     *  Calculate rectangles for the panes. All rectangles contain
+     *  the position of the panes relative to hwnd_main.
+     */
+
+    if(navpane_width_actual)
+    {
+        This->navpane.rc.left = This->navpane.rc.top = 0;
+        This->navpane.rc.right = navpane_width_actual;
+        This->navpane.rc.bottom = rc.bottom;
+
+        if(!This->navpane.hwnd_splitter)
+            initialize_navpane(This, This->hwnd_main, &This->navpane.rc);
+    }
+    else
+        ZeroMemory(&This->navpane.rc, sizeof(RECT));
+
+    This->sv_rc.left   = navpane_width_actual;
+    This->sv_rc.top    = 0;
+    This->sv_rc.right  = This->sv_rc.left + shellview_width_actual;
+    This->sv_rc.bottom = rc.bottom;
 }
 
 static void size_panes(ExplorerBrowserImpl *This)
 {
+    MoveWindow(This->navpane.hwnd_splitter,
+               This->navpane.rc.right - SPLITTER_WIDTH, This->navpane.rc.top,
+               SPLITTER_WIDTH, This->navpane.rc.bottom - This->navpane.rc.top,
+               TRUE);
+
     MoveWindow(This->hwnd_sv,
                This->sv_rc.left, This->sv_rc.top,
                This->sv_rc.right - This->sv_rc.left, This->sv_rc.bottom - This->sv_rc.top,
@@ -385,6 +453,269 @@ static void get_interfaces_from_site(ExplorerBrowserImpl *This)
     IServiceProvider_Release(psp);
 }
 
+/**************************************************************************
+ * General pane functionality.
+ */
+static void update_panestate(ExplorerBrowserImpl *This)
+{
+    EXPLORERPANESTATE eps = EPS_DONTCARE;
+    BOOL show_navpane;
+    TRACE("%p\n", This);
+
+    if(!This->pepv_site) return;
+
+    IExplorerPaneVisibility_GetPaneState(This->pepv_site, (REFEXPLORERPANE) &EP_NavPane, &eps);
+    if( !(eps & EPS_DEFAULT_OFF) )
+        show_navpane = TRUE;
+    else
+        show_navpane = FALSE;
+
+    if(This->navpane.show ^ show_navpane)
+    {
+        update_layout(This);
+        size_panes(This);
+    }
+
+    This->navpane.show = show_navpane;
+}
+
+static void splitter_draw(HWND hwnd, RECT *rc)
+{
+    HDC hdc = GetDC(hwnd);
+    InvertRect(hdc, rc);
+    ReleaseDC(hwnd, hdc);
+}
+
+/**************************************************************************
+ * The Navigation Pane.
+ */
+static LRESULT navpane_splitter_beginresize(ExplorerBrowserImpl *This, HWND hwnd, LPARAM lParam)
+{
+    TRACE("\n");
+
+    SetCapture(hwnd);
+
+    CopyRect(&This->splitter_rc, &This->navpane.rc);
+    This->splitter_rc.left = This->splitter_rc.right - SPLITTER_WIDTH;
+
+    splitter_draw(GetParent(hwnd), &This->splitter_rc);
+
+    return TRUE;
+}
+
+static LRESULT navpane_splitter_resizing(ExplorerBrowserImpl *This, HWND hwnd, LPARAM lParam)
+{
+    int new_width, dx;
+    RECT rc;
+
+    if(GetCapture() != hwnd) return FALSE;
+
+    dx = (SHORT)LOWORD(lParam);
+    TRACE("%d.\n", dx);
+
+    CopyRect(&rc, &This->navpane.rc);
+
+    new_width = This->navpane.width + dx;
+    if(new_width > NP_MIN_WIDTH && This->sv_rc.right - new_width > SV_MIN_WIDTH)
+    {
+        rc.right = new_width;
+        rc.left = rc.right - SPLITTER_WIDTH;
+        splitter_draw(GetParent(hwnd), &This->splitter_rc);
+        splitter_draw(GetParent(hwnd), &rc);
+        CopyRect(&This->splitter_rc, &rc);
+    }
+
+    return TRUE;
+}
+
+static LRESULT navpane_splitter_endresize(ExplorerBrowserImpl *This, HWND hwnd, LPARAM lParam)
+{
+    int new_width, dx;
+
+    if(GetCapture() != hwnd) return FALSE;
+
+    dx = (SHORT)LOWORD(lParam);
+    TRACE("%d.\n", dx);
+
+    splitter_draw(GetParent(hwnd), &This->splitter_rc);
+
+    new_width = This->navpane.width + dx;
+    if(new_width < NP_MIN_WIDTH)
+        new_width = NP_MIN_WIDTH;
+    else if(This->sv_rc.right - new_width < SV_MIN_WIDTH)
+        new_width = This->sv_rc.right - SV_MIN_WIDTH;
+
+    This->navpane.width = new_width;
+
+    update_layout(This);
+    size_panes(This);
+
+    ReleaseCapture();
+
+    return TRUE;
+}
+
+static LRESULT navpane_on_wm_create(HWND hwnd, CREATESTRUCTW *crs)
+{
+    ExplorerBrowserImpl *This = crs->lpCreateParams;
+    INameSpaceTreeControl2 *pnstc2;
+    DWORD style;
+    HRESULT hr;
+
+    TRACE("%p\n", This);
+    SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
+    This->navpane.hwnd_splitter = hwnd;
+
+    hr = CoCreateInstance(&CLSID_NamespaceTreeControl, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_INameSpaceTreeControl2, (void**)&pnstc2);
+
+    if(SUCCEEDED(hr))
+    {
+        style = NSTCS_HASEXPANDOS | NSTCS_ROOTHASEXPANDO | NSTCS_SHOWSELECTIONALWAYS;
+        hr = INameSpaceTreeControl2_Initialize(pnstc2, GetParent(hwnd), NULL, style);
+        if(SUCCEEDED(hr))
+        {
+            INameSpaceTreeControlEvents *pnstce;
+            IShellFolder *psfdesktop;
+            IShellItem *psi;
+            IOleWindow *pow;
+            LPITEMIDLIST pidl;
+            DWORD cookie, style2 = NSTCS2_DISPLAYPADDING;
+
+            hr = INameSpaceTreeControl2_SetControlStyle2(pnstc2, 0xFF, style2);
+            if(FAILED(hr))
+                ERR("SetControlStyle2 failed (0x%08x)\n", hr);
+
+            hr = INameSpaceTreeControl2_QueryInterface(pnstc2, &IID_IOleWindow, (void**)&pow);
+            if(SUCCEEDED(hr))
+            {
+                IOleWindow_GetWindow(pow, &This->navpane.hwnd_nstc);
+                IOleWindow_Release(pow);
+            }
+            else
+                ERR("QueryInterface(IOleWindow) failed (0x%08x)\n", hr);
+
+            pnstce = (INameSpaceTreeControlEvents *)&This->lpnstceVtbl;
+            hr = INameSpaceTreeControl2_TreeAdvise(pnstc2, (IUnknown*)pnstce, &cookie);
+            if(FAILED(hr))
+                ERR("TreeAdvise failed. (0x%08x).\n", hr);
+
+            /*
+             * Add the default roots
+             */
+
+            /* TODO: This should be FOLDERID_Links */
+            hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
+            if(SUCCEEDED(hr))
+            {
+                hr = SHCreateShellItem(NULL, NULL, pidl, &psi);
+                if(SUCCEEDED(hr))
+                {
+                    hr = INameSpaceTreeControl2_AppendRoot(pnstc2, psi, SHCONTF_NONFOLDERS, NSTCRS_VISIBLE, NULL);
+                    IShellItem_Release(psi);
+                }
+                ILFree(pidl);
+            }
+
+            SHGetDesktopFolder(&psfdesktop);
+            hr = SHGetItemFromObject((IUnknown*)psfdesktop, &IID_IShellItem, (void**)&psi);
+            IShellFolder_Release(psfdesktop);
+            if(SUCCEEDED(hr))
+            {
+                hr = INameSpaceTreeControl2_AppendRoot(pnstc2, psi, SHCONTF_FOLDERS, NSTCRS_EXPANDED, NULL);
+                IShellItem_Release(psi);
+            }
+
+            /* TODO:
+             * We should advertise IID_INameSpaceTreeControl to the site of the
+             * host through its IProfferService interface, if any.
+             */
+
+            This->navpane.pnstc2 = pnstc2;
+            This->navpane.nstc_cookie = cookie;
+
+            return TRUE;
+        }
+    }
+
+    This->navpane.pnstc2 = NULL;
+    ERR("Failed (0x%08x)\n", hr);
+
+    return FALSE;
+}
+
+static LRESULT navpane_on_wm_size_move(ExplorerBrowserImpl *This)
+{
+    UINT height, width;
+    TRACE("%p\n", This);
+
+    width = This->navpane.rc.right - This->navpane.rc.left - SPLITTER_WIDTH;
+    height = This->navpane.rc.bottom - This->navpane.rc.top;
+
+    MoveWindow(This->navpane.hwnd_nstc,
+               This->navpane.rc.left, This->navpane.rc.top,
+               width, height,
+               TRUE);
+
+    return FALSE;
+}
+
+static LRESULT navpane_on_wm_destroy(ExplorerBrowserImpl *This)
+{
+    INameSpaceTreeControl_TreeUnadvise(This->navpane.pnstc2, This->navpane.nstc_cookie);
+    INameSpaceTreeControl_Release(This->navpane.pnstc2);
+    This->navpane.pnstc2 = NULL;
+    return TRUE;
+}
+
+static LRESULT CALLBACK navpane_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
+{
+    ExplorerBrowserImpl *This = (ExplorerBrowserImpl*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+
+    switch(uMessage) {
+    case WM_CREATE:           return navpane_on_wm_create(hWnd, (CREATESTRUCTW*)lParam);
+    case WM_MOVE:             /* Fall through */
+    case WM_SIZE:             return navpane_on_wm_size_move(This);
+    case WM_DESTROY:          return navpane_on_wm_destroy(This);
+    case WM_LBUTTONDOWN:      return navpane_splitter_beginresize(This, hWnd, lParam);
+    case WM_MOUSEMOVE:        return navpane_splitter_resizing(This, hWnd, lParam);
+    case WM_LBUTTONUP:        return navpane_splitter_endresize(This, hWnd, lParam);
+    default:
+        return DefWindowProcW(hWnd, uMessage, wParam, lParam);
+    }
+    return 0;
+}
+
+static void initialize_navpane(ExplorerBrowserImpl *This, HWND hwnd_parent, RECT *rc)
+{
+    WNDCLASSW wc;
+    HWND splitter;
+    static const WCHAR navpane_classname[] = {'e','b','_','n','a','v','p','a','n','e',0};
+
+    if( !GetClassInfoW(shell32_hInstance, navpane_classname, &wc) )
+    {
+        wc.style            = CS_HREDRAW | CS_VREDRAW;
+        wc.lpfnWndProc      = navpane_wndproc;
+        wc.cbClsExtra       = 0;
+        wc.cbWndExtra       = 0;
+        wc.hInstance        = shell32_hInstance;
+        wc.hIcon            = 0;
+        wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_SIZEWE);
+        wc.hbrBackground    = (HBRUSH)(COLOR_HIGHLIGHT + 1);
+        wc.lpszMenuName     = NULL;
+        wc.lpszClassName    = navpane_classname;
+
+        if (!RegisterClassW(&wc)) return;
+    }
+
+    splitter = CreateWindowExW(0, navpane_classname, NULL,
+                               WS_CHILD | WS_TABSTOP | WS_VISIBLE,
+                               rc->right - SPLITTER_WIDTH, rc->top,
+                               SPLITTER_WIDTH, rc->bottom - rc->top,
+                               hwnd_parent, 0, shell32_hInstance, This);
+    if(!splitter)
+        ERR("Failed to create navpane : %d.\n", GetLastError());
+}
 
 /**************************************************************************
  * Main window related functions.
@@ -670,7 +1001,7 @@ static HRESULT WINAPI IExplorerBrowser_fnSetOptions(IExplorerBrowser *iface,
 {
     ExplorerBrowserImpl *This = (ExplorerBrowserImpl*)iface;
     static const EXPLORER_BROWSER_OPTIONS unsupported_options =
-        EBO_SHOWFRAMES | EBO_ALWAYSNAVIGATE | EBO_NOWRAPPERWINDOW | EBO_HTMLSHAREPOINTVIEW;
+        EBO_ALWAYSNAVIGATE | EBO_NOWRAPPERWINDOW | EBO_HTMLSHAREPOINTVIEW;
 
     TRACE("%p (0x%x)\n", This, dwFlag);
 
@@ -796,6 +1127,7 @@ static HRESULT WINAPI IExplorerBrowser_fnBrowseToIDList(IExplorerBrowser *iface,
     }
 
     get_interfaces_from_site(This);
+    update_panestate(This);
 
     /* Only browse if the new pidl differs from the old */
     if(!ILIsEqual(This->current_pidl, absolute_pidl))
@@ -828,6 +1160,18 @@ static HRESULT WINAPI IExplorerBrowser_fnBrowseToIDList(IExplorerBrowser *iface,
     ILFree(This->current_pidl);
     This->current_pidl = absolute_pidl;
 
+    /* Expand the NameSpaceTree to the current location. */
+    if(This->navpane.show && This->navpane.pnstc2)
+    {
+        IShellItem *psi;
+        hr = SHCreateItemFromIDList(This->current_pidl, &IID_IShellItem, (void**)&psi);
+        if(SUCCEEDED(hr))
+        {
+            INameSpaceTreeControl_EnsureItemVisible(This->navpane.pnstc2, psi);
+            IShellItem_Release(psi);
+        }
+    }
+
     return S_OK;
 }
 
@@ -1618,6 +1962,10 @@ HRESULT WINAPI ExplorerBrowser_Constructor(IUnknown *pUnkOuter, REFIID riid, voi
     eb->lpowsVtbl = &vt_IObjectWithSite;
     eb->lpnstceVtbl = &vt_INameSpaceTreeControlEvents;
 
+    /* Default settings */
+    eb->navpane.width = 150;
+    eb->navpane.show = TRUE;
+
     list_init(&eb->event_clients);
     list_init(&eb->travellog);
 
diff --git a/dlls/shell32/tests/ebrowser.c b/dlls/shell32/tests/ebrowser.c
index 8bd60aa..c1a9acd 100644
--- a/dlls/shell32/tests/ebrowser.c
+++ b/dlls/shell32/tests/ebrowser.c
@@ -973,7 +973,7 @@ static void test_SetSite(void)
     todo_wine ok(cdbimpl->OnPreviewCreated, "Got %d\n", cdbimpl->OnPreviewCreated);
 
     /* IExplorerPaneVisibility */
-    todo_wine ok(epvimpl->np, "Got %d\n", epvimpl->np);
+    ok(epvimpl->np, "Got %d\n", epvimpl->np);
     todo_wine ok(epvimpl->cp, "Got %d\n", epvimpl->cp);
     todo_wine ok(epvimpl->cp_o, "Got %d\n", epvimpl->cp_o);
     todo_wine ok(epvimpl->cp_v, "Got %d\n", epvimpl->cp_v);
-- 
1.7.2.2




More information about the wine-patches mailing list