Move explorer_old to the rosapps.
[reactos.git] / rosapps / applications / explorer-old / taskbar / taskbar.cpp
diff --git a/rosapps/applications/explorer-old/taskbar/taskbar.cpp b/rosapps/applications/explorer-old/taskbar/taskbar.cpp
new file mode 100644 (file)
index 0000000..5db82d9
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2003, 2004, 2005 Martin Fuchs
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+ //
+ // Explorer clone
+ //
+ // taskbar.cpp
+ //
+ // Martin Fuchs, 16.08.2003
+ //
+
+
+#include <precomp.h>
+
+#include "taskbar.h"
+#include "traynotify.h" // for NOTIFYAREA_WIDTH_DEF
+
+
+DynamicFct<BOOL (WINAPI*)(HWND hwnd)> g_SetTaskmanWindow(TEXT("user32"), "SetTaskmanWindow");
+DynamicFct<BOOL (WINAPI*)(HWND hwnd)> g_RegisterShellHookWindow(TEXT("user32"), "RegisterShellHookWindow");
+DynamicFct<BOOL (WINAPI*)(HWND hwnd)> g_DeregisterShellHookWindow(TEXT("user32"), "DeregisterShellHookWindow");
+
+/*
+DynamicFct<BOOL (WINAPI*)(HWND hWnd, DWORD dwType)> g_RegisterShellHook(TEXT("shell32"), (LPCSTR)0xb5);
+
+ // constants for RegisterShellHook()
+#define RSH_UNREGISTER                 0
+#define RSH_REGISTER                   1
+#define RSH_REGISTER_PROGMAN   2
+#define RSH_REGISTER_TASKMAN   3
+*/
+
+
+TaskBarEntry::TaskBarEntry()
+{
+       _id = 0;
+       _hbmp = 0;
+       _bmp_idx = 0;
+       _used = 0;
+       _btn_idx = 0;
+       _fsState = 0;
+}
+
+TaskBarMap::~TaskBarMap()
+{
+       while(!empty()) {
+               iterator it = begin();
+               DeleteBitmap(it->second._hbmp);
+               erase(it);
+       }
+}
+
+
+TaskBar::TaskBar(HWND hwnd)
+ :     super(hwnd),
+       WM_SHELLHOOK(RegisterWindowMessage(WINMSG_SHELLHOOK))
+{
+       _last_btn_width = 0;
+
+       _mmMetrics_org.cbSize = sizeof(MINIMIZEDMETRICS);
+
+       SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(_mmMetrics_org), &_mmMetrics_org, 0);
+
+        // configure the window manager to hide windows when they are minimized
+        // This is neccessary to enable shell hook messages.
+       if (!(_mmMetrics_org.iArrange & ARW_HIDE)) {
+               MINIMIZEDMETRICS _mmMetrics_new = _mmMetrics_org;
+
+               _mmMetrics_new.iArrange |= ARW_HIDE;
+
+               SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(_mmMetrics_new), &_mmMetrics_new, 0);
+       }
+}
+
+TaskBar::~TaskBar()
+{
+//     if (g_RegisterShellHook)
+//             (*g_RegisterShellHook)(_hwnd, RSH_UNREGISTER);
+
+       if (g_DeregisterShellHookWindow)
+               (*g_DeregisterShellHookWindow)(_hwnd);
+       else
+               KillTimer(_hwnd, 0);
+
+       if (g_SetTaskmanWindow)
+               (*g_SetTaskmanWindow)(0);
+
+       SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(_mmMetrics_org), &_mmMetrics_org, 0);
+}
+
+HWND TaskBar::Create(HWND hwndParent)
+{
+       ClientRect clnt(hwndParent);
+
+       int taskbar_pos = 80;   // This start position will be adjusted in DesktopBar::Resize().
+
+       return Window::Create(WINDOW_CREATOR(TaskBar), 0,
+                                                       BtnWindowClass(CLASSNAME_TASKBAR), TITLE_TASKBAR,
+                                                       WS_CHILD|WS_VISIBLE | CCS_TOP|CCS_NODIVIDER|CCS_NORESIZE,
+                                                       taskbar_pos, 0, clnt.right-taskbar_pos-(NOTIFYAREA_WIDTH_DEF+1), clnt.bottom, hwndParent);
+}
+
+LRESULT TaskBar::Init(LPCREATESTRUCT pcs)
+{
+       if (super::Init(pcs))
+               return 1;
+
+       /* FIXME: There's an internal padding for non-flat toolbar. Get rid of it somehow. */
+       _htoolbar = CreateToolbarEx(_hwnd,
+                                                               WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|
+                                                               CCS_TOP|CCS_NODIVIDER|TBSTYLE_LIST|TBSTYLE_TOOLTIPS|TBSTYLE_WRAPABLE,//|TBSTYLE_AUTOSIZE
+                                                               IDW_TASKTOOLBAR, 0, 0, 0, NULL, 0, 0, 0, 16, 16, sizeof(TBBUTTON));
+
+       SendMessage(_htoolbar, TB_SETBUTTONWIDTH, 0, MAKELONG(TASKBUTTONWIDTH_MAX,TASKBUTTONWIDTH_MAX));
+       //SendMessage(_htoolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
+       //SendMessage(_htoolbar, TB_SETDRAWTEXTFLAGS, DT_CENTER|DT_VCENTER, DT_CENTER|DT_VCENTER);
+       //SetWindowFont(_htoolbar, GetStockFont(ANSI_VAR_FONT), FALSE);
+       //SendMessage(_htoolbar, TB_SETPADDING, 0, MAKELPARAM(8,8));
+
+        // set metrics for the Taskbar toolbar to enable button spacing
+       TBMETRICS metrics;
+
+       metrics.cbSize = sizeof(TBMETRICS);
+       metrics.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
+       metrics.cxBarPad = 0;
+       metrics.cyBarPad = 0;
+       metrics.cxButtonSpacing = 3;
+       metrics.cyButtonSpacing = 3;
+
+       SendMessage(_htoolbar, TB_SETMETRICS, 0, (LPARAM)&metrics);
+
+       _next_id = IDC_FIRST_APP;
+
+        // register the taskbar window as task manager window to make the following call to RegisterShellHookWindow working
+       if (g_SetTaskmanWindow)
+               (*g_SetTaskmanWindow)(_hwnd);
+
+       if (g_RegisterShellHookWindow) {
+               LOG(TEXT("Using shell hooks for notification of shell events."));
+
+               (*g_RegisterShellHookWindow)(_hwnd);
+       } else {
+               LOG(TEXT("Shell hooks not available."));
+
+               SetTimer(_hwnd, 0, 200, NULL);
+       }
+
+/* Alternatively we could use the RegisterShellHook() function in SHELL32, but this is not yet implemented in the WINE code.
+       if (g_RegisterShellHook) {
+               (*g_RegisterShellHook)(0, RSH_REGISTER);
+
+               if ((HIWORD(GetVersion())>>14) == W_VER_NT)
+                       (*g_RegisterShellHook)(_hwnd, RSH_REGISTER_TASKMAN);
+               else
+                       (*g_RegisterShellHook)(_hwnd, RSH_REGISTER);
+       }
+*/
+       Refresh();
+
+       return 0;
+}
+
+LRESULT TaskBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
+{
+       switch(nmsg) {
+         case WM_SIZE:
+               SendMessage(_htoolbar, WM_SIZE, 0, 0);
+               ResizeButtons();
+               break;
+
+         case WM_TIMER:
+               Refresh();
+               return 0;
+
+         case WM_CONTEXTMENU: {
+               Point pt(lparam);
+               ScreenToClient(_htoolbar, &pt);
+
+               if ((HWND)wparam==_htoolbar && SendMessage(_htoolbar, TB_HITTEST, 0, (LPARAM)&pt)>=0)
+                       break;  // avoid displaying context menu for application button _and_ desktop bar at the same time
+
+               goto def;}
+
+         case PM_GET_LAST_ACTIVE:
+               return (LRESULT)(HWND)_last_foreground_wnd;
+
+         case WM_SYSCOLORCHANGE:
+               SendMessage(_htoolbar, WM_SYSCOLORCHANGE, 0, 0);
+               break;
+
+         default: def:
+               if (nmsg == WM_SHELLHOOK) {
+                       switch(wparam) {
+                         case HSHELL_WINDOWCREATED:
+                         case HSHELL_WINDOWDESTROYED:
+                         case HSHELL_WINDOWACTIVATED:
+                         case HSHELL_REDRAW:
+#ifdef HSHELL_FLASH
+                         case HSHELL_FLASH:
+#endif
+#ifdef HSHELL_RUDEAPPACTIVATED
+                         case HSHELL_RUDEAPPACTIVATED:
+#endif
+                               Refresh();
+                               break;
+                       }
+               } else {
+                       return super::WndProc(nmsg, wparam, lparam);
+               }
+       }
+
+       return 0;
+}
+
+int TaskBar::Command(int id, int code)
+{
+       TaskBarMap::iterator found = _map.find_id(id);
+
+       if (found != _map.end()) {
+               ActivateApp(found);
+               return 0;
+       }
+
+       return super::Command(id, code);
+}
+
+int TaskBar::Notify(int id, NMHDR* pnmh)
+{
+       if (pnmh->hwndFrom == _htoolbar)
+               switch(pnmh->code) {
+                 case NM_RCLICK: {
+                       TBBUTTONINFO btninfo;
+                       TaskBarMap::iterator it;
+                       Point pt(GetMessagePos());
+                       ScreenToClient(_htoolbar, &pt);
+
+                       btninfo.cbSize = sizeof(TBBUTTONINFO);
+                       btninfo.dwMask = TBIF_BYINDEX|TBIF_COMMAND;
+
+                       int idx = SendMessage(_htoolbar, TB_HITTEST, 0, (LPARAM)&pt);
+
+                       if (idx>=0 &&
+                               SendMessage(_htoolbar, TB_GETBUTTONINFO, idx, (LPARAM)&btninfo)!=-1 &&
+                               (it=_map.find_id(btninfo.idCommand))!=_map.end()) {
+                               //TaskBarEntry& entry = it->second;
+
+                               ActivateApp(it, false, false);  // don't restore minimized windows on right button click
+
+                               static DynamicFct<DWORD(STDAPICALLTYPE*)(RESTRICTIONS)> pSHRestricted(TEXT("SHELL32"), "SHRestricted");
+
+                               if (pSHRestricted && !(*pSHRestricted)(REST_NOTRAYCONTEXTMENU))
+                                       ShowAppSystemMenu(it);
+                       }
+                       break;}
+
+                 default:
+                       return super::Notify(id, pnmh);
+               }
+
+       return 0;
+}
+
+
+void TaskBar::ActivateApp(TaskBarMap::iterator it, bool can_minimize, bool can_restore)
+{
+       HWND hwnd = it->first;
+
+       bool minimize_it = can_minimize && !IsIconic(hwnd) &&
+                                               (hwnd==GetForegroundWindow() || hwnd==_last_foreground_wnd);
+
+        // switch to selected application window
+       if (can_restore && !minimize_it)
+               if (IsIconic(hwnd))
+                       PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+
+        // In case minimize_it is true, we _have_ to switch to the app before
+        // posting SW_MINIMIZE to be compatible with some applications (e.g. "Sleipnir")
+       SetForegroundWindow(hwnd);
+
+       if (minimize_it) {
+               PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
+               _last_foreground_wnd = 0;
+       } else
+               _last_foreground_wnd = hwnd;
+
+       Refresh();
+}
+
+void TaskBar::ShowAppSystemMenu(TaskBarMap::iterator it)
+{
+       HMENU hmenu = GetSystemMenu(it->first, FALSE);
+
+       if (hmenu) {
+               POINT pt;
+
+               GetCursorPos(&pt);
+               int cmd = TrackPopupMenu(hmenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, _hwnd, NULL);
+
+               if (cmd) {
+                       ActivateApp(it, false, false);  // reactivate window after the context menu has closed
+                       PostMessage(it->first, WM_SYSCOMMAND, cmd, 0);
+               }
+       }
+}
+
+
+HICON get_window_icon_small(HWND hwnd)
+{
+       HICON hIcon = 0;
+
+       SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (!hIcon)
+               hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICONSM);
+
+       if (!hIcon)
+               hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICON);
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_QUERYDRAGICON, 0, 0, 0, 1000, (PDWORD_PTR)&hIcon);
+
+       return hIcon;
+}
+
+HICON get_window_icon_big(HWND hwnd, bool allow_from_class)
+{
+       HICON hIcon = 0;
+
+       SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
+
+       if (allow_from_class) {
+               if (!hIcon)
+                       hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICON);
+
+               if (!hIcon)
+                       hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICONSM);
+       }
+
+       if (!hIcon)
+               SendMessageTimeout(hwnd, WM_QUERYDRAGICON, 0, 0, 0, 1000, (PDWORD_PTR)&hIcon);
+
+       return hIcon;
+}
+
+ // fill task bar with buttons for enumerated top level windows
+BOOL CALLBACK TaskBar::EnumWndProc(HWND hwnd, LPARAM lparam)
+{
+       TaskBar* pThis = (TaskBar*)lparam;
+
+       DWORD style = GetWindowStyle(hwnd);
+       DWORD ex_style = GetWindowExStyle(hwnd);
+
+       if ((style&WS_VISIBLE) && !(ex_style&WS_EX_TOOLWINDOW) &&
+               !GetParent(hwnd) && !GetWindow(hwnd,GW_OWNER)) {
+               TCHAR title[BUFFER_LEN];
+
+               if (!GetWindowText(hwnd, title, BUFFER_LEN))
+                       title[0] = '\0';
+
+               TaskBarMap::iterator found = pThis->_map.find(hwnd);
+               int last_id = 0;
+
+               if (found != pThis->_map.end()) {
+                       last_id = found->second._id;
+
+                       if (!last_id)
+                               found->second._id = pThis->_next_id++;
+               } else {
+                       HBITMAP hbmp;
+                       HICON hIcon = get_window_icon_small(hwnd);
+                       BOOL delete_icon = FALSE;
+
+                       if (!hIcon) {
+                               hIcon = LoadIcon(0, IDI_APPLICATION);
+                               delete_icon = TRUE;
+                       }
+
+                       if (hIcon) {
+                               hbmp = create_bitmap_from_icon(hIcon, GetSysColorBrush(COLOR_BTNFACE), WindowCanvas(pThis->_htoolbar));
+                               if (delete_icon)
+                                       DestroyIcon(hIcon); // some icons can be freed, some not - so ignore any error return of DestroyIcon()
+                       } else
+                               hbmp = 0;
+
+                       TBADDBITMAP ab = {0, (UINT_PTR)hbmp};
+                       int bmp_idx = SendMessage(pThis->_htoolbar, TB_ADDBITMAP, 1, (LPARAM)&ab);
+
+                       TaskBarEntry entry;
+
+                       entry._id = pThis->_next_id++;
+                       entry._hbmp = hbmp;
+                       entry._bmp_idx = bmp_idx;
+                       entry._title = title;
+
+                       pThis->_map[hwnd] = entry;
+                       found = pThis->_map.find(hwnd);
+               }
+
+               TBBUTTON btn = {-2/*I_IMAGENONE*/, 0, TBSTATE_ENABLED/*|TBSTATE_ELLIPSES*/, BTNS_BUTTON, {0, 0}, 0, 0};
+               TaskBarEntry& entry = found->second;
+
+               ++entry._used;
+               btn.idCommand = entry._id;
+
+               HWND foreground = GetForegroundWindow();
+               HWND foreground_owner = GetWindow(foreground, GW_OWNER);
+
+               if (hwnd==foreground || hwnd==foreground_owner) {
+                       btn.fsState |= TBSTATE_PRESSED|TBSTATE_CHECKED;
+                       pThis->_last_foreground_wnd = hwnd;
+               }
+
+               if (!last_id) {
+                        // create new toolbar buttons for new windows
+                       if (title[0])
+                               btn.iString = (INT_PTR)title;
+
+                       btn.iBitmap = entry._bmp_idx;
+                       entry._btn_idx = SendMessage(pThis->_htoolbar, TB_BUTTONCOUNT, 0, 0);
+
+                       SendMessage(pThis->_htoolbar, TB_INSERTBUTTON, entry._btn_idx, (LPARAM)&btn);
+
+                       pThis->ResizeButtons();
+               } else {
+                        // refresh attributes of existing buttons
+                       if (btn.fsState != entry._fsState)
+                               SendMessage(pThis->_htoolbar, TB_SETSTATE, entry._id, MAKELONG(btn.fsState,0));
+
+                       if (entry._title != title) {
+                               TBBUTTONINFO info;
+
+                               info.cbSize = sizeof(TBBUTTONINFO);
+                               info.dwMask = TBIF_TEXT;
+                               info.pszText = title;
+
+                               SendMessage(pThis->_htoolbar, TB_SETBUTTONINFO, entry._id, (LPARAM)&info);
+
+                               entry._title = title;
+                       }
+               }
+
+               entry._fsState = btn.fsState;
+
+#ifdef __REACTOS__     // now handled by activating the ARW_HIDE flag with SystemParametersInfo(SPI_SETMINIMIZEDMETRICS)
+                // move minimized windows out of sight
+               if (IsIconic(hwnd)) {
+                       RECT rect;
+
+                       GetWindowRect(hwnd, &rect);
+
+                       if (rect.bottom > 0)
+                               SetWindowPos(hwnd, 0, -32000, -32000, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
+               }
+#endif
+       }
+
+       return TRUE;
+}
+
+void TaskBar::Refresh()
+{
+       for(TaskBarMap::iterator it=_map.begin(); it!=_map.end(); ++it)
+               it->second._used = 0;
+
+       EnumWindows(EnumWndProc, (LPARAM)this);
+       //EnumDesktopWindows(GetThreadDesktop(GetCurrentThreadId()), EnumWndProc, (LPARAM)_htoolbar);
+
+       set<int> btn_idx_to_delete;
+       set<HBITMAP> hbmp_to_delete;
+
+       for(TaskBarMap::iterator it=_map.begin(); it!=_map.end(); ++it) {
+               TaskBarEntry& entry = it->second;
+
+               if (!entry._used && entry._id) {
+                        // store button indexes to remove
+                       btn_idx_to_delete.insert(entry._btn_idx);
+                       hbmp_to_delete.insert(entry._hbmp);
+                       entry._id = 0;
+               }
+       }
+
+       if (!btn_idx_to_delete.empty()) {
+                // remove buttons from right to left
+               for(set<int>::reverse_iterator it=btn_idx_to_delete.rbegin(); it!=btn_idx_to_delete.rend(); ++it) {
+                       int idx = *it;
+
+                       if (!SendMessage(_htoolbar, TB_DELETEBUTTON, idx, 0))
+                               MessageBoxW(NULL, L"failed to delete button", NULL, MB_OK);
+
+
+                       for(TaskBarMap::iterator it2=_map.begin(); it2!=_map.end(); ++it2) {
+                               TaskBarEntry& entry = it2->second;
+
+                                // adjust button indexes
+                               if (entry._btn_idx > idx) {
+                                       --entry._btn_idx;
+#if 0
+                                       --entry._bmp_idx;
+
+                                       TBBUTTONINFO info;
+
+                                       info.cbSize = sizeof(TBBUTTONINFO);
+                                       info.dwMask = TBIF_IMAGE;
+                                       info.iImage = entry._bmp_idx;
+
+                                       if (!SendMessage(_htoolbar, TB_SETBUTTONINFO, entry._id, (LPARAM)&info))
+                                               MessageBoxW(NULL, L"failed to set button info", NULL, MB_OK);
+#endif
+                               }
+                       }
+
+               }
+
+               for(set<HBITMAP>::iterator it=hbmp_to_delete.begin(); it!=hbmp_to_delete.end(); ++it) {
+                       HBITMAP hbmp = *it;
+#if 0
+                       TBREPLACEBITMAP tbrepl = {0, (UINT_PTR)hbmp, 0, 0};
+                       SendMessage(_htoolbar, TB_REPLACEBITMAP, 0, (LPARAM)&tbrepl);
+#endif
+                       DeleteObject(hbmp);
+
+                       for(TaskBarMap::iterator it=_map.begin(); it!=_map.end(); ++it)
+                               if (it->second._hbmp == hbmp) {
+                                       _map.erase(it);
+                                       break;
+                               }
+               }
+
+               ResizeButtons();
+       }
+}
+
+TaskBarMap::iterator TaskBarMap::find_id(int id)
+{
+       for(iterator it=begin(); it!=end(); ++it)
+               if (it->second._id == id)
+                       return it;
+
+       return end();
+}
+
+void TaskBar::ResizeButtons()
+{
+       int btns = _map.size();
+
+       if (btns > 0) {
+               int bar_width = ClientRect(_hwnd).right;
+               int btn_width = (bar_width / btns) - 3;
+
+               if (btn_width < TASKBUTTONWIDTH_MIN)
+                       btn_width = TASKBUTTONWIDTH_MIN;
+               else if (btn_width > TASKBUTTONWIDTH_MAX)
+                       btn_width = TASKBUTTONWIDTH_MAX;
+
+               if (btn_width != _last_btn_width) {
+                       _last_btn_width = btn_width;
+
+                       SendMessage(_htoolbar, TB_SETBUTTONWIDTH, 0, MAKELONG(btn_width,btn_width));
+                       SendMessage(_htoolbar, TB_AUTOSIZE, 0, 0);
+               }
+       }
+}