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