1 /*
2  * Copyright 2003, 2004 Martin Fuchs
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // desktopbar.cpp
24  //
25  // Martin Fuchs, 22.08.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 #include "desktopbar.h"
32 #include "taskbar.h"
33 #include "startmenu.h"
34 #include "traynotify.h"
35 #include "quicklaunch.h"
36 
37 #include "../dialogs/settings.h"
38 
39 
40 DesktopBar::DesktopBar(HWND hwnd)
41  :	super(hwnd),
42 #ifdef __REACTOS__
43 	_trayIcon(hwnd, ID_TRAY_VOLUME)
44 #else
45 	WM_TASKBARCREATED(RegisterWindowMessage(WINMSG_TASKBARCREATED))
46 #endif
47 {
48 	SetWindowIcon(hwnd, IDI_REACTOS);
49 
50 	SystemParametersInfo(SPI_GETWORKAREA, 0, &_work_area_org, 0);
51 }
52 
53 DesktopBar::~DesktopBar()
54 {
55 	 // restore work area to the previous size
56 	SystemParametersInfo(SPI_SETWORKAREA, 0, &_work_area_org, 0);
57 	PostMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETWORKAREA, 0);
58 
59 	 // exit application after destroying desktop window
60 	PostQuitMessage(0);
61 }
62 
63 
64 HWND DesktopBar::Create()
65 {
66 	static BtnWindowClass wcDesktopBar(CLASSNAME_EXPLORERBAR);
67 
68 	RECT rect;
69 
70 	rect.left = -2; // hide left border
71 #ifdef TASKBAR_AT_TOP
72 	rect.top = -2;	// hide top border
73 #else
74 	rect.top = GetSystemMetrics(SM_CYSCREEN) - DESKTOPBARBAR_HEIGHT;
75 #endif
76 	rect.right = GetSystemMetrics(SM_CXSCREEN) + 2;
77 	rect.bottom = rect.top + DESKTOPBARBAR_HEIGHT + 2;
78 
79 	return Window::Create(WINDOW_CREATOR(DesktopBar), WS_EX_PALETTEWINDOW,
80 							wcDesktopBar, TITLE_EXPLORERBAR,
81 							WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE,
82 							rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
83 }
84 
85 
86 LRESULT DesktopBar::Init(LPCREATESTRUCT pcs)
87 {
88 	if (super::Init(pcs))
89 		return 1;
90 
91 	 // create start button
92 	ResString start_str(IDS_START);
93 	WindowCanvas canvas(_hwnd);
94 	FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT));
95 	RECT rect = {0, 0};
96 	DrawText(canvas, start_str, -1, &rect, DT_SINGLELINE|DT_CALCRECT);
97 	int start_btn_width = rect.right+16+8;
98 
99 	_taskbar_pos = start_btn_width + 6;
100 
101 	 // create "Start" button
102 	HWND hwndStart = Button(_hwnd, start_str, 1, 1, start_btn_width, REBARBAND_HEIGHT, IDC_START, WS_VISIBLE|WS_CHILD|BS_OWNERDRAW);
103 	SetWindowFont(hwndStart, GetStockFont(ANSI_VAR_FONT), FALSE);
104 	new StartButton(hwndStart);
105 
106 	/* Save the handle to the window, needed for push-state handling */
107 	_hwndStartButton = hwndStart;
108 
109 	 // disable double clicks
110 	SetClassLongPtr(hwndStart, GCL_STYLE, GetClassLongPtr(hwndStart, GCL_STYLE) & ~CS_DBLCLKS);
111 
112 	 // create task bar
113 	_hwndTaskBar = TaskBar::Create(_hwnd);
114 
115 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOTRAYITEMSDISPLAY))
116 		 // create tray notification area
117 		_hwndNotify = NotifyArea::Create(_hwnd);
118 
119 
120 	 // notify all top level windows about the successfully created desktop bar
121 	 //@@ Use SendMessage() instead of PostMessage() to avoid problems with delayed created shell service objects?
122 	PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0);
123 
124 
125 	_hwndQuickLaunch = QuickLaunchBar::Create(_hwnd);
126 
127 	 // create rebar window to manage task and quick launch bar
128 #ifndef _NO_REBAR
129 	_hwndrebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL,
130 					WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|
131 					RBS_VARHEIGHT|RBS_AUTOSIZE|RBS_DBLCLKTOGGLE|	//|RBS_REGISTERDROP
132 					CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_TOP,
133 					0, 0, 0, 0, _hwnd, 0, g_Globals._hInstance, 0);
134 
135 	REBARBANDINFO rbBand;
136 
137 	rbBand.cbSize = sizeof(REBARBANDINFO);
138 	rbBand.fMask  = RBBIM_TEXT|RBBIM_STYLE|RBBIM_CHILD|RBBIM_CHILDSIZE|RBBIM_SIZE|RBBIM_ID|RBBIM_IDEALSIZE;
139 	rbBand.cyChild = REBARBAND_HEIGHT;
140 	rbBand.cyMaxChild = (ULONG)-1;
141 	rbBand.cyMinChild = REBARBAND_HEIGHT;
142 	rbBand.cyIntegral = REBARBAND_HEIGHT + 3;	//@@ OK?
143 	rbBand.fStyle = RBBS_VARIABLEHEIGHT|RBBS_GRIPPERALWAYS|RBBS_HIDETITLE;
144 
145 	TCHAR QuickLaunchBand[] = _T("Quicklaunch");
146 	rbBand.lpText = QuickLaunchBand;
147 	rbBand.hwndChild = _hwndQuickLaunch;
148 	rbBand.cx = 150;
149 	rbBand.cxMinChild = 150;
150 	rbBand.wID = IDW_QUICKLAUNCHBAR;
151 	SendMessage(_hwndrebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
152 
153 	TCHAR TaskbarBand[] = _T("Taskbar");
154 	rbBand.lpText = TaskbarBand;
155 	rbBand.hwndChild = _hwndTaskBar;
156 	rbBand.cx = 200;	//pcs->cx-_taskbar_pos-quicklaunch_width-(notifyarea_width+1);
157 	rbBand.cxMinChild = 50;
158 	rbBand.wID = IDW_TASKTOOLBAR;
159 	SendMessage(_hwndrebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
160 #endif
161 
162 
163 	RegisterHotkeys();
164 
165 	 // prepare Startmenu, but hide it for now
166 	_startMenuRoot = GET_WINDOW(StartMenuRoot, StartMenuRoot::Create(_hwndStartButton, STARTMENUROOT_ICON_SIZE));
167 	_startMenuRoot->_hwndStartButton = _hwndStartButton;
168 
169 	return 0;
170 }
171 
172 
173 StartButton::StartButton(HWND hwnd)
174  :	PictureButton(hwnd, SmallIcon(IDI_STARTMENU), GetSysColorBrush(COLOR_BTNFACE))
175 {
176 }
177 
178 LRESULT	StartButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
179 {
180 	switch(nmsg) {
181 	   // one click activation: handle button-down message, don't wait for button-up
182 	  case WM_LBUTTONDOWN:
183 		if (!Button_GetState(_hwnd)) {
184 			Button_SetState(_hwnd, TRUE);
185 
186 			SetCapture(_hwnd);
187 
188 			SendMessage(GetParent(_hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(_hwnd),0), 0);
189 		}
190 
191 		Button_SetState(_hwnd, FALSE);
192 		break;
193 
194 	   // re-target mouse move messages while moving the mouse cursor through the start menu
195 	  case WM_MOUSEMOVE:
196 		if (GetCapture() == _hwnd) {
197 			POINT pt = {GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)};
198 
199 			ClientToScreen(_hwnd, &pt);
200 			HWND hwnd = WindowFromPoint(pt);
201 
202 			if (hwnd && hwnd!=_hwnd) {
203 				ScreenToClient(hwnd, &pt);
204 				SendMessage(hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt.x, pt.y));
205 			}
206 		}
207 		break;
208 
209 	  case WM_LBUTTONUP:
210 		if (GetCapture() == _hwnd) {
211 			ReleaseCapture();
212 
213 			POINT pt = {GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)};
214 
215 			ClientToScreen(_hwnd, &pt);
216 			HWND hwnd = WindowFromPoint(pt);
217 
218 			if (hwnd && hwnd!=_hwnd) {
219 				ScreenToClient(hwnd, &pt);
220 				PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
221 				PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
222 			}
223 		}
224 		break;
225 
226 	  case WM_CANCELMODE:
227 		ReleaseCapture();
228 		break;
229 
230 	  default:
231 		return super::WndProc(nmsg, wparam, lparam);
232 	}
233 
234 	return 0;
235 }
236 
237 
238 void DesktopBar::RegisterHotkeys()
239 {
240 	 // register hotkey WIN+E opening explorer
241 	RegisterHotKey(_hwnd, IDHK_EXPLORER, MOD_WIN, 'E');
242 	RegisterHotKey(_hwnd, IDHK_RUN, MOD_WIN, 'R');
243 	RegisterHotKey(_hwnd, IDHK_DESKTOP, MOD_WIN, 'D');
244 	RegisterHotKey(_hwnd, IDHK_LOGOFF, MOD_WIN, 'L');
245 	RegisterHotKey(_hwnd, IDHK_STARTMENU, MOD_CONTROL, VK_ESCAPE);
246 
247 		///@todo register all common hotkeys
248 }
249 
250 void DesktopBar::ProcessHotKey(int id_hotkey)
251 {
252 	switch(id_hotkey) {
253 		case IDHK_EXPLORER:
254 			explorer_show_frame(SW_SHOWNORMAL);
255 			break;
256 
257 		case IDHK_RUN:
258 			_startMenuRoot->Command(IDC_LAUNCH, 0);
259 			break;
260 
261 		case IDHK_LOGOFF:
262 			_startMenuRoot->Command(IDC_LOGOFF, 0);
263 			break;
264 
265 		case IDHK_DESKTOP:
266 			g_Globals._desktops.ToggleMinimize();
267 			break;
268 
269 		case IDHK_STARTMENU:
270 			ShowOrHideStartMenu();
271 			break;
272 		///@todo implement all common hotkeys
273 	}
274 }
275 
276 
277 LRESULT DesktopBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
278 {
279 	switch(nmsg) {
280 	  case WM_NCHITTEST: {
281 		LRESULT res = super::WndProc(nmsg, wparam, lparam);
282 
283 		if (res>=HTSIZEFIRST && res<=HTSIZELAST) {
284 #ifdef TASKBAR_AT_TOP
285 			if (res == HTBOTTOM)	// enable vertical resizing at the lower border
286 #else
287 			if (res == HTTOP)		// enable vertical resizing at the upper border
288 #endif
289 				return res;
290 			else
291 				return HTCLIENT;	// disable any other resizing
292 		}
293 		return res;}
294 
295 	  case WM_SYSCOMMAND:
296 		if ((wparam&0xFFF0) == SC_SIZE) {
297 #ifdef TASKBAR_AT_TOP
298 			if (wparam == SC_SIZE+6)// enable vertical resizing at the lower border
299 #else
300 			if (wparam == SC_SIZE+3)// enable vertical resizing at the upper border
301 #endif
302 				goto def;
303 			else
304 				return 0;			// disable any other resizing
305 		} else if (wparam == SC_TASKLIST)
306 			ShowOrHideStartMenu();
307 		goto def;
308 
309 	  case WM_SIZE:
310 		Resize(LOWORD(lparam), HIWORD(lparam));
311 		break;
312 
313 	  case WM_SIZING:
314 		ControlResize(wparam, lparam);
315 		break;
316 
317 	  case PM_RESIZE_CHILDREN: {
318 		ClientRect size(_hwnd);
319 		Resize(size.right, size.bottom);
320 		break;}
321 
322 	  case WM_CLOSE:
323 		ShowExitWindowsDialog(_hwnd);
324 		break;
325 
326 	  case WM_HOTKEY:
327 		ProcessHotKey(wparam);
328 		break;
329 
330 	  case WM_COPYDATA:
331 		return ProcessCopyData((COPYDATASTRUCT*)lparam);
332 
333 	  case WM_CONTEXTMENU: {
334   		POINTS p;
335 		p.x = LOWORD(lparam);
336 		p.y = HIWORD(lparam);
337 		PopupMenu menu(IDM_DESKTOPBAR);
338 		SetMenuDefaultItem(menu, 0, MF_BYPOSITION);
339 		menu.TrackPopupMenu(_hwnd, p);
340 		break;}
341 
342 	  case PM_GET_LAST_ACTIVE:
343 		if (_hwndTaskBar)
344 			return SendMessage(_hwndTaskBar, nmsg, wparam, lparam);
345 		break;
346 
347 	  case PM_REFRESH_CONFIG:	///@todo read desktop bar settings
348 		SendMessage(_hwndNotify, PM_REFRESH_CONFIG, 0, 0);
349 		break;
350 
351 	  case WM_TIMER:
352 		if (wparam == ID_TRAY_VOLUME) {
353 			KillTimer(_hwnd, wparam);
354 			launch_file(_hwnd, TEXT("sndvol32.exe"), SW_SHOWNORMAL, TEXT("-t"));	// launch volume control in small mode
355 		}
356 		break;
357 
358 	  case PM_GET_NOTIFYAREA:
359 		return (LRESULT)(HWND)_hwndNotify;
360 
361 	  case WM_SYSCOLORCHANGE:
362 		/* Forward WM_SYSCOLORCHANGE to common controls */
363 #ifndef _NO_REBAR
364 		SendMessage(_hwndrebar, WM_SYSCOLORCHANGE, 0, 0);
365 #endif
366 		SendMessage(_hwndQuickLaunch, WM_SYSCOLORCHANGE, 0, 0);
367 		SendMessage(_hwndTaskBar, WM_SYSCOLORCHANGE, 0, 0);
368 		break;
369 
370 	  default: def:
371 		return super::WndProc(nmsg, wparam, lparam);
372 	}
373 
374 	return 0;
375 }
376 
377 int DesktopBar::Notify(int id, NMHDR* pnmh)
378 {
379 	if (pnmh->code == RBN_CHILDSIZE) {
380 		/* align the task bands to the top, so it's in row with the Start button */
381 		NMREBARCHILDSIZE* childSize = (NMREBARCHILDSIZE*)pnmh;
382 
383 		if (childSize->wID == IDW_TASKTOOLBAR) {
384 			int cy = childSize->rcChild.top - childSize->rcBand.top;
385 
386 			if (cy) {
387 				childSize->rcChild.bottom -= cy;
388 				childSize->rcChild.top -= cy;
389 			}
390 		}
391 	}
392 
393 	return 0;
394 }
395 
396 void DesktopBar::Resize(int cx, int cy)
397 {
398 	///@todo general children resizing algorithm
399 	int quicklaunch_width = SendMessage(_hwndQuickLaunch, PM_GET_WIDTH, 0, 0);
400 	int notifyarea_width = SendMessage(_hwndNotify, PM_GET_WIDTH, 0, 0);
401 
402 	HDWP hdwp = BeginDeferWindowPos(3);
403 
404 	if (_hwndrebar)
405 		DeferWindowPos(hdwp, _hwndrebar, 0, _taskbar_pos, 1, cx-_taskbar_pos-(notifyarea_width+1), cy-2, SWP_NOZORDER|SWP_NOACTIVATE);
406 	else {
407 		if (_hwndQuickLaunch)
408 			DeferWindowPos(hdwp, _hwndQuickLaunch, 0, _taskbar_pos, 1, quicklaunch_width, cy-2, SWP_NOZORDER|SWP_NOACTIVATE);
409 
410 		if (_hwndTaskBar)
411 			DeferWindowPos(hdwp, _hwndTaskBar, 0, _taskbar_pos+quicklaunch_width, 0, cx-_taskbar_pos-quicklaunch_width-(notifyarea_width+1), cy, SWP_NOZORDER|SWP_NOACTIVATE);
412 	}
413 
414 	if (_hwndNotify)
415 		DeferWindowPos(hdwp, _hwndNotify, 0, cx-(notifyarea_width+1), 1, notifyarea_width, cy-2, SWP_NOZORDER|SWP_NOACTIVATE);
416 
417 	EndDeferWindowPos(hdwp);
418 
419 	WindowRect rect(_hwnd);
420 	RECT work_area = {0, 0, GetSystemMetrics(SM_CXSCREEN), rect.top};
421 	SystemParametersInfo(SPI_SETWORKAREA, 0, &work_area, 0);	// don't use SPIF_SENDCHANGE because then we have to wait for any message being delivered
422 	PostMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETWORKAREA, 0);
423 }
424 
425 
426 int DesktopBar::Command(int id, int code)
427 {
428 	switch(id) {
429 	  case IDC_START:
430 		ShowOrHideStartMenu();
431 		break;
432 
433 	  case ID_ABOUT_EXPLORER:
434 		explorer_about(g_Globals._hwndDesktop);
435 		break;
436 
437 	  case ID_DESKTOPBAR_SETTINGS:
438 		ExplorerPropertySheet(g_Globals._hwndDesktop);
439 		break;
440 
441 	  case ID_MINIMIZE_ALL:
442 		g_Globals._desktops.ToggleMinimize();
443 		break;
444 
445 	  case ID_EXPLORE:
446 		explorer_show_frame(SW_SHOWNORMAL);
447 		break;
448 
449 	  case ID_TASKMGR:
450 		launch_file(_hwnd, TEXT("taskmgr.exe"), SW_SHOWNORMAL);
451 		break;
452 
453 	  case ID_SWITCH_DESKTOP_1:
454           case ID_SWITCH_DESKTOP_1+1:
455 	  case ID_SWITCH_DESKTOP_1+2:
456 	  case ID_SWITCH_DESKTOP_1+3: {
457 		int desktop_idx = id - ID_SWITCH_DESKTOP_1;
458 
459 		g_Globals._desktops.SwitchToDesktop(desktop_idx);
460 
461 		if (_hwndQuickLaunch)
462 			PostMessage(_hwndQuickLaunch, PM_UPDATE_DESKTOP, desktop_idx, 0);
463 		break;}
464 
465 #ifdef __REACTOS__
466 	  case ID_TRAY_VOLUME:
467 		launch_file(_hwnd, TEXT("sndvol32.exe"), SW_SHOWNORMAL);	// launch volume control application
468 		break;
469 
470 	  case ID_VOLUME_PROPERTIES:
471 		launch_cpanel(_hwnd, TEXT("mmsys.cpl"));
472 		break;
473 #endif
474 
475 	  default:
476 		if (_hwndQuickLaunch)
477 			return SendMessage(_hwndQuickLaunch, WM_COMMAND, MAKEWPARAM(id,code), 0);
478 		else
479 			return 1;
480 	}
481 
482 	return 0;
483 }
484 
485 
486 void DesktopBar::ShowOrHideStartMenu()
487 {
488 	if (_startMenuRoot)
489 	{
490 		// set the Button, if not set
491 		if (!Button_GetState(_hwndStartButton))
492 			Button_SetState(_hwndStartButton, TRUE);
493 
494         if (_startMenuRoot->IsStartMenuVisible())
495             _startMenuRoot->CloseStartMenu();
496         else
497             _startMenuRoot->TrackStartmenu();
498 
499 		// StartMenu was closed, release button state
500 		Button_SetState(_hwndStartButton, false);
501 	}
502 }
503 
504 
505  /// copy data structure for tray notifications
506 struct TrayNotifyCDS {
507 	DWORD	cookie;
508 	DWORD	notify_code;
509 	NOTIFYICONDATA nicon_data;
510 };
511 
512 LRESULT DesktopBar::ProcessCopyData(COPYDATASTRUCT* pcd)
513 {
514 	 // Is this a tray notification message?
515 	if (pcd->dwData == 1) {
516 		TrayNotifyCDS* ptr = (TrayNotifyCDS*) pcd->lpData;
517 
518 		NotifyArea* notify_area = GET_WINDOW(NotifyArea, _hwndNotify);
519 
520 		if (notify_area)
521 			return notify_area->ProcessTrayNotification(ptr->notify_code, &ptr->nicon_data);
522 	}
523 
524 	return FALSE;
525 }
526 
527 
528 void DesktopBar::ControlResize(WPARAM wparam, LPARAM lparam)
529 {
530 	PRECT dragRect = (PRECT) lparam;
531 	//int screenWidth = GetSystemMetrics(SM_CXSCREEN);
532 	int screenHeight = GetSystemMetrics(SM_CYSCREEN);
533 
534 	///@todo write code for taskbar being at sides or top.
535 
536 	switch(wparam) {
537 	  case WMSZ_BOTTOM:	///@todo Taskbar is at the top of the screen
538 		break;
539 
540 	  case WMSZ_TOP:	// Taskbar is at the bottom of the screen
541 		dragRect->top = screenHeight - (((screenHeight - dragRect->top) + DESKTOPBARBAR_HEIGHT/2) / DESKTOPBARBAR_HEIGHT) * DESKTOPBARBAR_HEIGHT;
542 		if (dragRect->top < screenHeight / 2)
543 			dragRect->top = screenHeight - (screenHeight/2 / DESKTOPBARBAR_HEIGHT * DESKTOPBARBAR_HEIGHT);
544 		else if (dragRect->top > screenHeight - 5)
545 			dragRect->top = screenHeight - 5;
546 		break;
547 
548 	  case WMSZ_RIGHT:	///@todo Taskbar is at the left of the screen
549 		break;
550 
551 	  case WMSZ_LEFT:	///@todo Taskbar is at the right of the screen
552 		break;
553 	}
554 }
555 
556 
557 #ifdef __REACTOS__
558 
559 void DesktopBar::AddTrayIcons()
560 {
561 	_trayIcon.Add(SmallIcon(IDI_SPEAKER), ResString(IDS_VOLUME));
562 }
563 
564 void DesktopBar::TrayClick(UINT id, int btn)
565 {
566 	switch(id) {
567 	  case ID_TRAY_VOLUME:
568 		if (btn == TRAYBUTTON_LEFT)
569 			SetTimer(_hwnd, ID_TRAY_VOLUME, 500, NULL); // wait a bit to correctly handle double clicks
570 		else {
571 			PopupMenu menu(IDM_VOLUME);
572 			SetMenuDefaultItem(menu, 0, MF_BYPOSITION);
573 			menu.TrackPopupMenuAtPos(_hwnd, GetMessagePos());
574 		}
575 		break;
576 	}
577 }
578 
579 void DesktopBar::TrayDblClick(UINT id, int btn)
580 {
581 	switch(id) {
582 	  case ID_TRAY_VOLUME:
583 		KillTimer(_hwnd, ID_TRAY_VOLUME);	// finish one-click timer
584 		launch_file(_hwnd, TEXT("sndvol32.exe"), SW_SHOWNORMAL);	// launch volume control application
585 		break;
586 	}
587 }
588 
589 #endif
590