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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
;
206 if (nmsg
== WM_SHELLHOOK
) {
208 case HSHELL_WINDOWCREATED
:
209 case HSHELL_WINDOWDESTROYED
:
210 case HSHELL_WINDOWACTIVATED
:
215 #ifdef HSHELL_RUDEAPPACTIVATED
216 case HSHELL_RUDEAPPACTIVATED
:
222 return super::WndProc(nmsg
, wparam
, lparam
);
229 int TaskBar::Command(int id
, int code
)
231 TaskBarMap::iterator found
= _map
.find_id(id
);
233 if (found
!= _map
.end()) {
238 return super::Command(id
, code
);
241 int TaskBar::Notify(int id
, NMHDR
* pnmh
)
243 if (pnmh
->hwndFrom
== _htoolbar
)
246 TBBUTTONINFO btninfo
;
247 TaskBarMap::iterator it
;
248 Point
pt(GetMessagePos());
249 ScreenToClient(_htoolbar
, &pt
);
251 btninfo
.cbSize
= sizeof(TBBUTTONINFO
);
252 btninfo
.dwMask
= TBIF_BYINDEX
|TBIF_COMMAND
;
254 int idx
= SendMessage(_htoolbar
, TB_HITTEST
, 0, (LPARAM
)&pt
);
257 SendMessage(_htoolbar
, TB_GETBUTTONINFO
, idx
, (LPARAM
)&btninfo
)!=-1 &&
258 (it
=_map
.find_id(btninfo
.idCommand
))!=_map
.end()) {
259 //TaskBarEntry& entry = it->second;
261 ActivateApp(it
, false, false); // don't restore minimized windows on right button click
263 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
264 static DynamicFct
<DWORD(STDAPICALLTYPE
*)(RESTRICTIONS
)> pSHRestricted(TEXT("SHELL32"), "SHRestricted");
266 if (pSHRestricted
&& !(*pSHRestricted
)(REST_NOTRAYCONTEXTMENU
))
268 ShowAppSystemMenu(it
);
273 return super::Notify(id
, pnmh
);
280 void TaskBar::ActivateApp(TaskBarMap::iterator it
, bool can_minimize
, bool can_restore
)
282 HWND hwnd
= it
->first
;
284 bool minimize_it
= can_minimize
&& !IsIconic(hwnd
) &&
285 (hwnd
==GetForegroundWindow() || hwnd
==_last_foreground_wnd
);
287 // switch to selected application window
288 if (can_restore
&& !minimize_it
)
290 PostMessage(hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0);
292 // In case minimize_it is true, we _have_ to switch to the app before
293 // posting SW_MINIMIZE to be compatible with some applications (e.g. "Sleipnir")
294 SetForegroundWindow(hwnd
);
297 PostMessage(hwnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0);
298 _last_foreground_wnd
= 0;
300 _last_foreground_wnd
= hwnd
;
305 void TaskBar::ShowAppSystemMenu(TaskBarMap::iterator it
)
307 HMENU hmenu
= GetSystemMenu(it
->first
, FALSE
);
313 int cmd
= TrackPopupMenu(hmenu
, TPM_LEFTBUTTON
|TPM_RIGHTBUTTON
|TPM_RETURNCMD
, pt
.x
, pt
.y
, 0, _hwnd
, NULL
);
316 ActivateApp(it
, false, false); // reactivate window after the context menu has closed
317 PostMessage(it
->first
, WM_SYSCOMMAND
, cmd
, 0);
323 HICON
get_window_icon_small(HWND hwnd
)
327 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
330 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
333 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
336 hIcon
= (HICON
)GetClassLong(hwnd
, GCL_HICONSM
);
339 hIcon
= (HICON
)GetClassLong(hwnd
, GCL_HICON
);
342 SendMessageTimeout(hwnd
, WM_QUERYDRAGICON
, 0, 0, 0, 1000, (LPDWORD
)&hIcon
);
347 HICON
get_window_icon_big(HWND hwnd
, bool allow_from_class
)
351 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_BIG
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
354 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL2
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
357 SendMessageTimeout(hwnd
, WM_GETICON
, ICON_SMALL
, 0, SMTO_ABORTIFHUNG
, 1000, (LPDWORD
)&hIcon
);
359 if (allow_from_class
) {
361 hIcon
= (HICON
)GetClassLong(hwnd
, GCL_HICON
);
364 hIcon
= (HICON
)GetClassLong(hwnd
, GCL_HICONSM
);
368 SendMessageTimeout(hwnd
, WM_QUERYDRAGICON
, 0, 0, 0, 1000, (LPDWORD
)&hIcon
);
373 // fill task bar with buttons for enumerated top level windows
374 BOOL CALLBACK
TaskBar::EnumWndProc(HWND hwnd
, LPARAM lparam
)
376 TaskBar
* pThis
= (TaskBar
*)lparam
;
378 DWORD style
= GetWindowStyle(hwnd
);
379 DWORD ex_style
= GetWindowExStyle(hwnd
);
381 if ((style
&WS_VISIBLE
) && !(ex_style
&WS_EX_TOOLWINDOW
) &&
382 !GetParent(hwnd
) && !GetWindow(hwnd
,GW_OWNER
)) {
383 TCHAR title
[BUFFER_LEN
];
385 if (!GetWindowText(hwnd
, title
, BUFFER_LEN
))
388 TaskBarMap::iterator found
= pThis
->_map
.find(hwnd
);
391 if (found
!= pThis
->_map
.end()) {
392 last_id
= found
->second
._id
;
395 found
->second
._id
= pThis
->_next_id
++;
398 HICON hIcon
= get_window_icon_small(hwnd
);
399 BOOL delete_icon
= FALSE
;
402 hIcon
= LoadIcon(0, IDI_APPLICATION
);
407 hbmp
= create_bitmap_from_icon(hIcon
, GetSysColorBrush(COLOR_BTNFACE
), WindowCanvas(pThis
->_htoolbar
));
409 DestroyIcon(hIcon
); // some icons can be freed, some not - so ignore any error return of DestroyIcon()
413 TBADDBITMAP ab
= {0, (UINT_PTR
)hbmp
};
414 int bmp_idx
= SendMessage(pThis
->_htoolbar
, TB_ADDBITMAP
, 1, (LPARAM
)&ab
);
418 entry
._id
= pThis
->_next_id
++;
420 entry
._bmp_idx
= bmp_idx
;
421 entry
._title
= title
;
423 pThis
->_map
[hwnd
] = entry
;
424 found
= pThis
->_map
.find(hwnd
);
427 TBBUTTON btn
= {-2/*I_IMAGENONE*/, 0, TBSTATE_ENABLED
/*|TBSTATE_ELLIPSES*/, BTNS_BUTTON
, {0, 0}, 0, 0};
428 TaskBarEntry
& entry
= found
->second
;
431 btn
.idCommand
= entry
._id
;
433 HWND foreground
= GetForegroundWindow();
434 HWND foreground_owner
= GetWindow(foreground
, GW_OWNER
);
436 if (hwnd
==foreground
|| hwnd
==foreground_owner
) {
437 btn
.fsState
|= TBSTATE_PRESSED
|TBSTATE_CHECKED
;
438 pThis
->_last_foreground_wnd
= hwnd
;
442 // create new toolbar buttons for new windows
444 btn
.iString
= (INT_PTR
)title
;
446 btn
.iBitmap
= entry
._bmp_idx
;
447 entry
._btn_idx
= SendMessage(pThis
->_htoolbar
, TB_BUTTONCOUNT
, 0, 0);
449 SendMessage(pThis
->_htoolbar
, TB_INSERTBUTTON
, entry
._btn_idx
, (LPARAM
)&btn
);
451 pThis
->ResizeButtons();
453 // refresh attributes of existing buttons
454 if (btn
.fsState
!= entry
._fsState
)
455 SendMessage(pThis
->_htoolbar
, TB_SETSTATE
, entry
._id
, MAKELONG(btn
.fsState
,0));
457 if (entry
._title
!= title
) {
460 info
.cbSize
= sizeof(TBBUTTONINFO
);
461 info
.dwMask
= TBIF_TEXT
;
462 info
.pszText
= title
;
464 SendMessage(pThis
->_htoolbar
, TB_SETBUTTONINFO
, entry
._id
, (LPARAM
)&info
);
466 entry
._title
= title
;
470 entry
._fsState
= btn
.fsState
;
472 #ifdef _ROS_ // now handled by activating the ARW_HIDE flag with SystemParametersInfo(SPI_SETMINIMIZEDMETRICS)
473 // move minimized windows out of sight
474 if (IsIconic(hwnd
)) {
477 GetWindowRect(hwnd
, &rect
);
480 SetWindowPos(hwnd
, 0, -32000, -32000, 0, 0, SWP_NOSIZE
|SWP_NOZORDER
|SWP_NOACTIVATE
);
488 void TaskBar::Refresh()
490 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
)
491 it
->second
._used
= 0;
493 EnumWindows(EnumWndProc
, (LPARAM
)this);
494 //EnumDesktopWindows(GetThreadDesktop(GetCurrentThreadId()), EnumWndProc, (LPARAM)_htoolbar);
496 set
<int> btn_idx_to_delete
;
497 set
<HBITMAP
> hbmp_to_delete
;
499 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
) {
500 TaskBarEntry
& entry
= it
->second
;
502 if (!entry
._used
&& entry
._id
) {
503 // store button indexes to remove
504 btn_idx_to_delete
.insert(entry
._btn_idx
);
505 hbmp_to_delete
.insert(entry
._hbmp
);
510 if (!btn_idx_to_delete
.empty()) {
511 // remove buttons from right to left
512 for(set
<int>::reverse_iterator it
=btn_idx_to_delete
.rbegin(); it
!=btn_idx_to_delete
.rend(); ++it
) {
515 SendMessage(_htoolbar
, TB_DELETEBUTTON
, idx
, 0);
517 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
) {
518 TaskBarEntry
& entry
= it
->second
;
520 // adjust button indexes
521 if (entry
._btn_idx
> idx
) {
527 info
.cbSize
= sizeof(TBBUTTONINFO
);
528 info
.dwMask
= TBIF_IMAGE
;
529 info
.iImage
= entry
._bmp_idx
;
531 SendMessage(_htoolbar
, TB_SETBUTTONINFO
, entry
._id
, (LPARAM
)&info
);
536 for(set
<HBITMAP
>::iterator it
=hbmp_to_delete
.begin(); it
!=hbmp_to_delete
.end(); ++it
) {
539 TBREPLACEBITMAP tbrepl
= {0, (UINT_PTR
)hbmp
, 0, 0};
540 SendMessage(_htoolbar
, TB_REPLACEBITMAP
, 0, (LPARAM
)&tbrepl
);
544 for(TaskBarMap::iterator it
=_map
.begin(); it
!=_map
.end(); ++it
)
545 if (it
->second
._hbmp
== hbmp
) {
555 TaskBarMap::iterator
TaskBarMap::find_id(int id
)
557 for(iterator it
=begin(); it
!=end(); ++it
)
558 if (it
->second
._id
== id
)
564 void TaskBar::ResizeButtons()
566 int btns
= _map
.size();
569 int bar_width
= ClientRect(_hwnd
).right
;
570 int btn_width
= (bar_width
/ btns
) - 3;
572 if (btn_width
< TASKBUTTONWIDTH_MIN
)
573 btn_width
= TASKBUTTONWIDTH_MIN
;
574 else if (btn_width
> TASKBUTTONWIDTH_MAX
)
575 btn_width
= TASKBUTTONWIDTH_MAX
;
577 if (btn_width
!= _last_btn_width
) {
578 _last_btn_width
= btn_width
;
580 SendMessage(_htoolbar
, TB_SETBUTTONWIDTH
, 0, MAKELONG(btn_width
,btn_width
));
581 SendMessage(_htoolbar
, TB_AUTOSIZE
, 0, 0);