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