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
DesktopBar(HWND hwnd)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
~DesktopBar()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
Create()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
Init(LPCREATESTRUCT pcs)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
StartButton(HWND hwnd)173 StartButton::StartButton(HWND hwnd)
174 : PictureButton(hwnd, SmallIcon(IDI_STARTMENU), GetSysColorBrush(COLOR_BTNFACE))
175 {
176 }
177
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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
RegisterHotkeys()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
ProcessHotKey(int id_hotkey)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
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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
Notify(int id,NMHDR * pnmh)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
Resize(int cx,int cy)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
Command(int id,int code)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
ShowOrHideStartMenu()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
ProcessCopyData(COPYDATASTRUCT * pcd)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
ControlResize(WPARAM wparam,LPARAM lparam)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
AddTrayIcons()559 void DesktopBar::AddTrayIcons()
560 {
561 _trayIcon.Add(SmallIcon(IDI_SPEAKER), ResString(IDS_VOLUME));
562 }
563
TrayClick(UINT id,int btn)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
TrayDblClick(UINT id,int btn)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