From 880c4059fecabfee5ab1db129fb942fc52c24ef8 Mon Sep 17 00:00:00 2001 From: Thomas Bluemel Date: Sun, 10 Jun 2007 08:09:42 +0000 Subject: [PATCH] Add a very experimental explorer that heavily relies on the shell. It's still very buggy, not very functional and will not work on ReactOS due to the lack of support in shell32.dll and friends. It will not be built by default. - todo.h is a header that contains definitions and GUIDs that we miss in the public sdk headers. - undoc.h contains functions that are not publicly documented. It also contains internal (not yet complete) COM interfaces used with the start menu and SHCreateDesktop. svn path=/trunk/; revision=27104 --- reactos/base/shell/explorer-new/comcsup.h | 53 + reactos/base/shell/explorer-new/desktop.c | 1419 +++++++++ reactos/base/shell/explorer-new/dragdrop.c | 411 +++ reactos/base/shell/explorer-new/explorer.c | 342 +++ .../base/shell/explorer-new/explorer.rbuild | 35 + reactos/base/shell/explorer-new/explorer.rc | 16 + reactos/base/shell/explorer-new/lang/en-US.rc | 59 + reactos/base/shell/explorer-new/manifest.xml | 22 + reactos/base/shell/explorer-new/precomp.h | 389 +++ reactos/base/shell/explorer-new/res/logov.bmp | Bin 0 -> 14966 bytes reactos/base/shell/explorer-new/res/start.ico | Bin 0 -> 25214 bytes reactos/base/shell/explorer-new/resource.h | 47 + reactos/base/shell/explorer-new/startmnu.c | 931 ++++++ reactos/base/shell/explorer-new/taskband.c | 665 +++++ reactos/base/shell/explorer-new/taskswnd.c | 2004 +++++++++++++ reactos/base/shell/explorer-new/tbsite.c | 1059 +++++++ reactos/base/shell/explorer-new/todo.h | 578 ++++ reactos/base/shell/explorer-new/trayntfy.c | 1063 +++++++ reactos/base/shell/explorer-new/trayprop.c | 29 + reactos/base/shell/explorer-new/traywnd.c | 2591 +++++++++++++++++ reactos/base/shell/explorer-new/undoc.h | 301 ++ 21 files changed, 12014 insertions(+) create mode 100644 reactos/base/shell/explorer-new/comcsup.h create mode 100644 reactos/base/shell/explorer-new/desktop.c create mode 100644 reactos/base/shell/explorer-new/dragdrop.c create mode 100644 reactos/base/shell/explorer-new/explorer.c create mode 100644 reactos/base/shell/explorer-new/explorer.rbuild create mode 100644 reactos/base/shell/explorer-new/explorer.rc create mode 100644 reactos/base/shell/explorer-new/lang/en-US.rc create mode 100644 reactos/base/shell/explorer-new/manifest.xml create mode 100644 reactos/base/shell/explorer-new/precomp.h create mode 100644 reactos/base/shell/explorer-new/res/logov.bmp create mode 100644 reactos/base/shell/explorer-new/res/start.ico create mode 100644 reactos/base/shell/explorer-new/resource.h create mode 100644 reactos/base/shell/explorer-new/startmnu.c create mode 100644 reactos/base/shell/explorer-new/taskband.c create mode 100644 reactos/base/shell/explorer-new/taskswnd.c create mode 100644 reactos/base/shell/explorer-new/tbsite.c create mode 100644 reactos/base/shell/explorer-new/todo.h create mode 100644 reactos/base/shell/explorer-new/trayntfy.c create mode 100644 reactos/base/shell/explorer-new/trayprop.c create mode 100644 reactos/base/shell/explorer-new/traywnd.c create mode 100644 reactos/base/shell/explorer-new/undoc.h diff --git a/reactos/base/shell/explorer-new/comcsup.h b/reactos/base/shell/explorer-new/comcsup.h new file mode 100644 index 00000000000..5513ecaa25f --- /dev/null +++ b/reactos/base/shell/explorer-new/comcsup.h @@ -0,0 +1,53 @@ +#ifndef __COMCSUP_H +#define __COMCSUP_H + +/* + ****************************************************************************** + * This header is for easier generation of IUnknown interfaces for inherited * + * classes and for casts from the interface to the implementation and vice * + * versa. * + ****************************************************************************** + */ + +/* Generates a Iiface::AddRef() method that forwards to Iimpl::AddRef() */ +#define METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(iface,impl) impl##Impl_##iface##_AddRef +#define METHOD_IUNKNOWN_INHERITED_ADDREF(iface,impl) \ +static ULONG STDMETHODCALLTYPE \ +impl##Impl_##iface##_AddRef(IN OUT iface *ifc) { \ + impl##Impl *This = impl##Impl_from_##iface (ifc); \ + impl *baseiface = impl##_from_##impl##Impl(This); \ + return impl##Impl_AddRef(baseiface); \ +} + +/* Generates a Iiface::Release() method that forwards to Iimpl::Release() */ +#define METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(iface,impl) impl##Impl_##iface##_Release +#define METHOD_IUNKNOWN_INHERITED_RELEASE(iface,impl) \ +static ULONG STDMETHODCALLTYPE \ +impl##Impl_##iface##_Release(IN OUT iface *ifc) { \ + impl##Impl *This = impl##Impl_from_##iface (ifc); \ + impl *baseiface = impl##_from_##impl##Impl(This); \ + return impl##Impl_AddRef(baseiface); \ +} + +/* Generates a Iiface::QueryInterface() method that forwards to Iimpl::QueryInterface() */ +#define METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(iface,impl) impl##Impl_##iface##_QueryInterface +#define METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(iface,impl) \ +static HRESULT STDMETHODCALLTYPE \ +impl##Impl_##iface##_QueryInterface(IN OUT iface *ifc, IN REFIID riid, OUT VOID **ppvObject) { \ + impl##Impl *This = impl##Impl_from_##iface (ifc); \ + impl *baseiface = impl##_from_##impl##Impl(This); \ + return impl##Impl_QueryInterface(baseiface, riid, ppvObject); \ +} + +/* Generates a Ixxx_from_IxxxImpl() and a IxxxImpl_from_Ixxx() inline function */ +#define IMPL_CASTS(iface,impl,vtbl) \ +static __inline iface * \ +iface##_from_##impl##Impl (impl##Impl *This) { \ + return (iface *)&This->vtbl; \ +} \ +static __inline impl##Impl * \ +impl##Impl_from_##iface (iface *ifc) { \ + return (impl##Impl *)((ULONG_PTR)ifc - FIELD_OFFSET(impl##Impl, vtbl)); \ +} + +#endif /* __COMCSUP_H */ diff --git a/reactos/base/shell/explorer-new/desktop.c b/reactos/base/shell/explorer-new/desktop.c new file mode 100644 index 00000000000..05141cd1db0 --- /dev/null +++ b/reactos/base/shell/explorer-new/desktop.c @@ -0,0 +1,1419 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#if USE_API_SHCREATEDESKTOP + +typedef struct _DESKCREATEINFO +{ + HANDLE hEvent; + ITrayWindow *Tray; + HANDLE hDesktop; +} DESKCREATEINFO, *PDESKCREATEINFO; + +static DWORD CALLBACK +DesktopThreadProc(IN OUT LPVOID lpParameter) +{ + volatile DESKCREATEINFO *DeskCreateInfo = (volatile DESKCREATEINFO *)lpParameter; + IShellDesktopTray *pSdt; + HANDLE hDesktop; + HRESULT hRet; + + hRet = ITrayWindow_QueryInterface(DeskCreateInfo->Tray, + &IID_IShellDesktopTray, + (PVOID*)&pSdt); + if (!SUCCEEDED(hRet)) + return 1; + + hDesktop = SHCreateDesktop(pSdt); + + IShellDesktopTray_Release(pSdt); + if (hDesktop == NULL) + return 1; + + (void)InterlockedExchangePointer(&DeskCreateInfo->hDesktop, + hDesktop); + + if (!SetEvent(DeskCreateInfo->hEvent)) + { + /* Failed to notify that we initialized successfully, kill ourselves + to make the main thread wake up! */ + return 1; + } + + SHDesktopMessageLoop(hDesktop); + + /* FIXME: Properly rundown the main thread! */ + ExitProcess(0); + + return 0; +} + +HANDLE +DesktopCreateWindow(IN OUT ITrayWindow *Tray) +{ + HANDLE hThread; + HANDLE hEvent; + DWORD DesktopThreadId; + HANDLE hDesktop = NULL; + HANDLE Handles[2]; + DWORD WaitResult; + + hEvent = CreateEvent(NULL, + FALSE, + FALSE, + NULL); + if (hEvent != NULL) + { + volatile DESKCREATEINFO DeskCreateInfo; + + DeskCreateInfo.hEvent = hEvent; + DeskCreateInfo.Tray = Tray; + DeskCreateInfo.hDesktop = NULL; + + hThread = CreateThread(NULL, + 0, + DesktopThreadProc, + (PVOID)&DeskCreateInfo, + 0, + &DesktopThreadId); + if (hThread != NULL) + { + Handles[0] = hThread; + Handles[1] = hEvent; + + for (;;) + { + WaitResult = MsgWaitForMultipleObjects(sizeof(Handles) / sizeof(Handles[0]), + Handles, + FALSE, + INFINITE, + QS_ALLEVENTS); + if (WaitResult == WAIT_OBJECT_0 + (sizeof(Handles) / sizeof(Handles[0]))) + TrayProcessMessages(Tray); + else if (WaitResult != WAIT_FAILED && WaitResult != WAIT_OBJECT_0) + { + hDesktop = DeskCreateInfo.hDesktop; + break; + } + } + + CloseHandle(hThread); + } + + CloseHandle(hEvent); + } + + return hDesktop; +} + +VOID +DesktopDestroyShellWindow(IN HANDLE hDesktop) +{ + return; +} + +#else /* USE_API_SHCREATEDESKTOP == 0 */ + +/* + ****************************************************************************** + * NOTE: This could may be reused in a shell implementation of * + * SHCreateDesktop(). * + ****************************************************************************** + */ + +#define WM_SHELL_ADDDRIVENOTIFY (WM_USER + 0x100) + +static const IShellBrowserVtbl IDesktopShellBrowserImpl_Vtbl; +static const ICommDlgBrowserVtbl IDesktopShellBrowserImpl_ICommDlgBrowser_Vtbl; +static const IServiceProviderVtbl IDesktopShellBrowserImpl_IServiceProvider_Vtbl; +static const IShellFolderViewCBVtbl IDesktopShellBrowserImpl_IShellFolderViewCB_Vtbl; + +/* + * IShellBrowser + */ + +typedef struct +{ + const IShellBrowserVtbl *lpVtbl; + const ICommDlgBrowserVtbl *lpVtblCommDlgBrowser; + const IServiceProviderVtbl *lpVtblServiceProvider; + const IShellFolderViewCBVtbl *lpVtblShellFolderViewCB; + LONG Ref; + + IShellView2 *DesktopView2; + IShellView *DesktopView; + IShellBrowser *DefaultShellBrowser; + HWND hWnd; + HWND hWndShellView; + HWND hWndDesktopListView; + + ITrayWindow *Tray; + + LPITEMIDLIST pidlDesktopDirectory; + LPITEMIDLIST pidlDesktop; + IDropTarget *DropTarget; + + ULONG ChangeNotificationId; +} IDesktopShellBrowserImpl; + +static IUnknown * +IUnknown_from_impl(IDesktopShellBrowserImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +static IShellBrowser * +IShellBrowser_from_impl(IDesktopShellBrowserImpl *This) +{ + return (IShellBrowser *)&This->lpVtbl; +} + +static IOleWindow * +IOleWindow_from_impl(IDesktopShellBrowserImpl *This) +{ + return (IOleWindow *)&This->lpVtbl; +} + +static ICommDlgBrowser * +ICommDlgBrowser_from_impl(IDesktopShellBrowserImpl *This) +{ + return (ICommDlgBrowser *)&This->lpVtblCommDlgBrowser; +} + +static IServiceProvider * +IServiceProvider_from_impl(IDesktopShellBrowserImpl *This) +{ + return (IServiceProvider *)&This->lpVtblServiceProvider; +} + +static IShellFolderViewCB * +IShellFolderViewCB_from_impl(IDesktopShellBrowserImpl *This) +{ + return (IShellFolderViewCB *)&This->lpVtblShellFolderViewCB; +} + +static IDesktopShellBrowserImpl * +impl_from_IShellBrowser(IShellBrowser *iface) +{ + return (IDesktopShellBrowserImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDesktopShellBrowserImpl, + lpVtbl)); +} + +static IDesktopShellBrowserImpl * +impl_from_ICommDlgBrowser(ICommDlgBrowser *iface) +{ + return (IDesktopShellBrowserImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDesktopShellBrowserImpl, + lpVtblCommDlgBrowser)); +} + +static IDesktopShellBrowserImpl * +impl_from_IServiceProvider(IServiceProvider *iface) +{ + return (IDesktopShellBrowserImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDesktopShellBrowserImpl, + lpVtblServiceProvider)); +} + +static IDesktopShellBrowserImpl * +impl_from_IShellFolderViewCB(IShellFolderViewCB *iface) +{ + return (IDesktopShellBrowserImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDesktopShellBrowserImpl, + lpVtblShellFolderViewCB)); +} + +static VOID +IDesktopShellBrowserImpl_Free(IDesktopShellBrowserImpl *This) +{ + if (This->DropTarget != NULL) + { + IDropTarget_Release(This->DropTarget); + This->DropTarget = NULL; + } + + if (This->Tray != NULL) + { + ITrayWindow_Release(This->Tray); + This->Tray = NULL; + } + + if (This->pidlDesktopDirectory != NULL) + { + ILFree(This->pidlDesktopDirectory); + This->pidlDesktopDirectory = NULL; + } + + if (This->pidlDesktop != NULL) + { + ILFree(This->pidlDesktop); + This->pidlDesktop = NULL; + } + + HeapFree(hProcessHeap, + 0, + This); +} + +static VOID +IDesktopShellBrowserImpl_Destroy(IDesktopShellBrowserImpl *This) +{ + if (This->ChangeNotificationId != 0) + { + SHChangeNotifyDeregister(This->ChangeNotificationId); + This->ChangeNotificationId = 0; + } + + if (This->Tray != NULL) + { + ITrayWindow_Close(This->Tray); + This->Tray = NULL; + } + + if (This->DropTarget != NULL && This->hWndDesktopListView != NULL) + { + RevokeDragDrop(This->hWndDesktopListView); + This->hWndDesktopListView = NULL; + } + + if (This->DesktopView2 != NULL) + { + IShellView2_Release(This->DesktopView2); + } + + if (This->DesktopView != NULL) + { + if (This->hWndShellView != NULL) + { + IShellView_DestroyViewWindow(This->DesktopView); + } + + IShellView_Release(This->DesktopView); + This->DesktopView = NULL; + This->hWndShellView = NULL; + This->hWndDesktopListView = NULL; + } +} + +static HRESULT +IDesktopShellBrowserImpl_GetNotify(IN OUT IDesktopShellBrowserImpl *This, + OUT LPITEMIDLIST *ppidl, + OUT LONG *plEvents) +{ + *ppidl = This->pidlDesktopDirectory; + *plEvents = SHCNE_DISKEVENTS; + return S_OK; +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_Release(IN OUT IShellBrowser *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + if (Ret == 0) + IDesktopShellBrowserImpl_Free(This); + + return Ret; +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_AddRef(IN OUT IShellBrowser *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + + return InterlockedIncrement(&This->Ref); +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_QueryInterface(IN OUT IShellBrowser *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IDesktopShellBrowserImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = impl_from_IShellBrowser(iface); + + if (IsEqualIID(riid, + &IID_IUnknown)) + { + *ppvObj = IUnknown_from_impl(This); + } + else if (This->DefaultShellBrowser != NULL) + { + return IShellBrowser_QueryInterface(This->DefaultShellBrowser, + riid, + ppvObj); + } + else if (IsEqualIID(riid, + &IID_IOleWindow)) + { + *ppvObj = IOleWindow_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_IShellBrowser)) + { + *ppvObj = IShellBrowser_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_ICommDlgBrowser)) + { + *ppvObj = ICommDlgBrowser_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_IServiceProvider)) + { + *ppvObj = IServiceProvider_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_IShellFolderViewCB)) + { + *ppvObj = IShellFolderViewCB_from_impl(This); + } + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + IDesktopShellBrowserImpl_AddRef(iface); + return S_OK; +} + +static IDesktopShellBrowserImpl * +IDesktopShellBrowserImpl_Construct(IN HWND hwndParent, + IN LPCREATESTRUCT lpCreateStruct OPTIONAL) +{ + IDesktopShellBrowserImpl *This; + IShellBrowser *ShellBrowser; + IShellFolder *psfDesktopFolder; +#if 1 + SFV_CREATE csfv; +#endif + FOLDERSETTINGS fs; + HRESULT hr; + RECT rcClient; + SHChangeNotifyEntry cne; + + This = HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return NULL; + + ZeroMemory(This, + sizeof(*This)); + This->lpVtbl = &IDesktopShellBrowserImpl_Vtbl; + This->lpVtblCommDlgBrowser = &IDesktopShellBrowserImpl_ICommDlgBrowser_Vtbl; + This->lpVtblServiceProvider = &IDesktopShellBrowserImpl_IServiceProvider_Vtbl; + This->lpVtblShellFolderViewCB = &IDesktopShellBrowserImpl_IShellFolderViewCB_Vtbl; + This->Ref = 1; + This->hWnd = hwndParent; + + ShellBrowser = IShellBrowser_from_impl(This); + + This->pidlDesktopDirectory = SHCloneSpecialIDList(This->hWnd, + CSIDL_DESKTOPDIRECTORY, + FALSE); + + hr = SHGetSpecialFolderLocation(This->hWnd, + CSIDL_DESKTOP, + &This->pidlDesktop); + if (!SUCCEEDED(hr)) + goto Fail; + +#if 1 + hr = SHGetDesktopFolder(&psfDesktopFolder); +#else + hr = CoCreateInstance(&CLSID_ShellDesktop, + NULL, + CLSCTX_INPROC, + &IID_IShellFolder, + (PVOID*)&psfDesktopFolder); +#endif + if (!SUCCEEDED(hr)) + goto Fail; + +#if 0 + hr = IShellFolder_CreateViewObject(psfDesktopFolder, + This->hWnd, + &IID_IShellView, + (PVOID*)&This->DesktopView); +#else + csfv.cbSize = sizeof(csfv); + csfv.pshf = psfDesktopFolder; + csfv.psvOuter = NULL; + + hr = IDesktopShellBrowserImpl_QueryInterface(ShellBrowser, + &IID_IShellFolderViewCB, + (PVOID*)&csfv.psfvcb); + if (!SUCCEEDED(hr)) + csfv.psfvcb = NULL; + + hr = SHCreateShellFolderView(&csfv, + &This->DesktopView); + + IShellFolder_Release(psfDesktopFolder); + + if (csfv.psfvcb != NULL) + csfv.psfvcb->lpVtbl->Release(csfv.psfvcb); +#endif + if (!SUCCEEDED(hr)) + goto Fail; + + hr = IShellView_QueryInterface(This->DesktopView, + &IID_IShellView2, + (PVOID*)&This->DesktopView2); + if (!SUCCEEDED(hr)) + This->DesktopView2 = NULL; + + if (lpCreateStruct == NULL) + { + if (!GetClientRect(This->hWnd, + &rcClient)) + { + goto Fail; + } + } + else + { + rcClient.left = 0; + rcClient.top = 0; + rcClient.right = lpCreateStruct->cx; + rcClient.bottom = lpCreateStruct->cy; + } + + fs.ViewMode = FVM_ICON; + fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT; + if (This->DesktopView2 != NULL) + { + SV2CVW2_PARAMS params; + + params.cbSize = sizeof(params); + params.psvPrev = NULL; + params.pfs = &fs; + params.psbOwner = ShellBrowser; + params.prcView = &rcClient; + params.pvid = &VID_LargeIcons; + hr = IShellView2_CreateViewWindow2(This->DesktopView2, + ¶ms); + + if (FAILED(hr)) + goto DefCreateViewWindow; + This->hWndShellView = params.hwndView; + } + else + { +DefCreateViewWindow: + hr = IShellView_CreateViewWindow(This->DesktopView, + NULL, + &fs, + ShellBrowser, + &rcClient, + &This->hWndShellView); + } + if (!SUCCEEDED(hr)) + goto Fail; + + IShellView_EnableModeless(This->DesktopView, + TRUE); + + hr = IShellView_UIActivate(This->DesktopView, + SVUIA_ACTIVATE_FOCUS); + if (!SUCCEEDED(hr)) + { + IShellView_DestroyViewWindow(This->DesktopView); + +Fail: + /* NOTE: the other references will be released in the + WM_NCDESTROY handler! */ + IDesktopShellBrowserImpl_Release(ShellBrowser); + return NULL; + } + + /* Now get the real window handle to the list view control! + We need to assume it is the child window of the default + shell view window. There doesn't seem to be another way + to get to the list view window handle... */ + This->hWndDesktopListView = FindWindowEx(This->hWndShellView, + NULL, + WC_LISTVIEW, + NULL); + + if (This->hWndDesktopListView != NULL) + { + /* Register drag&drop */ + hr = IShellView_QueryInterface(This->DesktopView, + &IID_IDropTarget, + (PVOID*)&This->DropTarget); + if (SUCCEEDED(hr)) + { + hr = RegisterDragDrop(This->hWndDesktopListView, + This->DropTarget); + if (!SUCCEEDED(hr)) + { + IDropTarget_Release(This->DropTarget); + This->DropTarget = NULL; + } + } + } + + /* Register a notification that is triggered when a new drive is added and the shell + should open that drive in a new window, such as USB sticks. */ + cne.pidl = NULL; + cne.fRecursive = TRUE; + This->ChangeNotificationId = SHChangeNotifyRegister(This->hWnd, + SHCNRF_ShellLevel | SHCNRF_NewDelivery, + SHCNE_DRIVEADDGUI, + WM_SHELL_ADDDRIVENOTIFY, + 1, + &cne); + + /* Create the tray window */ + This->Tray = CreateTrayWindow(This->hWnd); + return This; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_GetWindow(IN OUT IShellBrowser *iface, + OUT HWND *phwnd) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + + if (This->hWnd != NULL) + { + *phwnd = This->hWnd; + return S_OK; + } + + *phwnd = NULL; + return E_UNEXPECTED; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ContextSensitiveHelp(IN OUT IShellBrowser *iface, + IN BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_InsertMenusSB(IN OUT IShellBrowser *iface, + IN HMENU hmenuShared, + OUT LPOLEMENUGROUPWIDTHS lpMenuWidths) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_SetMenuSB(IN OUT IShellBrowser *iface, + IN HMENU hmenuShared, + IN HOLEMENU holemenuRes, + IN HWND hwndActiveObject) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_RemoveMenusSB(IN OUT IShellBrowser *iface, + IN HMENU hmenuShared) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_SetStatusTextSB(IN OUT IShellBrowser *iface, + IN LPCOLESTR lpszStatusText) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_EnableModelessSB(IN OUT IShellBrowser *iface, + IN BOOL fEnable) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_TranslateAcceleratorSB(IN OUT IShellBrowser *iface, + IN LPMSG lpmsg, + IN WORD wID) +{ + return S_FALSE; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_BrowseObject(IN OUT IShellBrowser *iface, + IN LPCITEMIDLIST pidl, + IN UINT wFlags) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_GetViewStateStream(IN OUT IShellBrowser *iface, + IN DWORD grfMode, + OUT IStream **ppStrm) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + static WCHAR szItemPos[] = L"ItemPos"; + HRESULT hRet; + IPropertyBag *PropBag = NULL; + WCHAR szPropName[64]; + + /* Determine the name of the property that contains the stream of + icon positions */ + wcscpy(szPropName, + szItemPos); + hRet = SHGetPerScreenResName(szPropName + wcslen(szPropName), + (sizeof(szPropName) / sizeof(szPropName[0])) - wcslen(szPropName), + 0); + if (SUCCEEDED(hRet)) + { + /* Locate the property bag for the desktop */ + hRet = SHGetViewStatePropertyBag(This->pidlDesktop, + L"Desktop", + SHGVSPB_FOLDERNODEFAULTS | SHGVSPB_ROAM, + &IID_IPropertyBag, + (PVOID*)&PropBag); + + if (SUCCEEDED(hRet)) + { + /* Create a stream for the ItemPos property */ + hRet = SHPropertyBag_ReadStream(PropBag, + szPropName, + ppStrm); + + IPropertyBag_Release(PropBag); + + if (SUCCEEDED(hRet)) + { + /* FIXME: For some reason the shell doesn't position the icons... */ + } + } + } + + return hRet; +} + +static HWND +DesktopGetWindowControl(IN IDesktopShellBrowserImpl *This, + IN UINT id) +{ + switch (id) + { + case FCW_TOOLBAR: + case FCW_STATUS: + case FCW_TREE: + case FCW_PROGRESS: + return NULL; + + default: + return NULL; + } + +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_GetControlWindow(IN OUT IShellBrowser *iface, + IN UINT id, + OUT HWND *lphwnd) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + HWND hWnd; + + hWnd = DesktopGetWindowControl(This, + id); + if (hWnd != NULL) + { + *lphwnd = hWnd; + return S_OK; + } + + *lphwnd = NULL; + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_SendControlMsg(IN OUT IShellBrowser *iface, + IN UINT id, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam, + OUT LRESULT *pret) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + HWND hWnd; + + if (pret == NULL) + return E_POINTER; + + hWnd = DesktopGetWindowControl(This, + id); + if (hWnd != NULL) + { + *pret = SendMessage(hWnd, + uMsg, + wParam, + lParam); + return S_OK; + } + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_QueryActiveShellView(IN OUT IShellBrowser *iface, + OUT IShellView **ppshv) +{ + IShellView *ActiveView; + IDesktopShellBrowserImpl *This = impl_from_IShellBrowser(iface); + + ActiveView = This->DesktopView; + IDesktopShellBrowserImpl_AddRef(iface); + *ppshv = ActiveView; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_OnViewWindowActive(IN OUT IShellBrowser *iface, + IN OUT IShellView *ppshv) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_SetToolbarItems(IN OUT IShellBrowser *iface, + IN LPTBBUTTON lpButtons, + IN UINT nButtons, + IN UINT uFlags) +{ + return E_NOTIMPL; +} + +static const IShellBrowserVtbl IDesktopShellBrowserImpl_Vtbl = +{ + /* IUnknown */ + IDesktopShellBrowserImpl_QueryInterface, + IDesktopShellBrowserImpl_AddRef, + IDesktopShellBrowserImpl_Release, + /* IOleWindow */ + IDesktopShellBrowserImpl_GetWindow, + IDesktopShellBrowserImpl_ContextSensitiveHelp, + /* IShellBrowser */ + IDesktopShellBrowserImpl_InsertMenusSB, + IDesktopShellBrowserImpl_SetMenuSB, + IDesktopShellBrowserImpl_RemoveMenusSB, + IDesktopShellBrowserImpl_SetStatusTextSB, + IDesktopShellBrowserImpl_EnableModelessSB, + IDesktopShellBrowserImpl_TranslateAcceleratorSB, + IDesktopShellBrowserImpl_BrowseObject, + IDesktopShellBrowserImpl_GetViewStateStream, + IDesktopShellBrowserImpl_GetControlWindow, + IDesktopShellBrowserImpl_SendControlMsg, + IDesktopShellBrowserImpl_QueryActiveShellView, + IDesktopShellBrowserImpl_OnViewWindowActive, + IDesktopShellBrowserImpl_SetToolbarItems +}; + +/* + * ICommDlgBrowser + */ + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_QueryInterface(IN OUT ICommDlgBrowser *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IDesktopShellBrowserImpl *This = impl_from_ICommDlgBrowser(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_QueryInterface(ShellBrowser, + riid, + ppvObj); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_Release(IN OUT ICommDlgBrowser *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_ICommDlgBrowser(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_Release(ShellBrowser); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_AddRef(IN OUT ICommDlgBrowser *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_ICommDlgBrowser(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_AddRef(ShellBrowser); +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_OnDefaultCommand(IN OUT ICommDlgBrowser *iface, + IN OUT IShellView *ppshv) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_OnStateChange(IN OUT ICommDlgBrowser *iface, + IN OUT IShellView *ppshv, + IN ULONG uChange) +{ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_ICommDlgBrowser_IncludeObject(IN OUT ICommDlgBrowser *iface, + IN OUT IShellView *ppshv, + IN LPCITEMIDLIST pidl) +{ + return S_OK; +} + +static const ICommDlgBrowserVtbl IDesktopShellBrowserImpl_ICommDlgBrowser_Vtbl = +{ + /* IUnknown */ + IDesktopShellBrowserImpl_ICommDlgBrowser_QueryInterface, + IDesktopShellBrowserImpl_ICommDlgBrowser_AddRef, + IDesktopShellBrowserImpl_ICommDlgBrowser_Release, + /* ICommDlgBrowser */ + IDesktopShellBrowserImpl_ICommDlgBrowser_OnDefaultCommand, + IDesktopShellBrowserImpl_ICommDlgBrowser_OnStateChange, + IDesktopShellBrowserImpl_ICommDlgBrowser_IncludeObject +}; + +/* + * IServiceProvider + */ + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IServiceProvider_QueryInterface(IN OUT IServiceProvider *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IDesktopShellBrowserImpl *This = impl_from_IServiceProvider(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_QueryInterface(ShellBrowser, + riid, + ppvObj); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IServiceProvider_Release(IN OUT IServiceProvider *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IServiceProvider(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_Release(ShellBrowser); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IServiceProvider_AddRef(IN OUT IServiceProvider *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IServiceProvider(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_AddRef(ShellBrowser); +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IServiceProvider_QueryService(IN OUT IServiceProvider *iface, + IN REFGUID guidService, + IN REFIID riid, + OUT PVOID *ppv) +{ + /* FIXME - handle guidService */ + return IDesktopShellBrowserImpl_IServiceProvider_QueryInterface(iface, + riid, + ppv); +} + +static const IServiceProviderVtbl IDesktopShellBrowserImpl_IServiceProvider_Vtbl = +{ + /* IUnknown */ + IDesktopShellBrowserImpl_IServiceProvider_QueryInterface, + IDesktopShellBrowserImpl_IServiceProvider_AddRef, + IDesktopShellBrowserImpl_IServiceProvider_Release, + /* IServiceProvider */ + IDesktopShellBrowserImpl_IServiceProvider_QueryService +}; + +/* + * IShellFolderViewCB + */ + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IShellFolderViewCB_QueryInterface(IN OUT IShellFolderViewCB *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellFolderViewCB(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_QueryInterface(ShellBrowser, + riid, + ppvObj); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IShellFolderViewCB_Release(IN OUT IShellFolderViewCB *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellFolderViewCB(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_Release(ShellBrowser); +} + +static ULONG STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IShellFolderViewCB_AddRef(IN OUT IShellFolderViewCB *iface) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellFolderViewCB(iface); + IShellBrowser *ShellBrowser = IShellBrowser_from_impl(This); + + return IDesktopShellBrowserImpl_AddRef(ShellBrowser); +} + +static HRESULT STDMETHODCALLTYPE +IDesktopShellBrowserImpl_IShellFolderViewCB_MessageSFVCB(IN OUT IShellFolderViewCB *iface, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + IDesktopShellBrowserImpl *This = impl_from_IShellFolderViewCB(iface); + HRESULT hr = S_OK; + switch (uMsg) + { + case SFVM_GETNOTIFY: + hr = IDesktopShellBrowserImpl_GetNotify(This, + (LPITEMIDLIST *)wParam, + (LONG *)lParam); + break; + + case SFVM_FSNOTIFY: + hr = S_OK; + break; + + default: + hr = E_NOTIMPL; + break; + } + + return hr; +} + +static const IShellFolderViewCBVtbl IDesktopShellBrowserImpl_IShellFolderViewCB_Vtbl = +{ + /* IUnknown */ + IDesktopShellBrowserImpl_IShellFolderViewCB_QueryInterface, + IDesktopShellBrowserImpl_IShellFolderViewCB_AddRef, + IDesktopShellBrowserImpl_IShellFolderViewCB_Release, + /* IShellFolderViewCB */ + IDesktopShellBrowserImpl_IShellFolderViewCB_MessageSFVCB +}; + +/*****************************************************************************/ + +static const TCHAR szProgmanClassName[] = TEXT("Progman"); +static const TCHAR szProgmanWindowName[] = TEXT("Program Manager"); + +static VOID +PositionIcons(IN OUT IDesktopShellBrowserImpl *This, + IN const RECT *prcDesktopRect) +{ + /* FIXME - Move all icons and align them on the destop. Before moving, we should + check if we're moving an icon above an existing one. If so, we should + let the list view control arrange the icons */ + return; +} + +static VOID +UpdateWorkArea(IN OUT IDesktopShellBrowserImpl *This, + IN const RECT *prcDesktopRect) +{ + RECT rcDesktopRect; + LONG Style; + + Style = GetWindowLong(This->hWndDesktopListView, + GWL_STYLE); + + if (!(Style & LVS_AUTOARRANGE ) && + GetWindowRect(This->hWnd, + &rcDesktopRect)) + { + /* Only resize the desktop if it was resized to something other + than the virtual screen size/position! */ + if (!EqualRect(prcDesktopRect, + &rcDesktopRect)) + { + SetWindowPos(This->hWnd, + NULL, + prcDesktopRect->left, + prcDesktopRect->top, + prcDesktopRect->right - prcDesktopRect->left, + prcDesktopRect->bottom - prcDesktopRect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + + /* Let's try to rearrange the icons on the desktop. This is + especially neccessary when switching screen resolutions... */ + PositionIcons(This, + prcDesktopRect); + } + } +} + +static LRESULT CALLBACK +ProgmanWindowProc(IN HWND hwnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + IDesktopShellBrowserImpl *This = NULL; + LRESULT Ret = FALSE; + + if (uMsg != WM_NCCREATE) + { + This = (IDesktopShellBrowserImpl*)GetWindowLongPtr(hwnd, + 0); + } + + if (This != NULL || uMsg == WM_NCCREATE) + { + switch (uMsg) + { + case WM_ERASEBKGND: + PaintDesktop((HDC)wParam); + break; + + case WM_GETISHELLBROWSER: + Ret = (LRESULT)IShellBrowser_from_impl(This); + break; + + case WM_SHELL_ADDDRIVENOTIFY: + { + HANDLE hLock; + LPITEMIDLIST *ppidl; + LONG lEventId; + + hLock = SHChangeNotification_Lock((HANDLE)wParam, + (DWORD)lParam, + &ppidl, + &lEventId); + if (hLock != NULL) + { + /* FIXME */ + SHChangeNotification_Unlock(hLock); + } + break; + } + + case WM_SIZE: + if (wParam == SIZE_MINIMIZED) + { + /* Hey, we're the desktop!!! */ + ShowWindow(hwnd, + SW_RESTORE); + } + else + { + RECT rcDesktop; + + rcDesktop.left = GetSystemMetrics(SM_XVIRTUALSCREEN); + rcDesktop.top = GetSystemMetrics(SM_YVIRTUALSCREEN); + rcDesktop.right = GetSystemMetrics(SM_CXVIRTUALSCREEN); + rcDesktop.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + UpdateWorkArea(This, + &rcDesktop); + } + break; + + case WM_SYSCOLORCHANGE: + { + InvalidateRect(This->hWnd, + NULL, + TRUE); + + if (This->hWndShellView != NULL) + { + /* Forward the message */ + SendMessage(This->hWndShellView, + WM_SYSCOLORCHANGE, + wParam, + lParam); + } + break; + } + + case WM_NCCREATE: + { + LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; + This = IDesktopShellBrowserImpl_Construct(hwnd, + CreateStruct); + if (This == NULL) + break; + + SetWindowLongPtr(hwnd, + 0, + (LONG_PTR)This); + + if (This->hWndShellView != NULL) + SetShellWindowEx(This->hWnd, + This->hWndDesktopListView); + else + SetShellWindow(This->hWnd); + + Ret = TRUE; + break; + } + + case WM_DESTROY: + { + IDesktopShellBrowserImpl_Destroy(This); + break; + } + + case WM_NCDESTROY: + { + IDesktopShellBrowserImpl_Release(IShellBrowser_from_impl(This)); + + PostQuitMessage(0); + break; + } + + default: + Ret = DefWindowProc(hwnd, uMsg, wParam, lParam); + break; + } + } + + return Ret; +} + +static BOOL +RegisterProgmanWindowClass(VOID) +{ + WNDCLASS wcProgman; + + wcProgman.style = CS_DBLCLKS; + wcProgman.lpfnWndProc = ProgmanWindowProc; + wcProgman.cbClsExtra = 0; + wcProgman.cbWndExtra = sizeof(IDesktopShellBrowserImpl *); + wcProgman.hInstance = hExplorerInstance; + wcProgman.hIcon = NULL; + wcProgman.hCursor = LoadCursor(NULL, + IDC_ARROW); + wcProgman.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wcProgman.lpszMenuName = NULL; + wcProgman.lpszClassName = szProgmanClassName; + + return RegisterClass(&wcProgman) != 0; +} + +static VOID +UnregisterProgmanWindowClass(VOID) +{ + UnregisterClass(szProgmanClassName, + hExplorerInstance); +} + +typedef struct _DESKCREATEINFO +{ + HANDLE hEvent; + HWND hWndDesktop; + IDesktopShellBrowserImpl *pDesktop; +} DESKCREATEINFO, *PDESKCREATEINFO; + +static DWORD CALLBACK +DesktopThreadProc(IN OUT LPVOID lpParameter) +{ + volatile DESKCREATEINFO *DeskCreateInfo = (volatile DESKCREATEINFO *)lpParameter; + HWND hwndDesktop; + IDesktopShellBrowserImpl *pDesktop; + IShellDesktopTray *pSdt; + MSG Msg; + BOOL Ret; + + OleInitialize(NULL); + hwndDesktop = CreateWindowEx(WS_EX_TOOLWINDOW, + szProgmanClassName, + szProgmanWindowName, + WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, + 0, + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN), + NULL, + NULL, + hExplorerInstance, + NULL); + + DeskCreateInfo->hWndDesktop = hWndDesktop; + if (hwndDesktop == NULL) + return 1; + + pDesktop = (IDesktopShellBrowserImpl*)GetWindowLongPtr(hwndDesktop, + 0); + (void)InterlockedExchangePointer(&DeskCreateInfo->pDesktop, + pDesktop); + + if (!SetEvent(DeskCreateInfo->hEvent)) + { + /* Failed to notify that we initialized successfully, kill ourselves + to make the main thread wake up! */ + return 1; + } + + while (1) + { + Ret = (GetMessage(&Msg, + NULL, + 0, + 0) != 0); + + if (Ret != -1) + { + if (!Ret) + break; + + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + } + + OleUninitialize(); + + /* FIXME: Properly rundown the main thread! */ + ExitProcess(0); + + return 0; +} + +HANDLE +DesktopCreateWindow(IN OUT ITrayWindow *Tray) +{ + HANDLE hThread; + HANDLE hEvent; + DWORD DesktopThreadId; + IDesktopShellBrowserImpl *pDesktop = NULL; + HANDLE Handles[2]; + DWORD WaitResult; + HWND hWndDesktop = NULL; + IShellDesktopTray *pSdt; + HRESULT hRet; + + if (!RegisterProgmanWindowClass()) + return NULL; + + if (!RegisterTrayWindowClass()) + { + UnregisterProgmanWindowClass(); + return NULL; + } + + if (!RegisterTaskSwitchWndClass()) + { + UnregisterProgmanWindowClass(); + UnregisterTrayWindowClass(); + return NULL; + } + + hEvent = CreateEvent(NULL, + FALSE, + FALSE, + NULL); + if (hEvent != NULL) + { + volatile DESKCREATEINFO DeskCreateInfo; + + DeskCreateInfo.hEvent = hEvent; + DeskCreateInfo.pDesktop = NULL; + + hThread = CreateThread(NULL, + 0, + DesktopThreadProc, + (PVOID)&DeskCreateInfo, + 0, + &DesktopThreadId); + if (hThread != NULL) + { + Handles[0] = hThread; + Handles[1] = hEvent; + + WaitResult = WaitForMultipleObjects(sizeof(Handles) / sizeof(Handles[0]), + Handles, + FALSE, + INFINITE); + if (WaitResult != WAIT_FAILED && WaitResult != WAIT_OBJECT_0) + { + pDesktop = DeskCreateInfo.pDesktop; + hWndDesktop = DeskCreateInfo.hWndDesktop; + } + + CloseHandle(hThread); + } + + CloseHandle(hEvent); + } + + if (pDesktop != NULL) + { + hRet = ITrayWindow_QueryInterface(Tray, + &IID_IShellDesktopTray, + (PVOID*)&pSdt); + if (SUCCEEDED(hRet)) + { + IShellDesktopTray_RegisterDesktopWindow(pSdt, + hWndDesktop); + IShellDesktopTray_Release(pSdt); + } + } + else + { + UnregisterProgmanWindowClass(); + UnregisterTrayWindowClass(); + UnregisterTaskSwitchWndClass(); + } + + return (HANDLE)pDesktop; +} + +VOID +DesktopDestroyShellWindow(IN HANDLE hDesktop) +{ + //IDesktopShellBrowserImpl *pDesktop = (IDesktopShellBrowserImpl *)hDesktop; + + /* FIXME - Destroy window (don't use DestroyWindow() as + the window belongs to another thread!) */ + + UnregisterProgmanWindowClass(); + UnregisterTrayWindowClass(); +} + +#endif /* USE_API_SHCREATEDESKTOP */ diff --git a/reactos/base/shell/explorer-new/dragdrop.c b/reactos/base/shell/explorer-new/dragdrop.c new file mode 100644 index 00000000000..67d3cc10bc8 --- /dev/null +++ b/reactos/base/shell/explorer-new/dragdrop.c @@ -0,0 +1,411 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +static const IDropTargetVtbl IDropTargetImpl_Vtbl; + +/* + * IDropTarget + */ + +typedef struct +{ + const IDropTargetVtbl *lpVtbl; + LONG Ref; + HWND hwndTarget; + IDropTargetHelper *DropTargetHelper; + PVOID Context; + BOOL CanDrop; + DROPTARGET_CALLBACKS Callbacks; + DWORD FormatsCount; + FORMATETC Formats[0]; +} IDropTargetImpl; + +static IUnknown * +IUnknown_from_impl(IDropTargetImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +static IDropTarget * +IDropTarget_from_impl(IDropTargetImpl *This) +{ + return (IDropTarget *)&This->lpVtbl; +} + +static IDropTargetImpl * +impl_from_IDropTarget(IDropTarget *iface) +{ + return (IDropTargetImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDropTargetImpl, + lpVtbl)); +} + +static VOID +IDropTargetImpl_Free(IDropTargetImpl *This) +{ + IDropTargetHelper_Release(This->DropTargetHelper); +} + +static ULONG STDMETHODCALLTYPE +IDropTargetImpl_Release(IN OUT IDropTarget *iface) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + if (Ret == 0) + IDropTargetImpl_Free(This); + + return Ret; +} + +static ULONG STDMETHODCALLTYPE +IDropTargetImpl_AddRef(IN OUT IDropTarget *iface) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + + return InterlockedIncrement(&This->Ref); +} + +static HRESULT STDMETHODCALLTYPE +IDropTargetImpl_QueryInterface(IN OUT IDropTarget *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IDropTargetImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = impl_from_IDropTarget(iface); + + if (IsEqualIID(riid, + &IID_IUnknown)) + { + *ppvObj = IUnknown_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_IDropTarget)) + { + *ppvObj = IDropTarget_from_impl(This); + } + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + IDropTargetImpl_AddRef(iface); + return S_OK; +} + +IDropTarget * +CreateDropTarget(IN HWND hwndTarget, + IN DWORD nSupportedFormats, + IN const FORMATETC *Formats OPTIONAL, + IN PVOID Context OPTIONAL, + IN const DROPTARGET_CALLBACKS *Callbacks OPTIONAL) +{ + IDropTargetImpl *This; + HRESULT hr; + + This = (IDropTargetImpl *)HeapAlloc(hProcessHeap, + 0, + FIELD_OFFSET(IDropTargetImpl, + Formats[nSupportedFormats])); + if (This != NULL) + { + ZeroMemory(This, + sizeof(*This)); + + This->lpVtbl = &IDropTargetImpl_Vtbl; + This->Ref = 1; + This->hwndTarget = hwndTarget; + This->FormatsCount = nSupportedFormats; + if (nSupportedFormats != 0) + { + CopyMemory(This->Formats, + Formats, + sizeof(Formats[0]) * nSupportedFormats); + } + + This->Context = Context; + if (Callbacks != NULL) + { + CopyMemory(&This->Callbacks, + Callbacks, + sizeof(Callbacks)); + } + + hr = CoCreateInstance(&CLSID_DragDropHelper, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IDropTargetHelper, + (PVOID)&This->DropTargetHelper); + + if (!SUCCEEDED(hr)) + { + HeapFree(hProcessHeap, + 0, + This); + return NULL; + } + + return IDropTarget_from_impl(This); + } + + return NULL; +} + +static const FORMATETC * +IDropTargetImpl_FindSupportedFormat(IN OUT IDropTargetImpl *This, + IN IDataObject *pDataObject) +{ + FORMATETC *Current, *Last; + HRESULT hr; + + /* NOTE: we could use IDataObject::EnumFormatEtc(), + but this appears to be a lot easier! */ + Last = This->Formats + This->FormatsCount; + for (Current = This->Formats; + Current != Last; + Current++) + { + hr = IDataObject_QueryGetData(pDataObject, + Current); + if (SUCCEEDED(hr)) + return Current; + } + + return NULL; +} + +static HRESULT STDMETHODCALLTYPE +IDropTargetImpl_DragEnter(IN OUT IDropTarget *iface, + IN IDataObject *pDataObject, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + const FORMATETC *Format; + HRESULT hr; + + if (pDataObject == NULL) + return E_INVALIDARG; + + This->CanDrop = FALSE; + + hr = IDropTargetHelper_DragEnter(This->DropTargetHelper, + This->hwndTarget, + pDataObject, + &pt, + *pdwEffect); + + if (SUCCEEDED(hr)) + { + Format = IDropTargetImpl_FindSupportedFormat(This, + pDataObject); + if (Format != NULL) + { + /* We found a format that we support! */ + if (This->Callbacks.OnDragEnter != NULL) + { + hr = This->Callbacks.OnDragEnter(iface, + This->Context, + Format, + grfKeyState, + pt, + pdwEffect); + if (SUCCEEDED(hr)) + { + if (hr == S_OK) + This->CanDrop = TRUE; + else + { + /* Special return value by the callback routine, + doesn't want to allow dragging */ + *pdwEffect = DROPEFFECT_NONE; + } + + hr = S_OK; + } + else + { + *pdwEffect = DROPEFFECT_NONE; + hr = S_OK; + } + } + else + *pdwEffect = DROPEFFECT_NONE; + } + else + *pdwEffect = DROPEFFECT_NONE; + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE +IDropTargetImpl_DragOver(IN OUT IDropTarget *iface, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + HRESULT hr; + + hr = IDropTargetHelper_DragOver(This->DropTargetHelper, + &pt, + *pdwEffect); + + if (SUCCEEDED(hr)) + { + if (This->CanDrop) + { + if (This->Callbacks.OnDragOver != NULL) + { + hr = This->Callbacks.OnDragOver(iface, + This->Context, + grfKeyState, + pt, + pdwEffect); + if (SUCCEEDED(hr)) + { + if (hr != S_OK) + { + /* Special return value by the callback routine, + doesn't want to allow dropping here */ + *pdwEffect = DROPEFFECT_NONE; + } + + hr = S_OK; + } + else + { + *pdwEffect = DROPEFFECT_NONE; + hr = S_OK; + } + } + else + *pdwEffect = DROPEFFECT_NONE; + } + else + *pdwEffect = DROPEFFECT_NONE; + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE +IDropTargetImpl_DragLeave(IN OUT IDropTarget *iface) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + HRESULT hr; + + hr = IDropTargetHelper_DragLeave(This->DropTargetHelper); + if (SUCCEEDED(hr)) + { + if (This->Callbacks.OnDragLeave != NULL) + { + hr = This->Callbacks.OnDragLeave(iface, + This->Context); + } + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE +IDropTargetImpl_Drop(IN OUT IDropTarget *iface, + IN IDataObject *pDataObject, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect) +{ + IDropTargetImpl *This = impl_from_IDropTarget(iface); + const FORMATETC *Format; + HRESULT hr; + + if (pDataObject == NULL) + return E_INVALIDARG; + + hr = IDropTargetHelper_Drop(This->DropTargetHelper, + pDataObject, + &pt, + *pdwEffect); + + if (SUCCEEDED(hr) && This->CanDrop) + { + Format = IDropTargetImpl_FindSupportedFormat(This, + pDataObject); + if (Format != NULL) + { + /* We found a format that we support! */ + if (This->Callbacks.OnDrop != NULL) + { + hr = This->Callbacks.OnDrop(iface, + This->Context, + Format, + grfKeyState, + pt, + pdwEffect); + if (SUCCEEDED(hr)) + { + if (hr == S_OK) + This->CanDrop = TRUE; + else + { + /* Special return value by the callback routine, + doesn't want to allow dragging */ + *pdwEffect = DROPEFFECT_NONE; + } + + hr = S_OK; + } + else + { + *pdwEffect = DROPEFFECT_NONE; + hr = S_OK; + } + } + else + *pdwEffect = DROPEFFECT_NONE; + } + else + *pdwEffect = DROPEFFECT_NONE; + } + + return hr; +} + +static const IDropTargetVtbl IDropTargetImpl_Vtbl = +{ + /* IUnknown */ + IDropTargetImpl_QueryInterface, + IDropTargetImpl_AddRef, + IDropTargetImpl_Release, + /* IDropTarget */ + IDropTargetImpl_DragEnter, + IDropTargetImpl_DragOver, + IDropTargetImpl_DragLeave, + IDropTargetImpl_Drop +}; diff --git a/reactos/base/shell/explorer-new/explorer.c b/reactos/base/shell/explorer-new/explorer.c new file mode 100644 index 00000000000..91c5dc0f4f8 --- /dev/null +++ b/reactos/base/shell/explorer-new/explorer.c @@ -0,0 +1,342 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +HINSTANCE hExplorerInstance; +HANDLE hProcessHeap; +HKEY hkExplorer = NULL; + +LONG +SetWindowStyle(IN HWND hWnd, + IN LONG dwStyleMask, + IN LONG dwStyle) +{ + LONG PrevStyle, Style; + + ASSERT((~dwStyleMask & dwStyle) == 0); + + PrevStyle = GetWindowLong(hWnd, + GWL_STYLE); + if (PrevStyle != 0 && + (PrevStyle & dwStyleMask) != dwStyle) + { + Style = PrevStyle & ~dwStyleMask; + Style |= dwStyle; + + PrevStyle = SetWindowLong(hWnd, + GWL_STYLE, + Style); + } + + return PrevStyle; +} + +LONG +SetWindowExStyle(IN HWND hWnd, + IN LONG dwStyleMask, + IN LONG dwStyle) +{ + LONG PrevStyle, Style; + + ASSERT((~dwStyleMask & dwStyle) == 0); + + PrevStyle = GetWindowLong(hWnd, + GWL_EXSTYLE); + if (PrevStyle != 0 && + (PrevStyle & dwStyleMask) != dwStyle) + { + Style = PrevStyle & ~dwStyleMask; + Style |= dwStyle; + + PrevStyle = SetWindowLong(hWnd, + GWL_EXSTYLE, + Style); + } + + return PrevStyle; +} + +HMENU +LoadPopupMenu(IN HINSTANCE hInstance, + IN LPCTSTR lpMenuName) +{ + HMENU hMenu, hSubMenu = NULL; + + hMenu = LoadMenu(hInstance, + lpMenuName); + + if (hMenu != NULL) + { + hSubMenu = GetSubMenu(hMenu, + 0); + if (hSubMenu != NULL && + !RemoveMenu(hMenu, + 0, + MF_BYPOSITION)) + { + hSubMenu = NULL; + } + + DestroyMenu(hMenu); + } + + return hSubMenu; +} + +HMENU +FindSubMenu(IN HMENU hMenu, + IN UINT uItem, + IN BOOL fByPosition) +{ + MENUITEMINFO mii; + + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_SUBMENU; + + if (GetMenuItemInfo(hMenu, + uItem, + fByPosition, + &mii)) + { + return mii.hSubMenu; + } + + return NULL; +} + +BOOL +GetCurrentLoggedOnUserName(OUT LPTSTR szBuffer, + IN DWORD dwBufferSize) +{ + DWORD dwType; + DWORD dwSize; + + /* Query the user name from the registry */ + dwSize = (dwBufferSize * sizeof(TCHAR)) - 1; + if (RegQueryValueEx(hkExplorer, + TEXT("Logon User Name"), + 0, + &dwType, + (LPBYTE)szBuffer, + &dwSize) == ERROR_SUCCESS && + (dwSize / sizeof(TCHAR)) > 1 && + szBuffer[0] != _T('\0')) + { + szBuffer[dwSize / sizeof(TCHAR)] = _T('\0'); + return TRUE; + } + + /* Fall back to GetUserName() */ + dwSize = dwBufferSize; + if (!GetUserName(szBuffer, + &dwSize)) + { + szBuffer[0] = _T('\0'); + return FALSE; + } + + return TRUE; +} + +BOOL +FormatMenuString(IN HMENU hMenu, + IN UINT uPosition, + IN UINT uFlags, + ...) +{ + va_list vl; + MENUITEMINFO mii; + TCHAR szBuf[128]; + TCHAR szBufFmt[128]; + + /* Find the menu item and read the formatting string */ + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING; + mii.dwTypeData = (LPTSTR)szBufFmt; + mii.cch = sizeof(szBufFmt) / sizeof(szBufFmt[0]); + if (GetMenuItemInfo(hMenu, + uPosition, + uFlags, + &mii)) + { + /* Format the string */ + va_start(vl, uFlags); + _vsntprintf(szBuf, + (sizeof(szBuf) / sizeof(szBuf[0])) - 1, + szBufFmt, + vl); + va_end(vl); + szBuf[(sizeof(szBuf) / sizeof(szBuf[0])) - 1] = _T('\0'); + + /* Update the menu item */ + mii.dwTypeData = (LPTSTR)szBuf; + if (SetMenuItemInfo(hMenu, + uPosition, + uFlags, + &mii)) + { + return TRUE; + } + } + + return FALSE; +} + +BOOL +GetExplorerRegValueSet(IN HKEY hKey, + IN LPCTSTR lpSubKey, + IN LPCTSTR lpValue) +{ + TCHAR szBuffer[MAX_PATH]; + HKEY hkSubKey; + DWORD dwType, dwSize; + BOOL Ret = FALSE; + + _tcscpy(szBuffer, + TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer")); + _tcscat(szBuffer, + _T("\\")); + _tcscat(szBuffer, + lpSubKey); + + dwSize = sizeof(szBuffer); + if (RegOpenKeyEx(hKey, + szBuffer, + 0, + KEY_QUERY_VALUE, + &hkSubKey) == ERROR_SUCCESS) + { + ZeroMemory(szBuffer, + sizeof(szBuffer)); + + if (RegQueryValueEx(hkSubKey, + lpValue, + 0, + &dwType, + (LPBYTE)szBuffer, + &dwSize) == ERROR_SUCCESS) + { + if (dwType == REG_DWORD && dwSize == sizeof(DWORD)) + Ret = *((PDWORD)szBuffer) != 0; + else if (dwSize > 0) + Ret = *((PUCHAR)szBuffer) != 0; + } + + RegCloseKey(hkSubKey); + } + return Ret; +} + + +#if 1 +/* FIXME: Should be implemented in shell32 */ +BOOL WINAPI IsUserAnAdmin(VOID) +{ + HRESULT WINAPI IsUserAdmin(void); + return (BOOL)IsUserAdmin(); +} +#endif + +static BOOL +SetShellReadyEvent(IN LPCTSTR lpEventName) +{ + HANDLE hEvent; + + hEvent = OpenEvent(EVENT_MODIFY_STATE, + FALSE, + lpEventName); + if (hEvent != NULL) + { + SetEvent(hEvent); + + CloseHandle(hEvent); + return TRUE; + } + + return FALSE; +} + +INT WINAPI +_tWinMain(IN HINSTANCE hInstance, + IN HINSTANCE hPrevInstance, + IN LPTSTR lpCmdLine, + IN INT nCmdShow) +{ + ITrayWindow *Tray = NULL; + HANDLE hShellDesktop = NULL; + BOOL CreateShellDesktop = FALSE; + + if (RegOpenKey(HKEY_CURRENT_USER, + TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), + &hkExplorer) != ERROR_SUCCESS) + { + /* FIXME - display error */ + return 1; + } + + hExplorerInstance = hInstance; + hProcessHeap = GetProcessHeap(); + + InitCommonControls(); + OleInitialize(NULL); + + if (GetShellWindow() == NULL) + CreateShellDesktop = TRUE; + + /* FIXME - initialize SSO Thread */ + + if (CreateShellDesktop) + { + if (RegisterTrayWindowClass() && RegisterTaskSwitchWndClass()) + { + Tray = CreateTrayWindow(); + + if (Tray != NULL) + hShellDesktop = DesktopCreateWindow(Tray); + } + + /* WinXP: Notify msgina to hide the welcome screen */ + if (!SetShellReadyEvent(TEXT("msgina: ShellReadyEvent"))) + SetShellReadyEvent(TEXT("Global\\msgina: ShellReadyEvent")); + } + else + { + /* A shell is already loaded. Parse the command line arguments + and unless we need to do something specific simply display + the desktop in a separate explorer window */ + /* FIXME */ + } + + if (Tray != NULL) + TrayMessageLoop(Tray); + + if (hShellDesktop != NULL) + DesktopDestroyShellWindow(hShellDesktop); + + /* FIXME - shutdown SSO Thread */ + + OleUninitialize(); + + RegCloseKey(hkExplorer); + hkExplorer = NULL; + + return 0; +} diff --git a/reactos/base/shell/explorer-new/explorer.rbuild b/reactos/base/shell/explorer-new/explorer.rbuild new file mode 100644 index 00000000000..8949270b981 --- /dev/null +++ b/reactos/base/shell/explorer-new/explorer.rbuild @@ -0,0 +1,35 @@ + + + + . + + 0x0600 + 0x0600 + 0x0600 + advapi32 + kernel32 + gdi32 + user32 + comctl32 + msvcrt20 + ntdll + ole32 + oleaut32 + shdocvw + shell32 + shlwapi + uuid + precomp.h + desktop.c + dragdrop.c + explorer.c + startmnu.c + taskband.c + taskswnd.c + tbsite.c + trayntfy.c + trayprop.c + traywnd.c + explorer.rc + + diff --git a/reactos/base/shell/explorer-new/explorer.rc b/reactos/base/shell/explorer-new/explorer.rc new file mode 100644 index 00000000000..eb8df6d424c --- /dev/null +++ b/reactos/base/shell/explorer-new/explorer.rc @@ -0,0 +1,16 @@ +#include +#include "resource.h" + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Explorer\0" +#define REACTOS_STR_INTERNAL_NAME "explorer\0" +#define REACTOS_STR_ORIGINAL_FILENAME "explorer.exe\0" +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +1 24 DISCARDABLE "manifest.xml" + +IDI_START ICON DISCARDABLE res/start.ico +IDB_STARTMENU BITMAP DISCARDABLE res/logov.bmp + +#include "lang/en-US.rc" diff --git a/reactos/base/shell/explorer-new/lang/en-US.rc b/reactos/base/shell/explorer-new/lang/en-US.rc new file mode 100644 index 00000000000..f24df7bc795 --- /dev/null +++ b/reactos/base/shell/explorer-new/lang/en-US.rc @@ -0,0 +1,59 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDM_TRAYWND MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Lock the Taskbar", ID_LOCKTASKBAR + MENUITEM "P&roperties", ID_SHELL_CMD_PROPERTIES + END +END + +IDM_STARTMENU MENUEX DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "", -1, MFT_SEPARATOR + POPUP "&Programs", IDM_PROGRAMS + BEGIN + MENUITEM "(Empty)", -1, MFT_STRING, MFS_GRAYED + END + POPUP "F&avorites", IDM_FAVORITES + BEGIN + MENUITEM "(Empty)", -1, MFT_STRING, MFS_GRAYED + END + POPUP "&Documents", IDM_DOCUMENTS + BEGIN + MENUITEM "(Empty)", -1, MFT_STRING, MFS_GRAYED + END + POPUP "&Settings", IDM_SETTINGS + BEGIN + MENUITEM "&Control Panel", IDM_CONTROLPANEL, MFT_STRING, MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "&Security...", IDM_SECURITY, MFT_STRING, MFS_ENABLED + MENUITEM "&Network Connections", IDM_NETWORKCONNECTIONS, MFT_STRING, MFS_ENABLED + MENUITEM "&Printers and Faxes", IDM_PRINTERSANDFAXES, MFT_STRING, MFS_ENABLED + MENUITEM "&Taskbar and Start Menu", IDM_TASKBARANDSTARTMENU, MFT_STRING, MFS_ENABLED + END + POPUP "Sear&ch", IDM_SEARCH + BEGIN + MENUITEM SEPARATOR + END + MENUITEM "&Help and Support", IDM_HELPANDSUPPORT, MFT_STRING, MFS_ENABLED + MENUITEM "&Run...", IDM_RUN, MFT_STRING, MFS_ENABLED + MENUITEM "", 450, MFT_SEPARATOR, MFS_ENABLED + MENUITEM "S&ynchronize", IDM_SYNCHRONIZE, MFT_STRING, MFS_ENABLED + MENUITEM "&Log Off %s...", IDM_LOGOFF, MFT_STRING, MFS_ENABLED + MENUITEM "D&isconnect...", IDM_DISCONNECT, MFT_STRING, MFS_ENABLED + MENUITEM "Undock Comput&er", IDM_UNDOCKCOMPUTER, MFT_STRING, MFS_ENABLED + MENUITEM "Sh&ut Down...", IDM_SHUTDOWN, MFT_STRING, MFS_ENABLED + END +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_START "Start" + IDS_PROPERTIES "P&roperties" + IDS_OPEN_ALL_USERS "O&pen All Users" + IDS_EXPLORE_ALL_USERS "E&xplore All Users" +END diff --git a/reactos/base/shell/explorer-new/manifest.xml b/reactos/base/shell/explorer-new/manifest.xml new file mode 100644 index 00000000000..1035bef08b2 --- /dev/null +++ b/reactos/base/shell/explorer-new/manifest.xml @@ -0,0 +1,22 @@ + + + +ROS Explorer + + + + + + diff --git a/reactos/base/shell/explorer-new/precomp.h b/reactos/base/shell/explorer-new/precomp.h new file mode 100644 index 00000000000..51138086a6b --- /dev/null +++ b/reactos/base/shell/explorer-new/precomp.h @@ -0,0 +1,389 @@ +#ifndef _EXPLORER_PRECOMP__H_ +#define _EXPLORER_PRECOMP__H_ +#define COBJMACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USE_API_SHCREATEDESKTOP 1 /* Use SHCreateDesktop() */ + +#include "resource.h" +#include "comcsup.h" +#include "todo.h" +#include "undoc.h" + +static ULONG __inline +Win32DbgPrint(const char *filename, int line, const char *lpFormat, ...) +{ + char szMsg[512]; + char *szMsgStart; + const char *fname; + va_list vl; + ULONG uRet; + + fname = strrchr(filename, '\\'); + if (fname == NULL) + { + fname = strrchr(filename, '/'); + if (fname != NULL) + fname++; + } + else + fname++; + + if (fname == NULL) + fname = filename; + + szMsgStart = szMsg + sprintf(szMsg, "%s:%d: ", fname, line); + + va_start(vl, lpFormat); + uRet = (ULONG)vsprintf(szMsgStart, lpFormat, vl); + va_end(vl); + + OutputDebugStringA(szMsg); + + return uRet; +} + +#define ASSERT(cond) \ + if (!(cond)) { \ + Win32DbgPrint(__FILE__, __LINE__, "ASSERTION %s FAILED!\n", #cond); \ + } + +#define DbgPrint(fmt, ...) \ + Win32DbgPrint(__FILE__, __LINE__, fmt, ##__VA_ARGS__); + +extern HINSTANCE hExplorerInstance; +extern HANDLE hProcessHeap; +extern HKEY hkExplorer; + +/* + * dragdrop.c + */ + +typedef struct _DROPTARGET_CALLBACKS +{ + HRESULT (*OnDragEnter)(IN IDropTarget *pDropTarget, + IN PVOID Context, + IN const FORMATETC *Format, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect); + HRESULT (*OnDragOver)(IN IDropTarget *pDropTarget, + IN PVOID Context, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect); + HRESULT (*OnDragLeave)(IN IDropTarget *pDropTarget, + IN PVOID Context); + HRESULT (*OnDrop)(IN IDropTarget *pDropTarget, + IN PVOID Context, + IN const FORMATETC *Format, + IN DWORD grfKeyState, + IN POINTL pt, + IN OUT DWORD *pdwEffect); +} DROPTARGET_CALLBACKS, *PDROPTARGET_CALLBACKS; + +IDropTarget * +CreateDropTarget(IN HWND hwndTarget, + IN DWORD nSupportedFormats, + IN const FORMATETC *Formats OPTIONAL, + IN PVOID Context OPTIONAL, + IN const DROPTARGET_CALLBACKS *Callbacks OPTIONAL); + +/* + * explorer.c + */ + +LONG +SetWindowStyle(IN HWND hWnd, + IN LONG dwStyleMask, + IN LONG dwStyle); + +LONG +SetWindowExStyle(IN HWND hWnd, + IN LONG dwStyleMask, + IN LONG dwStyle); + +HMENU +LoadPopupMenu(IN HINSTANCE hInstance, + IN LPCTSTR lpMenuName); + +HMENU +FindSubMenu(IN HMENU hMenu, + IN UINT uItem, + IN BOOL fByPosition); + +BOOL +GetCurrentLoggedOnUserName(OUT LPTSTR szBuffer, + IN DWORD dwBufferSize); + +BOOL +FormatMenuString(IN HMENU hMenu, + IN UINT uPosition, + IN UINT uFlags, + ...); + +BOOL +GetExplorerRegValueSet(IN HKEY hKey, + IN LPCTSTR lpSubKey, + IN LPCTSTR lpValue); + +/* + * traywnd.c + */ + +typedef HMENU (*PCREATECTXMENU)(IN HWND hWndOwner, + IN PVOID *ppcmContext, + IN PVOID Context OPTIONAL); +typedef VOID (*PCTXMENUCOMMAND)(IN HWND hWndOwner, + IN UINT uiCmdId, + IN PVOID pcmContext OPTIONAL, + IN PVOID Context OPTIONAL); + +typedef struct _TRAYWINDOW_CTXMENU +{ + PCREATECTXMENU CreateCtxMenu; + PCTXMENUCOMMAND CtxMenuCommand; +} TRAYWINDOW_CTXMENU, *PTRAYWINDOW_CTXMENU; + +extern const GUID IID_IShellDesktopTray; + +#define INTERFACE ITrayWindow +DECLARE_INTERFACE_(ITrayWindow,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface) (THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** ITrayWindow methods ***/ + STDMETHOD_(HRESULT,Open) (THIS) PURE; + STDMETHOD_(HRESULT,Close) (THIS) PURE; + STDMETHOD_(HWND,GetHWND) (THIS) PURE; + STDMETHOD_(BOOL,IsSpecialHWND) (THIS_ HWND hWnd) PURE; + STDMETHOD_(BOOL,IsHorizontal) (THIS) PURE; + STDMETHOD_(HFONT,GetCaptionFonts) (THIS_ HFONT *phBoldCaption) PURE; + STDMETHOD_(HWND,DisplayProperties) (THIS) PURE; + STDMETHOD_(BOOL,ExecContextMenuCmd) (THIS_ UINT uiCmd) PURE; + STDMETHOD_(BOOL,Lock) (THIS_ BOOL bLock) PURE; +}; +#undef INTERFACE + +#if defined(COBJMACROS) +/*** IUnknown methods ***/ +#define ITrayWindow_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define ITrayWindow_AddRef(p) (p)->lpVtbl->AddRef(p) +#define ITrayWindow_Release(p) (p)->lpVtbl->Release(p) +/*** ITrayWindow methods ***/ +#define ITrayWindow_Open(p) (p)->lpVtbl->Open(p) +#define ITrayWindow_Close(p) (p)->lpVtbl->Close(p) +#define ITrayWindow_GetHWND(p) (p)->lpVtbl->GetHWND(p) +#define ITrayWindow_IsSpecialHWND(p,a) (p)->lpVtbl->IsSpecialHWND(p,a) +#define ITrayWindow_IsHorizontal(p) (p)->lpVtbl->IsHorizontal(p) +#define ITrayWindow_GetCaptionFonts(p,a) (p)->lpVtbl->GetCaptionFonts(p,a) +#define ITrayWindow_DisplayProperties(p) (p)->lpVtbl->DisplayProperties(p) +#define ITrayWindow_ExecContextMenuCmd(p,a) (p)->lpVtbl->ExecContextMenuCmd(p,a) +#define ITrayWindow_Lock(p,a) (p)->lpVtbl->Lock(p,a) +#endif + +BOOL +RegisterTrayWindowClass(VOID); + +VOID +UnregisterTrayWindowClass(VOID); + +ITrayWindow * +CreateTrayWindow(VOID); + +VOID +TrayProcessMessages(IN OUT ITrayWindow *Tray); + +VOID +TrayMessageLoop(IN OUT ITrayWindow *Tray); + +/* + * trayprop.h + */ + +HWND +DisplayTrayProperties(ITrayWindow *Tray); + +/* + * desktop.c + */ + +#define SHCNRF_InterruptLevel (0x0001) +#define SHCNRF_ShellLevel (0x0002) +#define SHCNRF_RecursiveInterrupt (0x1000) +#define SHCNRF_NewDelivery (0x8000) + + +HANDLE +DesktopCreateWindow(IN OUT ITrayWindow *Tray); + +VOID +DesktopDestroyShellWindow(IN HANDLE hDesktop); + +/* + * taskband.c + */ + +/* Internal Task Band CLSID */ +extern const GUID CLSID_ITaskBand; + +#define INTERFACE ITaskBand +DECLARE_INTERFACE_(ITaskBand,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface) (THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** ITaskBand methods ***/ + STDMETHOD_(HRESULT,GetRebarBandID)(THIS_ DWORD *pdwBandID) PURE; +}; +#undef INTERFACE + +#if defined(COBJMACROS) +/*** IUnknown methods ***/ +#define ITaskBand_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define ITaskBand_AddRef(p) (p)->lpVtbl->AddRef(p) +#define ITaskBand_Release(p) (p)->lpVtbl->Release(p) +/*** ITaskBand methods ***/ +#define ITaskBand_GetRebarBandID(p,a) (p)->lpVtbl->GetRebarBandID(p,a) +#endif + +ITaskBand * +CreateTaskBand(IN OUT ITrayWindow *Tray); + +/* + * tbsite.c + */ + +#define INTERFACE ITrayBandSite +DECLARE_INTERFACE_(ITrayBandSite,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface) (THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IBandSiteStreamCallback ***/ + STDMETHOD_(HRESULT,OnLoad)(THIS_ IStream *pStm, REFIID riid, PVOID *pvObj) PURE; + STDMETHOD_(HRESULT,OnSave)(THIS_ IUnknown *pUnk, IStream *pStm) PURE; + /*** ITrayBandSite methods ***/ + STDMETHOD_(HRESULT,IsTaskBand) (THIS_ IUnknown *punk) PURE; + STDMETHOD_(HRESULT,ProcessMessage) (THIS_ HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) PURE; + STDMETHOD_(HRESULT,AddContextMenus) (THIS_ HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags, IContextMenu **ppcm) PURE; + STDMETHOD_(HRESULT,Lock) (THIS_ BOOL bLock) PURE; +}; +#undef INTERFACE + +#if defined(COBJMACROS) +/*** IUnknown methods ***/ +#define ITrayBandSite_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define ITrayBandSite_AddRef(p) (p)->lpVtbl->AddRef(p) +#define ITrayBandSite_Release(p) (p)->lpVtbl->Release(p) +/*** IBandSiteStreamCallback methods ***/ +#define ITrayBandSite_OnLoad(p,a,b,c) (p)->lpVtbl->OnLoad(p,a,b,c) +#define ITrayBandSite_OnSave(p,a,b) (p)->lpVtbl->OnSave(p,a,b) +/*** ITrayBandSite methods ***/ +#define ITrayBandSite_IsTaskBand(p,a) (p)->lpVtbl->IsTaskBand(p,a) +#define ITrayBandSite_ProcessMessage(p,a,b,c,d,e) (p)->lpVtbl->ProcessMessage(p,a,b,c,d,e) +#define ITrayBandSite_AddContextMenus(p,a,b,c,d,e,f) (p)->lpVtbl->AddContextMenus(p,a,b,c,d,e,f) +#define ITrayBandSite_Lock(p,a) (p)->lpVtbl->Lock(p,a) +#endif + +ITrayBandSite * +CreateTrayBandSite(IN OUT ITrayWindow *Tray, + OUT HWND *phWndRebar, + OUT HWND *phWndTaskSwitch); + +/* + * startmnu.c + */ + +extern const TRAYWINDOW_CTXMENU StartMenuBtnCtxMenu; + +#define INTERFACE IStartMenuSite +DECLARE_INTERFACE_(IStartMenuSite,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface) (THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IStartMenuSite ***/ +}; +#undef INTERFACE + +#if defined(COBJMACROS) +/*** IUnknown methods ***/ +#define IStartMenuSite_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IStartMenuSite_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IStartMenuSite_Release(p) (p)->lpVtbl->Release(p) +/*** IStartMenuSite methods ***/ +#endif + +IMenuPopup* +CreateStartMenu(IN ITrayWindow *Tray, + OUT IMenuBand **ppMenuBand, + IN HBITMAP hbmBanner OPTIONAL, + IN BOOL bSmallIcons); + +HRESULT +UpdateStartMenu(IN OUT IMenuPopup *pMenuPopup, + IN HBITMAP hbmBanner OPTIONAL, + IN BOOL bSmallIcons); + +/* + * trayntfy.c + */ + +/* TrayClockWnd */ +#define TCWM_GETMINIMUMSIZE (WM_USER + 0x100) +#define TCWM_UPDATETIME (WM_USER + 0x101) + +/* TrayNotifyWnd */ +#define TNWM_GETMINIMUMSIZE (WM_USER + 0x100) +#define TNWM_UPDATETIME (WM_USER + 0x101) +#define TNWM_SHOWCLOCK (WM_USER + 0x102) + +#define NTNWM_REALIGN (0x1) + +BOOL +RegisterTrayNotifyWndClass(VOID); + +VOID +UnregisterTrayNotifyWndClass(VOID); + +HWND +CreateTrayNotifyWnd(IN OUT ITrayWindow *TrayWindow, + IN BOOL bHideClock); + +/* + * taskswnd.c + */ + +#define TSWM_ENABLEGROUPING (WM_USER + 1) +#define TSWM_UPDATETASKBARPOS (WM_USER + 2) + +BOOL +RegisterTaskSwitchWndClass(VOID); + +VOID +UnregisterTaskSwitchWndClass(VOID); + +HWND +CreateTaskSwitchWnd(IN HWND hWndParent, + IN OUT ITrayWindow *Tray); + +#endif /* _EXPLORER_PRECOMP__H_ */ diff --git a/reactos/base/shell/explorer-new/res/logov.bmp b/reactos/base/shell/explorer-new/res/logov.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b4404ba9b901003e2853801df46fe6a7a59d47ee GIT binary patch literal 14966 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 literal 0 HcmV?d00001 diff --git a/reactos/base/shell/explorer-new/res/start.ico b/reactos/base/shell/explorer-new/res/start.ico new file mode 100644 index 0000000000000000000000000000000000000000..492e388a939f05b77da8a5a5681b2843e38b3a55 GIT binary patch literal 25214 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 ZRay4 + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* + * Start menu button context menu + */ + +typedef struct _STARTMNU_CTMENU_CTX +{ + IContextMenu *pcm; + LPITEMIDLIST pidl; +} STARTMNU_CTMENU_CTX, *PSTARTMNU_CTMENU_CTX; + +static HMENU +CreateStartContextMenu(IN HWND hWndOwner, + IN PVOID *ppcmContext, + IN PVOID Context OPTIONAL); + +static VOID +OnStartContextMenuCommand(IN HWND hWndOwner, + IN UINT uiCmdId, + IN PVOID pcmContext OPTIONAL, + IN PVOID Context OPTIONAL); + +const TRAYWINDOW_CTXMENU StartMenuBtnCtxMenu = { + CreateStartContextMenu, + OnStartContextMenuCommand +}; + +static HMENU +CreateContextMenuFromShellFolderPidl(IN HWND hWndOwner, + IN OUT IShellFolder *psf, + IN OUT LPITEMIDLIST pidl, + OUT IContextMenu **ppcm) +{ + IContextMenu *pcm; + HRESULT hRet; + HMENU hPopup; + + hRet = IShellFolder_GetUIObjectOf(psf, + hWndOwner, + 1, + (LPCITEMIDLIST*)&pidl, /* FIXME: shouldn't need a typecast! */ + &IID_IContextMenu, + NULL, + (PVOID*)&pcm); + if (SUCCEEDED(hRet)) + { + hPopup = CreatePopupMenu(); + + if (hPopup != NULL) + { + hRet = IContextMenu_QueryContextMenu(pcm, + hPopup, + 0, + ID_SHELL_CMD_FIRST, + ID_SHELL_CMD_LAST, + CMF_VERBSONLY); + + if (SUCCEEDED(hRet)) + { + *ppcm = pcm; + return hPopup; + } + + IContextMenu_Release(pcm); + DestroyMenu(hPopup); + } + } + + return NULL; +} + +static VOID +OnStartContextMenuCommand(IN HWND hWndOwner, + IN UINT uiCmdId, + IN PVOID pcmContext OPTIONAL, + IN PVOID Context OPTIONAL) +{ + PSTARTMNU_CTMENU_CTX psmcmc = (PSTARTMNU_CTMENU_CTX)pcmContext; + + if (uiCmdId != 0) + { + switch (uiCmdId) + { + case ID_SHELL_CMD_FIRST ... ID_SHELL_CMD_LAST: + { + CMINVOKECOMMANDINFO cmici = {0}; + CHAR szDir[MAX_PATH]; + + /* Setup and invoke the shell command */ + cmici.cbSize = sizeof(cmici); + cmici.hwnd = hWndOwner; + cmici.lpVerb = (LPCSTR)MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST); + cmici.nShow = SW_NORMAL; + + /* FIXME: Support Unicode!!! */ + if (SHGetPathFromIDListA(psmcmc->pidl, + szDir)) + { + cmici.lpDirectory = szDir; + } + + IContextMenu_InvokeCommand(psmcmc->pcm, + &cmici); + break; + } + + default: + ITrayWindow_ExecContextMenuCmd((ITrayWindow *)Context, + uiCmdId); + break; + } + } + + IContextMenu_Release(psmcmc->pcm); + + HeapFree(hProcessHeap, + 0, + psmcmc); +} + +static VOID +AddStartContextMenuItems(IN HWND hWndOwner, + IN HMENU hPopup) +{ + TCHAR szBuf[MAX_PATH]; + HRESULT hRet; + + /* Add the "Open All Users" menu item */ + if (LoadString(hExplorerInstance, + IDS_PROPERTIES, + szBuf, + sizeof(szBuf) / sizeof(szBuf[0]))) + { + AppendMenu(hPopup, + MF_STRING, + ID_SHELL_CMD_PROPERTIES, + szBuf); + } + + if (!SHRestricted(REST_NOCOMMONGROUPS)) + { + /* Check if we should add menu items for the common start menu */ + hRet = SHGetFolderPath(hWndOwner, + CSIDL_COMMON_STARTMENU, + NULL, + SHGFP_TYPE_CURRENT, + szBuf); + if (SUCCEEDED(hRet) && hRet != S_FALSE) + { + /* The directory exists, but only show the items if the + user can actually make any changes to the common start + menu. This is most likely only the case if the user + has administrative rights! */ + if (IsUserAnAdmin()) + { + AppendMenu(hPopup, + MF_SEPARATOR, + 0, + NULL); + + /* Add the "Open All Users" menu item */ + if (LoadString(hExplorerInstance, + IDS_OPEN_ALL_USERS, + szBuf, + sizeof(szBuf) / sizeof(szBuf[0]))) + { + AppendMenu(hPopup, + MF_STRING, + ID_SHELL_CMD_OPEN_ALL_USERS, + szBuf); + } + + /* Add the "Explore All Users" menu item */ + if (LoadString(hExplorerInstance, + IDS_EXPLORE_ALL_USERS, + szBuf, + sizeof(szBuf) / sizeof(szBuf[0]))) + { + AppendMenu(hPopup, + MF_STRING, + ID_SHELL_CMD_EXPLORE_ALL_USERS, + szBuf); + } + } + } + } +} + +static HMENU +CreateStartContextMenu(IN HWND hWndOwner, + IN PVOID *ppcmContext, + IN PVOID Context OPTIONAL) +{ + LPITEMIDLIST pidlStart, pidlLast; + IShellFolder *psfStart, *psfDesktop; + IContextMenu *pcm; + HRESULT hRet; + HMENU hPopup; + + pidlStart = SHCloneSpecialIDList(hWndOwner, + CSIDL_STARTMENU, + TRUE); + + if (pidlStart != NULL) + { + pidlLast = ILClone(ILFindLastID(pidlStart)); + ILRemoveLastID(pidlStart); + + if (pidlLast != NULL) + { + hRet = SHGetDesktopFolder(&psfDesktop); + if (SUCCEEDED(hRet)) + { + hRet = IShellFolder_BindToObject(psfDesktop, + pidlStart, + NULL, + (REFIID)&IID_IShellFolder, /* FIXME: Shouldn't require a typecast */ + (PVOID*)&psfStart); + if (SUCCEEDED(hRet)) + { + hPopup = CreateContextMenuFromShellFolderPidl(hWndOwner, + psfStart, + pidlLast, + &pcm); + + if (hPopup != NULL) + { + PSTARTMNU_CTMENU_CTX psmcmc; + + psmcmc = HeapAlloc(hProcessHeap, + 0, + sizeof(*psmcmc)); + if (psmcmc != NULL) + { + psmcmc->pcm = pcm; + psmcmc->pidl = pidlLast; + + AddStartContextMenuItems(hWndOwner, + hPopup); + + *((PSTARTMNU_CTMENU_CTX*)ppcmContext) = psmcmc; + return hPopup; + } + else + { + IContextMenu_Release(pcm); + + DestroyMenu(hPopup); + hPopup = NULL; + } + } + + IShellFolder_Release(psfStart); + } + + IShellFolder_Release(psfDesktop); + } + + ILFree(pidlLast); + } + + ILFree(pidlStart); + } + + return NULL; +} + +/***************************************************************************** + ** IStartMenuSite *********************************************************** + *****************************************************************************/ + +static const IStartMenuSiteVtbl IStartMenuSiteImpl_Vtbl; +static const IServiceProviderVtbl IServiceProviderImpl_Vtbl; +static const IStartMenuCallbackVtbl IStartMenuCallbackImpl_Vtbl; +static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl; + +typedef struct +{ + const IStartMenuSiteVtbl *lpVtbl; + const IServiceProviderVtbl *lpServiceProviderVtbl; + const IStartMenuCallbackVtbl *lpStartMenuCallbackVtbl; + const IOleCommandTargetVtbl *lpOleCommandTargetVtbl; + LONG Ref; + + ITrayWindow *Tray; +} IStartMenuSiteImpl; + +static IUnknown * +IUnknown_from_IStartMenuSiteImpl(IStartMenuSiteImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +IMPL_CASTS(IStartMenuSite, IStartMenuSite, lpVtbl) +IMPL_CASTS(IServiceProvider, IStartMenuSite, lpServiceProviderVtbl) +IMPL_CASTS(IStartMenuCallback, IStartMenuSite, lpStartMenuCallbackVtbl) +IMPL_CASTS(IOleCommandTarget, IStartMenuSite, lpOleCommandTargetVtbl) + +/*******************************************************************/ + +static ULONG STDMETHODCALLTYPE +IStartMenuSiteImpl_AddRef(IN OUT IStartMenuSite *iface) +{ + IStartMenuSiteImpl *This = IStartMenuSiteImpl_from_IStartMenuSite(iface); + + return InterlockedIncrement(&This->Ref); +} + +static VOID +IStartMenuSiteImpl_Free(IN OUT IStartMenuSiteImpl *This) +{ + HeapFree(hProcessHeap, + 0, + This); +} + +static ULONG STDMETHODCALLTYPE +IStartMenuSiteImpl_Release(IN OUT IStartMenuSite *iface) +{ + IStartMenuSiteImpl *This = IStartMenuSiteImpl_from_IStartMenuSite(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + + if (Ret == 0) + IStartMenuSiteImpl_Free(This); + + return Ret; +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_QueryInterface(IN OUT IStartMenuSite *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + IStartMenuSiteImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = IStartMenuSiteImpl_from_IStartMenuSite(iface); + + if (IsEqualIID(riid, + &IID_IUnknown)) + { + *ppvObj = IUnknown_from_IStartMenuSiteImpl(This); + } + else if (IsEqualIID(riid, + &IID_IServiceProvider)) + { + *ppvObj = IServiceProvider_from_IStartMenuSiteImpl(This); + } + else if (IsEqualIID(riid, + &IID_IStartMenuCallback) || + IsEqualIID(riid, + &IID_IOleWindow)) + { + *ppvObj = IStartMenuCallback_from_IStartMenuSiteImpl(This); + } + else if (IsEqualIID(riid, + &IID_IOleCommandTarget)) + { + *ppvObj = IOleCommandTarget_from_IStartMenuSiteImpl(This); + } + else + { + DbgPrint("IStartMenuSite::QueryInterface queried unsupported interface: " + "{0x%8x,0x%4x,0x%4x,{0x%2x,0x%2x,0x%2x,0x%2x,0x%2x,0x%2x,0x%2x,0x%2x}}\n", + riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], riid->Data4[1], + riid->Data4[2], riid->Data4[3], riid->Data4[4], riid->Data4[5], + riid->Data4[6], riid->Data4[7]); + *ppvObj = NULL; + return E_NOINTERFACE; + } + + IStartMenuSiteImpl_AddRef(iface); + return S_OK; +} + +static const IStartMenuSiteVtbl IStartMenuSiteImpl_Vtbl = +{ + /*** IUnknown methods ***/ + IStartMenuSiteImpl_QueryInterface, + IStartMenuSiteImpl_AddRef, + IStartMenuSiteImpl_Release, + /*** IStartMenuSite methods ***/ +}; + +/*******************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IServiceProvider, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_RELEASE(IServiceProvider, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IServiceProvider, IStartMenuSite) + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_QueryService(IN OUT IServiceProvider *iface, + IN REFGUID guidService, + IN REFIID riid, + OUT PVOID *ppvObject) +{ + IStartMenuSiteImpl *This = IStartMenuSiteImpl_from_IServiceProvider(iface); + + if (IsEqualGUID(guidService, + &SID_SMenuPopup)) + { + return IStartMenuSiteImpl_QueryInterface(IStartMenuSite_from_IStartMenuSiteImpl(This), + riid, + ppvObject); + } + + return E_NOINTERFACE; +} + +static const IServiceProviderVtbl IServiceProviderImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IServiceProvider, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IServiceProvider, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IServiceProvider, IStartMenuSite), + /*** IServiceProvider methods ***/ + IStartMenuSiteImpl_QueryService +}; + +/*******************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IStartMenuCallback, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_RELEASE(IStartMenuCallback, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IStartMenuCallback, IStartMenuSite) + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_GetWindow(IN OUT IStartMenuCallback *iface, + OUT HWND *phwnd) +{ + IStartMenuSiteImpl *This = IStartMenuSiteImpl_from_IStartMenuCallback(iface); + DbgPrint("IStartMenuCallback::GetWindow\n"); + + *phwnd = ITrayWindow_GetHWND(This->Tray); + if (*phwnd != NULL) + return S_OK; + + return E_FAIL; +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_ContextSensitiveHelp(IN OUT IStartMenuCallback *iface, + IN BOOL fEnterMode) +{ + DbgPrint("IStartMenuCallback::ContextSensitiveHelp\n"); + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_Execute(IN OUT IStartMenuCallback *iface, + IN IShellFolder *pShellFolder, + IN LPCITEMIDLIST pidl) +{ + IStartMenuSiteImpl *This = IStartMenuSiteImpl_from_IStartMenuCallback(iface); + + DbgPrint("IStartMenuCallback::Execute\n"); + return SHInvokeDefaultCommand(ITrayWindow_GetHWND(This->Tray), + pShellFolder, + pidl); +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_Unknown(IN OUT IStartMenuCallback *iface, + IN PVOID Unknown1, + IN PVOID Unknown2, + IN PVOID Unknown3, + IN PVOID Unknown4) +{ + DbgPrint("IStartMenuCallback::Unknown(0x%p,0x%p,0x%p,0x%p)\n", Unknown1, Unknown2, Unknown3, Unknown4); + return E_NOTIMPL; +} + +static BOOL +ShowUndockMenuItem(VOID) +{ + DbgPrint("ShowUndockMenuItem() not implemented!\n"); + /* FIXME: How do we detect this?! */ + return FALSE; +} + +static BOOL +ShowSynchronizeMenuItem(VOID) +{ + DbgPrint("ShowSynchronizeMenuItem() not implemented!\n"); + /* FIXME: How do we detect this?! */ + return FALSE; +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_AppendMenu(IN OUT IStartMenuCallback *iface, + OUT HMENU* phMenu) +{ + HMENU hMenu, hSettingsMenu; + DWORD dwLogoff; + BOOL bWantLogoff; + UINT uLastItemsCount = 5; /* 5 menu items below the last separator */ + TCHAR szUser[128]; + + DbgPrint("IStartMenuCallback::AppendMenu\n"); + + hMenu = LoadPopupMenu(hExplorerInstance, + MAKEINTRESOURCE(IDM_STARTMENU)); + *phMenu = hMenu; + if (hMenu == NULL) + return E_FAIL; + + /* Remove menu items that don't apply */ + + dwLogoff = SHRestricted(REST_STARTMENULOGOFF); + bWantLogoff = (dwLogoff == 2 || + SHRestricted(REST_FORCESTARTMENULOGOFF) || + GetExplorerRegValueSet(HKEY_CURRENT_USER, + TEXT("Advanced"), + TEXT("StartMenuLogoff"))); + + /* FIXME: Favorites */ + + /* Documents */ + if (SHRestricted(REST_NORECENTDOCSMENU)) + { + DeleteMenu(hMenu, + IDM_DOCUMENTS, + MF_BYCOMMAND); + } + + /* Settings */ + hSettingsMenu = FindSubMenu(hMenu, + IDM_SETTINGS, + FALSE); + if (hSettingsMenu != NULL) + { + if (SHRestricted(REST_NOSETFOLDERS)) + { + /* Control Panel */ + if (SHRestricted(REST_NOCONTROLPANEL)) + { + DeleteMenu(hSettingsMenu, + IDM_CONTROLPANEL, + MF_BYCOMMAND); + + /* Delete the separator below it */ + DeleteMenu(hSettingsMenu, + 0, + MF_BYPOSITION); + } + + /* Network Connections */ + if (SHRestricted(REST_NONETWORKCONNECTIONS)) + { + DeleteMenu(hSettingsMenu, + IDM_NETWORKCONNECTIONS, + MF_BYCOMMAND); + } + + /* Printers and Faxes */ + DeleteMenu(hSettingsMenu, + IDM_PRINTERSANDFAXES, + MF_BYCOMMAND); + } + + /* Security */ + if (GetSystemMetrics(SM_REMOTECONTROL) == 0 || + SHRestricted(REST_NOSECURITY)) + { + DeleteMenu(hSettingsMenu, + IDM_SECURITY, + MF_BYCOMMAND); + } + + if (GetMenuItemCount(hSettingsMenu) == 0) + { + DeleteMenu(hMenu, + IDM_SETTINGS, + MF_BYCOMMAND); + } + } + + /* Search */ + if (SHRestricted(REST_NOFIND)) + { + DeleteMenu(hMenu, + IDM_SEARCH, + MF_BYCOMMAND); + } + + /* FIXME: Help */ + + /* Run */ + if (SHRestricted(REST_NORUN)) + { + DeleteMenu(hMenu, + IDM_RUN, + MF_BYCOMMAND); + } + + /* Synchronize */ + if (!ShowSynchronizeMenuItem()) + { + DeleteMenu(hMenu, + IDM_SYNCHRONIZE, + MF_BYCOMMAND); + uLastItemsCount--; + } + + /* Log off */ + if (dwLogoff != 1 && bWantLogoff) + { + /* FIXME: We need a more sophisticated way to determine whether to show + or hide it, it might be hidden in too many cases!!! */ + + /* Update Log Off menu item */ + if (!GetCurrentLoggedOnUserName(szUser, + sizeof(szUser) / sizeof(szUser[0]))) + { + szUser[0] = _T('\0'); + } + + if (!FormatMenuString(hMenu, + IDM_LOGOFF, + MF_BYCOMMAND, + szUser)) + { + /* We couldn't update the menu item, delete it... */ + DeleteMenu(hMenu, + IDM_LOGOFF, + MF_BYCOMMAND); + } + } + else + { + DeleteMenu(hMenu, + IDM_LOGOFF, + MF_BYCOMMAND); + uLastItemsCount--; + } + + + /* Disconnect */ + if (GetSystemMetrics(SM_REMOTECONTROL) == 0) + { + DeleteMenu(hMenu, + IDM_DISCONNECT, + MF_BYCOMMAND); + uLastItemsCount--; + } + + /* Undock computer */ + if (!ShowUndockMenuItem()) + { + DeleteMenu(hMenu, + IDM_UNDOCKCOMPUTER, + MF_BYCOMMAND); + uLastItemsCount--; + } + + /* Shut down */ + if (SHRestricted(REST_NOCLOSE)) + { + DeleteMenu(hMenu, + IDM_SHUTDOWN, + MF_BYCOMMAND); + uLastItemsCount--; + } + + if (uLastItemsCount == 0) + { + /* Remove the separator at the end of the menu */ + DeleteMenu(hMenu, + IDM_LASTSTARTMENU_SEPARATOR, + MF_BYCOMMAND); + } + + return S_OK; +} + +static const IStartMenuCallbackVtbl IStartMenuCallbackImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IStartMenuCallback, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IStartMenuCallback, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IStartMenuCallback, IStartMenuSite), + /*** IOleWindow methods ***/ + IStartMenuSiteImpl_GetWindow, + IStartMenuSiteImpl_ContextSensitiveHelp, + /*** IStartMenuCallback methods ***/ + IStartMenuSiteImpl_Execute, + IStartMenuSiteImpl_Unknown, + IStartMenuSiteImpl_AppendMenu +}; + +/*******************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IOleCommandTarget, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_RELEASE(IOleCommandTarget, IStartMenuSite) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IOleCommandTarget, IStartMenuSite) + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_QueryStatus(IN OUT IOleCommandTarget *iface, + IN const GUID *pguidCmdGroup OPTIONAL, + IN ULONG cCmds, + IN OUT OLECMD *prgCmds, + IN OUT OLECMDTEXT *pCmdText OPTIONAL) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IStartMenuSiteImpl_Exec(IN OUT IOleCommandTarget *iface, + IN const GUID *pguidCmdGroup OPTIONAL, + IN DWORD nCmdID, + IN DWORD nCmdExecOpt, + IN VARIANTARG *pvaIn OPTIONAL, + IN VARIANTARG *pvaOut OPTIONAL) +{ + return E_NOTIMPL; +} + +static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IOleCommandTarget, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IOleCommandTarget, IStartMenuSite), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IOleCommandTarget, IStartMenuSite), + /*** IOleCommandTarget ***/ + IStartMenuSiteImpl_QueryStatus, + IStartMenuSiteImpl_Exec +}; + +/*******************************************************************/ + +static IStartMenuSiteImpl* +IStartMenuSiteImpl_Construct(IN ITrayWindow *Tray) +{ + IStartMenuSiteImpl *This; + + This = HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return NULL; + + ZeroMemory(This, + sizeof(*This)); + + This->lpVtbl = &IStartMenuSiteImpl_Vtbl; + This->lpServiceProviderVtbl = &IServiceProviderImpl_Vtbl; + This->lpStartMenuCallbackVtbl = &IStartMenuCallbackImpl_Vtbl; + This->lpOleCommandTargetVtbl = &IOleCommandTargetImpl_Vtbl; + This->Ref = 1; + + This->Tray = Tray; + + return This; +} + +static IStartMenuSite* +CreateStartMenuSite(IN ITrayWindow *Tray) +{ + IStartMenuSiteImpl *This; + + This = IStartMenuSiteImpl_Construct(Tray); + if (This != NULL) + { + return IStartMenuSite_from_IStartMenuSiteImpl(This); + } + + return NULL; +} + +HRESULT +UpdateStartMenu(IN OUT IMenuPopup *pMenuPopup, + IN HBITMAP hbmBanner OPTIONAL, + IN BOOL bSmallIcons) +{ + IBanneredBar *pbb; + HRESULT hRet; + + hRet = IMenuPopup_QueryInterface(pMenuPopup, + &IID_IBanneredBar, + (PVOID)&pbb); + if (SUCCEEDED(hRet)) + { + hRet = IBanneredBar_SetBitmap(pbb, + hbmBanner); + + + /* Update the icon size */ + hRet = IBanneredBar_SetIconSize(pbb, + bSmallIcons ? BMICON_SMALL : BMICON_LARGE); + + IBanneredBar_Release(pbb); + } + + return hRet; +} + +IMenuPopup* +CreateStartMenu(IN ITrayWindow *Tray, + OUT IMenuBand **ppMenuBand, + IN HBITMAP hbmBanner OPTIONAL, + IN BOOL bSmallIcons) +{ + HRESULT hRet; + IObjectWithSite *pOws = NULL; + IMenuPopup *pMp = NULL; + IStartMenuSite *pSms = NULL; + IMenuBand *pMb = NULL; + IInitializeObject *pIo; + IUnknown *pUnk; + IBandSite *pBs; + DWORD dwBandId = 0; + + pSms = CreateStartMenuSite(Tray); + if (pSms == NULL) + return NULL; + + hRet = CoCreateInstance(&CLSID_StartMenu, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IMenuPopup, + (PVOID*)&pMp); + if (SUCCEEDED(hRet)) + { + hRet = IMenuPopup_QueryInterface(pMp, + &IID_IObjectWithSite, + (PVOID*)&pOws); + if (SUCCEEDED(hRet)) + { + /* Set the menu site so we can handle messages */ + hRet = IObjectWithSite_SetSite(pOws, + (IUnknown*)pSms); + if (SUCCEEDED(hRet)) + { + /* Initialize the menu object */ + hRet = IMenuPopup_QueryInterface(pMp, + &IID_IInitializeObject, + (PVOID*)&pIo); + if (SUCCEEDED(hRet)) + { + hRet = IInitializeObject_Initialize(pIo); + + IInitializeObject_Release(pIo); + } + else + hRet = S_OK; + + /* Everything is initialized now. Let's get the IMenuBand interface. */ + if (SUCCEEDED(hRet)) + { + hRet = IMenuPopup_GetClient(pMp, + &pUnk); + + if (SUCCEEDED(hRet)) + { + hRet = IUnknown_QueryInterface(pUnk, + &IID_IBandSite, + (PVOID*)&pBs); + + if (SUCCEEDED(hRet)) + { + /* Finally we have the IBandSite interface, there's only one + band in it that apparently provides the IMenuBand interface */ + hRet = IBandSite_EnumBands(pBs, + 0, + &dwBandId); + if (SUCCEEDED(hRet)) + { + hRet = IBandSite_GetBandObject(pBs, + dwBandId, + &IID_IMenuBand, + (PVOID*)&pMb); + } + + IBandSite_Release(pBs); + } + + IUnknown_Release(pUnk); + } + } + } + + IObjectWithSite_Release(pOws); + } + } + + IStartMenuSite_Release(pSms); + + if (!SUCCEEDED(hRet)) + { + DbgPrint("Failed to initialize the start menu: 0x%x!\n", hRet); + + if (pMp != NULL) + IMenuPopup_Release(pMp); + + if (pMb != NULL) + IMenuBand_Release(pMb); + + return NULL; + } + + UpdateStartMenu(pMp, + hbmBanner, + bSmallIcons); + + *ppMenuBand = pMb; + return pMp; +} diff --git a/reactos/base/shell/explorer-new/taskband.c b/reactos/base/shell/explorer-new/taskband.c new file mode 100644 index 00000000000..d2450ee3714 --- /dev/null +++ b/reactos/base/shell/explorer-new/taskband.c @@ -0,0 +1,665 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/***************************************************************************** + ** ITaskBand **************************************************************** + *****************************************************************************/ + +const GUID CLSID_ITaskBand = {0x68284FAA,0x6A48,0x11D0,{0x8C,0x78,0x00,0xC0,0x4F,0xD9,0x18,0xB4}}; + +static const ITaskBandVtbl ITaskBandImpl_Vtbl; +static const IDeskBandVtbl IDeskBandImpl_Vtbl; +static const IObjectWithSiteVtbl IObjectWithSiteImpl_Vtbl; +static const IDeskBarVtbl IDeskBarImpl_Vtbl; +static const IPersistStreamVtbl IPersistStreamImpl_Vtbl; +static const IWindowEventHandlerVtbl IWindowEventHandlerImpl_Vtbl; + +typedef struct +{ + const ITaskBandVtbl *lpVtbl; + const IDeskBandVtbl *lpDeskBandVtbl; + const IObjectWithSiteVtbl *lpObjectWithSiteVtbl; + const IDeskBarVtbl *lpDeskBarVtbl; + const IPersistStreamVtbl *lpPersistStreamVtbl; + const IWindowEventHandlerVtbl *lpWindowEventHandlerVtbl; + /* FIXME: Implement IOleCommandTarget */ + LONG Ref; + + ITrayWindow *Tray; + IUnknown *punkSite; + + HWND hWnd; + DWORD dwBandID; +} ITaskBandImpl; + +static IUnknown * +IUnknown_from_ITaskBandImpl(ITaskBandImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +IMPL_CASTS(ITaskBand, ITaskBand, lpVtbl) +IMPL_CASTS(IDeskBand, ITaskBand, lpDeskBandVtbl) +IMPL_CASTS(IObjectWithSite, ITaskBand, lpObjectWithSiteVtbl) +IMPL_CASTS(IDeskBar, ITaskBand, lpDeskBarVtbl) +IMPL_CASTS(IPersistStream, ITaskBand, lpPersistStreamVtbl) +IMPL_CASTS(IWindowEventHandler, ITaskBand, lpWindowEventHandlerVtbl) + +static ULONG STDMETHODCALLTYPE +ITaskBandImpl_AddRef(IN OUT ITaskBand *iface) +{ + ITaskBandImpl *This = ITaskBandImpl_from_ITaskBand(iface); + + return InterlockedIncrement(&This->Ref); +} + +static VOID +ITaskBandImpl_Free(IN OUT ITaskBandImpl *This) +{ + if (This->punkSite != NULL) + { + IUnknown_Release(This->punkSite); + This->punkSite = NULL; + } + + HeapFree(hProcessHeap, + 0, + This); +} + +static ULONG STDMETHODCALLTYPE +ITaskBandImpl_Release(IN OUT ITaskBand *iface) +{ + ITaskBandImpl *This = ITaskBandImpl_from_ITaskBand(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + + if (Ret == 0) + ITaskBandImpl_Free(This); + + return Ret; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_QueryInterface(IN OUT ITaskBand *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + ITaskBandImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = ITaskBandImpl_from_ITaskBand(iface); + + if (IsEqualIID(riid, + &IID_IUnknown)) + { + *ppvObj = IUnknown_from_ITaskBandImpl(This); + } + else if (IsEqualIID(riid, + &IID_IDeskBand) || + IsEqualIID(riid, + &IID_IOleWindow) || + IsEqualIID(riid, + &IID_IDockingWindow)) + { + *ppvObj = IDeskBand_from_ITaskBandImpl(This); + } + else if (IsEqualIID(riid, + &IID_IObjectWithSite)) + { + *ppvObj = IObjectWithSite_from_ITaskBandImpl(This); + } + else if (IsEqualIID(riid, + &IID_IDeskBar)) + { + *ppvObj = IDeskBar_from_ITaskBandImpl(This); + } + else if (IsEqualIID(riid, + &IID_IWindowEventHandler)) + { + /* When run on Windows the system queries this interface, which is completely + undocumented :( It's queried during initialization of the tray band site. + The system apparently uses this interface to forward messages to be handled + by the band child window. This interface appears to be implemented by a number + of classes provided by the shell, including the IBandSite interface. In that + we (the host application) forward messages to the default message handler (in + our case the IBandSite default message handler for the Rebar control)! This + interface in the ITaskBand implementation is only actually used if we use + the same interface to forward messages to the IBandSite implementation of + the shell! */ + *ppvObj = IWindowEventHandler_from_ITaskBandImpl(This); + } +#if 0 + else if (IsEqualIID(riid, + &IID_IPersistStream) || + IsEqualIID(riid, + &IID_IPersist)) + { + *ppvObj = IPersistStream_from_ITaskBandImpl(This); + } +#endif + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + ITaskBandImpl_AddRef(iface); + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetRebarBandID(IN OUT ITaskBand *iface, + OUT DWORD *pdwBandID) +{ + ITaskBandImpl *This = ITaskBandImpl_from_ITaskBand(iface); + + if (This->dwBandID != (DWORD)-1) + { + if (pdwBandID != NULL) + *pdwBandID = This->dwBandID; + + return S_OK; + } + + return E_FAIL; +} + +static const ITaskBandVtbl ITaskBandImpl_Vtbl = +{ + /*** IUnknown methods ***/ + ITaskBandImpl_QueryInterface, + ITaskBandImpl_AddRef, + ITaskBandImpl_Release, + /*** ITaskBand methods ***/ + ITaskBandImpl_GetRebarBandID +}; + +/*****************************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IDeskBand, ITaskBand) +METHOD_IUNKNOWN_INHERITED_RELEASE(IDeskBand, ITaskBand) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IDeskBand, ITaskBand) + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetWindow(IN OUT IDeskBand *iface, + OUT HWND *phwnd) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IDeskBand(iface); + + /* NOTE: We have to return the tray window here so that ITaskBarClient + knows the parent window of the Rebar control it creates when + calling ITaskBarClient::SetDeskBarSite()! However, once we + created a window we return the task switch window! */ + if (This->hWnd != NULL) + *phwnd = This->hWnd; + else + *phwnd = ITrayWindow_GetHWND(This->Tray); + + DbgPrint("ITaskBand::GetWindow(0x%p->0x%p)\n", phwnd, *phwnd); + + if (*phwnd != NULL) + return S_OK; + + return E_FAIL; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_ContextSensitiveHelp(IN OUT IDeskBand *iface, + IN BOOL fEnterMode) +{ + /* FIXME: Implement */ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_ShowDW(IN OUT IDeskBand *iface, + IN BOOL bShow) +{ + /* We don't do anything... */ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_CloseDW(IN OUT IDeskBand *iface, + IN DWORD dwReserved) +{ + /* We don't do anything... */ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_ResizeBoderDW(IN OUT IDeskBand *iface, + IN LPCRECT prcBorder, + IN IUnknown *punkToolbarSite, + IN BOOL fReserved) +{ + /* No need to implement this method */ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetBandInfo(IN OUT IDeskBand *iface, + IN DWORD dwBandID, + IN DWORD dwViewMode, + IN OUT DESKBANDINFO *pdbi) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IDeskBand(iface); + DbgPrint("ITaskBand::GetBandInfo(0x%x,0x%x,0x%p) hWnd=0x%p\n", dwBandID, dwViewMode, pdbi, This->hWnd); + + /* NOTE: We could save dwBandID in the instance in case we need it later... */ + + if (This->hWnd != NULL) + { + /* The task band never has a title */ + pdbi->dwMask &= ~DBIM_TITLE; + + /* NOTE: We don't return DBIMF_UNDELETEABLE here, the band site will + handle us differently and add this flag for us. The reason for + this is future changes that might allow it to be deletable. + We want the band site to be in charge of this decision rather + the band itself! */ + /* FIXME: What about DBIMF_NOGRIPPER and DBIMF_ALWAYSGRIPPER */ + pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT; + + if (dwViewMode & DBIF_VIEWMODE_VERTICAL) + { + pdbi->ptIntegral.y = 1; + pdbi->ptMinSize.y = 1; + /* FIXME: Get the button metrics from the task bar object!!! */ + pdbi->ptMinSize.x = (3 * GetSystemMetrics(SM_CXEDGE) / 2) + /* FIXME: Might be wrong if only one column! */ + GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE)); /* FIXME: Min button size, query!!! */ + } + else + { + pdbi->ptMinSize.y = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE)); /* FIXME: Query */ + pdbi->ptIntegral.y = pdbi->ptMinSize.y + (3 * GetSystemMetrics(SM_CYEDGE) / 2); /* FIXME: Query metrics */ + /* We're not going to allow task bands where not even the minimum button size fits into the band */ + pdbi->ptMinSize.x = pdbi->ptIntegral.y; + } + + /* Ignored: pdbi->ptMaxSize.x */ + pdbi->ptMaxSize.y = -1; + + /* FIXME: We should query the height from the task bar object!!! */ + pdbi->ptActual.y = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE)); + + /* Save the band ID for future use in case we need to check whether a given band + is the task band */ + This->dwBandID = dwBandID; + + DbgPrint("H: %d, Min: %d,%d, Integral.y: %d Actual: %d,%d\n", (dwViewMode & DBIF_VIEWMODE_VERTICAL) == 0, + pdbi->ptMinSize.x, pdbi->ptMinSize.y, pdbi->ptIntegral.y, + pdbi->ptActual.x,pdbi->ptActual.y); + + return S_OK; + } + + return E_FAIL; +} + +static const IDeskBandVtbl IDeskBandImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IDeskBand, ITaskBand), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IDeskBand, ITaskBand), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IDeskBand, ITaskBand), + /*** IOleWindow methods ***/ + ITaskBandImpl_GetWindow, + ITaskBandImpl_ContextSensitiveHelp, + /*** IDockingWindow methods ***/ + ITaskBandImpl_ShowDW, + ITaskBandImpl_CloseDW, + ITaskBandImpl_ResizeBoderDW, + /*** IDeskBand methods ***/ + ITaskBandImpl_GetBandInfo +}; + +/*****************************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IDeskBar, ITaskBand) +METHOD_IUNKNOWN_INHERITED_RELEASE(IDeskBar, ITaskBand) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IDeskBar, ITaskBand) + +static HRESULT STDMETHODCALLTYPE +IDeskBarImpl_GetWindow(IN OUT IDeskBar *iface, + OUT HWND *phwnd) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IDeskBar(iface); + IDeskBand *DeskBand = IDeskBand_from_ITaskBandImpl(This); + + /* Proxy to IDeskBand interface */ + return IDeskBand_GetWindow(DeskBand, + phwnd); +} + +static HRESULT STDMETHODCALLTYPE +IDeskBarImpl_ContextSensitiveHelp(IN OUT IDeskBar *iface, + IN BOOL fEnterMode) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IDeskBar(iface); + IDeskBand *DeskBand = IDeskBand_from_ITaskBandImpl(This); + + /* Proxy to IDeskBand interface */ + return IDeskBand_ContextSensitiveHelp(DeskBand, + fEnterMode); +} + +static HRESULT STDMETHODCALLTYPE +IDeskBarImpl_SetClient(IN OUT IDeskBar *iface, + IN IUnknown *punkClient) +{ + DbgPrint("IDeskBar::SetClient(0x%p)\n", punkClient); + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDeskBarImpl_GetClient(IN OUT IDeskBar *iface, + OUT IUnknown **ppunkClient) +{ + DbgPrint("IDeskBar::GetClient(0x%p)\n", ppunkClient); + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IDeskBarImpl_OnPosRectChangeDB(IN OUT IDeskBar *iface, + IN RECT *prc) +{ + DbgPrint("IDeskBar::OnPosRectChangeDB(0x%p=(%d,%d,%d,%d))\n", prc, prc->left, prc->top, prc->right, prc->bottom); + if (prc->bottom - prc->top == 0) + return S_OK; + + return S_FALSE; +} + +static const IDeskBarVtbl IDeskBarImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IDeskBar, ITaskBand), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IDeskBar, ITaskBand), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IDeskBar, ITaskBand), + /*** IOleWindow methods ***/ + IDeskBarImpl_GetWindow, + IDeskBarImpl_ContextSensitiveHelp, + /*** IDeskBar methods ***/ + IDeskBarImpl_SetClient, + IDeskBarImpl_GetClient, + IDeskBarImpl_OnPosRectChangeDB +}; + +/*****************************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IPersistStream, ITaskBand) +METHOD_IUNKNOWN_INHERITED_RELEASE(IPersistStream, ITaskBand) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IPersistStream, ITaskBand) + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetClassID(IN OUT IPersistStream *iface, + OUT CLSID *pClassID) +{ + DbgPrint("ITaskBand::GetClassID(0x%p)\n", pClassID); + /* We're going to return the (internal!) CLSID of the task band interface */ + *pClassID = CLSID_ITaskBand; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_IsDirty(IN OUT IPersistStream *iface) +{ + /* The object hasn't changed since the last save! */ + return S_FALSE; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_Load(IN OUT IPersistStream *iface, + IN IStream *pStm) +{ + DbgPrint("ITaskBand::Load called\n"); + /* Nothing to do */ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_Save(IN OUT IPersistStream *iface, + IN IStream *pStm, + IN BOOL fClearDirty) +{ + /* Nothing to do */ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetSizeMax(IN OUT IPersistStream *iface, + OUT ULARGE_INTEGER *pcbSize) +{ + DbgPrint("ITaskBand::GetSizeMax called\n"); + /* We don't need any space for the task band */ + pcbSize->QuadPart = 0; + return S_OK; +} + +static const IPersistStreamVtbl IPersistStreamImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IPersistStream, ITaskBand), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IPersistStream, ITaskBand), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IPersistStream, ITaskBand), + /*** IPersist methods ***/ + ITaskBandImpl_GetClassID, + /*** IPersistStream methods ***/ + ITaskBandImpl_IsDirty, + ITaskBandImpl_Load, + ITaskBandImpl_Save, + ITaskBandImpl_GetSizeMax +}; + +/*****************************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IObjectWithSite, ITaskBand) +METHOD_IUNKNOWN_INHERITED_RELEASE(IObjectWithSite, ITaskBand) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IObjectWithSite, ITaskBand) + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_SetSite(IN OUT IObjectWithSite *iface, + IN IUnknown* pUnkSite) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IObjectWithSite(iface); + HRESULT hRet = E_FAIL; + + DbgPrint("ITaskBand::SetSite(0x%p)\n", pUnkSite); + + /* Release the current site */ + if (This->punkSite != NULL) + { + IUnknown_Release(This->punkSite); + } + + This->punkSite = NULL; + This->hWnd = NULL; + + if (pUnkSite != NULL) + { + IOleWindow *OleWindow; + + /* Check if the site supports IOleWindow */ + hRet = IUnknown_QueryInterface(pUnkSite, + &IID_IOleWindow, + (PVOID*)&OleWindow); + if (SUCCEEDED(hRet)) + { + HWND hWndParent = NULL; + + hRet = IOleWindow_GetWindow(OleWindow, + &hWndParent); + if (SUCCEEDED(hRet)) + { + /* Attempt to create the task switch window */ + + DbgPrint("CreateTaskSwitchWnd(Parent: 0x%p)\n", hWndParent); + This->hWnd = CreateTaskSwitchWnd(hWndParent, + This->Tray); + if (This->hWnd != NULL) + { + This->punkSite = pUnkSite; + hRet = S_OK; + } + else + { + DbgPrint("CreateTaskSwitchWnd() failed!\n"); + IUnknown_Release(OleWindow); + hRet = E_FAIL; + } + } + else + IUnknown_Release(OleWindow); + } + else + DbgPrint("Querying IOleWindow failed: 0x%x\n", hRet); + } + + return hRet; +} + +static HRESULT STDMETHODCALLTYPE +ITaskBandImpl_GetSite(IN OUT IObjectWithSite *iface, + IN REFIID riid, + OUT VOID **ppvSite) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IObjectWithSite(iface); + DbgPrint("ITaskBand::GetSite(0x%p,0x%p)\n", riid, ppvSite); + + if (This->punkSite != NULL) + { + return IUnknown_QueryInterface(This->punkSite, + riid, + ppvSite); + } + + *ppvSite = NULL; + return E_FAIL; +} + +static const IObjectWithSiteVtbl IObjectWithSiteImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IObjectWithSite, ITaskBand), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IObjectWithSite, ITaskBand), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IObjectWithSite, ITaskBand), + /*** IObjectWithSite methods ***/ + ITaskBandImpl_SetSite, + ITaskBandImpl_GetSite +}; + + +/*****************************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IWindowEventHandler, ITaskBand) +METHOD_IUNKNOWN_INHERITED_RELEASE(IWindowEventHandler, ITaskBand) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IWindowEventHandler, ITaskBand) + +static HRESULT STDMETHODCALLTYPE +IWindowEventHandlerImpl_ProcessMessage(IN OUT IWindowEventHandler *iface, + IN HWND hWnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam, + OUT LRESULT *plrResult) +{ + DbgPrint("ITaskBand: IWindowEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p)\n", hWnd, uMsg, wParam, lParam, plrResult); + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +IWindowEventHandlerImpl_ContainsWindow(IN OUT IWindowEventHandler *iface, + IN HWND hWnd) +{ + ITaskBandImpl *This = ITaskBandImpl_from_IWindowEventHandler(iface); + HRESULT hRet = S_OK; + + if (This->hWnd != hWnd || + !IsChild(This->hWnd, + hWnd)) + { + hRet = S_FALSE; + } + + DbgPrint("ITaskBand::ContainsWindow(0x%p) returns %s\n", hWnd, hRet == S_OK ? "S_OK" : "S_FALSE"); + + return hRet; +} + +static const IWindowEventHandlerVtbl IWindowEventHandlerImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IWindowEventHandler, ITaskBand), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IWindowEventHandler, ITaskBand), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IWindowEventHandler, ITaskBand), + /*** IWindowEventHandler methods ***/ + IWindowEventHandlerImpl_ProcessMessage, + IWindowEventHandlerImpl_ContainsWindow +}; + +/*****************************************************************************/ + +static ITaskBandImpl * +ITaskBandImpl_Construct(IN OUT ITrayWindow *Tray) +{ + ITaskBandImpl *This; + + This = HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return NULL; + + ZeroMemory(This, + sizeof(*This)); + This->lpVtbl = &ITaskBandImpl_Vtbl; + This->lpDeskBandVtbl = &IDeskBandImpl_Vtbl; + This->lpObjectWithSiteVtbl = &IObjectWithSiteImpl_Vtbl; + This->lpDeskBarVtbl = &IDeskBarImpl_Vtbl; + This->lpPersistStreamVtbl = &IPersistStreamImpl_Vtbl; + This->lpWindowEventHandlerVtbl = &IWindowEventHandlerImpl_Vtbl; + This->Ref = 1; + + This->Tray = Tray; + This->dwBandID = (DWORD)-1; + + return This; +} + +ITaskBand * +CreateTaskBand(IN OUT ITrayWindow *Tray) +{ + ITaskBandImpl *This; + + This = ITaskBandImpl_Construct(Tray); + if (This != NULL) + { + return ITaskBand_from_ITaskBandImpl(This); + } + + return NULL; +} diff --git a/reactos/base/shell/explorer-new/taskswnd.c b/reactos/base/shell/explorer-new/taskswnd.c new file mode 100644 index 00000000000..5d2f8b292d3 --- /dev/null +++ b/reactos/base/shell/explorer-new/taskswnd.c @@ -0,0 +1,2004 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* By default we don't use DrawCaptionTemp() because it causes some minimal + drawing glitches with the toolbar custom painting code */ +#define TASK_USE_DRAWCAPTIONTEMP 1 + +/* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every + 5 seconds */ +#define DUMP_TASKS 0 + +static const TCHAR szTaskSwitchWndClass[] = TEXT("MSTaskSwWClass"); +static const TCHAR szRunningApps[] = TEXT("Running Applications"); + +typedef struct _TASK_GROUP +{ + /* We have to use a linked list instead of an array so we don't have to + update all pointers to groups in the task item array when removing + groups. */ + struct _TASK_GROUP *Next; + + DWORD dwTaskCount; + DWORD dwProcessId; + INT Index; + union + { + DWORD dwFlags; + struct + { + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + + /* DisplayTooltip is TRUE when the group button text didn't fit into + the button. */ + DWORD DisplayTooltip : 1; + +#endif + + DWORD IsCollapsed : 1; + }; + }; +} TASK_GROUP, *PTASK_GROUP; + +typedef struct _TASK_ITEM +{ + HWND hWnd; + PTASK_GROUP Group; + INT Index; + +#if !(TASK_USE_DRAWCAPTIONTEMP != 0) + + INT IconIndex; + +#endif + + union + { + DWORD dwFlags; + struct + { + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + + /* DisplayTooltip is TRUE when the window text didn't fit into the + button. */ + DWORD DisplayTooltip : 1; + +#endif + + /* IsFlashing is TRUE when the task bar item should be flashing. */ + DWORD IsFlashing : 1; + + /* RenderFlashed is only TRUE if the task bar item should be + drawn with a flash. */ + DWORD RenderFlashed : 1; + }; + }; +} TASK_ITEM, *PTASK_ITEM; + +#define TASK_ITEM_ARRAY_ALLOC 64 + +typedef struct _TASK_SWITCH_WND +{ + HWND hWnd; + HWND hWndNotify; + + UINT ShellHookMsg; + ITrayWindow *Tray; + + PTASK_GROUP TaskGroups; + + WORD TaskItemCount; + WORD AllocatedTaskItems; + PTASK_ITEM TaskItems; + PTASK_ITEM ActiveTaskItem; + + HWND hWndToolbar; + UINT TbButtonsPerLine; + WORD ToolbarBtnCount; + + union + { + DWORD dwFlags; + struct + { + DWORD IsGroupingEnabled : 1; + DWORD IsDestroying : 1; + DWORD IsToolbarSubclassed : 1; + }; + }; + + SIZE ButtonSize; + TCHAR szBuf[255]; +} TASK_SWITCH_WND, *PTASK_SWITCH_WND; + +#define TSW_TOOLBAR_SUBCLASS_ID 1 + +#define MAX_TASKS_COUNT (0x7FFF) + +#define MAKE_TASKITEM_PTR(ti) ((DWORD_PTR)ti) +#define MAKE_TASKGROUP_PTR(tg) (((DWORD_PTR)tg) | 1) +#define IS_TASKGROUP_PTR(p) ((((DWORD_PTR)p) & 1) != 0) +#define GET_TASKITEM_PTR(lp) ((PTASK_ITEM)(((DWORD_PTR)lp) & ~1)) +#define GET_TASKGROUP_PTR(lp) ((PTASK_GROUP)(((DWORD_PTR)lp) & ~1)) + +#define MAKE_TASKITEM_CMD_ID(id) ((INT)(id)) +#define MAKE_TASKGROUP_CMD_ID(id) ((INT)(id) | (1 << 15)) +#define IS_TASKGROUP_CMD_ID(id) ((((INT)(id)) & (1 << 15)) != 0) +#define GET_INDEX_FROM_CMD_ID(id) ((INT)(id) & MAX_TASKS_COUNT) + +static VOID TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This, + IN BOOL bRedrawDisabled); + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + +#define TaskSwitchWnd_GetWndTextFromTaskItem(a,b) NULL + +#else /* !TASK_USE_DRAWCAPTIONTEMP */ + +static LPTSTR +TaskSwitchWnd_GetWndTextFromTaskItem(IN OUT PTASK_SWITCH_WND This, + IN PTASK_ITEM TaskItem) +{ + /* Get the window text without sending a message so we don't hang if an + application isn't responding! */ + if (InternalGetWindowText(TaskItem->hWnd, + This->szBuf, + sizeof(This->szBuf) / sizeof(This->szBuf[0])) > 0) + { + return This->szBuf; + } + + return NULL; +} + +#endif + +#if DUMP_TASKS != 0 +static VOID +TaskSwitchWnd_DumpTasks(IN OUT PTASK_SWITCH_WND This) +{ + PTASK_GROUP CurrentGroup; + PTASK_ITEM CurrentTaskItem, LastTaskItem; + + DbgPrint("Tasks dump:\n"); + if (This->IsGroupingEnabled) + { + CurrentGroup = This->TaskGroups; + while (CurrentGroup != NULL) + { + DbgPrint("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index); + + CurrentTaskItem = This->TaskItems; + LastTaskItem = CurrentTaskItem + This->TaskItemCount; + while (CurrentTaskItem != LastTaskItem) + { + if (CurrentTaskItem->Group == CurrentGroup) + { + DbgPrint(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); + } + CurrentTaskItem++; + } + + CurrentGroup = CurrentGroup->Next; + } + + CurrentTaskItem = This->TaskItems; + LastTaskItem = CurrentTaskItem + This->TaskItemCount; + while (CurrentTaskItem != LastTaskItem) + { + if (CurrentTaskItem->Group == NULL) + { + DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); + } + CurrentTaskItem++; + } + } + else + { + CurrentTaskItem = This->TaskItems; + LastTaskItem = CurrentTaskItem + This->TaskItemCount; + while (CurrentTaskItem != LastTaskItem) + { + DbgPrint("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); + CurrentTaskItem++; + } + } +} +#endif + +static VOID +TaskSwitchWnd_BeginUpdate(IN OUT PTASK_SWITCH_WND This) +{ + SendMessage(This->hWndToolbar, + WM_SETREDRAW, + FALSE, + 0); +} + +static VOID +TaskSwitchWnd_EndUpdate(IN OUT PTASK_SWITCH_WND This) +{ + SendMessage(This->hWndToolbar, + WM_SETREDRAW, + TRUE, + 0); + InvalidateRect(This->hWndToolbar, + NULL, + TRUE); +} + +static BOOL +TaskSwitchWnd_SetToolbarButtonCommandId(IN OUT PTASK_SWITCH_WND This, + IN INT iButtonIndex, + IN INT iCommandId) +{ + TBBUTTONINFO tbbi; + + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND; + tbbi.idCommand = iCommandId; + + return SendMessage(This->hWndToolbar, + TB_SETBUTTONINFO, + (WPARAM)iButtonIndex, + (LPARAM)&tbbi) != 0; +} + +static VOID +TaskSwitchWnd_UpdateIndexesAfterButtonInserted(IN OUT PTASK_SWITCH_WND This, + IN INT iIndex) +{ + PTASK_GROUP CurrentGroup; + PTASK_ITEM CurrentTaskItem, LastTaskItem; + INT NewIndex; + + if (This->IsGroupingEnabled) + { + /* Update all affected groups */ + CurrentGroup = This->TaskGroups; + while (CurrentGroup != NULL) + { + if (CurrentGroup->IsCollapsed && + CurrentGroup->Index >= iIndex) + { + /* Update the toolbar buttons */ + NewIndex = CurrentGroup->Index + 1; + if (TaskSwitchWnd_SetToolbarButtonCommandId(This, + CurrentGroup->Index + 1, + MAKE_TASKGROUP_CMD_ID(NewIndex))) + { + CurrentGroup->Index = NewIndex; + } + else + CurrentGroup->Index = -1; + } + + CurrentGroup = CurrentGroup->Next; + } + } + + /* Update all affected task items */ + CurrentTaskItem = This->TaskItems; + LastTaskItem = CurrentTaskItem + This->TaskItemCount; + while (CurrentTaskItem != LastTaskItem) + { + CurrentGroup = CurrentTaskItem->Group; + if (CurrentGroup != NULL) + { + if (!CurrentGroup->IsCollapsed && + CurrentTaskItem->Index >= iIndex) + { + goto UpdateTaskItemBtn; + } + } + else if (CurrentTaskItem->Index >= iIndex) + { +UpdateTaskItemBtn: + /* Update the toolbar buttons */ + NewIndex = CurrentTaskItem->Index + 1; + if (TaskSwitchWnd_SetToolbarButtonCommandId(This, + CurrentTaskItem->Index + 1, + MAKE_TASKITEM_CMD_ID(NewIndex))) + { + CurrentTaskItem->Index = NewIndex; + } + else + CurrentTaskItem->Index = -1; + } + + CurrentTaskItem++; + } +} + +static VOID +TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(IN OUT PTASK_SWITCH_WND This, + IN INT iIndex) +{ + PTASK_GROUP CurrentGroup; + PTASK_ITEM CurrentTaskItem, LastTaskItem; + INT NewIndex; + + if (This->IsGroupingEnabled) + { + /* Update all affected groups */ + CurrentGroup = This->TaskGroups; + while (CurrentGroup != NULL) + { + if (CurrentGroup->IsCollapsed && + CurrentGroup->Index > iIndex) + { + /* Update the toolbar buttons */ + NewIndex = CurrentGroup->Index - 1; + if (TaskSwitchWnd_SetToolbarButtonCommandId(This, + CurrentGroup->Index - 1, + MAKE_TASKGROUP_CMD_ID(NewIndex))) + { + CurrentGroup->Index = NewIndex; + } + else + CurrentGroup->Index = -1; + } + + CurrentGroup = CurrentGroup->Next; + } + } + + /* Update all affected task items */ + CurrentTaskItem = This->TaskItems; + LastTaskItem = CurrentTaskItem + This->TaskItemCount; + while (CurrentTaskItem != LastTaskItem) + { + CurrentGroup = CurrentTaskItem->Group; + if (CurrentGroup != NULL) + { + if (!CurrentGroup->IsCollapsed && + CurrentTaskItem->Index > iIndex) + { + goto UpdateTaskItemBtn; + } + } + else if (CurrentTaskItem->Index > iIndex) + { +UpdateTaskItemBtn: + /* Update the toolbar buttons */ + NewIndex = CurrentTaskItem->Index - 1; + if (TaskSwitchWnd_SetToolbarButtonCommandId(This, + CurrentTaskItem->Index - 1, + MAKE_TASKITEM_CMD_ID(NewIndex))) + { + CurrentTaskItem->Index = NewIndex; + } + else + CurrentTaskItem->Index = -1; + } + + CurrentTaskItem++; + } +} + +static INT +TaskSwitchWnd_UpdateTaskGroupButton(IN OUT PTASK_SWITCH_WND This, + IN PTASK_GROUP TaskGroup) +{ + ASSERT(TaskGroup->Index >= 0); + + /* FIXME: Implement */ + + return TaskGroup->Index; +} + +static VOID +TaskSwitchWnd_ExpandTaskGroup(IN OUT PTASK_SWITCH_WND This, + IN PTASK_GROUP TaskGroup) +{ + ASSERT(TaskGroup->dwTaskCount > 0); + ASSERT(TaskGroup->IsCollapsed); + ASSERT(TaskGroup->Index >= 0); + + /* FIXME: Implement */ +} + +static INT +TaskSwitchWnd_UpdateTaskItemButton(IN OUT PTASK_SWITCH_WND This, + IN PTASK_ITEM TaskItem) +{ + TBBUTTONINFO tbbi; + + ASSERT(TaskItem->Index >= 0); + + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT; + tbbi.fsState = TBSTATE_ENABLED; + if (This->ActiveTaskItem == TaskItem) + tbbi.fsState |= TBSTATE_CHECKED; + + /* Check if we're updating a button that is the last one in the + line. If so, we need to set the TBSTATE_WRAP flag! */ + if (This->TbButtonsPerLine != 0 && + (TaskItem->Index + 1) % This->TbButtonsPerLine == 0) + { + tbbi.fsState |= TBSTATE_WRAP; + } + + tbbi.pszText = TaskSwitchWnd_GetWndTextFromTaskItem(This, + TaskItem); + + if (!SendMessage(This->hWndToolbar, + TB_SETBUTTONINFO, + (WPARAM)TaskItem->Index, + (LPARAM)&tbbi)) + { + TaskItem->Index = -1; + return -1; + } + + DbgPrint("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd); + return TaskItem->Index; +} + +static PTASK_ITEM +TaskSwitchWnd_FindLastTaskItemOfGroup(IN OUT PTASK_SWITCH_WND This, + IN PTASK_GROUP TaskGroup OPTIONAL, + IN PTASK_ITEM NewTaskItem OPTIONAL) +{ + PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL; + DWORD dwTaskCount; + + ASSERT(This->IsGroupingEnabled); + + TaskItem = This->TaskItems; + LastTaskItem = TaskItem + This->TaskItemCount; + + dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT); + + ASSERT(dwTaskCount > 0); + + while (TaskItem != LastTaskItem) + { + if (TaskItem->Group == TaskGroup) + { + if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL) + { + FoundTaskItem = TaskItem; + } + + if (--dwTaskCount == 0) + { + /* We found the last task item in the group! */ + break; + } + } + + TaskItem++; + } + + return FoundTaskItem; +} + +static INT +TaskSwitchWnd_CalculateTaskItemNewButtonIndex(IN OUT PTASK_SWITCH_WND This, + IN PTASK_ITEM TaskItem) +{ + PTASK_GROUP TaskGroup; + PTASK_ITEM LastTaskItem; + + /* NOTE: This routine assumes that the group is *not* collapsed! */ + + TaskGroup = TaskItem->Group; + if (This->IsGroupingEnabled) + { + if (TaskGroup != NULL) + { + ASSERT(TaskGroup->Index < 0); + ASSERT(!TaskGroup->IsCollapsed); + + if (TaskGroup->dwTaskCount > 1) + { + LastTaskItem = TaskSwitchWnd_FindLastTaskItemOfGroup(This, + TaskGroup, + TaskItem); + if (LastTaskItem != NULL) + { + /* Since the group is expanded the task items must have an index */ + ASSERT(LastTaskItem->Index >= 0); + + return LastTaskItem->Index + 1; + } + } + } + else + { + /* Find the last NULL group button. NULL groups are added at the end of the + task item list when grouping is enabled */ + LastTaskItem = TaskSwitchWnd_FindLastTaskItemOfGroup(This, + NULL, + TaskItem); + if (LastTaskItem != NULL) + { + ASSERT(LastTaskItem->Index >= 0); + + return LastTaskItem->Index + 1; + } + } + } + + return This->ToolbarBtnCount; +} + +static INT +TaskSwitchWnd_AddTaskItemButton(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + TBBUTTON tbBtn; + INT iIndex; + + if (TaskItem->Index >= 0) + { + return TaskSwitchWnd_UpdateTaskItemButton(This, + TaskItem); + } + + if (TaskItem->Group != NULL && + TaskItem->Group->IsCollapsed) + { + /* The task group is collapsed, we only need to update the group button */ + return TaskSwitchWnd_UpdateTaskGroupButton(This, + TaskItem->Group); + } + + tbBtn.iBitmap = 0; + tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES; + tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT; + tbBtn.dwData = MAKE_TASKITEM_PTR(TaskItem); + + tbBtn.iString = (DWORD_PTR)TaskSwitchWnd_GetWndTextFromTaskItem(This, + TaskItem); + + /* Find out where to insert the new button */ + iIndex = TaskSwitchWnd_CalculateTaskItemNewButtonIndex(This, + TaskItem); + ASSERT(iIndex >= 0); + tbBtn.idCommand = MAKE_TASKITEM_CMD_ID(iIndex); + + TaskSwitchWnd_BeginUpdate(This); + + if (SendMessage(This->hWndToolbar, + TB_INSERTBUTTON, + (WPARAM)iIndex, + (LPARAM)&tbBtn)) + { + TaskSwitchWnd_UpdateIndexesAfterButtonInserted(This, + iIndex); + + DbgPrint("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd); + + TaskItem->Index = iIndex; + This->ToolbarBtnCount++; + + /* Update button sizes and fix the button wrapping */ + TaskSwitchWnd_UpdateButtonsSize(This, + TRUE); + return iIndex; + } + + TaskSwitchWnd_EndUpdate(This); + + return -1; +} + +static BOOL +TaskSwitchWnd_DeleteTaskItemButton(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + PTASK_GROUP TaskGroup; + INT iIndex; + + TaskGroup = TaskItem->Group; + + if (TaskItem->Index >= 0) + { + if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) || + TaskGroup == NULL) + { + TaskSwitchWnd_BeginUpdate(This); + + iIndex = TaskItem->Index; + if (SendMessage(This->hWndToolbar, + TB_DELETEBUTTON, + (WPARAM)iIndex, + 0)) + { + TaskItem->Index = -1; + This->ToolbarBtnCount--; + + TaskSwitchWnd_UpdateIndexesAfterButtonDeleted(This, + iIndex); + + /* Update button sizes and fix the button wrapping */ + TaskSwitchWnd_UpdateButtonsSize(This, + TRUE); + return TRUE; + } + + TaskSwitchWnd_EndUpdate(This); + } + } + + return FALSE; +} + +static PTASK_GROUP +TaskSwitchWnd_AddToTaskGroup(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + DWORD dwProcessId; + PTASK_GROUP TaskGroup, *PrevLink; + + if (!GetWindowThreadProcessId(hWnd, + &dwProcessId)) + { + DbgPrint("Cannot get process id of hwnd 0x%p\n", hWnd); + return NULL; + } + + /* Try to find an existing task group */ + TaskGroup = This->TaskGroups; + PrevLink = &This->TaskGroups; + while (TaskGroup != NULL) + { + if (TaskGroup->dwProcessId == dwProcessId) + { + TaskGroup->dwTaskCount++; + return TaskGroup; + } + + PrevLink = &TaskGroup->Next; + TaskGroup = TaskGroup->Next; + } + + /* Allocate a new task group */ + TaskGroup = HeapAlloc(hProcessHeap, + 0, + sizeof(*TaskGroup)); + if (TaskGroup != NULL) + { + ZeroMemory(TaskGroup, + sizeof(*TaskGroup)); + + TaskGroup->dwTaskCount = 1; + TaskGroup->dwProcessId = dwProcessId; + TaskGroup->Index = -1; + + /* Add the task group to the list */ + *PrevLink = TaskGroup; + } + + return TaskGroup; +} + +static VOID +TaskSwitchWnd_RemoveTaskFromTaskGroup(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink; + + TaskGroup = TaskItem->Group; + if (TaskGroup != NULL) + { + DWORD dwNewTaskCount = --TaskGroup->dwTaskCount; + if (dwNewTaskCount == 0) + { + /* Find the previous pointer in the chain */ + CurrentGroup = This->TaskGroups; + PrevLink = &This->TaskGroups; + while (CurrentGroup != TaskGroup) + { + PrevLink = &CurrentGroup->Next; + CurrentGroup = CurrentGroup->Next; + } + + /* Remove the group from the list */ + *PrevLink = TaskGroup->Next; + + /* Free the task group */ + HeapFree(hProcessHeap, + 0, + TaskGroup); + } + else if (TaskGroup->IsCollapsed && + TaskGroup->Index >= 0) + { + if (dwNewTaskCount > 1) + { + /* FIXME: Check if we should expand the group */ + /* Update the task group button */ + TaskSwitchWnd_UpdateTaskGroupButton(This, + TaskGroup); + } + else + { + /* Expand the group of one task button to a task button */ + TaskSwitchWnd_ExpandTaskGroup(This, + TaskGroup); + } + } + } +} + +static PTASK_ITEM +TaskSwitchWnd_FindTaskItem(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem, LastItem; + + TaskItem = This->TaskItems; + LastItem = TaskItem + This->TaskItemCount; + while (TaskItem != LastItem) + { + if (TaskItem->hWnd == hWnd) + return TaskItem; + + TaskItem++; + } + + return NULL; +} + +static PTASK_ITEM +TaskSwitchWnd_FindOtherTaskItem(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM LastItem, TaskItem; + PTASK_GROUP TaskGroup; + DWORD dwProcessId; + + if (!GetWindowThreadProcessId(hWnd, + &dwProcessId)) + { + return NULL; + } + + /* Try to find another task that belongs to the same + process as the given window */ + TaskItem = This->TaskItems; + LastItem = TaskItem + This->TaskItemCount; + while (TaskItem != LastItem) + { + TaskGroup = TaskItem->Group; + if (TaskGroup != NULL) + { + if (TaskGroup->dwProcessId == dwProcessId) + return TaskItem; + } + else + { + DWORD dwProcessIdTask; + + if (GetWindowThreadProcessId(TaskItem->hWnd, + &dwProcessIdTask) && + dwProcessIdTask == dwProcessId) + { + return TaskItem; + } + } + + TaskItem++; + } + + return NULL; +} + +static PTASK_ITEM +TaskSwitchWnd_AllocTaskItem(IN OUT PTASK_SWITCH_WND This) +{ + if (This->TaskItemCount >= MAX_TASKS_COUNT) + { + /* We need the most significant bit in 16 bit command IDs to indicate whether it + is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */ + return NULL; + } + + if (This->AllocatedTaskItems <= This->TaskItemCount) + { + if (This->TaskItemCount != 0) + { + PTASK_ITEM NewArray; + WORD NewArrayLength; + + NewArrayLength = This->AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC; + + NewArray = HeapReAlloc(hProcessHeap, + 0, + This->TaskItems, + NewArrayLength * sizeof(*This->TaskItems)); + if (NewArray != NULL) + { + This->AllocatedTaskItems = NewArrayLength; + This->TaskItems = NewArray; + } + else + return NULL; + } + else + { + This->TaskItems = HeapAlloc(hProcessHeap, + 0, + TASK_ITEM_ARRAY_ALLOC * sizeof(*This->TaskItems)); + if (This->TaskItems != NULL) + { + This->AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC; + } + else + return NULL; + } + } + + return This->TaskItems + This->TaskItemCount++; +} + +static VOID +TaskSwitchWnd_FreeTaskItem(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + WORD wIndex; + + if (TaskItem == This->ActiveTaskItem) + This->ActiveTaskItem = NULL; + + wIndex = (WORD)(TaskItem - This->TaskItems); + if (wIndex + 1 < This->TaskItemCount) + { + MoveMemory(TaskItem, + TaskItem + 1, + (This->TaskItemCount - wIndex - 1) * sizeof(*TaskItem)); + } + + This->TaskItemCount--; +} + +static VOID +TaskSwitchWnd_DeleteTaskItem(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + if (!This->IsDestroying) + { + /* Delete the task button from the toolbar */ + TaskSwitchWnd_DeleteTaskItemButton(This, + TaskItem); + } + + /* Remove the task from it's group */ + TaskSwitchWnd_RemoveTaskFromTaskGroup(This, + TaskItem); + + /* Free the task item */ + TaskSwitchWnd_FreeTaskItem(This, + TaskItem); +} + +static VOID +TaskSwitchWnd_CheckActivateTaskItem(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + PTASK_ITEM ActiveTaskItem; + PTASK_GROUP TaskGroup = NULL; + + ActiveTaskItem = This->ActiveTaskItem; + + if (TaskItem != NULL) + TaskGroup = TaskItem->Group; + + if (This->IsGroupingEnabled && TaskGroup != NULL) + { + if (TaskGroup->IsCollapsed) + { + /* FIXME */ + } + else + goto ChangeTaskItemButton; + } + else + { +ChangeTaskItemButton: + if (ActiveTaskItem != NULL) + { + PTASK_GROUP ActiveTaskGroup; + + if (ActiveTaskItem == TaskItem) + return; + + ActiveTaskGroup = ActiveTaskItem->Group; + + if (This->IsGroupingEnabled && ActiveTaskGroup != NULL) + { + if (ActiveTaskGroup->IsCollapsed) + { + if (ActiveTaskGroup == TaskGroup) + return; + + /* FIXME */ + } + else + goto ChangeActiveTaskItemButton; + } + else + { +ChangeActiveTaskItemButton: + This->ActiveTaskItem = NULL; + if (ActiveTaskItem->Index >= 0) + { + TaskSwitchWnd_UpdateTaskItemButton(This, + ActiveTaskItem); + } + } + } + + This->ActiveTaskItem = TaskItem; + + if (TaskItem != NULL && TaskItem->Index >= 0) + { + TaskSwitchWnd_UpdateTaskItemButton(This, + TaskItem); + } + } +} + +static BOOL +TaskSwitchWnd_AddTask(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem; + + TaskItem = TaskSwitchWnd_FindTaskItem(This, + hWnd); + if (TaskItem == NULL) + { + DbgPrint("Add window 0x%p\n", hWnd); + TaskItem = TaskSwitchWnd_AllocTaskItem(This); + if (TaskItem != NULL) + { + ZeroMemory(TaskItem, + sizeof(*TaskItem)); + TaskItem->hWnd = hWnd; + TaskItem->Index = -1; + TaskItem->Group = TaskSwitchWnd_AddToTaskGroup(This, + hWnd); + + if (!This->IsDestroying) + { + TaskSwitchWnd_AddTaskItemButton(This, + TaskItem); + } + } + } + + return TaskItem != NULL; +} + +static BOOL +TaskSwitchWnd_ActivateTask(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem; + + TaskItem = TaskSwitchWnd_FindTaskItem(This, + hWnd); + if (TaskItem == NULL) + { + TaskItem = TaskSwitchWnd_FindOtherTaskItem(This, + hWnd); + } + + if (TaskItem != NULL) + { + DbgPrint("Activate window 0x%p on button %d\n", hWnd, TaskItem->Index); + } + else + { + DbgPrint("Activate window 0x%p, could not find task\n", hWnd); + } + + TaskSwitchWnd_CheckActivateTaskItem(This, + TaskItem); + + return FALSE; +} + +static BOOL +TaskSwitchWnd_DeleteTask(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem; + + TaskItem = TaskSwitchWnd_FindTaskItem(This, + hWnd); + if (TaskItem != NULL) + { + DbgPrint("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index); + TaskSwitchWnd_DeleteTaskItem(This, + TaskItem); + return TRUE; + } + //else + //DbgPrint("Failed to delete window 0x%p\n", hWnd); + + return FALSE; +} + +static VOID +TaskSwitchWnd_DeleteAllTasks(IN OUT PTASK_SWITCH_WND This) +{ + PTASK_ITEM CurrentTask; + + if (This->TaskItemCount > 0) + { + CurrentTask = This->TaskItems + This->TaskItemCount; + do + { + TaskSwitchWnd_DeleteTaskItem(This, + --CurrentTask); + } while (CurrentTask != This->TaskItems); + } +} + +static VOID +TaskSwitchWnd_FlashTaskItem(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + /* FIXME: Implement */ +} + +static BOOL +TaskSwitchWnd_FlashTask(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem; + + TaskItem = TaskSwitchWnd_FindTaskItem(This, + hWnd); + if (TaskItem != NULL) + { + DbgPrint("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index); + TaskSwitchWnd_FlashTaskItem(This, + TaskItem); + return TRUE; + } + + return FALSE; +} + +static VOID +TaskSwitchWnd_RedrawTaskItem(IN OUT PTASK_SWITCH_WND This, + IN OUT PTASK_ITEM TaskItem) +{ + PTASK_GROUP TaskGroup; + + TaskGroup = TaskItem->Group; + if (This->IsGroupingEnabled && TaskGroup != NULL) + { + if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0) + { + TaskSwitchWnd_UpdateTaskGroupButton(This, + TaskGroup); + } + else if (TaskItem->Index >= 0) + { + goto UpdateTaskItem; + } + } + else if (TaskItem->Index >= 0) + { +UpdateTaskItem: + TaskSwitchWnd_UpdateTaskItemButton(This, + TaskItem); + } +} + + +static BOOL +TaskSwitchWnd_RedrawTask(IN OUT PTASK_SWITCH_WND This, + IN HWND hWnd) +{ + PTASK_ITEM TaskItem; + + TaskItem = TaskSwitchWnd_FindTaskItem(This, + hWnd); + if (TaskItem != NULL) + { + TaskSwitchWnd_RedrawTaskItem(This, + TaskItem); + return TRUE; + } + + return FALSE; +} + +static INT +TaskSwitchWnd_UpdateTbButtonSpacing(IN OUT PTASK_SWITCH_WND This, + IN BOOL bHorizontal, + IN UINT uiBtnsPerLine) +{ + TBMETRICS tbm; + + tbm.cbSize = sizeof(tbm); + tbm.dwMask = TBMF_BUTTONSPACING; + + if (bHorizontal || uiBtnsPerLine > 1) + tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2); + else + tbm.cxButtonSpacing = 0; + + tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2); + + SendMessage(This->hWndToolbar, + TB_SETMETRICS, + 0, + (LPARAM)&tbm); + + return tbm.cxButtonSpacing; +} + + +static VOID +TaskSwitchWnd_UpdateButtonsSize(IN OUT PTASK_SWITCH_WND This, + IN BOOL bRedrawDisabled) +{ + RECT rcClient; + UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui; + LONG NewBtnSize; + BOOL Horizontal; + TBBUTTONINFO tbbi; + TBMETRICS tbm; + + if (GetClientRect(This->hWnd, + &rcClient) && + !IsRectEmpty(&rcClient)) + { + if (This->ToolbarBtnCount > 0) + { + tbm.cbSize = sizeof(tbm); + tbm.dwMask = TBMF_PAD | TBMF_BUTTONSPACING; + SendMessage(This->hWndToolbar, + TB_GETMETRICS, + 0, + (LPARAM)&tbm); + + uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (This->ButtonSize.cy + tbm.cyButtonSpacing); + if (uiRows == 0) + uiRows = 1; + + uiBtnsPerLine = (This->ToolbarBtnCount + uiRows - 1) / uiRows; + + Horizontal = ITrayWindow_IsHorizontal(This->Tray); + + if (!bRedrawDisabled) + TaskSwitchWnd_BeginUpdate(This); + + /* We might need to update the button spacing */ + tbm.cxButtonSpacing = TaskSwitchWnd_UpdateTbButtonSpacing(This, + Horizontal, + uiBtnsPerLine); + + /* Calculate the ideal width and make sure it's within the allowed range */ + NewBtnSize = (rcClient.right - (uiBtnsPerLine * tbm.cxButtonSpacing)) / uiBtnsPerLine; + + /* Determine the minimum and maximum width of a button */ + if (Horizontal) + uiMax = GetSystemMetrics(SM_CXMINIMIZED); + else + uiMax = rcClient.right; + + uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE)); + + if (NewBtnSize < uiMin) + NewBtnSize = uiMin; + if (NewBtnSize > uiMax) + NewBtnSize = uiMax; + + This->ButtonSize.cx = NewBtnSize; + + /* Recalculate how many buttons actually fit into one line */ + uiBtnsPerLine = rcClient.right / (NewBtnSize + tbm.cxButtonSpacing); + if (uiBtnsPerLine == 0) + uiBtnsPerLine++; + This->TbButtonsPerLine = uiBtnsPerLine; + + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE; + tbbi.cx = (INT)NewBtnSize; + + for (ui = 0; ui != This->ToolbarBtnCount; ui++) + { + tbbi.fsState = TBSTATE_ENABLED; + + /* Check if we're updating a button that is the last one in the + line. If so, we need to set the TBSTATE_WRAP flag! */ + if ((ui + 1) % uiBtnsPerLine == 0) + tbbi.fsState |= TBSTATE_WRAP; + + if (This->ActiveTaskItem != NULL && + This->ActiveTaskItem->Index == ui) + { + tbbi.fsState |= TBSTATE_CHECKED; + } + + SendMessage(This->hWndToolbar, + TB_SETBUTTONINFO, + (WPARAM)ui, + (LPARAM)&tbbi); + } + +#if 0 + /* FIXME: Force the window to the correct position in case some idiot + did something to us */ + SetWindowPos(This->hWndToolbar, + NULL, + 0, + 0, + rcClient.right, /* FIXME */ + rcClient.bottom, /* FIXME */ + SWP_NOACTIVATE | SWP_NOZORDER); +#endif + } + else + { + This->TbButtonsPerLine = 0; + This->ButtonSize.cx = 0; + } + } + + TaskSwitchWnd_EndUpdate(This); +} + +static BOOL CALLBACK +TaskSwitchWnd_EnumWindowsProc(IN HWND hWnd, + IN LPARAM lParam) +{ + PTASK_SWITCH_WND This = (PTASK_SWITCH_WND)lParam; + + /* Only show windows that still exist and are visible and none of explorer's + special windows (such as the desktop or the tray window) */ + if (IsWindow(hWnd) && IsWindowVisible(hWnd) && + !ITrayWindow_IsSpecialHWND(This->Tray, + hWnd)) + { + /* Don't list popup windows and also no tool windows */ + if (GetWindow(hWnd, + GW_OWNER) == NULL && + !(GetWindowLong(hWnd, + GWL_EXSTYLE) & WS_EX_TOOLWINDOW)) + { + TaskSwitchWnd_AddTask(This, + hWnd); + } + } + + return TRUE; +} + +static LRESULT CALLBACK +TaskSwichWnd_ToolbarSubclassedProc(IN HWND hWnd, + IN UINT msg, + IN WPARAM wParam, + IN LPARAM lParam, + IN UINT_PTR uIdSubclass, + IN DWORD_PTR dwRefData) +{ + LRESULT Ret; + + Ret = DefSubclassProc(hWnd, + msg, + wParam, + lParam); + + if (msg == WM_NCHITTEST && Ret == HTCLIENT) + { + POINT pt; + + /* See if the mouse is on a button */ + pt.x = (SHORT)LOWORD(lParam); + pt.y = (SHORT)HIWORD(lParam); + + if (MapWindowPoints(HWND_DESKTOP, + hWnd, + &pt, + 1) != 0 && + (INT)SendMessage(hWnd, + TB_HITTEST, + 0, + (LPARAM)&pt) < 0) + { + /* Make the control appear to be transparent outside of any buttons */ + Ret = HTTRANSPARENT; + } + } + + return Ret; +} + +static VOID +TaskSwitchWnd_Create(IN OUT PTASK_SWITCH_WND This) +{ + This->hWndToolbar = CreateWindowEx(0, + TOOLBARCLASSNAME, + szRunningApps, + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | + TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | + TBSTYLE_TRANSPARENT | + CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER, + 0, + 0, + 0, + 0, + This->hWnd, + NULL, + hExplorerInstance, + NULL); + + if (This->hWndToolbar != NULL) + { + SIZE BtnSize; + + /* Identify the version we're using */ + SendMessage(This->hWndToolbar, + TB_BUTTONSTRUCTSIZE, + sizeof(TBBUTTON), + 0); + + /* Calculate the default button size. Don't save this in This->ButtonSize.cx so that + the actual button width gets updated correctly on the first recalculation */ + BtnSize.cx = GetSystemMetrics(SM_CXMINIMIZED); + This->ButtonSize.cy = BtnSize.cy = GetSystemMetrics(SM_CYSIZE) + (2 * GetSystemMetrics(SM_CYEDGE)); + SendMessage(This->hWndToolbar, + TB_SETBUTTONSIZE, + 0, + (LPARAM)MAKELONG(BtnSize.cx, + BtnSize.cy)); + + /* We don't want to see partially clipped buttons...not that we could see them... */ +#if 1 + SendMessage(This->hWndToolbar, + TB_SETEXTENDEDSTYLE, + 0, + TBSTYLE_EX_HIDECLIPPEDBUTTONS); +#endif + + /* Set proper spacing between buttons */ + TaskSwitchWnd_UpdateTbButtonSpacing(This, + ITrayWindow_IsHorizontal(This->Tray), + 0); + + /* Register the shell hook */ + This->ShellHookMsg = RegisterWindowMessage(TEXT("SHELLHOOK")); + RegisterShellHook(This->hWnd, + 3); /* 1 if not NT! We're targeting NT so we don't care! */ + + /* Add all windows to the toolbar */ + EnumWindows(TaskSwitchWnd_EnumWindowsProc, + (LPARAM)This); + + /* Recalculate the button size */ + TaskSwitchWnd_UpdateButtonsSize(This, + FALSE); + + /* Subclass the toolbar control because it doesn't provide a + NM_NCHITTEST notification */ + This->IsToolbarSubclassed = SetWindowSubclass(This->hWndToolbar, + TaskSwichWnd_ToolbarSubclassedProc, + TSW_TOOLBAR_SUBCLASS_ID, + (DWORD_PTR)This); + } +} + +static VOID +TaskSwitchWnd_NCDestroy(IN OUT PTASK_SWITCH_WND This) +{ + This->IsDestroying = TRUE; + + /* Unregister the shell hook */ + RegisterShellHook(This->hWnd, + FALSE); + + TaskSwitchWnd_DeleteAllTasks(This); +} + +static BOOL +TaskSwitchWnd_HandleAppCommand(IN OUT PTASK_SWITCH_WND This, + IN WPARAM wParam, + IN LPARAM lParam) +{ + BOOL Ret = FALSE; + + switch (GET_APPCOMMAND_LPARAM(lParam)) + { + case APPCOMMAND_BROWSER_SEARCH: + Ret = SHFindFiles(NULL, + NULL); + break; + + case APPCOMMAND_BROWSER_HOME: + case APPCOMMAND_LAUNCH_MAIL: + default: + DbgPrint("Shell app command %d unhandled!\n", (INT)GET_APPCOMMAND_LPARAM(lParam)); + break; + } + + return Ret; +} + + +static LRESULT +TaskSwitchWnd_HandleShellHookMsg(IN OUT PTASK_SWITCH_WND This, + IN WPARAM wParam, + IN LPARAM lParam) +{ + BOOL Ret = FALSE; + + switch ((INT)wParam) + { + case HSHELL_APPCOMMAND: + TaskSwitchWnd_HandleAppCommand(This, + wParam, + lParam); + Ret = TRUE; + break; + + case HSHELL_WINDOWCREATED: + TaskSwitchWnd_AddTask(This, + (HWND)lParam); + Ret = TRUE; + break; + + case HSHELL_WINDOWDESTROYED: + /* The window still exists! Delay destroying it a bit */ + TaskSwitchWnd_DeleteTask(This, + (HWND)lParam); + Ret = TRUE; + break; + + case HSHELL_ACTIVATESHELLWINDOW: + goto UnhandledShellMessage; + + case HSHELL_RUDEAPPACTIVATED: + goto UnhandledShellMessage; + + case HSHELL_WINDOWACTIVATED: + TaskSwitchWnd_ActivateTask(This, + (HWND)lParam); + Ret = TRUE; + break; + + case HSHELL_GETMINRECT: + goto UnhandledShellMessage; + + case HSHELL_FLASH: + TaskSwitchWnd_FlashTask(This, + (HWND)lParam); + Ret = TRUE; + break; + + case HSHELL_REDRAW: + TaskSwitchWnd_RedrawTask(This, + (HWND)lParam); + Ret = TRUE; + break; + + case HSHELL_TASKMAN: + case HSHELL_LANGUAGE: + case HSHELL_SYSMENU: + case HSHELL_ENDTASK: + case HSHELL_ACCESSIBILITYSTATE: + case HSHELL_WINDOWREPLACED: + case HSHELL_WINDOWREPLACING: + default: + { + static const struct { + INT msg; + LPCWSTR msg_name; + } hshell_msg[] = { + {HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED"}, + {HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED"}, + {HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW"}, + {HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED"}, + {HSHELL_GETMINRECT, L"HSHELL_GETMINRECT"}, + {HSHELL_REDRAW, L"HSHELL_REDRAW"}, + {HSHELL_TASKMAN, L"HSHELL_TASKMAN"}, + {HSHELL_LANGUAGE, L"HSHELL_LANGUAGE"}, + {HSHELL_SYSMENU, L"HSHELL_SYSMENU"}, + {HSHELL_ENDTASK, L"HSHELL_ENDTASK"}, + {HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE"}, + {HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND"}, + {HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED"}, + {HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING"}, + {HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED"}, + }; + int i, found; +UnhandledShellMessage: + for (i = 0, found = 0; i != sizeof(hshell_msg) / sizeof(hshell_msg[0]); i++) + { + if (hshell_msg[i].msg == (INT)wParam) + { + DbgPrint("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam); + found = 1; + break; + } + } + if (!found) + { + DbgPrint("Shell message %d unhandled (lParam = 0x%p)!\n", (INT)wParam, lParam); + } + break; + } + } + + return Ret; +} + +static VOID +TaskSwitchWnd_EnableGrouping(IN OUT PTASK_SWITCH_WND This, + IN BOOL bEnable) +{ + This->IsGroupingEnabled = bEnable; + + /* Collapse or expand groups if neccessary */ + TaskSwitchWnd_UpdateButtonsSize(This, + FALSE); +} + +static LRESULT +TaskSwichWnd_HandleItemPaint(IN OUT PTASK_SWITCH_WND This, + IN OUT NMTBCUSTOMDRAW *nmtbcd) +{ + HFONT hCaptionFont, hBoldCaptionFont; + LRESULT Ret = CDRF_DODEFAULT; + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + + UINT uidctFlags = DC_TEXT | DC_ICON | DC_NOSENDMSG; + +#endif + + if (!IS_TASKGROUP_PTR(nmtbcd->nmcd.lItemlParam)) + { + PTASK_ITEM TaskItem = GET_TASKITEM_PTR(nmtbcd->nmcd.lItemlParam); + + if (TaskItem != NULL && IsWindow(TaskItem->hWnd)) + { + hCaptionFont = ITrayWindow_GetCaptionFonts(This->Tray, + &hBoldCaptionFont); + if (nmtbcd->nmcd.uItemState & CDIS_CHECKED) + hCaptionFont = hBoldCaptionFont; + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + + /* Make sure we don't draw on the button edges */ + InflateRect(&nmtbcd->nmcd.rc, + -GetSystemMetrics(SM_CXEDGE), + -GetSystemMetrics(SM_CYEDGE)); + + if ((nmtbcd->nmcd.uItemState & CDIS_MARKED) && TaskItem->RenderFlashed) + { + /* This is a slight glitch. We have to move the rectangle so that + the button content appears to be pressed. However, when flashing + is enabled, we can see a light line at the top and left inner + border. We need to fill that area with the flashing color. Note + that since we're using DrawCaptionTemp() the flashing color is + COLOR_ACTIVECAPTION, not COLOR_HIGHLIGHT! */ + FillRect(nmtbcd->nmcd.hdc, + &nmtbcd->nmcd.rc, + (HBRUSH)(COLOR_ACTIVECAPTION + 1)); + + /* Make the button content appear pressed. This however draws a bit + into the right and bottom border of the button edge, making it + look a bit odd. However, selecting a clipping region to prevent + that from happening causes problems with DrawCaptionTemp()! */ + OffsetRect(&nmtbcd->nmcd.rc, + 1, + 1); + + /* Render flashed */ + uidctFlags |= DC_ACTIVE; + } + else + { + uidctFlags |= DC_INBUTTON; + if (nmtbcd->nmcd.uItemState & CDIS_CHECKED) + uidctFlags |= DC_ACTIVE; + } + + /* Draw the button content */ + TaskItem->DisplayTooltip = !DrawCaptionTemp(TaskItem->hWnd, + nmtbcd->nmcd.hdc, + &nmtbcd->nmcd.rc, + hCaptionFont, + NULL, + NULL, + uidctFlags); + + return CDRF_SKIPDEFAULT; + +#else /* !TASK_USE_DRAWCAPTIONTEMP */ + + /* Make the entire button flashing if neccessary */ + if (nmtbcd->nmcd.uItemState & CDIS_MARKED) + { + if (TaskItem->RenderFlashed) + { + nmtbcd->hbrMonoDither = GetSysColorBrush(COLOR_HIGHLIGHT); + nmtbcd->clrTextHighlight = GetSysColor(COLOR_HIGHLIGHTTEXT); + nmtbcd->nHLStringBkMode = TRANSPARENT; + + /* We don't really need to set clrMark because we set the + background mode to TRANSPARENT! */ + nmtbcd->clrMark = GetSysColor(COLOR_HIGHLIGHT); + + Ret |= TBCDRF_USECDCOLORS; + } + else + Ret |= TBCDRF_NOMARK; + } + + /* Select the font we want to use */ + SelectObject(nmtbcd->nmcd.hdc, + hCaptionFont); + return Ret | CDRF_NEWFONT; + +#endif + + } + } + else + { + PTASK_GROUP TaskGroup = GET_TASKGROUP_PTR(nmtbcd->nmcd.lItemlParam); + + if (TaskGroup != NULL) + { + /* FIXME: Implement painting for task groups */ + } + } + + return Ret; +} + +static LRESULT +TaskSwitchWnd_HandleToolbarNotification(IN OUT PTASK_SWITCH_WND This, + IN const NMHDR *nmh) +{ + LRESULT Ret = 0; + + switch (nmh->code) + { + case NM_CUSTOMDRAW: + { + LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW)nmh; + + switch (nmtbcd->nmcd.dwDrawStage) + { + +#if TASK_USE_DRAWCAPTIONTEMP != 0 + + case CDDS_ITEMPREPAINT: + /* We handle drawing in the post-paint stage so that we + don't have to draw the button edges, etc */ + Ret = CDRF_NOTIFYPOSTPAINT; + break; + + case CDDS_ITEMPOSTPAINT: + +#else /* !TASK_USE_DRAWCAPTIONTEMP */ + + case CDDS_ITEMPREPAINT: + +#endif + + Ret = TaskSwichWnd_HandleItemPaint(This, + nmtbcd); + break; + + case CDDS_PREPAINT: + Ret = CDRF_NOTIFYITEMDRAW; + break; + + default: + Ret = CDRF_DODEFAULT; + break; + } + break; + } + } + + return Ret; +} + +static LRESULT CALLBACK +TaskSwitchWndProc(IN HWND hwnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + PTASK_SWITCH_WND This = NULL; + LRESULT Ret = FALSE; + + if (uMsg != WM_NCCREATE) + { + This = (PTASK_SWITCH_WND)GetWindowLongPtr(hwnd, + 0); + } + + if (This != NULL || uMsg == WM_NCCREATE) + { + switch (uMsg) + { + case WM_SIZE: + { + SIZE szClient; + + szClient.cx = LOWORD(lParam); + szClient.cy = HIWORD(lParam); + if (This->hWndToolbar != NULL) + { + SetWindowPos(This->hWndToolbar, + NULL, + 0, + 0, + szClient.cx, + szClient.cy, + SWP_NOZORDER); + + TaskSwitchWnd_UpdateButtonsSize(This, + FALSE); + } + break; + } + + case WM_NCHITTEST: + { + /* We want the tray window to be draggable everywhere, so make the control + appear transparent */ + Ret = DefWindowProc(hwnd, + uMsg, + wParam, + lParam); + if (Ret != HTVSCROLL && Ret != HTHSCROLL) + Ret = HTTRANSPARENT; + break; + } + + case WM_COMMAND: + { + if (lParam != 0 && (HWND)lParam == This->hWndToolbar) + { + DbgPrint("WM_COMMAND %u:%u (%u)\n", (UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (UINT)wParam); + } + break; + } + + case WM_NOTIFY: + { + const NMHDR *nmh = (const NMHDR *)lParam; + + if (nmh->hwndFrom == This->hWndToolbar) + { + Ret = TaskSwitchWnd_HandleToolbarNotification(This, + nmh); + } + break; + } + + case TSWM_ENABLEGROUPING: + { + Ret = This->IsGroupingEnabled; + if (wParam != This->IsGroupingEnabled) + { + TaskSwitchWnd_EnableGrouping(This, + (BOOL)wParam); + } + break; + } + + case TSWM_UPDATETASKBARPOS: + { + /* Update the button spacing */ + TaskSwitchWnd_UpdateTbButtonSpacing(This, + ITrayWindow_IsHorizontal(This->Tray), + 0); + break; + } + + case WM_CONTEXTMENU: + { + if (This->hWndToolbar != NULL) + { + POINT pt; + INT_PTR iBtn; + + pt.x = (LONG)LOWORD(lParam); + pt.y = (LONG)HIWORD(lParam); + + MapWindowPoints(NULL, + This->hWndToolbar, + &pt, + 1); + + iBtn = (INT_PTR)SendMessage(This->hWndToolbar, + TB_HITTEST, + 0, + (LPARAM)&pt); + if (iBtn >= 0) + { + /* FIXME: Display the system menu of the window */ + } + else + goto ForwardContextMenuMsg; + } + else + { +ForwardContextMenuMsg: + /* Forward message */ + Ret = SendMessage(ITrayWindow_GetHWND(This->Tray), + uMsg, + wParam, + lParam); + } + break; + } + + case WM_NCCREATE: + { + LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; + This = (PTASK_SWITCH_WND)HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return FALSE; + + ZeroMemory(This, + sizeof(*This)); + This->hWnd = hwnd; + This->hWndNotify = CreateStruct->hwndParent; + This->Tray = (ITrayWindow*)CreateStruct->lpCreateParams; + This->IsGroupingEnabled = TRUE; /* FIXME */ + SetWindowLongPtr(hwnd, + 0, + (LONG_PTR)This); + + return TRUE; + } + + case WM_CREATE: + TaskSwitchWnd_Create(This); + +#if DUMP_TASKS != 0 + SetTimer(hwnd, + 1, + 5000, + NULL); +#endif + + break; + + case WM_DESTROY: + if (This->IsToolbarSubclassed) + { + if (RemoveWindowSubclass(This->hWndToolbar, + TaskSwichWnd_ToolbarSubclassedProc, + TSW_TOOLBAR_SUBCLASS_ID)) + { + This->IsToolbarSubclassed = FALSE; + } + } + break; + + case WM_NCDESTROY: + TaskSwitchWnd_NCDestroy(This); + HeapFree(hProcessHeap, + 0, + This); + SetWindowLongPtr(hwnd, + 0, + 0); + break; + +#if DUMP_TASKS != 0 + case WM_TIMER: + switch(wParam) + { + case 1: + TaskSwitchWnd_DumpTasks(This); + break; + } + break; +#endif + + default: +/* HandleDefaultMessage: */ + if (uMsg == This->ShellHookMsg && This->ShellHookMsg != 0) + { + /* Process shell messages */ + Ret = (LRESULT)TaskSwitchWnd_HandleShellHookMsg(This, + wParam, + lParam); + break; + } + + Ret = DefWindowProc(hwnd, + uMsg, + wParam, + lParam); + break; + } + } + + return Ret; +} + + +HWND +CreateTaskSwitchWnd(IN HWND hWndParent, + IN OUT ITrayWindow *Tray) +{ + HWND hwndTaskBar; + + hwndTaskBar = CreateWindowEx(0, + szTaskSwitchWndClass, + szRunningApps, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP, + 0, + 0, + 0, + 0, + hWndParent, + NULL, + hExplorerInstance, + (LPVOID)Tray); + + return hwndTaskBar; +} + +BOOL +RegisterTaskSwitchWndClass(VOID) +{ + WNDCLASS wc; + + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = TaskSwitchWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(PTASK_SWITCH_WND); + wc.hInstance = hExplorerInstance; + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, + IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = szTaskSwitchWndClass; + + return RegisterClass(&wc) != 0; +} + +VOID +UnregisterTaskSwitchWndClass(VOID) +{ + UnregisterClass(szTaskSwitchWndClass, + hExplorerInstance); +} diff --git a/reactos/base/shell/explorer-new/tbsite.c b/reactos/base/shell/explorer-new/tbsite.c new file mode 100644 index 00000000000..ab96ddac2c2 --- /dev/null +++ b/reactos/base/shell/explorer-new/tbsite.c @@ -0,0 +1,1059 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/***************************************************************************** + ** ITrayBandSite ************************************************************ + *****************************************************************************/ + +static const ITrayBandSiteVtbl ITrayBandSiteImpl_Vtbl; +static const IBandSiteVtbl IBandSiteImpl_Vtbl; + +typedef struct +{ + const ITrayBandSiteVtbl *lpVtbl; + const IBandSiteVtbl *lpBandSiteVtbl; + LONG Ref; + + ITrayWindow *Tray; + + IUnknown *punkInner; + IBandSite *BandSite; + ITaskBand *TaskBand; + IWindowEventHandler *WindowEventHandler; + IContextMenu *ContextMenu; + + HWND hWndRebar; + + union + { + DWORD dwFlags; + struct + { + DWORD Locked : 1; + }; + }; +} ITrayBandSiteImpl; + +static HRESULT +ITrayBandSiteImpl_Update(IN OUT ITrayBandSiteImpl *This); + +static IUnknown * +IUnknown_from_ITrayBandSiteImpl(ITrayBandSiteImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +IMPL_CASTS(ITrayBandSite, ITrayBandSite, lpVtbl) +IMPL_CASTS(IBandSite, ITrayBandSite, lpBandSiteVtbl) + +static ULONG STDMETHODCALLTYPE +ITrayBandSiteImpl_AddRef(IN OUT ITrayBandSite *iface) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + + return InterlockedIncrement(&This->Ref); +} + +static VOID +ITrayBandSiteImpl_Free(IN OUT ITrayBandSiteImpl *This) +{ + if (This->BandSite != NULL) + { + IBandSite_Release(This->BandSite); + This->BandSite = NULL; + } + + if (This->WindowEventHandler != NULL) + { + IWindowEventHandler_Release(This->WindowEventHandler); + This->WindowEventHandler = NULL; + } + + if (This->ContextMenu != NULL) + { + IContextMenu_Release(This->ContextMenu); + This->ContextMenu = NULL; + } + + if (This->punkInner != NULL) + { + IUnknown_Release(This->punkInner); + This->punkInner = NULL; + } + + HeapFree(hProcessHeap, + 0, + This); +} + +static ULONG STDMETHODCALLTYPE +ITrayBandSiteImpl_Release(IN OUT ITrayBandSite *iface) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + + if (Ret == 0) + ITrayBandSiteImpl_Free(This); + + return Ret; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_QueryInterface(IN OUT ITrayBandSite *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + ITrayBandSiteImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + + if (IsEqualIID(riid, + &IID_IUnknown) || + IsEqualIID(riid, + &IID_IBandSiteStreamCallback)) + { + /* NOTE: IID_IBandSiteStreamCallback is queried by the shell, we + implement this interface directly */ + *ppvObj = IUnknown_from_ITrayBandSiteImpl(This); + } + else if (IsEqualIID(riid, + &IID_IBandSite)) + { + *ppvObj = IBandSite_from_ITrayBandSiteImpl(This); + } + else if (IsEqualIID(riid, + &IID_IWindowEventHandler)) + { + DbgPrint("ITaskBandSite: IWindowEventHandler queried!\n"); + *ppvObj = NULL; + return E_NOINTERFACE; + } + else if (This->punkInner != NULL) + { + return IUnknown_QueryInterface(This->punkInner, + riid, + ppvObj); + } + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + ITrayBandSiteImpl_AddRef(iface); + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_OnLoad(IN OUT ITrayBandSite *iface, + IN OUT IStream *pStm, + IN REFIID riid, + OUT PVOID *pvObj) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + LARGE_INTEGER liPosZero; + ULARGE_INTEGER liCurrent; + CLSID clsid; + ULONG ulRead; + HRESULT hRet; + + /* NOTE: Callback routine called by the shell while loading the task band + stream. We use it to intercept the default behavior when the task + band is loaded from the stream. + + NOTE: riid always points to IID_IUnknown! This is because the shell hasn't + read anything from the stream and therefore doesn't know what CLSID + it's dealing with. We'll have to find it out ourselves by reading + the GUID from the stream. */ + + /* Read the current position of the stream, we'll have to reset it everytime + we read a CLSID that's not the task band... */ + ZeroMemory(&liPosZero, + sizeof(liPosZero)); + hRet = IStream_Seek(pStm, + liPosZero, + STREAM_SEEK_CUR, + &liCurrent); + + if (SUCCEEDED(hRet)) + { + /* Now let's read the CLSID from the stream and see if it's our task band */ + hRet = IStream_Read(pStm, + &clsid, + sizeof(clsid), + &ulRead); + if (SUCCEEDED(hRet) && ulRead == sizeof(clsid)) + { + if (IsEqualGUID(&clsid, + &CLSID_ITaskBand)) + { + ASSERT(This->TaskBand != NULL); + /* We're trying to load the task band! Let's create it... */ + + hRet = ITaskBand_QueryInterface(This->TaskBand, + riid, + pvObj); + if (SUCCEEDED(hRet)) + { + /* Load the stream */ + DbgPrint("IBandSiteStreamCallback::OnLoad intercepted the task band CLSID!\n"); + } + + return hRet; + } + } + } + + /* Reset the position and let the shell do all the work for us */ + hRet = IStream_Seek(pStm, + *(LARGE_INTEGER*)&liCurrent, + STREAM_SEEK_SET, + NULL); + if (SUCCEEDED(hRet)) + { + /* Let the shell handle everything else for us :) */ + hRet = OleLoadFromStream(pStm, + riid, + pvObj); + } + + if (!SUCCEEDED(hRet)) + { + DbgPrint("IBandSiteStreamCallback::OnLoad(0x%p, 0x%p, 0x%p) returns 0x%x\n", pStm, riid, pvObj, hRet); + } + + return hRet; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_OnSave(IN OUT ITrayBandSite *iface, + IN OUT IUnknown *pUnk, + IN OUT IStream *pStm) +{ + /* NOTE: Callback routine called by the shell while saving the task band + stream. We use it to intercept the default behavior when the task + band is saved to the stream */ + /* FIXME: Implement */ + DbgPrint("IBandSiteStreamCallback::OnSave(0x%p, 0x%p) returns E_NOTIMPL\n", pUnk, pStm); + return E_NOTIMPL; +} + +static HRESULT +IsSameObject(IN IUnknown *punk1, + IN IUnknown *punk2) +{ + HRESULT hRet; + + hRet = IUnknown_QueryInterface(punk1, + &IID_IUnknown, + (PVOID*)&punk1); + if (!SUCCEEDED(hRet)) + return hRet; + + hRet = IUnknown_QueryInterface(punk2, + &IID_IUnknown, + (PVOID*)&punk2); + IUnknown_Release(punk1); + + if (!SUCCEEDED(hRet)) + return hRet; + + IUnknown_Release(punk2); + + /* We're dealing with the same object if the IUnknown pointers are equal */ + return (punk1 == punk2) ? S_OK : S_FALSE; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_IsTaskBand(IN OUT ITrayBandSite *iface, + IN IUnknown *punk) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + return IsSameObject((IUnknown *)This->BandSite, + punk); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_ProcessMessage(IN OUT ITrayBandSite *iface, + IN HWND hWnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam, + OUT LRESULT *plResult) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + HRESULT hRet; + + ASSERT(This->hWndRebar != NULL); + + /* Custom task band behavior */ + switch (uMsg) + { + case WM_NOTIFY: + { + const NMHDR *nmh = (const NMHDR *)lParam; + + if (nmh->hwndFrom == This->hWndRebar) + { + switch (nmh->code) + { + case NM_NCHITTEST: + { + LPNMMOUSE nmm = (LPNMMOUSE)lParam; + + if (nmm->dwHitInfo == RBHT_CLIENT || nmm->dwItemSpec == (DWORD_PTR)-1) + { + /* Make the rebar control appear transparent so the user + can drag the tray window */ + *plResult = HTTRANSPARENT; + } + return S_OK; + } + + case RBN_MINMAX: + /* Deny if an Administrator disabled this "feature" */ + *plResult = (SHRestricted(REST_NOMOVINGBAND) != 0); + return S_OK; + } + } + + //DbgPrint("ITrayBandSite::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u...\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code); + break; + } + }; + + /* Forward to the shell's IWindowEventHandler interface to get the default + shell behavior! */ + if (This->WindowEventHandler != NULL) + { + DbgPrint("Calling IWindowEventHandler::ProcessMessage(0x%p, 0x%x, 0x%p, 0x%p, 0x%p) This->hWndRebar=0x%p\n", hWnd, uMsg, wParam, lParam, plResult, This->hWndRebar); + hRet = IWindowEventHandler_ProcessMessage(This->WindowEventHandler, + hWnd, + uMsg, + wParam, + lParam, + plResult); + if (!SUCCEEDED(hRet)) + { + if (uMsg == WM_NOTIFY) + { + const NMHDR *nmh = (const NMHDR *)lParam; + DbgPrint("ITrayBandSite->IWindowEventHandler::ProcessMessage: WM_NOTIFY for 0x%p, From: 0x%p, Code: NM_FIRST-%u returned 0x%x\n", hWnd, nmh->hwndFrom, NM_FIRST - nmh->code, hRet); + } + else + { + DbgPrint("ITrayBandSite->IWindowEventHandler::ProcessMessage(0x%p,0x%x,0x%p,0x%p,0x%p->0x%p) returned: 0x%x\n", hWnd, uMsg, wParam, lParam, plResult, *plResult, hRet); + } + } + } + else + hRet = E_FAIL; + + return hRet; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_AddContextMenus(IN OUT ITrayBandSite *iface, + IN HMENU hmenu, + IN UINT indexMenu, + IN UINT idCmdFirst, + IN UINT idCmdLast, + IN UINT uFlags, + OUT IContextMenu **ppcm) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + IShellService *pSs; + HRESULT hRet; + + if (This->ContextMenu == NULL) + { + /* Cache the context menu so we don't need to CoCreateInstance all the time... */ + hRet = CoCreateInstance(&CLSID_IShellBandSiteMenu, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellService, + (PVOID*)&pSs); + DbgPrint("CoCreateInstance(CLSID_IShellBandSiteMenu) for IShellService returned: 0x%x\n", hRet); + if (!SUCCEEDED(hRet)) + return hRet; + + hRet = IShellService_SetOwner(pSs, + IUnknown_from_ITrayBandSiteImpl(This)); + if (!SUCCEEDED(hRet)) + { + IShellService_Release(pSs); + return hRet; + } + + hRet = IShellService_QueryInterface(pSs, + &IID_IContextMenu, + (PVOID*)&This->ContextMenu); + + IShellService_Release(pSs); + + if (!SUCCEEDED(hRet)) + return hRet; + } + + if (ppcm != NULL) + { + IContextMenu_AddRef(This->ContextMenu); + *ppcm = This->ContextMenu; + } + + /* Add the menu items */ + return IContextMenu_QueryContextMenu(This->ContextMenu, + hmenu, + indexMenu, + idCmdFirst, + idCmdLast, + uFlags); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_Lock(IN OUT ITrayBandSite *iface, + IN BOOL bLock) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_ITrayBandSite(iface); + BOOL bPrevLocked = This->Locked; + BANDSITEINFO bsi; + HRESULT hRet; + + ASSERT(This->BandSite != NULL); + + if (bPrevLocked != bLock) + { + This->Locked = bLock; + + bsi.dwMask = BSIM_STYLE; + bsi.dwStyle = (This->Locked ? BSIS_LOCKED | BSIS_NOGRIPPER : BSIS_AUTOGRIPPER); + + hRet = IBandSite_SetBandSiteInfo(This->BandSite, + &bsi); + if (SUCCEEDED(hRet)) + { + hRet = ITrayBandSiteImpl_Update(This); + } + + return hRet; + } + + return S_FALSE; +} + +static const ITrayBandSiteVtbl ITrayBandSiteImpl_Vtbl = +{ + /*** IUnknown methods ***/ + ITrayBandSiteImpl_QueryInterface, + ITrayBandSiteImpl_AddRef, + ITrayBandSiteImpl_Release, + /*** IBandSiteStreamCallback methods ***/ + ITrayBandSiteImpl_OnLoad, + ITrayBandSiteImpl_OnSave, + /*** ITrayBandSite methods ***/ + ITrayBandSiteImpl_IsTaskBand, + ITrayBandSiteImpl_ProcessMessage, + ITrayBandSiteImpl_AddContextMenus, + ITrayBandSiteImpl_Lock +}; + +/*******************************************************************/ + +METHOD_IUNKNOWN_INHERITED_ADDREF(IBandSite, ITrayBandSite) +METHOD_IUNKNOWN_INHERITED_RELEASE(IBandSite, ITrayBandSite) +METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE(IBandSite, ITrayBandSite) + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_AddBand(IN OUT IBandSite *iface, + IN IUnknown *punk) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + IOleCommandTarget *pOct; + HRESULT hRet; + + hRet = IUnknown_QueryInterface(punk, + &IID_IOleCommandTarget, + (PVOID*)&pOct); + if (SUCCEEDED(hRet)) + { + /* Send the DBID_DELAYINIT command to initialize the band to be added */ + /* FIXME: Should be delayed */ + IOleCommandTarget_Exec(pOct, + &IID_IDeskBand, + DBID_DELAYINIT, + 0, + NULL, + NULL); + + IOleCommandTarget_Release(pOct); + } + + return IBandSite_AddBand(This->BandSite, + punk); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_EnumBands(IN OUT IBandSite *iface, + IN UINT uBand, + OUT DWORD *pdwBandID) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_EnumBands(This->BandSite, + uBand, + pdwBandID); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_QueryBand(IN OUT IBandSite *iface, + IN DWORD dwBandID, + OUT IDeskBand **ppstb, + OUT DWORD *pdwState, + OUT LPWSTR pszName, + IN int cchName) +{ + HRESULT hRet; + IDeskBand *pstb = NULL; + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + + hRet = IBandSite_QueryBand(This->BandSite, + dwBandID, + &pstb, + pdwState, + pszName, + cchName); + + if (SUCCEEDED(hRet)) + { + hRet = IsSameObject((IUnknown *)pstb, + (IUnknown *)This->TaskBand); + if (hRet == S_OK) + { + /* Add the BSSF_UNDELETEABLE flag to pdwState because the task bar band shouldn't be deletable */ + if (pdwState != NULL) + *pdwState |= BSSF_UNDELETEABLE; + } + else if (!SUCCEEDED(hRet)) + { + IDeskBand_Release(pstb); + pstb = NULL; + } + + if (ppstb != NULL) + *ppstb = pstb; + } + + return hRet; +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_SetBandState(IN OUT IBandSite *iface, + IN DWORD dwBandID, + IN DWORD dwMask, + IN DWORD dwState) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_SetBandState(This->BandSite, + dwBandID, + dwMask, + dwState); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_RemoveBand(IN OUT IBandSite *iface, + IN DWORD dwBandID) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_RemoveBand(This->BandSite, + dwBandID); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_GetBandObject(IN OUT IBandSite *iface, + IN DWORD dwBandID, + IN REFIID riid, + OUT VOID **ppv) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_GetBandObject(This->BandSite, + dwBandID, + riid, + ppv); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_SetBandSiteInfo(IN OUT IBandSite *iface, + IN const BANDSITEINFO *pbsinfo) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_SetBandSiteInfo(This->BandSite, + pbsinfo); +} + +static HRESULT STDMETHODCALLTYPE +ITrayBandSiteImpl_GetBandSiteInfo(IN OUT IBandSite *iface, + IN OUT BANDSITEINFO *pbsinfo) +{ + ITrayBandSiteImpl *This = ITrayBandSiteImpl_from_IBandSite(iface); + return IBandSite_GetBandSiteInfo(This->BandSite, + pbsinfo); +} + +static const IBandSiteVtbl IBandSiteImpl_Vtbl = +{ + /*** IUnknown methods ***/ + METHOD_IUNKNOWN_INHERITED_QUERYINTERFACE_NAME(IBandSite, ITrayBandSite), + METHOD_IUNKNOWN_INHERITED_ADDREF_NAME(IBandSite, ITrayBandSite), + METHOD_IUNKNOWN_INHERITED_RELEASE_NAME(IBandSite, ITrayBandSite), + /*** IBandSite methods ***/ + ITrayBandSiteImpl_AddBand, + ITrayBandSiteImpl_EnumBands, + ITrayBandSiteImpl_QueryBand, + ITrayBandSiteImpl_SetBandState, + ITrayBandSiteImpl_RemoveBand, + ITrayBandSiteImpl_GetBandObject, + ITrayBandSiteImpl_SetBandSiteInfo, + ITrayBandSiteImpl_GetBandSiteInfo, +}; + +static BOOL +ITrayBandSiteImpl_HasTaskBand(IN OUT ITrayBandSiteImpl *This) +{ + ASSERT(This->TaskBand != NULL); + + return SUCCEEDED(ITaskBand_GetRebarBandID(This->TaskBand, + NULL)); +} + +static HRESULT +ITrayBandSiteImpl_AddTaskBand(IN OUT ITrayBandSiteImpl *This) +{ +#if 0 + /* FIXME: This is the code for the simple taskbar */ + IObjectWithSite *pOws; + HRESULT hRet; + + hRet = ITaskBand_QueryInterface(This->TaskBand, + &IID_IObjectWithSite, + (PVOID*)&pOws); + if (SUCCEEDED(hRet)) + { + hRet = IObjectWithSite_SetSite(pOws, + (IUnknown *)This->TaskBand); + + IObjectWithSite_Release(pOws); + } + + return hRet; +#else + if (!ITrayBandSiteImpl_HasTaskBand(This)) + { + return IBandSite_AddBand(This->BandSite, + (IUnknown *)This->TaskBand); + } + + return S_OK; +#endif +} + +static HRESULT +ITrayBandSiteImpl_Update(IN OUT ITrayBandSiteImpl *This) +{ + IOleCommandTarget *pOct; + HRESULT hRet; + + hRet = IUnknown_QueryInterface(This->punkInner, + &IID_IOleCommandTarget, + (PVOID*)&pOct); + if (SUCCEEDED(hRet)) + { + /* Send the DBID_BANDINFOCHANGED command to update the band site */ + hRet = IOleCommandTarget_Exec(pOct, + &IID_IDeskBand, + DBID_BANDINFOCHANGED, + 0, + NULL, + NULL); + + IOleCommandTarget_Release(pOct); + } + + return hRet; +} + +static VOID +ITrayBandSiteImpl_BroadcastOleCommandExec(IN OUT ITrayBandSiteImpl *This, + const GUID *pguidCmdGroup, + DWORD nCmdID, + DWORD nCmdExecOpt, + VARIANTARG *pvaIn, + VARIANTARG *pvaOut) +{ + IOleCommandTarget *pOct; + DWORD dwBandID; + UINT uBand = 0; + + /* Enumerate all bands */ + while (SUCCEEDED(IBandSite_EnumBands(This->BandSite, + uBand, + &dwBandID))) + { + if (SUCCEEDED(IBandSite_GetBandObject(This->BandSite, + uBand, + &IID_IOleCommandTarget, + (PVOID*)&pOct))) + { + /* Execute the command */ + IOleCommandTarget_Exec(pOct, + pguidCmdGroup, + nCmdID, + nCmdExecOpt, + pvaIn, + pvaOut); + + IOleCommandTarget_Release(pOct); + } + + uBand++; + } +} + +static HRESULT +ITrayBandSiteImpl_FinishInit(IN OUT ITrayBandSiteImpl *This) +{ + /* Broadcast the DBID_FINISHINIT command */ + ITrayBandSiteImpl_BroadcastOleCommandExec(This, + &IID_IDeskBand, + DBID_FINISHINIT, + 0, + NULL, + NULL); + + return S_OK; +} + +static HRESULT +ITrayBandSiteImpl_Show(IN OUT ITrayBandSiteImpl *This, + IN BOOL bShow) +{ + IDeskBarClient *pDbc; + HRESULT hRet; + + hRet = IBandSite_QueryInterface(This->BandSite, + &IID_IDeskBarClient, + (PVOID*)&pDbc); + if (SUCCEEDED(hRet)) + { + hRet = IDeskBarClient_UIActivateDBC(pDbc, + bShow ? DBC_SHOW : DBC_HIDE); + IDeskBarClient_Release(pDbc); + } + + return hRet; +} + +static HRESULT +ITrayBandSiteImpl_LoadFromStream(IN OUT ITrayBandSiteImpl *This, + IN OUT IStream *pStm) +{ + IPersistStream *pPStm; + HRESULT hRet; + + ASSERT(This->BandSite != NULL); + + /* We implement the undocumented COM interface IBandSiteStreamCallback + that the shell will query so that we can intercept and custom-load + the task band when it finds the task band's CLSID (which is internal). + This way we can prevent the shell from attempting to CoCreateInstance + the (internal) task band, resulting in a failure... */ + hRet = IBandSite_QueryInterface(This->BandSite, + &IID_IPersistStream, + (PVOID*)&pPStm); + if (SUCCEEDED(hRet)) + { + hRet = IPersistStream_Load(pPStm, + pStm); + DbgPrint("IPersistStream_Load() returned 0x%x\n", hRet); + IPersistStream_Release(pPStm); + } + + return hRet; +} + +static IStream * +GetUserBandsStream(IN DWORD grfMode) +{ + HKEY hkStreams; + IStream *Stream = NULL; + + if (RegCreateKey(hkExplorer, + TEXT("Streams"), + &hkStreams) == ERROR_SUCCESS) + { + Stream = SHOpenRegStream(hkStreams, + TEXT("Desktop"), + TEXT("TaskbarWinXP"), + grfMode); + + RegCloseKey(hkStreams); + } + + return Stream; +} + +static IStream * +GetDefaultBandsStream(IN DWORD grfMode) +{ + HKEY hkStreams; + IStream *Stream = NULL; + + if (RegCreateKey(HKEY_LOCAL_MACHINE, + TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams"), + &hkStreams) == ERROR_SUCCESS) + { + Stream = SHOpenRegStream(hkStreams, + TEXT("Desktop"), + TEXT("Default Taskbar"), + grfMode); + + RegCloseKey(hkStreams); + } + + return Stream; +} + +static HRESULT +ITrayBandSiteImpl_Load(IN OUT ITrayBandSiteImpl *This) +{ + IStream *pStm; + HRESULT hRet; + + /* Try to load the user's settings */ + pStm = GetUserBandsStream(STGM_READ); + if (pStm != NULL) + { + hRet = ITrayBandSiteImpl_LoadFromStream(This, + pStm); + + DbgPrint("Loaded user bands settings: 0x%x\n", hRet); + IStream_Release(pStm); + } + else + hRet = E_FAIL; + + /* If the user's settings couldn't be loaded, try with + default settings (ie. when the user logs in for the + first time! */ + if (!SUCCEEDED(hRet)) + { + pStm = GetDefaultBandsStream(STGM_READ); + if (pStm != NULL) + { + hRet = ITrayBandSiteImpl_LoadFromStream(This, + pStm); + + DbgPrint("Loaded default user bands settings: 0x%x\n", hRet); + IStream_Release(pStm); + } + else + hRet = E_FAIL; + } + + return hRet; +} + +static ITrayBandSiteImpl * +ITrayBandSiteImpl_Construct(IN OUT ITrayWindow *Tray, + OUT HWND *phWndRebar, + OUT HWND *phwndTaskSwitch) +{ + ITrayBandSiteImpl *This; + IDeskBarClient *pDbc; + IDeskBand *pDb; + IOleWindow *pOw; + HRESULT hRet; + + *phWndRebar = NULL; + *phwndTaskSwitch = NULL; + + This = HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return NULL; + + ZeroMemory(This, + sizeof(*This)); + This->lpVtbl = &ITrayBandSiteImpl_Vtbl; + This->lpBandSiteVtbl = &IBandSiteImpl_Vtbl; + This->Ref = 1; + This->Tray = Tray; + + /* Create a RebarBandSite provided by the shell */ + hRet = CoCreateInstance(&CLSID_RebarBandSite, + (LPUNKNOWN)IBandSite_from_ITrayBandSiteImpl(This), + CLSCTX_INPROC_SERVER, + &IID_IUnknown, + (LPVOID*)&This->punkInner); + if (!SUCCEEDED(hRet)) + { + ITrayBandSiteImpl_Free(This); + return NULL; + } + + hRet = IUnknown_QueryInterface(This->punkInner, + &IID_IBandSite, + (PVOID*)&This->BandSite); + if (!SUCCEEDED(hRet)) + { + ITrayBandSiteImpl_Free(This); + return NULL; + } + + hRet = IUnknown_QueryInterface(This->punkInner, + &IID_IWindowEventHandler, + (PVOID*)&This->WindowEventHandler); + if (!SUCCEEDED(hRet)) + { + ITrayBandSiteImpl_Free(This); + return NULL; + } + + This->TaskBand = CreateTaskBand(Tray); + if (This->TaskBand != NULL) + { + /* Add the task band to the site */ + hRet = IBandSite_QueryInterface(This->BandSite, + &IID_IDeskBarClient, + (PVOID*)&pDbc); + if (SUCCEEDED(hRet)) + { + hRet = ITaskBand_QueryInterface(This->TaskBand, + &IID_IOleWindow, + (PVOID*)&pOw); + if (SUCCEEDED(hRet)) + { + /* We cause IDeskBarClient to create the rebar control by passing the new + task band to it. The band reports the tray window handle as window handle + so that IDeskBarClient knows the parent window of the Rebar control that + it wants to create. */ + hRet = IDeskBarClient_SetDeskBarSite(pDbc, + (IUnknown *)pOw); + + if (SUCCEEDED(hRet)) + { + /* The Rebar control is now created, we can query the window handle */ + hRet = IDeskBarClient_GetWindow(pDbc, + &This->hWndRebar); + + if (SUCCEEDED(hRet)) + { + /* We need to manually remove the RBS_BANDBORDERS style! */ + SetWindowStyle(This->hWndRebar, + RBS_BANDBORDERS, + 0); + } + } + + IOleWindow_Release(pOw); + } + + if (SUCCEEDED(hRet)) + { + DWORD dwMode = 0; + + /* Set the Desk Bar mode to the current one */ + + /* FIXME: We need to set the mode (and update) whenever the user docks + the tray window to another monitor edge! */ + + if (!ITrayWindow_IsHorizontal(This->Tray)) + dwMode = DBIF_VIEWMODE_VERTICAL; + + hRet = IDeskBarClient_SetModeDBC(pDbc, + dwMode); + } + + IDeskBarClient_Release(pDbc); + } + + /* Load the saved state of the task band site */ + /* FIXME: We should delay loading shell extensions, also see DBID_DELAYINIT */ + ITrayBandSiteImpl_Load(This); + + /* Add the task bar band if it hasn't been added already */ + hRet = ITrayBandSiteImpl_AddTaskBand(This); + if (SUCCEEDED(hRet)) + { + hRet = ITaskBand_QueryInterface(This->TaskBand, + &IID_IDeskBand, + (PVOID*)&pDb); + if (SUCCEEDED(hRet)) + { + hRet = IDeskBand_GetWindow(pDb, + phwndTaskSwitch); + if (!SUCCEEDED(hRet)) + *phwndTaskSwitch = NULL; + + IDeskBand_Release(pDb); + } + } + + /* Should we send this after showing it? */ + ITrayBandSiteImpl_Update(This); + + /* FIXME: When should we send this? Does anyone care anyway? */ + ITrayBandSiteImpl_FinishInit(This); + + /* Activate the band site */ + ITrayBandSiteImpl_Show(This, + TRUE); + } + + *phWndRebar = This->hWndRebar; + + return This; +} + +/*******************************************************************/ + +ITrayBandSite * +CreateTrayBandSite(IN OUT ITrayWindow *Tray, + OUT HWND *phWndRebar, + OUT HWND *phWndTaskSwitch) +{ + ITrayBandSiteImpl *This; + + This = ITrayBandSiteImpl_Construct(Tray, + phWndRebar, + phWndTaskSwitch); + if (This != NULL) + { + return ITrayBandSite_from_ITrayBandSiteImpl(This); + } + + return NULL; +} diff --git a/reactos/base/shell/explorer-new/todo.h b/reactos/base/shell/explorer-new/todo.h new file mode 100644 index 00000000000..5f8b089009d --- /dev/null +++ b/reactos/base/shell/explorer-new/todo.h @@ -0,0 +1,578 @@ +#ifndef __TODO_H +#define __TODO_H + +/* + * Stuff missing in our headers + */ + +#define SM_REMOTECONTROL 0x2001 + +/* FIXME: Ugly hack!!! FIX ASAP! Move to uuid! */ +static const GUID IID_HACK_IShellView2 = {0x88E39E80,0x3578,0x11CF,{0xAE,0x69,0x08,0x00,0x2B,0x2E,0x12,0x62}}; +#define IID_IShellView2 IID_HACK_IShellView2 +static const GUID IID_HACK_IShellView3 = {0xEC39FA88,0xF8AF,0x41CF,{0x84,0x21,0x38,0xBE,0xD2,0x8F,0x46,0x73}}; +#define IID_IShellView3 IID_HACK_IShellView2 +static const GUID VID_HACK_LargeIcons = {0x0057D0E0, 0x3573, 0x11CF, {0xAE, 0x69, 0x08, 0x00, 0x2B, 0x2E, 0x12, 0x62}}; +#define VID_LargeIcons VID_HACK_LargeIcons + +static const GUID IID_HACK_IDeskBarClient = {0xEB0FE175, 0x1A3A, 0x11D0, {0x89, 0xB3, 0x00, 0xA0, 0xC9, 0x0A, 0x90, 0xAC}}; +#define IID_IDeskBarClient IID_HACK_IDeskBarClient +static const GUID IID_HACK_IDeskBar = {0xEB0FE173, 0x1A3A, 0x11D0, {0x89, 0xB3, 0x00, 0xA0, 0xC9, 0x0A, 0x90, 0xAC}}; +#define IID_IDeskBar IID_HACK_IDeskBar + +static const GUID IID_HACK_IMenuPopup = {0xD1E7AFEB,0x6A2E,0x11D0,{0x8C,0x78,0x00,0xC0,0x4F,0xD9,0x18,0xB4}}; +#define IID_IMenuPopup IID_HACK_IMenuPopup +static const GUID IID_HACK_IBanneredBar = {0x596A9A94,0x013E,0x11D1,{0x8D,0x34,0x00,0xA0,0xC9,0x0F,0x27,0x19}}; +#define IID_IBanneredBar IID_HACK_IBanneredBar + +static const GUID IID_HACK_IInitializeObject = {0x4622AD16,0xFF23,0x11D0,{0x8D,0x34,0x00,0xA0,0xC9,0x0F,0x27,0x19}}; +#define IID_IInitializeObject IID_HACK_IInitializeObject + +static const GUID SID_HACK_SMenuPopup = {0xD1E7AFEB,0x6A2E,0x11D0,{0x8C,0x78,0x00,0xC0,0x4F,0xD9,0x18,0xB4}}; +#define SID_SMenuPopup SID_HACK_SMenuPopup + +HANDLE WINAPI SHChangeNotification_Lock( + HANDLE hChange, + DWORD dwProcessId, + LPITEMIDLIST **lppidls, + LPLONG lpwEventId); +BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock); + +typedef struct +{ + HIMAGELIST himl; + RECT margin; + UINT uAlign; +} BUTTON_IMAGELIST, *PBUTTON_IMAGELIST; +#define BUTTON_IMAGELIST_ALIGN_LEFT 0 + +#ifndef BCM_FIRST +#define BCM_FIRST 0x1600 +#define BCM_GETIDEALSIZE (BCM_FIRST + 1) +#define BCM_SETIMAGELIST (BCM_FIRST + 2) +#endif /* BCM_FIRST */ + +DWORD WINAPI SHRestricted(RESTRICTIONS); +BOOL WINAPI SHFindFiles(LPCITEMIDLIST,LPCITEMIDLIST);/* FIXME: Parameters should be PCIDLIST_ABSOLUTE */ + +HMONITOR WINAPI MonitorFromRect(LPCRECT,DWORD); +HMONITOR WINAPI MonitorFromWindow(HWND,DWORD); +HMONITOR WINAPI MonitorFromPoint(POINT,DWORD); + +#define HSHELL_SYSMENU 9 +#define HSHELL_ENDTASK 10 +#define HSHELL_ACCESSIBILITYSTATE 11 +#define HSHELL_APPCOMMAND 12 +#define HSHELL_WINDOWREPLACED 13 +#define HSHELL_WINDOWREPLACING 14 +#define APPCOMMAND_BROWSER_SEARCH 5 +#define APPCOMMAND_BROWSER_HOME 7 +#define APPCOMMAND_LAUNCH_MAIL 15 + +#define HSHELL_HIGHBIT 0x8000 +#define HSHELL_FLASH (HSHELL_REDRAW|HSHELL_HIGHBIT) +#define HSHELL_RUDEAPPACTIVATED (HSHELL_WINDOWACTIVATED|HSHELL_HIGHBIT) + +#define FAPPCOMMAND_MOUSE 0x8000 +#define FAPPCOMMAND_KEY 0 +#define FAPPCOMMAND_OEM 0x1000 +#define FAPPCOMMAND_MASK 0xf000 + +#define GET_APPCOMMAND_LPARAM(lParam) ((SHORT)(HIWORD(lParam) & ~FAPPCOMMAND_MASK)) +#define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK)) +#define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM +#define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam)) +#define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam) + +#define TBSTATE_ELLIPSES 0x40 + +#define TPM_VERPOSANIMATION 0x1000 +#define TPM_VERNEGANIMATION 0x2000 + +BOOL WINAPI IsUserAnAdmin(VOID); + +#define INTERFACE IDockingWindow +DECLARE_INTERFACE_(IDockingWindow,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IDockingWindow methods ***/ + STDMETHOD_(HRESULT,ShowDW)(THIS_ BOOL) PURE; + STDMETHOD_(HRESULT,CloseDW)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,ResizeBoderDW)(THIS_ LPCRECT,IUnknown*,BOOL) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IDockingWindow_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IDockingWindow_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IDockingWindow_Release(T) (T)->lpVtbl->Release(T) +#define IDockingWindow_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IDockingWindow_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IDockingWindow_ShowDW(T,a) (T)->lpVtbl->ShowDW(T,a) +#define IDockingWindow_CloseDW(T,a) (T)->lpVtbl->CloseDW(T,a) +#define IDockingWindow_ResizeBorderDW(T,a,b,c) (T)->lpVtbl->ResizeBorderDW(T,a,b,c) +#endif + +#define INTERFACE IDeskBarClient +DECLARE_INTERFACE_(IDeskBarClient,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IDeskBarClient methods ***/ + STDMETHOD_(HRESULT,SetDeskBarSite)(THIS_ IUnknown*) PURE; + STDMETHOD_(HRESULT,SetModeDBC)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,UIActivateDBC)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,GetSize)(THIS_ DWORD,LPRECT) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IDeskBarClient_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IDeskBarClient_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IDeskBarClient_Release(T) (T)->lpVtbl->Release(T) +#define IDeskBarClient_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IDeskBarClient_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IDeskBarClient_SetDeskBarSite(T,a) (T)->lpVtbl->SetDeskBarSite(T,a) +#define IDeskBarClient_SetModeDBC(T,a) (T)->lpVtbl->SetModeDBC(T,a) +#define IDeskBarClient_UIActivateDBC(T,a) (T)->lpVtbl->UIActivateDBC(T,a) +#define IDeskBarClient_GetSize(T,a,b) (T)->lpVtbl->GetSize(T,a,b) +#endif + +#define DBIM_TITLE 0x10 +#define DBIMF_VARIABLEHEIGHT 0x8 +#define DBIMF_DEBOSSED 0x20 +#define DBIF_VIEWMODE_VERTICAL 0x1 + +#include +typedef struct tagDESKBANDINFO +{ + DWORD dwMask; + POINTL ptMinSize; + POINTL ptMaxSize; + POINTL ptIntegral; + POINTL ptActual; + WCHAR wszTitle[256]; + DWORD dwModeFlags; + COLORREF crBkgnd; +} DESKBANDINFO; +#include + +#define INTERFACE IDeskBand +DECLARE_INTERFACE_(IDeskBand,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IDockingWindow methods ***/ + STDMETHOD_(HRESULT,ShowDW)(THIS_ BOOL) PURE; + STDMETHOD_(HRESULT,CloseDW)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,ResizeBoderDW)(THIS_ LPCRECT,IUnknown*,BOOL) PURE; + /*** IDeskBand methods ***/ + STDMETHOD_(HRESULT,GetBandInfo)(THIS_ DWORD,DWORD,DESKBANDINFO*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IDeskBand_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IDeskBand_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IDeskBand_Release(T) (T)->lpVtbl->Release(T) +#define IDeskBand_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IDeskBand_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IDeskBand_ShowDW(T,a) (T)->lpVtbl->ShowDW(T,a) +#define IDeskBand_CloseDW(T,a) (T)->lpVtbl->CloseDW(T,a) +#define IDeskBand_ResizeBorderDW(T,a,b,c) (T)->lpVtbl->ResizeBorderDW(T,a,b,c) +#define IDeskBand_GetBandInfo(T,a,b,c) (T)->lpVtbl->GetBandInfo(T,a,b,c) +#endif + +#define INTERFACE IDeskBar +DECLARE_INTERFACE_(IDeskBar,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IDeskBar methods ***/ + STDMETHOD_(HRESULT,SetClient)(THIS_ IUnknown*) PURE; + STDMETHOD_(HRESULT,GetClient)(THIS_ IUnknown**) PURE; + STDMETHOD_(HRESULT,OnPosRectChangeDB)(THIS_ RECT*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IDeskBar_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IDeskBar_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IDeskBar_Release(T) (T)->lpVtbl->Release(T) +#define IDeskBar_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IDeskBar_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IDeskBar_SetClient(T,a) (T)->lpVtbl->SetClient(T,a) +#define IDeskBar_GetClient(T,a) (T)->lpVtbl->GetClient(T,a) +#define IDeskBar_OnPosRectChangeDB(T,a) (T)->lpVtbl->OnPosRectChangeDB(T,a) +#endif + +#include +typedef struct +{ + DWORD dwMask; + DWORD dwState; + DWORD dwStyle; +} BANDSITEINFO; +#include + +#define BSIM_STATE 0x1 +#define BSIM_STYLE 0x2 + +#define BSIS_AUTOGRIPPER 0x0 +#define BSIS_NOGRIPPER 0x1 +#define BSIS_ALWAYSGRIPPER 0x2 +#define BSIS_LOCKED 0x100 + +#define BSSF_UNDELETEABLE 0x1000 + +DEFINE_GUID(IID_IBandSite, 0x4CF504B0, 0xDE96, 0x11D0, 0x8B, 0x3F, 0x00, 0xA0, 0xC9, 0x11, 0xE8, 0xE5); + +#define INTERFACE IBandSite +DECLARE_INTERFACE_(IBandSite,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface) (THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IBandSite methods ***/ + STDMETHOD_(HRESULT,AddBand) (THIS_ IUnknown *punk) PURE; + STDMETHOD_(HRESULT,EnumBands) (THIS_ UINT uBand, DWORD *pdwBandID) PURE; + STDMETHOD_(HRESULT,QueryBand) (THIS_ DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName) PURE; + STDMETHOD_(HRESULT,SetBandState) (THIS_ DWORD dwBandID, DWORD dwMask, DWORD dwState) PURE; + STDMETHOD_(HRESULT,RemoveBand) (THIS_ DWORD dwBandID) PURE; + STDMETHOD_(HRESULT,GetBandObject) (THIS_ DWORD dwBandID, REFIID riid, VOID **ppv) PURE; + STDMETHOD_(HRESULT,SetBandSiteInfo) (THIS_ const BANDSITEINFO *pbsinfo) PURE; + STDMETHOD_(HRESULT,GetBandSiteInfo) (THIS_ BANDSITEINFO *pbsinfo) PURE; +}; +#undef INTERFACE + +#if defined(COBJMACROS) +/*** IUnknown methods ***/ +#define IBandSite_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IBandSite_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IBandSite_Release(p) (p)->lpVtbl->Release(p) +/*** IBandSite methods ***/ +#define IBandSite_AddBand(p,a) (p)->lpVtbl->AddBand(p,a) +#define IBandSite_EnumBands(p,a,b) (p)->lpVtbl->EnumBands(p,a,b) +#define IBandSite_QueryBand(p,a,b,c,d,e) (p)->lpVtbl->QueryBand(p,a,b,c,d,e) +#define IBandSite_SetBandState(p,a,b,c) (p)->lpVtbl->SetBandState(p,a,b,c) +#define IBandSite_RemoveBand(p,a) (p)->lpVtbl->RemoveBand(p,a) +#define IBandSite_GetBandObject(p,a,b,c) (p)->lpVtbl->GetBandObject(p,a,b,c) +#define IBandSite_SetBandSiteInfo(p,a) (p)->lpVtbl->SetBandSiteInfo(p,a) +#define IBandSite_GetBandSiteInfo(p,a) (p)->lpVtbl->GetBandSiteInfo(p,a) +#endif + +#include +typedef struct _SV2CVW2_PARAMS +{ + DWORD cbSize; + IShellView *psvPrev; + LPCFOLDERSETTINGS pfs; + IShellBrowser *psbOwner; + RECT *prcView; + SHELLVIEWID const *pvid; + HWND hwndView; +} SV2CVW2_PARAMS, *LPSV2CVW2_PARAMS; +#include + +typedef ITEMIDLIST ITEMID_CHILD; +#define PITEMID_CHILD LPITEMIDLIST +typedef const ITEMID_CHILD /* __unaligned */ *PCUITEMID_CHILD; + +enum tagSV3CVW3 +{ + SV3CVW3_DEFAULT = 0x0, + SV3CVW3_NONINTERACTIVE = 0x1, + SV3CVW3_FORCEVIEWMODE = 0x2, + SV3CVW3_FORCEFOLDERFLAGS = 0x4 +} ; +typedef DWORD SV3CVW3_FLAGS; +#define INTERFACE IShellView3 +DECLARE_INTERFACE_(IShellView3,IShellView) +{ + + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(GetWindow)(THIS_ HWND*) PURE; + STDMETHOD(ContextSensitiveHelp)(THIS_ BOOL) PURE; + STDMETHOD(TranslateAccelerator) (THIS_ LPMSG) PURE; +#ifdef _FIX_ENABLEMODELESS_CONFLICT + STDMETHOD(EnableModelessSV)(THIS_ BOOL) PURE; +#else + STDMETHOD(EnableModeless)(THIS_ BOOL) PURE; +#endif + STDMETHOD(UIActivate)(THIS_ UINT) PURE; + STDMETHOD(Refresh) (THIS) PURE; + STDMETHOD(CreateViewWindow)(THIS_ IShellView*,LPCFOLDERSETTINGS,LPSHELLBROWSER,RECT*,HWND*) PURE; + STDMETHOD(DestroyViewWindow)(THIS) PURE; + STDMETHOD(GetCurrentInfo)(THIS_ LPFOLDERSETTINGS) PURE; + STDMETHOD(AddPropertySheetPages)(THIS_ DWORD,LPFNADDPROPSHEETPAGE,LPARAM) PURE; + STDMETHOD(SaveViewState)(THIS) PURE; + STDMETHOD(SelectItem)(THIS_ LPCITEMIDLIST,UINT) PURE; + STDMETHOD(GetItemObject)(THIS_ UINT,REFIID,PVOID*) PURE; + STDMETHOD(GetView)(THIS_ SHELLVIEWID*,ULONG) PURE; + STDMETHOD(CreateViewWindow2)(THIS_ LPSV2CVW2_PARAMS) PURE; + STDMETHOD(HandleRename)(THIS_ PCUITEMID_CHILD) PURE; + STDMETHOD(SelectAndPositionItem)(THIS_ PCUITEMID_CHILD,UINT,POINT*) PURE; + STDMETHOD(CreateViewWindow3)(THIS_ IShellBrowser*,IShellView*,SV3CVW3_FLAGS,FOLDERFLAGS,FOLDERFLAGS,FOLDERVIEWMODE,const SHELLVIEWID*,const RECT*,HWND*) PURE; +}; +#undef INTERFACE +#ifdef COBJMACROS +#define IShellView3_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IShellView3_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IShellView3_Release(T) (T)->lpVtbl->Release(T) +#define IShellView3_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IShellView3_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IShellView3_TranslateAccelerator(T,a) (T)->lpVtbl->TranslateAccelerator(T,a) +#ifdef _FIX_ENABLEMODELESS_CONFLICT +#define IShellView3_EnableModeless(T,a) (T)->lpVtbl->EnableModelessSV(T,a) +#else +#define IShellView3_EnableModeless(T,a) (T)->lpVtbl->EnableModeless(T,a) +#endif +#define IShellView3_UIActivate(T,a) (T)->lpVtbl->UIActivate(T,a) +#define IShellView3_Refresh(T) (T)->lpVtbl->Refresh(T) +#define IShellView3_CreateViewWindow(T,a,b,c,d,e) (T)->lpVtbl->CreateViewWindow(T,a,b,c,d,e) +#define IShellView3_DestroyViewWindow(T) (T)->lpVtbl->DestroyViewWindow(T) +#define IShellView3_GetCurrentInfo(T,a) (T)->lpVtbl->GetCurrentInfo(T,a) +#define IShellView3_AddPropertySheetPages(T,a,b,c) (T)->lpVtbl->AddPropertySheetPages(T,a,b,c) +#define IShellView3_SaveViewState(T) (T)->lpVtbl->SaveViewState(T) +#define IShellView3_SelectItem(T,a,b) (T)->lpVtbl->SelectItem(T,a,b) +#define IShellView3_GetItemObject(T,a,b,c) (T)->lpVtbl->GetItemObject(T,a,b,c) +#define IShellView3_GetView(T,a,b) (T)->lpVtbl->GetView(T,a,b) +#define IShellView3_CreateViewWindow2(T,a) (T)->lpVtbl->CreateViewWindow2(T,a) +#define IShellView3_HandleRename(T,a) (T)->lpVtbl->HandleRename(T,a) +#define IShellView3_SelectAndPositionItem(T,a,b,c) (T)->lpVtbl->SelectAndPositionItem(T,a,b,c) +#define IShellView3_CreateViewWindow3(T,a,b,c,d,e,f,g,h,i) (T)->lpVtbl->CreateViewWindow3(T,a,b,c,d,e,f,g,h,i) +#endif + +#define SHGVSPB_PERUSER 0x1 +#define SHGVSPB_PERFOLDER 0x4 +#define SHGVSPB_ROAM 0x00000020 +#define SHGVSPB_NOAUTODEFAULTS 0x80000000 +#define SHGVSPB_FOLDER (SHGVSPB_PERUSER | SHGVSPB_PERFOLDER) +#define SHGVSPB_FOLDERNODEFAULTS (SHGVSPB_PERUSER | SHGVSPB_PERFOLDER | SHGVSPB_NOAUTODEFAULTS) + + +/* + * DeskBand Command IDs + */ +enum tagDESKBANDCID +{ + DBID_BANDINFOCHANGED = 0, + DBID_SHOWONLY, + DBID_MAXIMIZEBAND, + DBID_PUSHCHEVRON, + DBID_DELAYINIT, + DBID_FINISHINIT, + DBID_SETWINDOWTHEME, + DBID_PERMITAUTOHIDE +}; + +#define DBC_SHOW 1 +#define DBC_HIDE 0 + +static const GUID IID_HACK_IShellService = {0x5836FB00,0x8187,0x11CF,{0xA1,0x2B,0x00,0xAA,0x00,0x4A,0xE8,0x37}}; +#define IID_IShellService IID_HACK_IShellService + +#define INTERFACE IShellService +DECLARE_INTERFACE_(IShellService,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IShellService methods ***/ + STDMETHOD_(HRESULT,SetOwner)(THIS_ IUnknown*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IShellService_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IShellService_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IShellService_Release(T) (T)->lpVtbl->Release(T) +#define IShellService_SetOwner(T,a) (T)->lpVtbl->SetOwner(T,a) +#endif + +#if 0 +HRESULT WINAPI SHGetViewStatePropertyBag(LPCITEMIDLIST,LPCWSTR,DWORD,REFIID,PVOID*);/* FIXME: Parameter should be PCIDLIST_ABSOLUTE */ +#else +typedef HRESULT (WINAPI *PSHGetViewStatePropertyBag)(LPCITEMIDLIST,LPCWSTR,DWORD,REFIID,PVOID*); +static HRESULT __inline +SHGetViewStatePropertyBag(IN LPCITEMIDLIST pidl, + IN LPCWSTR pszBagName, + IN DWORD dwFlags, + IN REFIID riid, + OUT PVOID* ppv) +{ + static PSHGetViewStatePropertyBag Func = NULL; + + if (Func == NULL) + { + HMODULE hShlwapi; + hShlwapi = LoadLibrary(TEXT("SHLWAPI.DLL")); + if (hShlwapi != NULL) + { + Func = (PSHGetViewStatePropertyBag)GetProcAddress(hShlwapi, "SHGetViewStatePropertyBag"); + } + } + + if (Func != NULL) + { + return Func(pidl, pszBagName, dwFlags, riid, ppv); + } + + MessageBox(NULL, TEXT("SHGetViewStatePropertyBag not available"), NULL, 0); + return E_NOTIMPL; +} +#endif + +#define PIDLIST_ABSOLUTE LPITEMIDLIST +PIDLIST_ABSOLUTE WINAPI SHCloneSpecialIDList(HWND hwnd, int csidl, BOOL fCreate); + +enum +{ + BMICON_LARGE = 0, + BMICON_SMALL +}; +#define INTERFACE IBanneredBar +DECLARE_INTERFACE_(IBanneredBar,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IBanneredBar methods ***/ + STDMETHOD_(HRESULT,SetIconSize)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,GetIconSize)(THIS_ DWORD*) PURE; + STDMETHOD_(HRESULT,SetBitmap)(THIS_ HBITMAP) PURE; + STDMETHOD_(HRESULT,GetBitmap)(THIS_ HBITMAP*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IBanneredBar_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IBanneredBar_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IBanneredBar_Release(T) (T)->lpVtbl->Release(T) +#define IBanneredBar_SetIconSize(T,a) (T)->lpVtbl->SetIconSize(T,a) +#define IBanneredBar_GetIconSize(T,a) (T)->lpVtbl->GetIconSize(T,a) +#define IBanneredBar_SetBitmap(T,a) (T)->lpVtbl->SetBitmap(T,a) +#define IBanneredBar_GetBitmap(T,a) (T)->lpVtbl->GetBitmap(T,a) +#endif + +enum tagMENUPOPUPPOPUPFLAGS +{ + MPPF_SETFOCUS = 0x1, + MPPF_INITIALSELECT = 0x2, + MPPF_NOANIMATE = 0x4, + MPPF_KEYBOARD = 0x10, + MPPF_REPOSITION = 0x20, + MPPF_FORCEZORDER = 0x40, + MPPF_FINALSELECT = 0x80, + MPPF_TOP = 0x20000000, + MPPF_LEFT = 0x40000000, + MPPF_RIGHT = 0x60000000, + MPPF_BOTTOM = 0x80000000, + MPPF_POS_MASK = 0xE0000000, + MPPF_ALIGN_LEFT = 0x2000000, + MPPF_ALIGN_RIGHT = 0x4000000, +}; +typedef int MP_POPUPFLAGS; + +#define INTERFACE IMenuPopup +DECLARE_INTERFACE_(IMenuPopup,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IDeskBar methods ***/ + STDMETHOD_(HRESULT,SetClient)(THIS_ IUnknown*) PURE; + STDMETHOD_(HRESULT,GetClient)(THIS_ IUnknown**) PURE; + STDMETHOD_(HRESULT,OnPosRectChangeDB)(THIS_ RECT*) PURE; + /*** IMenuPopup methods ***/ + STDMETHOD_(HRESULT,Popup)(THIS_ POINTL*,RECTL*,MP_POPUPFLAGS) PURE; + STDMETHOD_(HRESULT,OnSelect)(THIS_ DWORD) PURE; + STDMETHOD_(HRESULT,SetSubMenu)(THIS_ IMenuPopup*,BOOL) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IMenuPopup_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IMenuPopup_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IMenuPopup_Release(T) (T)->lpVtbl->Release(T) +#define IMenuPopup_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IMenuPopup_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IMenuPopup_SetClient(T,a) (T)->lpVtbl->SetClient(T,a) +#define IMenuPopup_GetClient(T,a) (T)->lpVtbl->GetClient(T,a) +#define IMenuPopup_OnPosRectChangeDB(T,a) (T)->lpVtbl->OnPosRectChangeDB(T,a) +#define IMenuPopup_Popup(T,a,b,c) (T)->lpVtbl->Popup(T,a,b,c) +#define IMenuPopup_OnSelect(T,a) (T)->lpVtbl->OnSelect(T,a) +#define IMenuPopup_SetSubMenu(T,a,b) (T)->lpVtbl->SetSubMenu(T,a,b) +#endif + +#define INTERFACE IMenuBand +DECLARE_INTERFACE_(IMenuBand,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IMenuBand methods ***/ + STDMETHOD_(HRESULT,IsMenuMessage)(THIS_ MSG*) PURE; + STDMETHOD_(HRESULT,TranslateMenuMessage)(THIS_ MSG*,LRESULT*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IMenuBand_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IMenuBand_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IMenuBand_Release(T) (T)->lpVtbl->Release(T) +#define IMenuBand_IsMenuMessage(T,a) (T)->lpVtbl->IsMenuMessage(T,a) +#define IMenuBand_TranslateMenuMessage(T,a,b) (T)->lpVtbl->TranslateMenuMessage(T,a,b) +#endif + +#define INTERFACE IInitializeObject +DECLARE_INTERFACE_(IInitializeObject,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IInitializeObject methods ***/ + STDMETHOD_(HRESULT,Initialize)(THIS) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IInitializeObject_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IInitializeObject_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IInitializeObject_Release(T) (T)->lpVtbl->Release(T) +#define IInitializeObject_Initialize(T) (T)->lpVtbl->Initialize(T) +#endif + +#endif /* __TODO_H */ diff --git a/reactos/base/shell/explorer-new/trayntfy.c b/reactos/base/shell/explorer-new/trayntfy.c new file mode 100644 index 00000000000..f9391cbc317 --- /dev/null +++ b/reactos/base/shell/explorer-new/trayntfy.c @@ -0,0 +1,1063 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* + * TrayClockWnd + */ + +static const TCHAR szTrayClockWndClass[] = TEXT("TrayClockWClass"); + +#define ID_TRAYCLOCK_TIMER 0 +#define ID_TRAYCLOCK_TIMER_INIT 1 + +static const struct +{ + BOOL IsTime; + DWORD dwFormatFlags; + LPCTSTR lpFormat; +} ClockWndFormats[] = { + {TRUE, TIME_NOSECONDS, NULL}, + {FALSE, 0, TEXT("dddd")}, + {FALSE, DATE_SHORTDATE, NULL}, +}; + +#define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0])) + +#define TRAY_CLOCK_WND_SPACING_X 0 +#define TRAY_CLOCK_WND_SPACING_Y 0 + +typedef struct _TRAY_CLOCK_WND_DATA +{ + HWND hWnd; + HWND hWndNotify; + HFONT hFont; + RECT rcText; + SYSTEMTIME LocalTime; + union + { + DWORD dwFlags; + struct + { + DWORD IsTimerEnabled : 1; + DWORD IsInitTimerEnabled : 1; + DWORD LinesMeasured : 1; + DWORD IsHorizontal : 1; + }; + }; + DWORD LineSpacing; + SIZE CurrentSize; + WORD VisibleLines; + SIZE LineSizes[CLOCKWND_FORMAT_COUNT]; + TCHAR szLines[CLOCKWND_FORMAT_COUNT][48]; +} TRAY_CLOCK_WND_DATA, *PTRAY_CLOCK_WND_DATA; + +static BOOL +TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + HDC hDC; + HFONT hPrevFont; + INT c, i; + BOOL bRet = TRUE; + + hDC = GetDC(This->hWnd); + if (hDC != NULL) + { + hPrevFont = SelectObject(hDC, + This->hFont); + + for (i = 0; + i != CLOCKWND_FORMAT_COUNT && bRet; + i++) + { + if (This->szLines[i][0] != TEXT('\0') && + !GetTextExtentPoint(hDC, + This->szLines[i], + _tcslen(This->szLines[i]), + &This->LineSizes[i])) + { + bRet = FALSE; + break; + } + } + + SelectObject(hDC, + hPrevFont); + + ReleaseDC(This->hWnd, + hDC); + + if (bRet) + { + This->LineSpacing = 0; + + /* calculate the line spacing */ + for (i = 0, c = 0; + i != CLOCKWND_FORMAT_COUNT; + i++) + { + if (This->LineSizes[i].cx > 0) + { + This->LineSpacing += This->LineSizes[i].cy; + c++; + } + } + + if (c > 0) + { + /* We want a spaceing of 1/2 line */ + This->LineSpacing = (This->LineSpacing / c) / 2; + } + + return TRUE; + } + } + + return FALSE; +} + +static WORD +TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This, + IN BOOL Horizontal, + IN OUT PSIZE pSize) +{ + WORD iLinesVisible = 0; + INT i; + SIZE szMax = {0}; + + if (!This->LinesMeasured) + This->LinesMeasured = TrayClockWnd_MeasureLines(This); + + if (!This->LinesMeasured) + return 0; + + for (i = 0; + i != CLOCKWND_FORMAT_COUNT; + i++) + { + if (This->LineSizes[i].cx != 0) + { + if (iLinesVisible > 0) + { + if (Horizontal) + { + if (szMax.cy + This->LineSizes[i].cy + This->LineSpacing > + pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y)) + { + break; + } + } + else + { + if (This->LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X)) + break; + } + + /* Add line spacing */ + szMax.cy += This->LineSpacing; + } + + iLinesVisible++; + + /* Increase maximum rectangle */ + szMax.cy += This->LineSizes[i].cy; + if (This->LineSizes[i].cx > szMax.cx - (2 * TRAY_CLOCK_WND_SPACING_X)) + szMax.cx = This->LineSizes[i].cx + (2 * TRAY_CLOCK_WND_SPACING_X); + } + } + + szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X; + szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y; + + *pSize = szMax; + + return iLinesVisible; +} + + +static VOID +TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + SIZE szPrevCurrent; + INT BufSize, iRet, i; + RECT rcClient; + + ZeroMemory(This->LineSizes, + sizeof(This->LineSizes)); + + szPrevCurrent = This->CurrentSize; + + for (i = 0; + i != CLOCKWND_FORMAT_COUNT; + i++) + { + This->szLines[i][0] = TEXT('\0'); + BufSize = sizeof(This->szLines[0]) / sizeof(This->szLines[0][0]); + + if (ClockWndFormats[i].IsTime) + { + iRet = GetTimeFormat(LOCALE_USER_DEFAULT, + ClockWndFormats[i].dwFormatFlags, + &This->LocalTime, + ClockWndFormats[i].lpFormat, + This->szLines[i], + BufSize); + } + else + { + iRet = GetDateFormat(LOCALE_USER_DEFAULT, + ClockWndFormats[i].dwFormatFlags, + &This->LocalTime, + ClockWndFormats[i].lpFormat, + This->szLines[i], + BufSize); + } + + if (iRet != 0 && i == 0) + { + /* Set the window text to the time only */ + SetWindowText(This->hWnd, + This->szLines[i]); + } + } + + This->LinesMeasured = TrayClockWnd_MeasureLines(This); + + if (This->LinesMeasured && + GetClientRect(This->hWnd, + &rcClient)) + { + SIZE szWnd; + + szWnd.cx = rcClient.right; + szWnd.cy = rcClient.bottom; + + This->VisibleLines = TrayClockWnd_GetMinimumSize(This, + This->IsHorizontal, + &szWnd); + This->CurrentSize = szWnd; + } + + if (IsWindowVisible(This->hWnd)) + { + InvalidateRect(This->hWnd, + NULL, + TRUE); + + if (This->hWndNotify != NULL && + (szPrevCurrent.cx != This->CurrentSize.cx || + szPrevCurrent.cy != This->CurrentSize.cy)) + { + NMHDR nmh; + + nmh.hwndFrom = This->hWnd; + nmh.idFrom = GetWindowLong(This->hWnd, + GWL_ID); + nmh.code = NTNWM_REALIGN; + + SendMessage(This->hWndNotify, + WM_NOTIFY, + (WPARAM)nmh.idFrom, + (LPARAM)&nmh); + } + } +} + +static VOID +TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + GetLocalTime(&This->LocalTime); + TrayClockWnd_UpdateWnd(This); +} + +static UINT +TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + UINT uiDueTime; + + /* Calculate the due time */ + GetLocalTime(&This->LocalTime); + uiDueTime = 1000 - (UINT)This->LocalTime.wMilliseconds; + uiDueTime += (59 - (UINT)This->LocalTime.wSecond) * 1000; + + if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM) + uiDueTime = 1000; + else + { + /* Add an artificial delay of 0.05 seconds to make sure the timer + doesn't fire too early*/ + uiDueTime += 50; + } + + return uiDueTime; +} + +static BOOL +TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + UINT uiDueTime; + BOOL Ret; + + /* Disable all timers */ + if (This->IsTimerEnabled) + { + KillTimer(This->hWnd, + ID_TRAYCLOCK_TIMER); + This->IsTimerEnabled = FALSE; + } + + if (This->IsInitTimerEnabled) + { + KillTimer(This->hWnd, + ID_TRAYCLOCK_TIMER_INIT); + } + + uiDueTime = TrayClockWnd_CalculateDueTime(This); + + /* Set the new timer */ + Ret = SetTimer(This->hWnd, + ID_TRAYCLOCK_TIMER_INIT, + uiDueTime, + NULL) != 0; + This->IsInitTimerEnabled = Ret; + + /* Update the time */ + TrayClockWnd_Update(This); + + return Ret; +} + +static VOID +TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + UINT uiDueTime; + BOOL Ret; + + /* Kill the initialization timer */ + KillTimer(This->hWnd, + ID_TRAYCLOCK_TIMER_INIT); + This->IsInitTimerEnabled = FALSE; + + uiDueTime = TrayClockWnd_CalculateDueTime(This); + + if (uiDueTime > (60 * 1000) - 200) + { + /* The update of the clock will be up to 200 ms late, but that's + acceptable. We're going to setup a timer that fires every + minute. */ + Ret = SetTimer(This->hWnd, + ID_TRAYCLOCK_TIMER, + 60 * 1000, + NULL) != 0; + This->IsTimerEnabled = Ret; + + /* Update the time */ + TrayClockWnd_Update(This); + } + else + { + /* Recalibrate the timer and recalculate again when the current + minute ends. */ + TrayClockWnd_ResetTime(This); + } +} + +static VOID +TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This) +{ + /* Disable all timers */ + if (This->IsTimerEnabled) + { + KillTimer(This->hWnd, + ID_TRAYCLOCK_TIMER); + } + + if (This->IsInitTimerEnabled) + { + KillTimer(This->hWnd, + ID_TRAYCLOCK_TIMER_INIT); + } + + /* Free allocated resources */ + SetWindowLongPtr(This->hWnd, + 0, + 0); + HeapFree(hProcessHeap, + 0, + This); +} + +static VOID +TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This, + IN HDC hDC) +{ + RECT rcClient; + HFONT hPrevFont; + int iPrevBkMode, i, line; + + if (This->LinesMeasured && + GetClientRect(This->hWnd, + &rcClient)) + { + iPrevBkMode = SetBkMode(hDC, + TRANSPARENT); + + hPrevFont = SelectObject(hDC, + This->hFont); + + rcClient.left = (rcClient.right / 2) - (This->CurrentSize.cx / 2); + rcClient.top = (rcClient.bottom / 2) - (This->CurrentSize.cy / 2); + rcClient.right = rcClient.left + This->CurrentSize.cx; + rcClient.bottom = rcClient.top + This->CurrentSize.cy; + + for (i = 0, line = 0; + i != CLOCKWND_FORMAT_COUNT && line < This->VisibleLines; + i++) + { + if (This->LineSizes[i].cx != 0) + { + TextOut(hDC, + rcClient.left + (This->CurrentSize.cx / 2) - (This->LineSizes[i].cx / 2) + + TRAY_CLOCK_WND_SPACING_X, + rcClient.top + TRAY_CLOCK_WND_SPACING_Y, + This->szLines[i], + _tcslen(This->szLines[i])); + + rcClient.top += This->LineSizes[i].cy + This->LineSpacing; + line++; + } + } + + SelectObject(hDC, + hPrevFont); + + SetBkMode(hDC, + iPrevBkMode); + } +} + +static VOID +TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This, + IN HFONT hNewFont, + IN BOOL bRedraw) +{ + This->hFont = hNewFont; + This->LinesMeasured = TrayClockWnd_MeasureLines(This); + if (bRedraw) + { + InvalidateRect(This->hWnd, + NULL, + TRUE); + } +} + +static LRESULT CALLBACK +TrayClockWndProc(IN HWND hwnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + PTRAY_CLOCK_WND_DATA This = NULL; + LRESULT Ret = FALSE; + + if (uMsg != WM_NCCREATE) + { + This = (PTRAY_CLOCK_WND_DATA)GetWindowLongPtr(hwnd, + 0); + } + + if (This != NULL || uMsg == WM_NCCREATE) + { + switch (uMsg) + { + case WM_PAINT: + case WM_PRINTCLIENT: + { + PAINTSTRUCT ps; + HDC hDC = (HDC)wParam; + + if (wParam == 0) + { + hDC = BeginPaint(This->hWnd, + &ps); + } + + if (hDC != NULL) + { + TrayClockWnd_Paint(This, + hDC); + + if (wParam == 0) + { + EndPaint(This->hWnd, + &ps); + } + } + break; + } + + case WM_TIMER: + switch (wParam) + { + case ID_TRAYCLOCK_TIMER: + TrayClockWnd_Update(This); + break; + + case ID_TRAYCLOCK_TIMER_INIT: + TrayClockWnd_CalibrateTimer(This); + break; + } + break; + + case WM_NCHITTEST: + /* We want the user to be able to drag the task bar when clicking the clock */ + Ret = HTTRANSPARENT; + break; + + case TCWM_GETMINIMUMSIZE: + { + This->IsHorizontal = (BOOL)wParam; + + Ret = (LRESULT)TrayClockWnd_GetMinimumSize(This, + (BOOL)wParam, + (PSIZE)lParam) != 0; + break; + } + + case TCWM_UPDATETIME: + { + Ret = (LRESULT)TrayClockWnd_ResetTime(This); + break; + } + + case WM_NCCREATE: + { + LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; + This = (PTRAY_CLOCK_WND_DATA)CreateStruct->lpCreateParams; + This->hWnd = hwnd; + This->hWndNotify = CreateStruct->hwndParent; + + SetWindowLongPtr(hwnd, + 0, + (LONG_PTR)This); + + return TRUE; + } + + case WM_SETFONT: + { + TrayClockWnd_SetFont(This, + (HFONT)wParam, + (BOOL)LOWORD(lParam)); + break; + } + + case WM_CREATE: + TrayClockWnd_ResetTime(This); + break; + + case WM_NCDESTROY: + TrayClockWnd_NCDestroy(This); + break; + + case WM_SIZE: + { + SIZE szClient; + + szClient.cx = LOWORD(lParam); + szClient.cy = HIWORD(lParam); + This->VisibleLines = TrayClockWnd_GetMinimumSize(This, + This->IsHorizontal, + &szClient); + This->CurrentSize = szClient; + + InvalidateRect(This->hWnd, + NULL, + TRUE); + break; + } + + default: + Ret = DefWindowProc(hwnd, + uMsg, + wParam, + lParam); + break; + } + } + + return Ret; +} + +static HWND +CreateTrayClockWnd(IN HWND hWndParent, + IN BOOL bVisible) +{ + PTRAY_CLOCK_WND_DATA TcData; + DWORD dwStyle; + HWND hWnd = NULL; + + TcData = HeapAlloc(hProcessHeap, + 0, + sizeof(*TcData)); + if (TcData != NULL) + { + ZeroMemory(TcData, + sizeof(*TcData)); + + TcData->IsHorizontal = TRUE; + /* Create the window. The tray window is going to move it to the correct + position and resize it as needed. */ + dwStyle = WS_CHILD | WS_CLIPSIBLINGS; + if (bVisible) + dwStyle |= WS_VISIBLE; + + hWnd = CreateWindowEx(0, + szTrayClockWndClass, + NULL, + dwStyle, + 0, + 0, + 0, + 0, + hWndParent, + NULL, + hExplorerInstance, + (LPVOID)TcData); + + if (hWnd == NULL) + { + HeapFree(hProcessHeap, + 0, + TcData); + } + } + + return hWnd; + +} + +static BOOL +RegisterTrayClockWndClass(VOID) +{ + WNDCLASS wcTrayClock; + + wcTrayClock.style = CS_DBLCLKS; + wcTrayClock.lpfnWndProc = TrayClockWndProc; + wcTrayClock.cbClsExtra = 0; + wcTrayClock.cbWndExtra = sizeof(PTRAY_CLOCK_WND_DATA); + wcTrayClock.hInstance = hExplorerInstance; + wcTrayClock.hIcon = NULL; + wcTrayClock.hCursor = LoadCursor(NULL, + IDC_ARROW); + wcTrayClock.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wcTrayClock.lpszMenuName = NULL; + wcTrayClock.lpszClassName = szTrayClockWndClass; + + return RegisterClass(&wcTrayClock) != 0; +} + +static VOID +UnregisterTrayClockWndClass(VOID) +{ + UnregisterClass(szTrayClockWndClass, + hExplorerInstance); +} + +/* + * TrayNotifyWnd + */ + +static const TCHAR szTrayNotifyWndClass[] = TEXT("TrayNotifyWnd"); + +#define TRAY_NOTIFY_WND_SPACING_X 2 +#define TRAY_NOTIFY_WND_SPACING_Y 2 + +typedef struct _TRAY_NOTIFY_WND_DATA +{ + HWND hWnd; + HWND hWndTrayClock; + HWND hWndNotify; + SIZE szTrayClockMin; + SIZE szNonClient; + ITrayWindow *TrayWindow; + union + { + DWORD dwFlags; + struct + { + DWORD HideClock : 1; + DWORD IsHorizontal : 1; + }; + }; +} TRAY_NOTIFY_WND_DATA, *PTRAY_NOTIFY_WND_DATA; + +static VOID +TrayNotifyWnd_UpdateStyle(IN OUT PTRAY_NOTIFY_WND_DATA This) +{ + RECT rcClient = {0}; + + if (AdjustWindowRectEx(&rcClient, + GetWindowLong(This->hWnd, + GWL_STYLE), + FALSE, + GetWindowLong(This->hWnd, + GWL_EXSTYLE))) + { + This->szNonClient.cx = rcClient.right - rcClient.left; + This->szNonClient.cy = rcClient.bottom - rcClient.top; + } + else + This->szNonClient.cx = This->szNonClient.cy = 0; +} + +static VOID +TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This) +{ + This->hWndTrayClock = CreateTrayClockWnd(This->hWnd, + !This->HideClock); + + TrayNotifyWnd_UpdateStyle(This); +} + +static VOID +TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This) +{ + SetWindowLongPtr(This->hWnd, + 0, + 0); + HeapFree(hProcessHeap, + 0, + This); +} + +static BOOL +TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This, + IN BOOL Horizontal, + IN OUT PSIZE pSize) +{ + This->IsHorizontal = Horizontal; + + if (!This->HideClock) + { + SIZE szClock = {0}; + + if (Horizontal) + { + szClock.cy = pSize->cy - This->szNonClient.cy - (2 * TRAY_NOTIFY_WND_SPACING_Y); + if (szClock.cy <= 0) + goto NoClock; + } + else + { + szClock.cx = pSize->cx - This->szNonClient.cx - (2 * TRAY_NOTIFY_WND_SPACING_X); + if (szClock.cx <= 0) + goto NoClock; + } + + SendMessage(This->hWndTrayClock, + TCWM_GETMINIMUMSIZE, + (WPARAM)Horizontal, + (LPARAM)&szClock); + + This->szTrayClockMin = szClock; + } + else +NoClock: + This->szTrayClockMin = This->szNonClient; + + if (Horizontal) + { + pSize->cx = This->szNonClient.cx + (2 * TRAY_NOTIFY_WND_SPACING_X); + + if (!This->HideClock) + pSize->cx += TRAY_NOTIFY_WND_SPACING_X + This->szTrayClockMin.cx; + } + else + { + pSize->cy = This->szNonClient.cy + (2 * TRAY_NOTIFY_WND_SPACING_Y); + + if (!This->HideClock) + pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + This->szTrayClockMin.cy; + } + + return TRUE; +} + +static VOID +TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This, + IN const SIZE *pszClient) +{ + if (!This->HideClock) + { + POINT ptClock; + SIZE szClock; + + if (This->IsHorizontal) + { + ptClock.x = pszClient->cx - TRAY_NOTIFY_WND_SPACING_X - This->szTrayClockMin.cx; + ptClock.y = TRAY_NOTIFY_WND_SPACING_Y; + szClock.cx = This->szTrayClockMin.cx; + szClock.cy = pszClient->cy - (2 * TRAY_NOTIFY_WND_SPACING_Y); + } + else + { + ptClock.x = TRAY_NOTIFY_WND_SPACING_X; + ptClock.y = pszClient->cy - TRAY_NOTIFY_WND_SPACING_Y - This->szTrayClockMin.cy; + szClock.cx = pszClient->cx - (2 * TRAY_NOTIFY_WND_SPACING_X); + szClock.cy = This->szTrayClockMin.cy; + } + + SetWindowPos(This->hWndTrayClock, + NULL, + ptClock.x, + ptClock.y, + szClock.cx, + szClock.cy, + SWP_NOZORDER); + } +} + +static LRESULT CALLBACK +TrayNotifyWndProc(IN HWND hwnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + PTRAY_NOTIFY_WND_DATA This = NULL; + LRESULT Ret = FALSE; + + if (uMsg != WM_NCCREATE) + { + This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, + 0); + } + + if (This != NULL || uMsg == WM_NCCREATE) + { + switch (uMsg) + { + case TNWM_GETMINIMUMSIZE: + { + Ret = (LRESULT)TrayNotifyWnd_GetMinimumSize(This, + (BOOL)wParam, + (PSIZE)lParam); + break; + } + + case TNWM_UPDATETIME: + { + if (This->hWndTrayClock != NULL) + { + /* Forward the message to the tray clock window procedure */ + Ret = TrayClockWndProc(This->hWndTrayClock, + TCWM_UPDATETIME, + wParam, + lParam); + } + break; + } + + case WM_SIZE: + { + SIZE szClient; + + szClient.cx = LOWORD(lParam); + szClient.cy = HIWORD(lParam); + + TrayNotifyWnd_Size(This, + &szClient); + break; + } + + case WM_NCHITTEST: + /* We want the user to be able to drag the task bar when clicking the + tray notification window */ + Ret = HTTRANSPARENT; + break; + + case TNWM_SHOWCLOCK: + { + BOOL PrevHidden = This->HideClock; + This->HideClock = (wParam == 0); + + if (This->hWndTrayClock != NULL && PrevHidden != This->HideClock) + { + ShowWindow(This->hWndTrayClock, + This->HideClock ? SW_HIDE : SW_SHOW); + } + + Ret = (LRESULT)(!PrevHidden); + break; + } + + case WM_NOTIFY: + { + const NMHDR *nmh = (const NMHDR *)lParam; + + if (nmh->hwndFrom == This->hWndTrayClock) + { + /* Pass down notifications */ + Ret = SendMessage(This->hWndNotify, + WM_NOTIFY, + wParam, + lParam); + } + break; + } + + case WM_SETFONT: + { + if (This->hWndTrayClock != NULL) + { + SendMessage(This->hWndTrayClock, + WM_SETFONT, + wParam, + lParam); + } + goto HandleDefaultMessage; + } + + case WM_NCCREATE: + { + LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; + This = (PTRAY_NOTIFY_WND_DATA)CreateStruct->lpCreateParams; + This->hWnd = hwnd; + This->hWndNotify = CreateStruct->hwndParent; + + SetWindowLongPtr(hwnd, + 0, + (LONG_PTR)This); + + return TRUE; + } + + case WM_CREATE: + TrayNotifyWnd_Create(This); + break; + + case WM_NCDESTROY: + TrayNotifyWnd_NCDestroy(This); + break; + + default: +HandleDefaultMessage: + Ret = DefWindowProc(hwnd, + uMsg, + wParam, + lParam); + break; + } + } + + return Ret; +} + +HWND +CreateTrayNotifyWnd(IN OUT ITrayWindow *TrayWindow, + IN BOOL bHideClock) +{ + PTRAY_NOTIFY_WND_DATA TnData; + HWND hWndTrayWindow; + HWND hWnd = NULL; + + hWndTrayWindow = ITrayWindow_GetHWND(TrayWindow); + if (hWndTrayWindow == NULL) + return NULL; + + TnData = HeapAlloc(hProcessHeap, + 0, + sizeof(*TnData)); + if (TnData != NULL) + { + ZeroMemory(TnData, + sizeof(*TnData)); + + TnData->TrayWindow = TrayWindow; + TnData->HideClock = bHideClock; + + /* Create the window. The tray window is going to move it to the correct + position and resize it as needed. */ + hWnd = CreateWindowEx(WS_EX_STATICEDGE, + szTrayNotifyWndClass, + NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, + 0, + 0, + 0, + hWndTrayWindow, + NULL, + hExplorerInstance, + (LPVOID)TnData); + + if (hWnd == NULL) + { + HeapFree(hProcessHeap, + 0, + TnData); + } + } + + return hWnd; +} + +BOOL +RegisterTrayNotifyWndClass(VOID) +{ + WNDCLASS wcTrayWnd; + BOOL Ret; + + wcTrayWnd.style = CS_DBLCLKS; + wcTrayWnd.lpfnWndProc = TrayNotifyWndProc; + wcTrayWnd.cbClsExtra = 0; + wcTrayWnd.cbWndExtra = sizeof(PTRAY_NOTIFY_WND_DATA); + wcTrayWnd.hInstance = hExplorerInstance; + wcTrayWnd.hIcon = NULL; + wcTrayWnd.hCursor = LoadCursor(NULL, + IDC_ARROW); + wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wcTrayWnd.lpszMenuName = NULL; + wcTrayWnd.lpszClassName = szTrayNotifyWndClass; + + Ret = RegisterClass(&wcTrayWnd) != 0; + + if (Ret) + { + Ret = RegisterTrayClockWndClass(); + if (!Ret) + { + UnregisterClass(szTrayNotifyWndClass, + hExplorerInstance); + } + } + + return Ret; +} + +VOID +UnregisterTrayNotifyWndClass(VOID) +{ + UnregisterTrayClockWndClass(); + + UnregisterClass(szTrayNotifyWndClass, + hExplorerInstance); +} diff --git a/reactos/base/shell/explorer-new/trayprop.c b/reactos/base/shell/explorer-new/trayprop.c new file mode 100644 index 00000000000..d412bef8dc5 --- /dev/null +++ b/reactos/base/shell/explorer-new/trayprop.c @@ -0,0 +1,29 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +HWND +DisplayTrayProperties(ITrayWindow *Tray) +{ + DbgPrint("DisplayTrayProperties() not implemented!\n"); + MessageBox(NULL, _T("Not implemented"), NULL, 0); + return NULL; +} diff --git a/reactos/base/shell/explorer-new/traywnd.c b/reactos/base/shell/explorer-new/traywnd.c new file mode 100644 index 00000000000..cb3b60449f0 --- /dev/null +++ b/reactos/base/shell/explorer-new/traywnd.c @@ -0,0 +1,2591 @@ +/* + * ReactOS Explorer + * + * Copyright 2006 - 2007 Thomas Weidenmueller + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu; + +#define WM_APP_TRAYDESTROY (WM_APP + 0x100) + +static LONG TrayWndCount = 0; + +static const TCHAR szTrayWndClass[] = TEXT("Shell_TrayWnd"); + +static const ITrayWindowVtbl ITrayWindowImpl_Vtbl; +static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl; + +/* + * ITrayWindow + */ + +const GUID IID_IShellDesktopTray = {0x213e2df9, 0x9a14, 0x4328, {0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9}}; + +typedef struct +{ + const ITrayWindowVtbl *lpVtbl; + const IShellDesktopTrayVtbl *lpVtblShellDesktopTray; + LONG Ref; + + HWND hWnd; + HWND hWndDesktop; + + HWND hwndStart; + HIMAGELIST himlStartBtn; + SIZE StartBtnSize; + HFONT hStartBtnFont; + HFONT hCaptionFont; + + ITrayBandSite *TrayBandSite; + HWND hwndRebar; + HWND hwndTaskSwitch; + HWND hwndTrayNotify; + + DWORD Position; + HMONITOR Monitor; + DWORD DraggingPosition; + HMONITOR DraggingMonitor; + + RECT rcTrayWnd[4]; + RECT rcNewPosSize; + SIZE TraySize; + union + { + DWORD Flags; + struct + { + DWORD AutoHide : 1; + DWORD AlwaysOnTop : 1; + DWORD SmSmallIcons : 1; + DWORD HideClock : 1; + DWORD Locked : 1; + + /* UI Status */ + DWORD InSizeMove : 1; + DWORD IsDragging : 1; + DWORD NewPosSize : 1; + }; + }; + + NONCLIENTMETRICS ncm; + HFONT hFont; + + IMenuBand *StartMenuBand; + IMenuPopup *StartMenuPopup; + HBITMAP hbmStartMenu; + + HWND hWndTrayProperties; +} ITrayWindowImpl; + +static IUnknown * +IUnknown_from_impl(ITrayWindowImpl *This) +{ + return (IUnknown *)&This->lpVtbl; +} + +static ITrayWindow * +ITrayWindow_from_impl(ITrayWindowImpl *This) +{ + return (ITrayWindow *)&This->lpVtbl; +} + +static IShellDesktopTray * +IShellDesktopTray_from_impl(ITrayWindowImpl *This) +{ + return (IShellDesktopTray *)&This->lpVtblShellDesktopTray; +} + +static ITrayWindowImpl * +impl_from_ITrayWindow(ITrayWindow *iface) +{ + return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl, + lpVtbl)); +} + +static ITrayWindowImpl * +impl_from_IShellDesktopTray(IShellDesktopTray *iface) +{ + return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl, + lpVtblShellDesktopTray)); +} + +/* + * ITrayWindow + */ + +static BOOL +ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl *This) +{ + This->ncm.cbSize = sizeof(This->ncm); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, + sizeof(This->ncm), + &This->ncm, + 0)) + { + if (This->hFont != NULL) + DeleteObject(This->hFont); + + This->hFont = CreateFontIndirect(&This->ncm.lfMessageFont); + return TRUE; + } + + return FALSE; +} + +static VOID +ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl *This) +{ + if (This->hwndTrayNotify != NULL) + { + SendMessage(This->hwndTrayNotify, + WM_SETFONT, + (WPARAM)This->hFont, + TRUE); + } +} + +static HMONITOR +ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl *This, + IN OUT RECT *pRect, + IN DWORD dwFlags) +{ + MONITORINFO mi; + HMONITOR hMon; + + mi.cbSize = sizeof(mi); + hMon = MonitorFromRect(pRect, + dwFlags); + if (hMon != NULL && + GetMonitorInfo(hMon, + &mi)) + { + *pRect = mi.rcMonitor; + } + else + { + pRect->left = 0; + pRect->top = 0; + pRect->right = GetSystemMetrics(SM_CXSCREEN); + pRect->bottom = GetSystemMetrics(SM_CYSCREEN); + + hMon = NULL; + } + + return hMon; +} + +static HMONITOR +ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl *This, + IN const RECT *pRect) +{ + HMONITOR hMon; + + /* In case the monitor sizes or saved sizes differ a bit (probably + not a lot, only so the tray window overlaps into another monitor + now), minimize the risk that we determine a wrong monitor by + using the center point of the tray window if we can't determine + it using the rectangle. */ + hMon = MonitorFromRect(pRect, + MONITOR_DEFAULTTONULL); + if (hMon == NULL) + { + POINT pt; + + pt.x = pRect->left + ((pRect->right - pRect->left) / 2); + pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); + + /* be less error-prone, find the nearest monitor */ + hMon = MonitorFromPoint(pt, + MONITOR_DEFAULTTONEAREST); + } + + return hMon; +} + +static HMONITOR +ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl *This, + IN HMONITOR hMonitor, + IN OUT RECT *pRect) +{ + HMONITOR hMon = NULL; + + if (hMonitor != NULL) + { + MONITORINFO mi; + + mi.cbSize = sizeof(mi); + if (!GetMonitorInfo(This->Monitor, + &mi)) + { + /* Hm, the monitor is gone? Try to find a monitor where it + could be located now */ + hMon = ITrayWindowImpl_GetMonitorFromRect(This, + pRect); + if (hMon == NULL || + !GetMonitorInfo(hMon, + &mi)) + { + hMon = NULL; + goto GetPrimaryRect; + } + } + + *pRect = mi.rcMonitor; + } + else + { +GetPrimaryRect: + pRect->left = 0; + pRect->top = 0; + pRect->right = GetSystemMetrics(SM_CXSCREEN); + pRect->bottom = GetSystemMetrics(SM_CYSCREEN); + } + + return hMon; +} + +static VOID +ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position, + IN const SIZE *pTraySize, + IN OUT RECT *pRect) +{ + switch (Position) + { + case ABE_LEFT: + pRect->right = pRect->left + pTraySize->cx; + break; + + case ABE_TOP: + pRect->bottom = pRect->top + pTraySize->cy; + break; + + case ABE_RIGHT: + pRect->left = pRect->right - pTraySize->cx; + break; + + case ABE_BOTTOM: + default: + pRect->top = pRect->bottom - pTraySize->cy; + break; + } +} + +static VOID +ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl *This, + IN DWORD Position, + IN const RECT *pScreen, + IN const SIZE *pTraySize OPTIONAL, + OUT RECT *pRect) +{ + if (pTraySize == NULL) + pTraySize = &This->TraySize; + + *pRect = *pScreen; + + /* Move the border outside of the screen */ + InflateRect(pRect, + GetSystemMetrics(SM_CXEDGE), + GetSystemMetrics(SM_CYEDGE)); + + ITrayWindowImpl_MakeTrayRectWithSize(Position, + pTraySize, + pRect); +} + +BOOL +ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl *This) +{ + return This->Position == ABE_TOP || This->Position == ABE_BOTTOM; +} + +static HMONITOR +ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl *This, + IN DWORD Position, + IN OUT RECT *pRect) +{ + RECT rcScreen; + BOOL Horizontal; + HMONITOR hMon; + SIZE szMax, szWnd; + + Horizontal = ITrayWindowImpl_IsPosHorizontal(This); + + szWnd.cx = pRect->right - pRect->left; + szWnd.cy = pRect->bottom - pRect->top; + + rcScreen = *pRect; + hMon = ITrayWindowImpl_GetScreenRectFromRect(This, + &rcScreen, + MONITOR_DEFAULTTONEAREST); + + /* Calculate the maximum size of the tray window and limit the window + size to half of the screen's size. */ + szMax.cx = (rcScreen.right - rcScreen.left) / 2; + szMax.cy = (rcScreen.bottom - rcScreen.top) / 2; + if (szWnd.cx > szMax.cx) + szWnd.cx = szMax.cx; + if (szWnd.cy > szMax.cy) + szWnd.cy = szMax.cy; + + /* FIXME - calculate */ + + ITrayWindowImpl_GetTrayRectFromScreenRect(This, + Position, + &rcScreen, + &szWnd, + pRect); + + return hMon; +} + +#if 0 +static VOID +ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl *This, + OUT RECT *pRect) +{ + RECT rcMin = {0}; + + AdjustWindowRectEx(&rcMin, + GetWindowLong(This->hWnd, + GWL_STYLE), + FALSE, + GetWindowLong(This->hWnd, + GWL_EXSTYLE)); + + *pRect = rcMin; +} +#endif + + +static DWORD +ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl *This, + IN POINT pt, + OUT RECT *pRect, + OUT HMONITOR *phMonitor) +{ + HMONITOR hMon, hMonNew; + DWORD PosH, PosV, Pos; + SIZE DeltaPt, ScreenOffset; + RECT rcScreen; + + rcScreen.left = 0; + rcScreen.top = 0; + + /* Determine the screen rectangle */ + hMon = MonitorFromPoint(pt, + MONITOR_DEFAULTTONULL); + + if (hMon != NULL) + { + MONITORINFO mi; + + mi.cbSize = sizeof(mi); + if (!GetMonitorInfo(hMon, + &mi)) + { + hMon = NULL; + goto GetPrimaryScreenRect; + } + + /* make left top corner of the screen zero based to + make calculations easier */ + pt.x -= mi.rcMonitor.left; + pt.y -= mi.rcMonitor.top; + + ScreenOffset.cx = mi.rcMonitor.left; + ScreenOffset.cy = mi.rcMonitor.top; + rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left; + rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top; + } + else + { +GetPrimaryScreenRect: + ScreenOffset.cx = 0; + ScreenOffset.cy = 0; + rcScreen.right = GetSystemMetrics(SM_CXSCREEN); + rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); + } + + /* Calculate the nearest screen border */ + if (pt.x < rcScreen.right / 2) + { + DeltaPt.cx = pt.x; + PosH = ABE_LEFT; + } + else + { + DeltaPt.cx = rcScreen.right - pt.x; + PosH = ABE_RIGHT; + } + + if (pt.y < rcScreen.bottom / 2) + { + DeltaPt.cy = pt.y; + PosV = ABE_TOP; + } + else + { + DeltaPt.cy = rcScreen.bottom - pt.y; + PosV = ABE_BOTTOM; + } + + Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV; + + /* Fix the screen origin to be relative to the primary monitor again */ + OffsetRect(&rcScreen, + ScreenOffset.cx, + ScreenOffset.cy); + + hMonNew = ITrayWindowImpl_GetMonitorFromRect(This, + &This->rcTrayWnd[Pos]); + if (hMon != hMonNew) + { + SIZE szTray; + + /* Recalculate the rectangle, we're dragging to another monitor. + We don't need to recalculate the rect on single monitor systems. */ + szTray.cx = This->rcTrayWnd[Pos].right - This->rcTrayWnd[Pos].left; + szTray.cy = This->rcTrayWnd[Pos].bottom - This->rcTrayWnd[Pos].top; + + ITrayWindowImpl_GetTrayRectFromScreenRect(This, + Pos, + &rcScreen, + &szTray, + pRect); + + hMon = hMonNew; + } + else + { + /* The user is dragging the tray window on the same monitor. We don't need + to recalculate the rectangle */ + *pRect = This->rcTrayWnd[Pos]; + } + + *phMonitor = hMon; + + return Pos; +} + +static DWORD +ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl *This, + IN OUT RECT *pRect, + OUT HMONITOR *phMonitor) +{ + POINT pt; + + /* Calculate the center of the rectangle. We call + ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid + dragging rectangle */ + pt.x = pRect->left + ((pRect->right - pRect->left) / 2); + pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); + + return ITrayWindowImpl_GetDraggingRectFromPt(This, + pt, + pRect, + phMonitor); +} + +static VOID +ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl *This, + IN OUT LPWINDOWPOS pwp) +{ + RECT rcTray; + + if (This->IsDragging) + { + rcTray.left = pwp->x; + rcTray.top = pwp->y; + rcTray.right = rcTray.left + pwp->cx; + rcTray.bottom = rcTray.top + pwp->cy; + + if (!EqualRect(&rcTray, + &This->rcTrayWnd[This->DraggingPosition])) + { + /* Recalculate the rectangle, the user dragged the tray + window to another monitor or the window was somehow else + moved or resized */ + This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromRect(This, + &rcTray, + &This->DraggingMonitor); + //This->rcTrayWnd[This->DraggingPosition] = rcTray; + } + + //This->Monitor = ITrayWindowImpl_CalculateValidSize(This, + // This->DraggingPosition, + // &rcTray); + + This->Monitor = This->DraggingMonitor; + This->Position = This->DraggingPosition; + This->IsDragging = FALSE; + + This->rcTrayWnd[This->Position] = rcTray; + goto ChangePos; + } + else if (GetWindowRect(This->hWnd, + &rcTray)) + { + if (This->InSizeMove) + { + if (!(pwp->flags & SWP_NOMOVE)) + { + rcTray.left = pwp->x; + rcTray.top = pwp->y; + } + + if (!(pwp->flags & SWP_NOSIZE)) + { + rcTray.right = rcTray.left + pwp->cx; + rcTray.bottom = rcTray.top + pwp->cy; + } + + This->Position = ITrayWindowImpl_GetDraggingRectFromRect(This, + &rcTray, + &This->Monitor); + + if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE))) + { + SIZE szWnd; + + szWnd.cx = pwp->cx; + szWnd.cy = pwp->cy; + + ITrayWindowImpl_MakeTrayRectWithSize(This->Position, + &szWnd, + &rcTray); + } + + This->rcTrayWnd[This->Position] = rcTray; + } + else + { + /* If the user isn't resizing the tray window we need to make sure the + new size or position is valid. This is to prevent changes to the window + without user interaction. */ + rcTray = This->rcTrayWnd[This->Position]; + } + +ChangePos: + This->TraySize.cx = rcTray.right - rcTray.left; + This->TraySize.cy = rcTray.bottom - rcTray.top; + + pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE); + pwp->x = rcTray.left; + pwp->y = rcTray.top; + pwp->cx = This->TraySize.cx; + pwp->cy = This->TraySize.cy; + } +} + +static VOID +ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl *This, + IN BOOL Clip) +{ + RECT rcClip, rcWindow; + HRGN hClipRgn; + + if (GetWindowRect(This->hWnd, + &rcWindow)) + { + /* Disable clipping on systems with only one monitor */ + if (GetSystemMetrics(SM_CMONITORS) <= 1) + Clip = FALSE; + + if (Clip) + { + rcClip = rcWindow; + + ITrayWindowImpl_GetScreenRect(This, + This->Monitor, + &rcClip); + + if (!IntersectRect(&rcClip, + &rcClip, + &rcWindow)) + { + rcClip = rcWindow; + } + + OffsetRect(&rcClip, + -rcWindow.left, + -rcWindow.top); + + hClipRgn = CreateRectRgnIndirect(&rcClip); + } + else + hClipRgn = NULL; + + /* Set the clipping region or make sure the window isn't clipped + by disabling it explicitly. */ + SetWindowRgn(This->hWnd, + hClipRgn, + TRUE); + } +} + +static VOID +ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl *This) +{ + RECT rcTray; + + rcTray = This->rcTrayWnd[This->Position]; +// DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom); + + /* Move the tray window */ + SetWindowPos(This->hWnd, + NULL, + rcTray.left, + rcTray.top, + rcTray.right - rcTray.left, + rcTray.bottom - rcTray.top, + SWP_NOZORDER); + + ITrayWindowImpl_ApplyClipping(This, + TRUE); +} + +typedef struct _TW_STUCKRECTS2 +{ + DWORD cbSize; + LONG Unknown; + DWORD dwFlags; + DWORD Position; + SIZE Size; + RECT Rect; +} TW_STRUCKRECTS2, *PTW_STUCKRECTS2; + +static VOID +ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl *This) +{ + DWORD Pos; + TW_STRUCKRECTS2 sr; + RECT rcScreen; + SIZE WndSize, EdgeSize, DlgFrameSize; + DWORD cbSize = sizeof(sr); + + EdgeSize.cx = GetSystemMetrics(SM_CXEDGE); + EdgeSize.cy = GetSystemMetrics(SM_CYEDGE); + DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME); + DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME); + + if (SHGetValue(hkExplorer, + TEXT("StuckRects2"), + TEXT("Settings"), + NULL, + &sr, + &cbSize) == ERROR_SUCCESS && + sr.cbSize == sizeof(sr)) + { + This->AutoHide = (sr.dwFlags & ABS_AUTOHIDE) != 0; + This->AlwaysOnTop = (sr.dwFlags & ABS_ALWAYSONTOP) != 0; + This->SmSmallIcons = (sr.dwFlags & 0x4) != 0; + This->HideClock = (sr.dwFlags & 0x8) != 0; + + /* FIXME: Are there more flags? */ + + if (sr.Position > ABE_BOTTOM) + This->Position = ABE_BOTTOM; + else + This->Position = sr.Position; + + /* Try to find out which monitor the tray window was located on last. + Here we're only interested in the monitor screen that we think + is the last one used. We're going to determine on which monitor + we really are after calculating the docked position. */ + rcScreen = sr.Rect; + ITrayWindowImpl_GetScreenRectFromRect(This, + &rcScreen, + MONITOR_DEFAULTTONEAREST); + } + else + { + This->Position = ABE_BOTTOM; + + /* Use the minimum size of the taskbar, we'll use the start + button as a minimum for now. Make sure we calculate the + entire window size, not just the client size. However, we + use a thinner border than a standard thick border, so that + the start button and bands are not stuck to the screen border. */ + sr.Size.cx = This->StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx)); + sr.Size.cy = This->StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); + + /* Use the primary screen by default */ + rcScreen.left = 0; + rcScreen.top = 0; + rcScreen.right = GetSystemMetrics(SM_CXSCREEN); + rcScreen.right = GetSystemMetrics(SM_CYSCREEN); + ITrayWindowImpl_GetScreenRectFromRect(This, + &rcScreen, + MONITOR_DEFAULTTOPRIMARY); + } + + /* Determine a minimum tray window rectangle. The "client" height is + zero here since we cannot determine an optimal minimum width when + loaded as a vertical tray window. We just need to make sure the values + loaded from the registry are at least. The windows explorer behaves + the same way, it allows the user to save a zero width vertical tray + window, but not a zero height horizontal tray window. */ + WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx); + WndSize.cy = This->StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); + + if (WndSize.cx < sr.Size.cx) + WndSize.cx = sr.Size.cx; + if (WndSize.cy < sr.Size.cy) + WndSize.cy = sr.Size.cy; + + /* Save the calculated size */ + This->TraySize = WndSize; + + /* Calculate all docking rectangles. We need to do this here so they're + initialized and dragging the tray window to another position gives + usable results */ + for (Pos = ABE_LEFT; + Pos <= ABE_BOTTOM; + Pos++) + { + ITrayWindowImpl_GetTrayRectFromScreenRect(This, + Pos, + &rcScreen, + &This->TraySize, + &This->rcTrayWnd[Pos]); +// DbgPrint("rcTrayWnd[%d(%d)]: %d,%d,%d,%d\n", Pos, This->Position, This->rcTrayWnd[Pos].left, This->rcTrayWnd[Pos].top, This->rcTrayWnd[Pos].right, This->rcTrayWnd[Pos].bottom); + } + + /* Determine which monitor we are on. It shouldn't matter which docked + position rectangle we use */ + This->Monitor = ITrayWindowImpl_GetMonitorFromRect(This, + &This->rcTrayWnd[ABE_LEFT]); +} + +static UINT +ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl *This, + IN HMENU hMenu, + IN POINT *ppt OPTIONAL, + IN HWND hwndExclude OPTIONAL, + IN BOOL TrackUp, + IN BOOL IsContextMenu) +{ + TPMPARAMS tmp, *ptmp = NULL; + POINT pt; + UINT cmdId; + UINT fuFlags; + + if (hwndExclude != NULL) + { + /* Get the client rectangle and map it to screen coordinates */ + if (GetClientRect(hwndExclude, + &tmp.rcExclude) && + MapWindowPoints(hwndExclude, + NULL, + (LPPOINT)&tmp.rcExclude, + 2) != 0) + { + ptmp = &tmp; + } + } + + if (ppt == NULL) + { + if (ptmp == NULL && + GetClientRect(This->hWnd, + &tmp.rcExclude) && + MapWindowPoints(This->hWnd, + NULL, + (LPPOINT)&tmp.rcExclude, + 2) != 0) + { + ptmp = &tmp; + } + + if (ptmp != NULL) + { + /* NOTE: TrackPopupMenuEx will eventually align the track position + for us, no need to take care of it here as long as the + coordinates are somewhere within the exclusion rectangle */ + pt.x = ptmp->rcExclude.left; + pt.y = ptmp->rcExclude.top; + } + else + pt.x = pt.y = 0; + } + else + pt = *ppt; + + tmp.cbSize = sizeof(tmp); + + fuFlags = TPM_RETURNCMD | TPM_VERTICAL; + fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN); + if (IsContextMenu) + fuFlags |= TPM_RIGHTBUTTON; + else + fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION); + + cmdId = TrackPopupMenuEx(hMenu, + fuFlags, + pt.x, + pt.y, + This->hWnd, + ptmp); + + return cmdId; +} + +static UINT +ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl *This, + IN const TRAYWINDOW_CTXMENU *pMenu, + IN POINT *ppt OPTIONAL, + IN HWND hwndExclude OPTIONAL, + IN BOOL TrackUp, + IN PVOID Context OPTIONAL) +{ + HMENU hPopup; + UINT cmdId = 0; + PVOID pcmContext = NULL; + + hPopup = pMenu->CreateCtxMenu(This->hWnd, + &pcmContext, + Context); + if (hPopup != NULL) + { + cmdId = ITrayWindowImpl_TrackMenu(This, + hPopup, + ppt, + hwndExclude, + TrackUp, + TRUE); + + pMenu->CtxMenuCommand(This->hWnd, + cmdId, + pcmContext, + Context); + } + + return cmdId; +} + +static VOID +ITrayWindowImpl_Free(ITrayWindowImpl *This) +{ + HeapFree(hProcessHeap, + 0, + This); +} + + +static ULONG STDMETHODCALLTYPE +ITrayWindowImpl_Release(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + ULONG Ret; + + Ret = InterlockedDecrement(&This->Ref); + if (Ret == 0) + ITrayWindowImpl_Free(This); + + return Ret; +} + +static VOID +ITrayWindowImpl_Destroy(ITrayWindowImpl *This) +{ + (void)InterlockedExchangePointer((PVOID*)&This->hWnd, + NULL); + + if (This->himlStartBtn != NULL) + { + ImageList_Destroy(This->himlStartBtn); + This->himlStartBtn = NULL; + } + + if (This->hCaptionFont != NULL) + { + DeleteObject(This->hCaptionFont); + This->hCaptionFont = NULL; + } + + if (This->hStartBtnFont != NULL) + { + DeleteObject(This->hStartBtnFont); + This->hStartBtnFont = NULL; + } + + if (This->hFont != NULL) + { + DeleteObject(This->hFont); + This->hFont = NULL; + } + + if (This->StartMenuPopup != NULL) + { + IMenuPopup_Release(This->StartMenuPopup); + This->StartMenuPopup = NULL; + } + + if (This->hbmStartMenu != NULL) + { + DeleteObject(This->hbmStartMenu); + This->hbmStartMenu = NULL; + } + + if (This->StartMenuBand != NULL) + { + IMenuBand_Release(This->StartMenuBand); + This->StartMenuBand = NULL; + } + + if (This->TrayBandSite != NULL) + { + /* FIXME: Unload bands */ + ITrayBandSite_Release(This->TrayBandSite); + This->TrayBandSite = NULL; + } + + ITrayWindowImpl_Release(ITrayWindow_from_impl(This)); + + if (InterlockedDecrement(&TrayWndCount) == 0) + PostQuitMessage(0); +} + +static ULONG STDMETHODCALLTYPE +ITrayWindowImpl_AddRef(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + return InterlockedIncrement(&This->Ref); +} + + +static BOOL +ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl *This) +{ + ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This)); + + return TRUE; +} + +static VOID +ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl *This, + IN HBITMAP hbmStart OPTIONAL) +{ + SIZE Size = {0}; + + if (This->himlStartBtn == NULL || + !SendMessage(This->hwndStart, + BCM_GETIDEALSIZE, + 0, + (LPARAM)&Size)) + { + Size.cx = GetSystemMetrics(SM_CXEDGE); + Size.cy = GetSystemMetrics(SM_CYEDGE); + + if (hbmStart == NULL) + { + hbmStart = (HBITMAP)SendMessage(This->hwndStart, + BM_GETIMAGE, + IMAGE_BITMAP, + 0); + } + + if (hbmStart != NULL) + { + BITMAP bmp; + + if (GetObject(hbmStart, + sizeof(bmp), + &bmp) != 0) + { + Size.cx += max(bmp.bmWidth, + GetSystemMetrics(SM_CXMINIMIZED)); + Size.cy += max(bmp.bmHeight, + GetSystemMetrics(SM_CYCAPTION)); + } + else + { + /* Huh?! Shouldn't happen... */ + goto DefSize; + } + } + else + { +DefSize: + Size.cx += GetSystemMetrics(SM_CXMINIMIZED); + Size.cy += GetSystemMetrics(SM_CYCAPTION); + } + } + + /* Save the size of the start button */ + This->StartBtnSize = Size; +} + +static VOID +ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl *This, + IN PRECT prcClient OPTIONAL) +{ + RECT rcClient; + SIZE TraySize, StartSize; + POINT ptTrayNotify = {0}; + BOOL Horizontal; + HDWP dwp; + + if (prcClient != NULL) + { + rcClient = *prcClient; + } + else + { + if (!GetClientRect(This->hWnd, + &rcClient)) + { + return; + } + } + + Horizontal = ITrayWindowImpl_IsPosHorizontal(This); + + /* We're about to resize/move the start button, the rebar control and + the tray notification control */ + dwp = BeginDeferWindowPos(3); + if (dwp == NULL) + return; + + /* Limit the Start button width to the client width, if neccessary */ + StartSize = This->StartBtnSize; + if (StartSize.cx > rcClient.right) + StartSize.cx = rcClient.right; + + if (This->hwndStart != NULL) + { + /* Resize and reposition the button */ + dwp = DeferWindowPos(dwp, + This->hwndStart, + NULL, + 0, + 0, + StartSize.cx, + StartSize.cy, + SWP_NOZORDER | SWP_NOACTIVATE); + if (dwp == NULL) + return; + } + + /* Determine the size that the tray notification window needs */ + if (Horizontal) + { + TraySize.cx = 0; + TraySize.cy = rcClient.bottom; + } + else + { + TraySize.cx = rcClient.right; + TraySize.cy = 0; + } + + if (This->hwndTrayNotify != NULL && + SendMessage(This->hwndTrayNotify, + TNWM_GETMINIMUMSIZE, + (WPARAM)Horizontal, + (LPARAM)&TraySize)) + { + /* Move the tray notification window to the desired location */ + if (Horizontal) + ptTrayNotify.x = rcClient.right - TraySize.cx; + else + ptTrayNotify.y = rcClient.bottom - TraySize.cy; + + dwp = DeferWindowPos(dwp, + This->hwndTrayNotify, + NULL, + ptTrayNotify.x, + ptTrayNotify.y, + TraySize.cx, + TraySize.cy, + SWP_NOZORDER | SWP_NOACTIVATE); + if (dwp == NULL) + return; + } + + /* Resize/Move the rebar control */ + if (This->hwndRebar != NULL) + { + POINT ptRebar = {0}; + SIZE szRebar; + + SetWindowStyle(This->hwndRebar, + CCS_VERT, + Horizontal ? 0 : CCS_VERT); + + if (Horizontal) + { + ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME); + szRebar.cx = ptTrayNotify.x - ptRebar.x; + szRebar.cy = rcClient.bottom; + } + else + { + ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME); + szRebar.cx = rcClient.right; + szRebar.cy = ptTrayNotify.y - ptRebar.y; + } + + dwp = DeferWindowPos(dwp, + This->hwndRebar, + NULL, + ptRebar.x, + ptRebar.y, + szRebar.cx, + szRebar.cy, + SWP_NOZORDER | SWP_NOACTIVATE); + } + + if (dwp != NULL) + EndDeferWindowPos(dwp); + + if (This->hwndTaskSwitch != NULL) + { + /* Update the task switch window configuration */ + SendMessage(This->hwndTaskSwitch, + TSWM_UPDATETASKBARPOS, + 0, + 0); + } +} + +static BOOL +ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl *This) +{ + HICON hIconStart; + SIZE IconSize; + + if (This->himlStartBtn != NULL) + return TRUE; + + IconSize.cx = GetSystemMetrics(SM_CXSMICON); + IconSize.cy = GetSystemMetrics(SM_CYSMICON); + + /* Load the start button icon and create a image list for it */ + hIconStart = LoadImage(hExplorerInstance, + MAKEINTRESOURCE(IDI_START), + IMAGE_ICON, + IconSize.cx, + IconSize.cy, + LR_SHARED | LR_DEFAULTCOLOR); + + if (hIconStart != NULL) + { + This->himlStartBtn = ImageList_Create(IconSize.cx, + IconSize.cy, + ILC_COLOR32 | ILC_MASK, + 1, + 1); + if (This->himlStartBtn != NULL) + { + if (ImageList_AddIcon(This->himlStartBtn, + hIconStart) >= 0) + { + return TRUE; + } + + /* Failed to add the icon! */ + ImageList_Destroy(This->himlStartBtn); + This->himlStartBtn = NULL; + } + } + + return FALSE; +} + +static HBITMAP +ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl *This) +{ + TCHAR szStartCaption[32]; + HFONT hFontOld; + HDC hDC = NULL; + HDC hDCScreen = NULL; + SIZE Size, SmallIcon; + HBITMAP hbmpOld, hbmp = NULL; + HBITMAP hBitmap = NULL; + HICON hIconStart; + BOOL Ret; + UINT Flags; + RECT rcButton; + + /* NOTE: This is the backwards compatibility code that is used if the + Common Controls Version 6.0 are not available! */ + + if (!LoadString(hExplorerInstance, + IDS_START, + szStartCaption, + sizeof(szStartCaption) / sizeof(szStartCaption[0]))) + { + return NULL; + } + + /* Load the start button icon */ + SmallIcon.cx = GetSystemMetrics(SM_CXSMICON); + SmallIcon.cy = GetSystemMetrics(SM_CYSMICON); + hIconStart = LoadImage(hExplorerInstance, + MAKEINTRESOURCE(IDI_START), + IMAGE_ICON, + SmallIcon.cx, + SmallIcon.cy, + LR_SHARED | LR_DEFAULTCOLOR); + + hDCScreen = GetDC(NULL); + if (hDCScreen == NULL) + goto Cleanup; + + hDC = CreateCompatibleDC(hDCScreen); + if (hDC == NULL) + goto Cleanup; + + hFontOld = SelectObject(hDC, + This->hStartBtnFont); + + Ret = GetTextExtentPoint32(hDC, + szStartCaption, + _tcslen(szStartCaption), + &Size); + + SelectObject(hDC, + hFontOld); + if (!Ret) + goto Cleanup; + + /* Make sure the height is at least the size of a caption icon. */ + if (hIconStart != NULL) + Size.cx += SmallIcon.cx + 4; + Size.cy = max(Size.cy, + SmallIcon.cy); + + /* Create the bitmap */ + hbmp = CreateCompatibleBitmap(hDCScreen, + Size.cx, + Size.cy); + if (hbmp == NULL) + goto Cleanup; + + /* Caluclate the button rect */ + rcButton.left = 0; + rcButton.top = 0; + rcButton.right = Size.cx; + rcButton.bottom = Size.cy; + + /* Draw the button */ + hbmpOld = SelectObject(hDC, + hbmp); + + Flags = DC_TEXT | DC_INBUTTON; + if (hIconStart != NULL) + Flags |= DC_ICON; + + Ret = DrawCaptionTemp(NULL, + hDC, + &rcButton, + This->hStartBtnFont, + hIconStart, + szStartCaption, + Flags); + + SelectObject(hDC, + hbmpOld); + + if (!Ret) + goto Cleanup; + + /* We successfully created the bitmap! */ + hBitmap = hbmp; + hbmp = NULL; + +Cleanup: + if (hDCScreen != NULL) + { + ReleaseDC(NULL, + hDCScreen); + } + + if (hbmp != NULL) + DeleteObject(hbmp); + + if (hDC != NULL) + DeleteDC(hDC); + + return hBitmap; +} + +static VOID +ITrayWindowImpl_Create(IN OUT ITrayWindowImpl *This) +{ + TCHAR szStartCaption[32]; + + InterlockedIncrement(&TrayWndCount); + + if (!LoadString(hExplorerInstance, + IDS_START, + szStartCaption, + sizeof(szStartCaption) / sizeof(szStartCaption[0]))) + { + szStartCaption[0] = TEXT('\0'); + } + + if (This->hStartBtnFont == NULL || This->hCaptionFont == NULL) + { + NONCLIENTMETRICS ncm; + + /* Get the system fonts, we use the caption font, + always bold, though. */ + ncm.cbSize = sizeof(ncm); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, + sizeof(ncm), + &ncm, + FALSE)) + { + if (This->hCaptionFont == NULL) + { + ncm.lfCaptionFont.lfWeight = FW_NORMAL; + This->hCaptionFont = CreateFontIndirect(&ncm.lfCaptionFont); + } + + if (This->hStartBtnFont == NULL) + { + ncm.lfCaptionFont.lfWeight = FW_BOLD; + This->hStartBtnFont = CreateFontIndirect(&ncm.lfCaptionFont); + } + } + } + + /* Create the Start button */ + This->hwndStart = CreateWindowEx(0, + WC_BUTTON, + szStartCaption, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | + BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, + 0, + 0, + 0, + 0, + This->hWnd, + (HMENU)IDC_STARTBTN, + hExplorerInstance, + NULL); + if (This->hwndStart) + { + SendMessage(This->hwndStart, + WM_SETFONT, + (WPARAM)This->hStartBtnFont, + FALSE); + + if (ITrayWindowImpl_CreateStartBtnImageList(This)) + { + BUTTON_IMAGELIST bil; + + /* Try to set the start button image. This requires the Common + Controls 6.0 to be present (XP and later) */ + bil.himl = This->himlStartBtn; + bil.margin.left = bil.margin.right = 1; + bil.margin.top = bil.margin.bottom = 1; + bil.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; + + if (!SendMessage(This->hwndStart, + BCM_SETIMAGELIST, + 0, + (LPARAM)&bil)) + { + /* Fall back to the deprecated method on older systems that don't + support Common Controls 6.0 */ + ImageList_Destroy(This->himlStartBtn); + This->himlStartBtn = NULL; + + goto SetStartBtnImage; + } + + /* We're using the image list, remove the BS_BITMAP style and + don't center it horizontally */ + SetWindowStyle(This->hwndStart, + BS_BITMAP | BS_RIGHT, + 0); + + ITrayWindowImpl_UpdateStartButton(This, + NULL); + } + else + { + HBITMAP hbmStart, hbmOld; + +SetStartBtnImage: + hbmStart = ITrayWindowImpl_CreateStartButtonBitmap(This); + if (hbmStart != NULL) + { + ITrayWindowImpl_UpdateStartButton(This, + hbmStart); + + hbmOld = (HBITMAP)SendMessage(This->hwndStart, + BM_SETIMAGE, + IMAGE_BITMAP, + (LPARAM)hbmStart); + + if (hbmOld != NULL) + DeleteObject(hbmOld); + } + } + } + + /* Load the saved tray window settings */ + ITrayWindowImpl_RegLoadSettings(This); + + /* Create and initialize the start menu */ + This->hbmStartMenu = LoadBitmap(hExplorerInstance, + MAKEINTRESOURCE(IDB_STARTMENU)); + This->StartMenuPopup = CreateStartMenu(ITrayWindow_from_impl(This), + &This->StartMenuBand, + This->hbmStartMenu, + 0); + + /* Load the tray band site */ + if (This->TrayBandSite != NULL) + { + ITrayBandSite_Release(This->TrayBandSite); + } + + This->TrayBandSite = CreateTrayBandSite(ITrayWindow_from_impl(This), + &This->hwndRebar, + &This->hwndTaskSwitch); + + /* Create the tray notification window */ + This->hwndTrayNotify = CreateTrayNotifyWnd(ITrayWindow_from_impl(This), + This->HideClock); + + if (ITrayWindowImpl_UpdateNonClientMetrics(This)) + { + ITrayWindowImpl_SetWindowsFont(This); + } + + /* Move the tray window to the right position and resize it if neccessary */ + ITrayWindowImpl_CheckTrayWndPosition(This); + + /* Align all controls on the tray window */ + ITrayWindowImpl_AlignControls(This, + NULL); +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + ITrayWindowImpl *This; + + if (ppvObj == NULL) + return E_POINTER; + + This = impl_from_ITrayWindow(iface); + + if (IsEqualIID(riid, + &IID_IUnknown)) + { + *ppvObj = IUnknown_from_impl(This); + } + else if (IsEqualIID(riid, + &IID_IShellDesktopTray)) + { + *ppvObj = IShellDesktopTray_from_impl(This); + } + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + ITrayWindowImpl_AddRef(iface); + return S_OK; +} + +static ITrayWindowImpl * +ITrayWindowImpl_Construct(VOID) +{ + ITrayWindowImpl *This; + + This = HeapAlloc(hProcessHeap, + 0, + sizeof(*This)); + if (This == NULL) + return NULL; + + ZeroMemory(This, + sizeof(*This)); + This->lpVtbl = &ITrayWindowImpl_Vtbl; + This->lpVtblShellDesktopTray = &IShellDesktopTrayImpl_Vtbl; + This->Ref = 1; + This->Position = (DWORD)-1; + + return This; +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_Open(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + HRESULT Ret = S_OK; + HWND hWnd; + DWORD dwExStyle; + + /* Check if there's already a window created and try to show it. + If it was somehow destroyed just create a new tray window. */ + if (This->hWnd != NULL) + { + if (IsWindow(This->hWnd)) + { + if (!IsWindowVisible(This->hWnd)) + { + ITrayWindowImpl_CheckTrayWndPosition(This); + + ShowWindow(This->hWnd, + SW_SHOW); + } + } + else + goto TryCreateTrayWnd; + } + else + { + RECT rcWnd; + +TryCreateTrayWnd: + dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; + if (This->AlwaysOnTop) + dwExStyle |= WS_EX_TOPMOST; + + if (This->Position != (DWORD)-1) + rcWnd = This->rcTrayWnd[This->Position]; + else + { + ZeroMemory(&rcWnd, + sizeof(rcWnd)); + } + + hWnd = CreateWindowEx(dwExStyle, + szTrayWndClass, + NULL, + WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | + WS_BORDER | WS_THICKFRAME, + rcWnd.left, + rcWnd.top, + rcWnd.right - rcWnd.left, + rcWnd.bottom - rcWnd.top, + NULL, + NULL, + hExplorerInstance, + This); + if (hWnd == NULL) + Ret = E_FAIL; + } + + return Ret; +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_Close(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + if (This->hWnd != NULL) + { + SendMessage(This->hWnd, + WM_APP_TRAYDESTROY, + 0, + 0); + } + + return S_OK; +} + +static HWND STDMETHODCALLTYPE +ITrayWindowImpl_GetHWND(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + return This->hWnd; +} + +static BOOL STDMETHODCALLTYPE +ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow *iface, + IN HWND hWnd) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + return (hWnd == This->hWnd || + (This->hWndDesktop != NULL && hWnd == This->hWndDesktop)); +} + +static BOOL STDMETHODCALLTYPE +ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + return ITrayWindowImpl_IsPosHorizontal(This); +} + +static HFONT STDMETHODCALLTYPE +ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow *iface, + OUT HFONT *phBoldCaption OPTIONAL) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + if (phBoldCaption != NULL) + *phBoldCaption = This->hStartBtnFont; + + return This->hCaptionFont; +} + +static HWND STDMETHODCALLTYPE +ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow *iface) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + if (This->hWndTrayProperties != NULL) + { + BringWindowToTop(This->hWndTrayProperties); + return This->hWndTrayProperties; + } + + This->hWndTrayProperties = DisplayTrayProperties(ITrayWindow_from_impl(This)); + return This->hWndTrayProperties; +} + +static VOID +OpenCommonStartMenuDirectory(IN HWND hWndOwner, + IN LPCTSTR lpOperation) +{ + TCHAR szDir[MAX_PATH]; + + if (SHGetSpecialFolderPath(hWndOwner, + szDir, + CSIDL_COMMON_STARTMENU, + FALSE)) + { + ShellExecute(hWndOwner, + lpOperation, + NULL, + NULL, + szDir, + SW_SHOWNORMAL); + } +} + +static BOOL STDMETHODCALLTYPE +ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow *iface, + IN UINT uiCmd) +{ + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + BOOL bHandled = TRUE; + + switch (uiCmd) + { + case ID_SHELL_CMD_PROPERTIES: + ITrayWindow_DisplayProperties(iface); + break; + + case ID_SHELL_CMD_OPEN_ALL_USERS: + OpenCommonStartMenuDirectory(This->hWnd, + TEXT("open")); + break; + + case ID_SHELL_CMD_EXPLORE_ALL_USERS: + OpenCommonStartMenuDirectory(This->hWnd, + TEXT("explore")); + break; + + case ID_LOCKTASKBAR: + if (SHRestricted(REST_CLASSICSHELL) == 0) + { + ITrayWindow_Lock(iface, + !This->Locked); + } + break; + + default: + DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd); + bHandled = FALSE; + break; + } + + return bHandled; +} + +static BOOL STDMETHODCALLTYPE +ITrayWindowImpl_Lock(IN OUT ITrayWindow *iface, + IN BOOL bLock) +{ + BOOL bPrevLock; + ITrayWindowImpl *This = impl_from_ITrayWindow(iface); + + bPrevLock = This->Locked; + if (This->Locked != bLock) + { + This->Locked = bLock; + + if (This->TrayBandSite != NULL) + { + if (!SUCCEEDED(ITrayBandSite_Lock(This->TrayBandSite, + bLock))) + { + /* Reset?? */ + This->Locked = bLock; + } + } + } + + return bPrevLock; +} + +static const ITrayWindowVtbl ITrayWindowImpl_Vtbl = +{ + /* IUnknown */ + ITrayWindowImpl_QueryInterface, + ITrayWindowImpl_AddRef, + ITrayWindowImpl_Release, + /* ITrayWindow */ + ITrayWindowImpl_Open, + ITrayWindowImpl_Close, + ITrayWindowImpl_GetHWND, + ITrayWindowImpl_IsSpecialHWND, + ITrayWindowImpl_IsHorizontal, + ITrayWIndowImpl_GetCaptionFonts, + ITrayWindowImpl_DisplayProperties, + ITrayWindowImpl_ExecContextMenuCmd, + ITrayWindowImpl_Lock +}; + +static LRESULT CALLBACK +TrayWndProc(IN HWND hwnd, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + ITrayWindowImpl *This = NULL; + LRESULT Ret = FALSE; + + if (uMsg != WM_NCCREATE) + { + This = (ITrayWindowImpl*)GetWindowLongPtr(hwnd, + 0); + } + + if (This != NULL || uMsg == WM_NCCREATE) + { + if (This != NULL && This->StartMenuBand != NULL) + { + MSG Msg; + LRESULT lRet; + + Msg.hwnd = hwnd; + Msg.message = uMsg; + Msg.wParam = wParam; + Msg.lParam = lParam; + + if (IMenuBand_TranslateMenuMessage(This->StartMenuBand, + &Msg, + &lRet) == S_OK) + { + return lRet; + } + + wParam = Msg.wParam; + lParam = Msg.lParam; + } + + switch (uMsg) + { + case WM_NCHITTEST: + { + RECT rcClient; + POINT pt; + + if (This->Locked) + { + /* The user may not be able to resize the tray window. + Pretend like the window is not sizeable when the user + clicks on the border. */ + return HTBORDER; + } + + if (GetClientRect(hwnd, + &rcClient) && + MapWindowPoints(hwnd, + NULL, + (LPPOINT)&rcClient, + 2) != 0) + { + pt.x = (SHORT)LOWORD(lParam); + pt.y = (SHORT)HIWORD(lParam); + + if (PtInRect(&rcClient, + pt)) + { + /* The user is trying to drag the tray window */ + return HTCAPTION; + } + + /* Depending on the position of the tray window, allow only + changing the border next to the monitor working area */ + switch (This->Position) + { + case ABE_TOP: + if (pt.y > rcClient.bottom) + return HTBOTTOM; + break; + + case ABE_BOTTOM: + if (pt.y < rcClient.top) + return HTTOP; + break; + + case ABE_LEFT: + if (pt.x > rcClient.right) + return HTRIGHT; + break; + + case ABE_RIGHT: + if (pt.x < rcClient.left) + return HTLEFT; + break; + + default: + break; + } + } + + return HTBORDER; + } + + case WM_MOVING: + { + POINT ptCursor; + PRECT pRect = (PRECT)lParam; + + /* We need to ensure that an application can not accidently + move the tray window (using SetWindowPos). However, we still + need to be able to move the window in case the user wants to + drag the tray window to another position or in case the user + wants to resize the tray window. */ + if (!This->Locked && GetCursorPos(&ptCursor)) + { + This->IsDragging = TRUE; + This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromPt(This, + ptCursor, + pRect, + &This->DraggingMonitor); + } + else + { + *pRect = This->rcTrayWnd[This->Position]; + } + return TRUE; + } + + case WM_SIZING: + { + PRECT pRect = (PRECT)lParam; + + if (!This->Locked) + { + ITrayWindowImpl_CalculateValidSize(This, + This->Position, + pRect); + } + else + { + *pRect = This->rcTrayWnd[This->Position]; + } + return TRUE; + } + + case WM_WINDOWPOSCHANGING: + { + ITrayWindowImpl_ChangingWinPos(This, + (LPWINDOWPOS)lParam); + break; + } + + case WM_SIZE: + { + RECT rcClient; + + if (wParam == SIZE_RESTORED && lParam == 0) + { + /* Clip the tray window on multi monitor systems so the edges can't + overlap into another monitor */ + ITrayWindowImpl_ApplyClipping(This, + TRUE); + + if (!GetClientRect(This->hWnd, + &rcClient)) + { + break; + } + } + else + { + rcClient.left = rcClient.top = 0; + rcClient.right = LOWORD(lParam); + rcClient.bottom = HIWORD(lParam); + } + + ITrayWindowImpl_AlignControls(This, + &rcClient); + break; + } + + case WM_ENTERSIZEMOVE: + This->InSizeMove = TRUE; + This->IsDragging = FALSE; + if (!This->Locked) + { + /* Remove the clipping on multi monitor systems while dragging around */ + ITrayWindowImpl_ApplyClipping(This, + FALSE); + } + break; + + case WM_EXITSIZEMOVE: + This->InSizeMove = FALSE; + if (!This->Locked) + { + /* Apply clipping */ + PostMessage(This->hWnd, + WM_SIZE, + SIZE_RESTORED, + 0); + } + break; + + case WM_SYSCHAR: + switch (wParam) + { + case TEXT(' '): + { + /* The user pressed Alt+Space, this usually brings up the system menu of a window. + The tray window needs to handle this specially, since it normally doesn't have + a system menu. */ + + static const UINT uidDisableItem[] = { + SC_RESTORE, + SC_MOVE, + SC_SIZE, + SC_MAXIMIZE, + SC_MINIMIZE, + }; + HMENU hSysMenu; + INT i; + UINT uId; + + /* temporarily enable the system menu */ + SetWindowStyle(hwnd, + WS_SYSMENU, + WS_SYSMENU); + + hSysMenu = GetSystemMenu(hwnd, + FALSE); + if (hSysMenu != NULL) + { + /* Disable all items that are not relevant */ + for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++) + { + EnableMenuItem(hSysMenu, + uidDisableItem[i], + MF_BYCOMMAND | MF_GRAYED); + } + + EnableMenuItem(hSysMenu, + SC_CLOSE, + MF_BYCOMMAND | + (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED)); + + /* Display the system menu */ + uId = ITrayWindowImpl_TrackMenu(This, + hSysMenu, + NULL, + This->hwndStart, + This->Position != ABE_TOP, + FALSE); + if (uId != 0) + { + SendMessage(This->hWnd, + WM_SYSCOMMAND, + (WPARAM)uId, + 0); + } + } + + /* revert the system menu window style */ + SetWindowStyle(hwnd, + WS_SYSMENU, + 0); + break; + } + + default: + goto DefHandler; + } + break; + + case WM_NCRBUTTONUP: + /* We want the user to be able to get a context menu even on the nonclient + area (including the sizing border)! */ + uMsg = WM_CONTEXTMENU; + wParam = (WPARAM)hwnd; + /* fall through */ + + case WM_CONTEXTMENU: + { + POINT pt, *ppt = NULL; + HWND hWndExclude = NULL; + + /* Check if the administrator has forbidden access to context menus */ + if (SHRestricted(REST_NOTRAYCONTEXTMENU)) + break; + + pt.x = (SHORT)LOWORD(lParam); + pt.y = (SHORT)HIWORD(lParam); + + if (pt.x != -1 || pt.y != -1) + ppt = &pt; + else + hWndExclude = This->hwndStart; + + if ((HWND)wParam == This->hwndStart) + { + /* Make sure we can't track the context menu if the start + menu is currently being shown */ + if (!(SendMessage(This->hwndStart, + BM_GETSTATE, + 0, + 0) & BST_PUSHED)) + { + ITrayWindowImpl_TrackCtxMenu(This, + &StartMenuBtnCtxMenu, + ppt, + hWndExclude, + This->Position == ABE_BOTTOM, + This); + } + } + else + { + /* See if the context menu should be handled by the task band site */ + if (ppt != NULL && This->TrayBandSite != NULL) + { + HWND hWndAtPt; + POINT ptClient = *ppt; + + /* Convert the coordinates to client-coordinates */ + MapWindowPoints(NULL, + This->hWnd, + &ptClient, + 1); + + hWndAtPt = ChildWindowFromPoint(This->hWnd, + ptClient); + if (hWndAtPt != NULL && + (hWndAtPt == This->hwndRebar || IsChild(This->hwndRebar, + hWndAtPt))) + { + /* Check if the user clicked on the task switch window */ + ptClient = *ppt; + MapWindowPoints(NULL, + This->hwndRebar, + &ptClient, + 1); + + hWndAtPt = ChildWindowFromPointEx(This->hwndRebar, + ptClient, + CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); + if (hWndAtPt == This->hwndTaskSwitch) + goto HandleTrayContextMenu; + + /* Forward the message to the task band site */ + ITrayBandSite_ProcessMessage(This->TrayBandSite, + hwnd, + uMsg, + wParam, + lParam, + &Ret); + } + else + goto HandleTrayContextMenu; + } + else + { +HandleTrayContextMenu: + /* Tray the default tray window context menu */ + ITrayWindowImpl_TrackCtxMenu(This, + &TrayWindowCtxMenu, + ppt, + NULL, + FALSE, + This); + } + } + break; + } + + case WM_NOTIFY: + { + /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside + the rebar control! But we shouldn't forward messages that the band + site doesn't handle, such as other controls (start button, tray window */ + if (This->TrayBandSite == NULL || + !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite, + hwnd, + uMsg, + wParam, + lParam, + &Ret))) + { + const NMHDR *nmh = (const NMHDR *)lParam; + + if (nmh->hwndFrom == This->hwndTrayNotify) + { + switch (nmh->code) + { + case NTNWM_REALIGN: + /* Cause all controls to be aligned */ + PostMessage(This->hWnd, + WM_SIZE, + SIZE_RESTORED, + 0); + break; + } + } + } + break; + } + + case WM_NCLBUTTONDBLCLK: + /* We "handle" this message so users can't cause a weird maximize/restore + window animation when double-clicking the tray window! */ + break; + + case WM_NCCREATE: + { + LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam; + This = (ITrayWindowImpl*)CreateStruct->lpCreateParams; + + if (InterlockedCompareExchangePointer((PVOID*)&This->hWnd, + (PVOID)hwnd, + NULL) != NULL) + { + /* Somebody else was faster... */ + return FALSE; + } + + SetWindowLongPtr(hwnd, + 0, + (LONG_PTR)This); + + return ITrayWindowImpl_NCCreate(This); + } + + case WM_CREATE: + ITrayWindowImpl_Create(This); + break; + + case WM_NCDESTROY: + ITrayWindowImpl_Destroy(This); + break; + + case WM_APP_TRAYDESTROY: + DestroyWindow(hwnd); + break; + + case WM_COMMAND: + if ((HWND)lParam == This->hwndStart) + { + if (This->StartMenuPopup != NULL) + { + POINTL pt; + RECTL rcExclude; + DWORD dwFlags = 0; + + if (GetWindowRect(This->hwndStart, + (RECT*)&rcExclude)) + { + if (ITrayWindowImpl_IsPosHorizontal(This)) + { + pt.x = rcExclude.left; + pt.y = rcExclude.top; + dwFlags |= MPPF_BOTTOM; + } + else + { + if (This->Position == ABE_LEFT) + pt.x = rcExclude.left; + else + pt.x = rcExclude.right; + + pt.y = rcExclude.bottom; + dwFlags |= MPPF_BOTTOM; + } + + IMenuPopup_Popup(This->StartMenuPopup, + &pt, + &rcExclude, + dwFlags); + } + } + break; + } + + if (This->TrayBandSite == NULL || + !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite, + hwnd, + uMsg, + wParam, + lParam, + &Ret))) + { + /* FIXME: Handle own commands */ + } + break; + + default: + goto DefHandler; + } + } + else + { +DefHandler: + Ret = DefWindowProc(hwnd, + uMsg, + wParam, + lParam); + } + + return Ret; +} + +/* + * Tray Window Context Menu + */ + +static HMENU +CreateTrayWindowContextMenu(IN HWND hWndOwner, + IN PVOID *ppcmContext, + IN PVOID Context OPTIONAL) +{ + ITrayWindowImpl *This = (ITrayWindowImpl *)Context; + IContextMenu *pcm = NULL; + HMENU hPopup; + + hPopup = LoadPopupMenu(hExplorerInstance, + MAKEINTRESOURCE(IDM_TRAYWND)); + + if (hPopup != NULL) + { + if (SHRestricted(REST_CLASSICSHELL) != 0) + { + DeleteMenu(hPopup, + ID_LOCKTASKBAR, + MF_BYCOMMAND); + } + + CheckMenuItem(hPopup, + ID_LOCKTASKBAR, + MF_BYCOMMAND | (This->Locked ? MF_CHECKED : MF_UNCHECKED)); + + if (This->TrayBandSite != NULL) + { + if (SUCCEEDED(ITrayBandSite_AddContextMenus(This->TrayBandSite, + hPopup, + 0, + ID_SHELL_CMD_FIRST, + ID_SHELL_CMD_LAST, + CMF_NORMAL, + &pcm))) + { + DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n"); + *(IContextMenu **)ppcmContext = pcm; + } + } + } + + return hPopup; +} + +static VOID +OnTrayWindowContextMenuCommand(IN HWND hWndOwner, + IN UINT uiCmdId, + IN PVOID pcmContext OPTIONAL, + IN PVOID Context OPTIONAL) +{ + ITrayWindowImpl *This = (ITrayWindowImpl *)Context; + IContextMenu *pcm = (IContextMenu *)pcmContext; + + if (uiCmdId != 0) + { + switch (uiCmdId) + { + case ID_SHELL_CMD_FIRST ... ID_SHELL_CMD_LAST: + { + CMINVOKECOMMANDINFO cmici = {0}; + + if (pcm == NULL) + break; + + /* Setup and invoke the shell command */ + cmici.cbSize = sizeof(cmici); + cmici.hwnd = hWndOwner; + cmici.lpVerb = (LPCSTR)MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST); + cmici.nShow = SW_NORMAL; + + IContextMenu_InvokeCommand(pcm, + &cmici); + break; + } + + default: + ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This), + uiCmdId); + break; + } + } + + if (pcm != NULL) + IContextMenu_Release(pcm); +} + +static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu = { + CreateTrayWindowContextMenu, + OnTrayWindowContextMenuCommand +}; + +/*****************************************************************************/ + +BOOL +RegisterTrayWindowClass(VOID) +{ + WNDCLASS wcTrayWnd; + BOOL Ret; + + if (!RegisterTrayNotifyWndClass()) + return FALSE; + + wcTrayWnd.style = CS_DBLCLKS; + wcTrayWnd.lpfnWndProc = TrayWndProc; + wcTrayWnd.cbClsExtra = 0; + wcTrayWnd.cbWndExtra = sizeof(ITrayWindowImpl *); + wcTrayWnd.hInstance = hExplorerInstance; + wcTrayWnd.hIcon = NULL; + wcTrayWnd.hCursor = LoadCursor(NULL, + IDC_ARROW); + wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wcTrayWnd.lpszMenuName = NULL; + wcTrayWnd.lpszClassName = szTrayWndClass; + + Ret = RegisterClass(&wcTrayWnd) != 0; + + if (!Ret) + UnregisterTrayNotifyWndClass(); + + return Ret; +} + +VOID +UnregisterTrayWindowClass(VOID) +{ + UnregisterTrayNotifyWndClass(); + + UnregisterClass(szTrayWndClass, + hExplorerInstance); +} + +ITrayWindow * +CreateTrayWindow(VOID) +{ + ITrayWindowImpl *This; + ITrayWindow *TrayWindow; + + This = ITrayWindowImpl_Construct(); + if (This != NULL) + { + TrayWindow = ITrayWindow_from_impl(This); + + ITrayWindowImpl_Open(TrayWindow); + + return TrayWindow; + } + + return NULL; +} + +VOID +TrayProcessMessages(IN OUT ITrayWindow *Tray) +{ + ITrayWindowImpl *This; + MSG Msg; + + This = impl_from_ITrayWindow(Tray); + + /* FIXME: We should keep a reference here... */ + + while (PeekMessage(&Msg, + NULL, + 0, + 0, + PM_REMOVE)) + { + if (Msg.message == WM_QUIT) + break; + + if (This->StartMenuBand == NULL || + IMenuBand_IsMenuMessage(This->StartMenuBand, + &Msg) != S_OK) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + } +} + +VOID +TrayMessageLoop(IN OUT ITrayWindow *Tray) +{ + ITrayWindowImpl *This; + MSG Msg; + BOOL Ret; + + This = impl_from_ITrayWindow(Tray); + + /* FIXME: We should keep a reference here... */ + + while (1) + { + Ret = (GetMessage(&Msg, + NULL, + 0, + 0) != 0); + + if (Ret != -1) + { + if (!Ret) + break; + + if (This->StartMenuBand == NULL || + IMenuBand_IsMenuMessage(This->StartMenuBand, + &Msg) != S_OK) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + } + } +} + +/* + * IShellDesktopTray + * + * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()! + * These are the calls I observed, it may be wrong/incomplete/buggy!!! + * The reason we implement it is because we have to use SHCreateDesktop() so + * that the shell provides the desktop window and all the features that come + * with it (especially positioning of desktop icons) + */ + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray *iface, + IN REFIID riid, + OUT LPVOID *ppvObj) +{ + ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); + ITrayWindow *tray = ITrayWindow_from_impl(This); + + DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid, ppvObj); + return ITrayWindowImpl_QueryInterface(tray, + riid, + ppvObj); +} + +static ULONG STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray *iface) +{ + ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); + ITrayWindow *tray = ITrayWindow_from_impl(This); + + DbgPrint("IShellDesktopTray::Release()\n"); + return ITrayWindowImpl_Release(tray); +} + +static ULONG STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray *iface) +{ + ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); + ITrayWindow *tray = ITrayWindow_from_impl(This); + + DbgPrint("IShellDesktopTray::AddRef()\n"); + return ITrayWindowImpl_AddRef(tray); +} + +static ULONG STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray *iface) +{ + /* FIXME: Return ABS_ flags? */ + DbgPrint("IShellDesktopTray::GetState() unimplemented!\n"); + return 0; +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray *iface, + OUT HWND *phWndTray) +{ + ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); + DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray); + *phWndTray = This->hWnd; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray *iface, + IN HWND hWndDesktop) +{ + ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface); + DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop); + + This->hWndDesktop = hWndDesktop; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray *iface, + IN DWORD dwUnknown1, + IN DWORD dwUnknown2) +{ + DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2); + return S_OK; +} + +static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl = +{ + /*** IUnknown ***/ + ITrayWindowImpl_IShellDesktopTray_QueryInterface, + ITrayWindowImpl_IShellDesktopTray_AddRef, + ITrayWindowImpl_IShellDesktopTray_Release, + /*** IShellDesktopTray ***/ + ITrayWindowImpl_IShellDesktopTray_GetState, + ITrayWindowImpl_IShellDesktopTray_GetTrayWindow, + ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow, + ITrayWindowImpl_IShellDesktopTray_Unknown +}; diff --git a/reactos/base/shell/explorer-new/undoc.h b/reactos/base/shell/explorer-new/undoc.h new file mode 100644 index 00000000000..bc644321a27 --- /dev/null +++ b/reactos/base/shell/explorer-new/undoc.h @@ -0,0 +1,301 @@ +#ifndef _EXPLORER_UNDOC__H +#define _EXPLORER_UNDOC__H + +/* + * Undocumented stuff + */ + +/* IMenuDeskBar provides the band site toolbars menu */ +static const CLSID CLSID_HACK_IShellBandSiteMenu = {0xECD4FC4E,0x521C,0x11D0,{0xB7,0x92,0x00,0xA0,0xC9,0x03,0x12,0xE1}}; +#define CLSID_IShellBandSiteMenu CLSID_HACK_IShellBandSiteMenu + +static const GUID IID_HACK_IBandSiteStreamCallback = {0xD1E7AFEA,0x6A2E,0x11D0,{0x8C,0x78,0x00,0xC0,0x4F,0xD9,0x18,0xB4}}; +#define IID_IBandSiteStreamCallback IID_HACK_IBandSiteStreamCallback + +static const GUID CLSID_HACK_StartMenu = {0x4622AD11,0xFF23,0x11D0,{0x8D,0x34,0x00,0xA0,0xC9,0x0F,0x27,0x19}}; +#define CLSID_StartMenu CLSID_HACK_StartMenu +static const GUID CLSID_HACK_PersonalStartMenu = {0x3F6953F0,0x5359,0x47FC,{0xBD,0x99,0x9F,0x2C,0xB9,0x5A,0x62,0xFD}}; +#define CLSID_PersonalStartMenu CLSID_HACK_PersonalStartMenu + +static const GUID IID_HACK_IMenuBand = {0x568804CD,0xCBD7,0x11D0,{0x98,0x16,0x00,0xC0,0x4F,0xD9,0x19,0x72}}; +#define IID_IMenuBand IID_HACK_IMenuBand + +static const GUID IID_HACK_IStartMenuCallback = {0x4622AD10,0xFF23,0x11D0,{0x8D,0x34,0x00,0xA0,0xC9,0x0F,0x27,0x19}}; +#define IID_IStartMenuCallback IID_HACK_IStartMenuCallback + +#define INTERFACE IStartMenuCallback +DECLARE_INTERFACE_(IStartMenuCallback,IUnknown) +{ + /*** IUnknown ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IOleWindow methods ***/ + STDMETHOD_(HRESULT,GetWindow)(THIS_ HWND*) PURE; + STDMETHOD_(HRESULT,ContextSensitiveHelp)(THIS_ BOOL) PURE; + /*** IStartMenuCallback ***/ + STDMETHOD_(HRESULT,Execute)(THIS_ IShellFolder*,LPCITEMIDLIST) PURE; + STDMETHOD_(HRESULT,Unknown)(THIS_ PVOID,PVOID,PVOID,PVOID) PURE; + STDMETHOD_(HRESULT,AppendMenu)(THIS_ HMENU*) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IStartMenuCallback_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IStartMenuCallback_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IStartMenuCallback_Release(T) (T)->lpVtbl->Release(T) +#define IStartMenuCallback_GetWindow(T,a) (T)->lpVtbl->GetWindow(T,a) +#define IStartMenuCallback_ContextSensitiveHelp(T,a) (T)->lpVtbl->ContextSensitiveHelp(T,a) +#define IStartMenuCallback_Execute(T,a,b) (T)->lpVtbl->Execute(T,a,b) +#define IStartMenuCallback_Unknown(T,a,b,c,d) (T)->lpVtbl->Unknown(T,a,b,c,d) +#define IStartMenuCallback_AppendMenu(T,a) (T)->lpVtbl->AppendMenu(T,a) +#endif + +#define INTERFACE IBandSiteStreamCallback +DECLARE_INTERFACE_(IBandSiteStreamCallback,IUnknown) +{ + /*** IUnknown ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IBandSiteStreamCallback ***/ + STDMETHOD_(HRESULT,OnLoad)(THIS_ IStream *pStm, REFIID riid, PVOID *pvObj) PURE; + STDMETHOD_(HRESULT,OnSave)(THIS_ IUnknown *pUnk, IStream *pStm) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IBandSiteStreamCallback_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IBandSiteStreamCallback_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IBandSiteStreamCallback_Release(T) (T)->lpVtbl->Release(T) +#define IBandSiteStreamCallback_OnLoad(T,a,b,c) (T)->lpVtbl->OnLoad(T,a,b,c) +#define IBandSiteStreamCallback_OnSave(T,a,b) (T)->lpVtbl->OnSave(T,a,b) +#endif + +static const GUID IID_HACK_IWindowEventHandler = {0xEA5F2D61,0xE008,0x11CF,{0x99,0xCB,0x00,0xC0,0x4F,0xD6,0x44,0x97}}; +#define IID_IWindowEventHandler IID_HACK_IWindowEventHandler + +#define INTERFACE IWindowEventHandler +DECLARE_INTERFACE_(IWindowEventHandler,IUnknown) +{ + /*** IUnknown ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IWindowEventHandler ***/ + STDMETHOD(ProcessMessage)(THIS_ HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plrResult) PURE; + STDMETHOD(ContainsWindow)(THIS_ HWND hWnd) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IWindowEventHandler_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IWindowEventHandler_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IWindowEventHandler_Release(T) (T)->lpVtbl->Release(T) +#define IWindowEventHandler_ProcessMessage(T,a,b,c,d,e) (T)->lpVtbl->ProcessMessage(T,a,b,c,d,e) +#endif + +#if USE_API_SHCREATEDESKTOP != 0 +#define INTERFACE IShellDesktopTray +DECLARE_INTERFACE_(IShellDesktopTray,IUnknown) +{ + /*** IUnknown ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IShellDesktopTray ***/ + STDMETHOD_(ULONG,GetState)(THIS) PURE; + STDMETHOD(GetTrayWindow)(THIS_ HWND*) PURE; + STDMETHOD(RegisterDesktopWindow)(THIS_ HWND) PURE; + STDMETHOD(Unknown)(THIS_ DWORD,DWORD) PURE; +}; +#undef INTERFACE + +#ifdef COBJMACROS +#define IShellDesktopTray_QueryInterface(T,a,b) (T)->lpVtbl->QueryInterface(T,a,b) +#define IShellDesktopTray_AddRef(T) (T)->lpVtbl->AddRef(T) +#define IShellDesktopTray_Release(T) (T)->lpVtbl->Release(T) +#define IShellDesktopTray_GetState(T) (T)->lpVtbl->GetState(T) +#define IShellDesktopTray_GetTrayWindow(T,a) (T)->lpVtbl->GetTrayWindow(T,a) +#define IShellDesktopTray_RegisterDesktopWindow(T,a) (T)->lpVtbl->RegisterDesktopWindow(T,a) +#define IShellDesktopTray_Unknown(T,a,b) (T)->lpVtbl->Unknown(T,a,b) +#endif + +#if 0 +HANDLE WINAPI SHCreateDesktop(IShellDesktopTray*); +BOOL WINAPI SHDesktopMessageLoop(HANDLE); +#else +typedef HANDLE (WINAPI *PSHCreateDesktop)(IShellDesktopTray*); +static HANDLE __inline +SHCreateDesktop(IShellDesktopTray* sdt) +{ + static PSHCreateDesktop Func = NULL; + + if (Func == NULL) + { + HMODULE hShlwapi; + hShlwapi = LoadLibrary(TEXT("SHELL32.DLL")); + if (hShlwapi != NULL) + { + Func = (PSHCreateDesktop)GetProcAddress(hShlwapi, (LPCSTR)200); + } + } + + if (Func != NULL) + { + return Func(sdt); + } + + MessageBox(NULL, TEXT("SHCreateDesktop not available"), NULL, 0); + return NULL; +} + +typedef BOOL (WINAPI *PSHDesktopMessageLoop)(HANDLE); +static BOOL __inline +SHDesktopMessageLoop(IN HANDLE hDesktop) +{ + static PSHDesktopMessageLoop Func = NULL; + + if (Func == NULL) + { + HMODULE hShlwapi; + hShlwapi = LoadLibrary(TEXT("SHELL32.DLL")); + if (hShlwapi != NULL) + { + Func = (PSHDesktopMessageLoop)GetProcAddress(hShlwapi, (LPCSTR)201); + } + } + + if (Func != NULL) + { + return Func(hDesktop); + } + + MessageBox(NULL, TEXT("SHDesktopMessageLoop not available"), NULL, 0); + return FALSE; +} +#endif + +#endif /* USE_API_SHCREATEDESKTOP */ + +#define WM_GETISHELLBROWSER (WM_USER+7) +BOOL WINAPI SetShellWindow(HWND); +BOOL WINAPI SetShellWindowEx(HWND, HWND); +BOOL WINAPI RegisterShellHook(HWND, DWORD); +IStream* WINAPI SHGetViewStream(LPCITEMIDLIST, DWORD, LPCTSTR, LPCTSTR, LPCTSTR); +BOOL WINAPI SHIsEmptyStream(IStream*); + +typedef struct tagCREATEMRULISTA +{ + DWORD cbSize; + DWORD nMaxItems; + DWORD dwFlags; + HKEY hKey; + LPCSTR lpszSubKey; + PROC lpfnCompare; +} CREATEMRULISTA, *LPCREATEMRULISTA; +typedef struct tagCREATEMRULISTW +{ + DWORD cbSize; + DWORD nMaxItems; + DWORD dwFlags; + HKEY hKey; + LPCWSTR lpszSubKey; + PROC lpfnCompare; +} CREATEMRULISTW, *LPCREATEMRULISTW; + +#define MRU_BINARY 0x1 +#define MRU_CACHEWRITE 0x2 + +HANDLE WINAPI CreateMRUListW(LPCREATEMRULISTW); +HANDLE WINAPI CreateMRUListA(LPCREATEMRULISTA); +INT WINAPI AddMRUData(HANDLE,LPCVOID,DWORD); +INT WINAPI FindMRUData(HANDLE,LPCVOID,DWORD,LPINT); +VOID WINAPI FreeMRUList(HANDLE); + +#define DC_NOSENDMSG 0x2000 +BOOL WINAPI DrawCaptionTempA(HWND,HDC,const RECT*,HFONT,HICON,LPCSTR,UINT); +BOOL WINAPI DrawCaptionTempW(HWND,HDC,const RECT*,HFONT,HICON,LPCWSTR,UINT); + +#ifdef UNICODE +typedef CREATEMRULISTW CREATEMRULIST, *PCREATEMRULIST; +#define CreateMRUList CreateMRUListW +#define DrawCaptionTemp DrawCaptionTempW +#else +typedef CREATEMRULISTA CREATEMRULIST, *PCREATEMRULIST; +#define CreateMRUList CreateMRUListA +#define DrawCaptionTemp DrawCaptionTempA +#endif + +DEFINE_GUID(CLSID_RebarBandSite, 0xECD4FC4D, 0x521C, 0x11D0, 0xB7, 0x92, 0x00, 0xA0, 0xC9, 0x03, 0x12, 0xE1); +DEFINE_GUID(IID_IDeskBand, 0xEB0FE172, 0x1A3A, 0x11D0, 0x89, 0xB3, 0x00, 0xA0, 0xC9, 0x0A, 0x90, 0xAC); + +HRESULT WINAPI SHInvokeDefaultCommand(HWND,IShellFolder*,LPCITEMIDLIST); + +HRESULT WINAPI SHPropertyBag_ReadPOINTL(IPropertyBag*,LPCWSTR,POINTL*); + +#if 0 +HRESULT WINAPI SHGetPerScreenResName(OUT LPWSTR lpResName, + IN INT cchResName, + IN DWORD dwReserved); +#else +typedef HRESULT (WINAPI *PSHGetPerScreenResName)(LPWSTR,INT,DWORD); +static HRESULT __inline +SHGetPerScreenResName(OUT LPWSTR lpResName, + IN INT cchResName, + IN DWORD dwReserved OPTIONAL) +{ + static PSHGetPerScreenResName Func = NULL; + + if (Func == NULL) + { + HMODULE hShlwapi; + hShlwapi = LoadLibrary(TEXT("SHLWAPI.DLL")); + if (hShlwapi != NULL) + { + Func = (PSHGetPerScreenResName)GetProcAddress(hShlwapi, (LPCSTR)533); + } + } + + if (Func != NULL) + { + return Func(lpResName, cchResName, dwReserved); + } + + MessageBox(NULL, TEXT("SHGetPerScreenResName not available"), NULL, 0); + return E_NOTIMPL; +} +#endif + +#if 0 +HRESULT WINAPI SHPropertyBag_ReadStream(IPropertyBag*,LPCWSTR,IStream**); +#else +typedef HRESULT (WINAPI *PSHPropertyBag_ReadStream)(IPropertyBag*,LPCWSTR,IStream**); +static HRESULT __inline +SHPropertyBag_ReadStream(IN IPropertyBag *ppb, + IN LPCWSTR pszPropName, + OUT IStream **ppStream) +{ + static PSHPropertyBag_ReadStream Func = NULL; + + if (Func == NULL) + { + HMODULE hShlwapi; + hShlwapi = LoadLibrary(TEXT("SHLWAPI.DLL")); + if (hShlwapi != NULL) + { + Func = (PSHPropertyBag_ReadStream)GetProcAddress(hShlwapi, (LPCSTR)531); + } + } + + if (Func != NULL) + { + return Func(ppb, pszPropName, ppStream); + } + + MessageBox(NULL, TEXT("SHPropertyBag_ReadStream not available"), NULL, 0); + return E_NOTIMPL; +} +#endif + +#endif /* _EXPLORER_UNDOC__H */