2 * Copyright 2003, 2004, 2005 Martin Fuchs
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 // Martin Fuchs, 16.08.2003
32 #include "traynotify.h" // for NOTIFYAREA_WIDTH_DEF
35 DynamicFct
<BOOL (WINAPI
*)(HWND hwnd
)> g_SetTaskmanWindow(TEXT("user32"), "SetTaskmanWindow");
36 DynamicFct
<BOOL (WINAPI
*)(HWND hwnd
)> g_RegisterShellHookWindow(TEXT("user32"), "RegisterShellHookWindow");
37 DynamicFct
<BOOL (WINAPI
*)(HWND hwnd
)> g_DeregisterShellHookWindow(TEXT("user32"), "DeregisterShellHookWindow");
40 DynamicFct<BOOL (WINAPI*)(HWND hWnd, DWORD dwType)> g_RegisterShellHook(TEXT("shell32"), (LPCSTR)0xb5);
42 // constants for RegisterShellHook()
43 #define RSH_UNREGISTER 0
44 #define RSH_REGISTER 1
45 #define RSH_REGISTER_PROGMAN 2
46 #define RSH_REGISTER_TASKMAN 3
50 TaskBarEntry::TaskBarEntry()
60 TaskBarMap::~TaskBarMap()
63 iterator it
= begin();
64 DeleteBitmap(it
->second
._hbmp
);
70 TaskBar::TaskBar(HWND hwnd
)
72 WM_SHELLHOOK(RegisterWindowMessage(WINMSG_SHELLHOOK
))
76 _mmMetrics_org
.cbSize
= sizeof(MINIMIZEDMETRICS
);
78 SystemParametersInfo(SPI_GETMINIMIZEDMETRICS
, sizeof(_mmMetrics_org
), &_mmMetrics_org
, 0);
80 // configure the window manager to hide windows when they are minimized
81 // This is neccessary to enable shell hook messages.
82 if (!(_mmMetrics_org
.iArrange
& ARW_HIDE
)) {
83 MINIMIZEDMETRICS _mmMetrics_new
= _mmMetrics_org
;
85 _mmMetrics_new
.iArrange
|= ARW_HIDE
;
87 SystemParametersInfo(SPI_SETMINIMIZEDMETRICS
, sizeof(_mmMetrics_new
), &_mmMetrics_new
, 0);
93 // if (g_RegisterShellHook)
94 // (*g_RegisterShellHook)(_hwnd, RSH_UNREGISTER);
96 if (g_DeregisterShellHookWindow
)
97 (*g_DeregisterShellHookWindow
)(_hwnd
);
101 if (g_SetTaskmanWindow
)
102 (*g_SetTaskmanWindow
)(0);
104 SystemParametersInfo(SPI_GETMINIMIZEDMETRICS
, sizeof(_mmMetrics_org
), &_mmMetrics_org
, 0);
107 HWND
TaskBar::Create(HWND hwndParent
)
109 ClientRect
clnt(hwndParent
);
111 int taskbar_pos
= 80; // This start position will be adjusted in DesktopBar::Resize().
113 return Window::Create(WINDOW_CREATOR(TaskBar
), 0,
114 BtnWindowClass(CLASSNAME_TASKBAR
), TITLE_TASKBAR
,
115 WS_CHILD
|WS_VISIBLE
| CCS_TOP
|CCS_NODIVIDER
|CCS_NORESIZE
,
116 taskbar_pos
, 0, clnt
.right
-taskbar_pos
-(NOTIFYAREA_WIDTH_DEF
+1), clnt
.bottom
, hwndParent
);
119 LRESULT
TaskBar::Init(LPCREATESTRUCT pcs
)
121 if (super::Init(pcs
))
124 /* FIXME: There's an internal padding for non-flat toolbar. Get rid of it somehow. */
125 _htoolbar
= CreateToolbarEx(_hwnd
,
126 WS_CHILD
|WS_VISIBLE
|WS_CLIPSIBLINGS
|WS_CLIPCHILDREN
|
127 CCS_TOP
|CCS_NODIVIDER
|TBSTYLE_LIST
|TBSTYLE_TOOLTIPS
|TBSTYLE_WRAPABLE
,//|TBSTYLE_AUTOSIZE
128 IDW_TASKTOOLBAR
, 0, 0, 0, NULL
, 0, 0, 0, 16, 16, sizeof(TBBUTTON
));
130 SendMessage(_htoolbar
, TB_SETBUTTONWIDTH
, 0, MAKELONG(TASKBUTTONWIDTH_MAX
,TASKBUTTONWIDTH_MAX
));
131 //SendMessage(_htoolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
132 //SendMessage(_htoolbar, TB_SETDRAWTEXTFLAGS, DT_CENTER|DT_VCENTER, DT_CENTER|DT_VCENTER);
133 //SetWindowFont(_htoolbar, GetStockFont(ANSI_VAR_FONT), FALSE);
134 //SendMessage(_htoolbar, TB_SETPADDING, 0, MAKELPARAM(8,8));
136 #ifndef __MINGW32__ // TBMETRICS missing in MinGW (as of 20.09.2005)
137 // set metrics for the Taskbar toolbar to enable button spacing
140 metrics
.cbSize
= sizeof(TBMETRICS
);
141 metrics
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
142 metrics
.cxBarPad
= 0;
143 metrics
.cyBarPad
= 0;
144 metrics
.cxButtonSpacing
= 3;
145 metrics
.cyButtonSpacing
= 3;
147 SendMessage(_htoolbar
, TB_SETMETRICS
, 0, (LPARAM
)&metrics
);
150 _next_id
= IDC_FIRST_APP
;
152 // register the taskbar window as task manager window to make the following call to RegisterShellHookWindow working
153 if (g_SetTaskmanWindow
)
154 (*g_SetTaskmanWindow
)(_hwnd
);
156 if (g_RegisterShellHookWindow
) {
157 LOG(TEXT("Using shell hooks for notification of shell events."));
159 (*g_RegisterShellHookWindow
)(_hwnd
);
161 LOG(TEXT("Shell hooks not available."));
163 SetTimer(_hwnd
, 0, 200, NULL
);
166 /* Alternatively we could use the RegisterShellHook() function in SHELL32, but this is not yet implemented in the WINE code.
167 if (g_RegisterShellHook) {
168 (*g_RegisterShellHook)(0, RSH_REGISTER);
170 if ((HIWORD(GetVersion())>>14) == W_VER_NT)
171 (*g_RegisterShellHook)(_hwnd, RSH_REGISTER_TASKMAN);
173 (*g_RegisterShellHook)(_hwnd, RSH_REGISTER);
181 LRESULT
TaskBar::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
185 SendMessage(_htoolbar
, WM_SIZE
, 0, 0);
193 case WM_CONTEXTMENU
: {
195 ScreenToClient(_htoolbar
, &pt
);
197 if ((HWND
)wparam
==_htoolbar
&& SendMessage(_htoolbar
, TB_HITTEST
, 0, (LPARAM
)&pt
)>=0)
198 break; // avoid displaying context menu for application button _and_ desktop bar at the same time
202 case PM_GET_LAST_ACTIVE
:
203 return (LRESULT
)(HWND
)_last_foreground_wnd
;
205 case WM_SYSCOLORCHANGE
:
206 SendMessage(_htoolbar
, WM_SYSCOLORCHANGE
, 0, 0);
210 if (nmsg
== WM_SHELLHOOK
) {
212 case HSHELL_WINDOWCREATED
:
213 case HSHELL_WINDOWDESTROYED
:
214 case HSHELL_WINDOWACTIVATED
:
219 #ifdef HSHELL_RUDEAPPACTIVATED
220 case HSHELL_RUDEAPPACTIVATED
:
226 return super::WndProc(nmsg
, wparam
, lparam
);
233 int TaskBar::Command(int id
, int code
)
235 TaskBarMap::iterator found
= _map
.find_id(id
);
237 if (found
!= _map
.end()) {
242 return super::Command(id
, code
);
245 int TaskBar::Notify(int id
, NMHDR
* pnmh
)
247 if (pnmh
->hwndFrom
== _htoolbar
)
250 TBBUTTONINFO btninfo
;
251 TaskBarMap::iterator it
;
252 Point
pt(GetMessagePos());
253 ScreenToClient(_htoolbar
, &pt
);
255 btninfo
.cbSize
= sizeof(TBBUTTONINFO
);
256 btninfo
.dwMask
= TBIF_BYINDEX
|TBIF_COMMAND
;
258 int idx
= SendMessage(_htoolbar
, TB_HITTEST
, 0, (LPARAM
)&pt
);
261 SendMessage(_htoolbar
, TB_GETBUTTONINFO
, idx
, (LPARAM
)&btninfo
)!=-1 &&
262 (it
=_map
.find_id(btninfo
.idCommand
))!=_map
.end()) {
263 //TaskBarEntry& entry = it->second;
265 ActivateApp(it
, false, false); // don't restore minimized windows on right button click
267 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
268 static DynamicFct
<DWORD(STDAPICALLTYPE
*)(RESTRICTIONS
)> pSHRestricted(TEXT("SHELL32"), "SHRestricted");
270 if (pSHRestricted
&& !(*pSHRestricted
)(REST_NOTRAYCONTEXTMENU
))
272 ShowAppSystemMenu(it
);
277 return super::Notify(id
, pnmh
);
284 void TaskBar::ActivateApp(TaskBarMap::iterator it
, bool can_minimize
, bool can_restore
)
286 HWND hwnd
= it
->first
;
288 bool minimize_it
= can_minimize
&& !IsIconic(hwnd
) &&
289 (hwnd
==GetForegroundWindow() || hwnd
==_last_foreground_wnd
);
291 // switch to selected application window
292 if (can_restore
&& !minimize_it
)
294 PostMessage(hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0);
296 // In case minimize_it is true, we _have_ to switch to the app before
297 // posting SW_MINIMIZE to be compatible with some applications (e.g. "Sleipnir")
298 SetForegroundWindow(hwnd
);
301 PostMessage(hwnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0);
302 _last_foreground_wnd
= 0;
304 _last_foreground_wnd
= hwnd
;
309 void TaskBar::ShowAppSystemMenu(TaskBarMap::iterator it
)
311 HMENU hmenu
= GetSystemMenu(it
->first
, FALSE
);
317 int cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
|TPM_RIGHTBUTTON
|TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, _hwnd
, NULL
);
320 ActivateApp(it
, false, false); // reactivate window after the context menu has closed
321 PostMessage(it
->first
, WM_SYSCOMMAND
, cmd
, 0);
327 HICON
get_window_icon_small(HWND hwnd
)
331 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
334 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
337 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
340 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICONSM
);
343 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICON
);
346 SendMessageTimeout(hwnd
, WM_QUERYDRAGICON
, 0, 0, 0, 1000, (LPDWORD
)&hIcon
);
351 HICON
get_window_icon_big(HWND hwnd
, bool allow_from_class
)
355 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
358 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
361 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
363 if (allow_from_class
) {
365 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICON
);
368 hIcon
= (HICON
)GetClassLongPtr(hwnd
, GCL_HICONSM
);
372 SendMessageTimeout(hwnd
, WM_QUERYDRAGICON
, 0, 0, 0, 1000, (LPDWORD
)&hIcon
);
377 // fill task bar with buttons for enumerated top level windows
378 BOOL CALLBACK
TaskBar::EnumWndProc(HWND hwnd
, LPARAM lparam
)
380 TaskBar
* pThis
= (TaskBar
*)lparam
;
382 DWORD style
= GetWindowStyle(hwnd
);
383 DWORD ex_style
= GetWindowExStyle(hwnd
);
385 if ((style
&WS_VISIBLE
) && !(ex_style
&WS_EX_TOOLWINDOW
) &&
386 !GetParent(hwnd
) && !GetWindow(hwnd
,GW_OWNER
)) {
387 TCHAR title
[BUFFER_LEN
];
389 if (!GetWindowText(hwnd
, title
, BUFFER_LEN
))
392 TaskBarMap::iterator found
= pThis
->_map
.find(hwnd
);
395 if (found
!= pThis
->_map
.end()) {
396 last_id
= found
->second
._id
;
399 found
->second
._id
= pThis
->_next_id
++;
402 HICON hIcon
= get_window_icon_small(hwnd
);
403 BOOL delete_icon
= FALSE
;
406 hIcon
= LoadIcon(0, IDI_APPLICATION
);
411 hbmp
= create_bitmap_from_icon(hIcon
, GetSysColorBrush(COLOR_BTNFACE
), WindowCanvas(pThis
->_htoolbar
));
413 DestroyIcon(hIcon
); // some icons can be freed, some not - so ignore any error return of DestroyIcon()
417 TBADDBITMAP ab
= {0, (UINT_PTR
)hbmp
};
418 int bmp_idx
= SendMessage(pThis
->_htoolbar
, TB_ADDBITMAP
, 1, (LPARAM
)&ab
);
422 entry
._id
= pThis
->_next_id
++;
424 entry
._bmp_idx
= bmp_idx
;
425 entry
._title
= title
;
427 pThis
->_map
[hwnd
] = entry
;
428 found
= pThis
->_map
.find(hwnd
);
431 TBBUTTON btn
= {-2/*I_IMAGENONE*/, 0, TBSTATE_ENABLED
/*|TBSTATE_ELLIPSES*/, BTNS_BUTTON
, {0, 0}, 0, 0};
432 TaskBarEntry
& entry
= found
->second
;
435 btn
.idCommand
= entry
._id
;
437 HWND foreground
= GetForegroundWindow();
438 HWND foreground_owner
= GetWindow(foreground
, GW_OWNER
);
440 if (hwnd
==foreground
|| hwnd
==foreground_owner
) {
441 btn
.fsState
|= TBSTATE_PRESSED
|TBSTATE_CHECKED
;
442 pThis
->_last_foreground_wnd
= hwnd
;
446 // create new toolbar buttons for new windows
448 btn
.iString
= (INT_PTR
)title
;
450 btn
.iBitmap
= entry
._bmp_idx
;
451 entry
._btn_idx
= SendMessage(pThis
->_htoolbar
, TB_BUTTONCOUNT
, 0, 0);
453 SendMessage(pThis
->_htoolbar
, TB_INSERTBUTTON
, entry
._btn_idx
, (LPARAM
)&btn
);
455 pThis
->ResizeButtons();
457 // refresh attributes of existing buttons
458 if (btn
.fsState
!= entry
._fsState
)
459 SendMessage(pThis
->_htoolbar
, TB_SETSTATE
, entry
._id
, MAKELONG(btn
.fsState
,0));
461 if (entry
._title
!= title
) {
464 info
.cbSize
= sizeof(TBBUTTONINFO
);
465 info
.dwMask
= TBIF_TEXT
;
466 info
.pszText
= title
;
468 SendMessage(pThis
->_htoolbar
, TB_SETBUTTONINFO
, entry
._id
, (LPARAM
)&info
);
470 entry
._title
= title
;
474 entry
._fsState
= btn
.fsState
;
476 #ifdef __REACTOS__ // now handled by activating the ARW_HIDE flag with SystemParametersInfo(SPI_SETMINIMIZEDMETRICS)
477 // move minimized windows out of sight
478 if (IsIconic(hwnd
)) {
481 GetWindowRect(hwnd
, &rect
);
484 SetWindowPos(hwnd
, 0, -32000, -32000, 0, 0, SWP_NOSIZE
|SWP_NOZORDER
|SWP_NOACTIVATE
);
492 void TaskBar::Refresh()
494 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
)
495 it
->second
._used
= 0;
497 EnumWindows(EnumWndProc
, (LPARAM
)this);
498 //EnumDesktopWindows(GetThreadDesktop(GetCurrentThreadId()), EnumWndProc, (LPARAM)_htoolbar);
500 set
<int> btn_idx_to_delete
;
501 set
<HBITMAP
> hbmp_to_delete
;
503 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
) {
504 TaskBarEntry
& entry
= it
->second
;
506 if (!entry
._used
&& entry
._id
) {
507 // store button indexes to remove
508 btn_idx_to_delete
.insert(entry
._btn_idx
);
509 hbmp_to_delete
.insert(entry
._hbmp
);
514 if (!btn_idx_to_delete
.empty()) {
515 // remove buttons from right to left
516 for(set
<int>::reverse_iterator it
=btn_idx_to_delete
.rbegin(); it
!=btn_idx_to_delete
.rend(); ++it
) {
519 SendMessage(_htoolbar
, TB_DELETEBUTTON
, idx
, 0);
521 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
) {
522 TaskBarEntry
& entry
= it
->second
;
524 // adjust button indexes
525 if (entry
._btn_idx
> idx
) {
531 info
.cbSize
= sizeof(TBBUTTONINFO
);
532 info
.dwMask
= TBIF_IMAGE
;
533 info
.iImage
= entry
._bmp_idx
;
535 SendMessage(_htoolbar
, TB_SETBUTTONINFO
, entry
._id
, (LPARAM
)&info
);
540 for(set
<HBITMAP
>::iterator it
=hbmp_to_delete
.begin(); it
!=hbmp_to_delete
.end(); ++it
) {
543 TBREPLACEBITMAP tbrepl
= {0, (UINT_PTR
)hbmp
, 0, 0};
544 SendMessage(_htoolbar
, TB_REPLACEBITMAP
, 0, (LPARAM
)&tbrepl
);
548 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
)
549 if (it
->second
._hbmp
== hbmp
) {
559 TaskBarMap::iterator
TaskBarMap::find_id(int id
)
561 for(iterator it
=begin(); it
!=end(); ++it
)
562 if (it
->second
._id
== id
)
568 void TaskBar::ResizeButtons()
570 int btns
= _map
.size();
573 int bar_width
= ClientRect(_hwnd
).right
;
574 int btn_width
= (bar_width
/ btns
) - 3;
576 if (btn_width
< TASKBUTTONWIDTH_MIN
)
577 btn_width
= TASKBUTTONWIDTH_MIN
;
578 else if (btn_width
> TASKBUTTONWIDTH_MAX
)
579 btn_width
= TASKBUTTONWIDTH_MAX
;
581 if (btn_width
!= _last_btn_width
) {
582 _last_btn_width
= btn_width
;
584 SendMessage(_htoolbar
, TB_SETBUTTONWIDTH
, 0, MAKELONG(btn_width
,btn_width
));
585 SendMessage(_htoolbar
, TB_AUTOSIZE
, 0, 0);