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