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 00000000000..b4404ba9b90 Binary files /dev/null and b/reactos/base/shell/explorer-new/res/logov.bmp differ 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 00000000000..492e388a939 Binary files /dev/null and b/reactos/base/shell/explorer-new/res/start.ico differ diff --git a/reactos/base/shell/explorer-new/resource.h b/reactos/base/shell/explorer-new/resource.h new file mode 100644 index 00000000000..51702212ba6 --- /dev/null +++ b/reactos/base/shell/explorer-new/resource.h @@ -0,0 +1,47 @@ +#ifndef __RESOURCE_H +#define __RESOURCE_H + +#define IDI_START 101 + +#define IDS_START 101 +#define IDS_PROPERTIES 102 +#define IDS_OPEN_ALL_USERS 103 +#define IDS_EXPLORE_ALL_USERS 104 + +#define IDC_STARTBTN 304 + +#define IDM_TRAYWND 101 +#define IDM_STARTMENU 102 + +#define IDB_STARTMENU 101 + +#define ID_SHELL_CMD_FIRST 0xF +#define ID_SHELL_CMD_LAST 0x7FEF +#define ID_SHELL_CMD_PROPERTIES (ID_SHELL_CMD_LAST + 1) +#define ID_SHELL_CMD_OPEN_ALL_USERS (ID_SHELL_CMD_LAST + 2) +#define ID_SHELL_CMD_EXPLORE_ALL_USERS (ID_SHELL_CMD_LAST + 3) +#define ID_LOCKTASKBAR (ID_SHELL_CMD_LAST + 4) + +/* NOTE: The following constants may *NOT* be changed because + they're hardcoded and need to be the exact values + in order to get the start menu to work! */ +#define IDM_PROGRAMS 504 +#define IDM_FAVORITES 507 +#define IDM_DOCUMENTS 501 +#define IDM_SETTINGS 508 +#define IDM_CONTROLPANEL 505 +#define IDM_SECURITY 5001 +#define IDM_NETWORKCONNECTIONS 557 +#define IDM_PRINTERSANDFAXES 510 +#define IDM_TASKBARANDSTARTMENU 413 +#define IDM_SEARCH 520 +#define IDM_HELPANDSUPPORT 503 +#define IDM_RUN 401 +#define IDM_SYNCHRONIZE 553 +#define IDM_LOGOFF 402 +#define IDM_DISCONNECT 5000 +#define IDM_UNDOCKCOMPUTER 410 +#define IDM_SHUTDOWN 506 +#define IDM_LASTSTARTMENU_SEPARATOR 450 + +#endif /* __RESOURCE_H */ diff --git a/reactos/base/shell/explorer-new/startmnu.c b/reactos/base/shell/explorer-new/startmnu.c new file mode 100644 index 00000000000..598a6a00664 --- /dev/null +++ b/reactos/base/shell/explorer-new/startmnu.c @@ -0,0 +1,931 @@ +/* + * 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 + +/* + * 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 */