1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Clipboard Viewer 4 * FILE: base/applications/clipbrd/clipbrd.c 5 * PURPOSE: Provides a view of the contents of the ReactOS clipboard. 6 * PROGRAMMERS: Ricardo Hanke 7 */ 8 9 #include "precomp.h" 10 11 static const WCHAR szClassName[] = L"ClipBookWClass"; 12 13 CLIPBOARD_GLOBALS Globals; 14 SCROLLSTATE Scrollstate; 15 16 static void UpdateLinesToScroll(void) 17 { 18 UINT uLinesToScroll; 19 20 if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uLinesToScroll, 0)) 21 { 22 Globals.uLinesToScroll = 3; 23 } 24 else 25 { 26 Globals.uLinesToScroll = uLinesToScroll; 27 } 28 } 29 30 static void SaveClipboardToFile(void) 31 { 32 OPENFILENAMEW sfn; 33 WCHAR szFileName[MAX_PATH]; 34 WCHAR szFilterMask[MAX_STRING_LEN + 10]; 35 LPWSTR c; 36 37 ZeroMemory(&szFilterMask, sizeof(szFilterMask)); 38 c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_NT, szFilterMask, MAX_STRING_LEN) + 1; 39 wcscpy(c, L"*.clp"); 40 41 ZeroMemory(&szFileName, sizeof(szFileName)); 42 ZeroMemory(&sfn, sizeof(sfn)); 43 sfn.lStructSize = sizeof(sfn); 44 sfn.hwndOwner = Globals.hMainWnd; 45 sfn.hInstance = Globals.hInstance; 46 sfn.lpstrFilter = szFilterMask; 47 sfn.lpstrFile = szFileName; 48 sfn.nMaxFile = ARRAYSIZE(szFileName); 49 sfn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; 50 sfn.lpstrDefExt = L"clp"; 51 52 if (!GetSaveFileNameW(&sfn)) 53 return; 54 55 if (!OpenClipboard(Globals.hMainWnd)) 56 { 57 ShowLastWin32Error(Globals.hMainWnd); 58 return; 59 } 60 61 WriteClipboardFile(szFileName, CLIP_FMT_NT /* CLIP_FMT_31 */); 62 63 CloseClipboard(); 64 } 65 66 static void LoadClipboardDataFromFile(LPWSTR lpszFileName) 67 { 68 if (MessageBoxRes(Globals.hMainWnd, Globals.hInstance, 69 STRING_DELETE_MSG, STRING_DELETE_TITLE, 70 MB_ICONWARNING | MB_YESNO) != IDYES) 71 { 72 return; 73 } 74 75 if (!OpenClipboard(Globals.hMainWnd)) 76 { 77 ShowLastWin32Error(Globals.hMainWnd); 78 return; 79 } 80 81 EmptyClipboard(); 82 ReadClipboardFile(lpszFileName); 83 84 CloseClipboard(); 85 } 86 87 static void LoadClipboardFromFile(void) 88 { 89 OPENFILENAMEW ofn; 90 WCHAR szFileName[MAX_PATH]; 91 WCHAR szFilterMask[MAX_STRING_LEN + 10]; 92 LPWSTR c; 93 94 ZeroMemory(&szFilterMask, sizeof(szFilterMask)); 95 c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_GEN, szFilterMask, MAX_STRING_LEN) + 1; 96 wcscpy(c, L"*.clp"); 97 98 ZeroMemory(&szFileName, sizeof(szFileName)); 99 ZeroMemory(&ofn, sizeof(ofn)); 100 ofn.lStructSize = sizeof(ofn); 101 ofn.hwndOwner = Globals.hMainWnd; 102 ofn.hInstance = Globals.hInstance; 103 ofn.lpstrFilter = szFilterMask; 104 ofn.lpstrFile = szFileName; 105 ofn.nMaxFile = ARRAYSIZE(szFileName); 106 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; 107 108 if (!GetOpenFileNameW(&ofn)) 109 return; 110 111 LoadClipboardDataFromFile(szFileName); 112 } 113 114 static void LoadClipboardFromDrop(HDROP hDrop) 115 { 116 WCHAR szFileName[MAX_PATH]; 117 118 DragQueryFileW(hDrop, 0, szFileName, ARRAYSIZE(szFileName)); 119 DragFinish(hDrop); 120 121 LoadClipboardDataFromFile(szFileName); 122 } 123 124 static void SetDisplayFormat(UINT uFormat) 125 { 126 CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_UNCHECKED); 127 Globals.uCheckedItem = uFormat + CMD_AUTOMATIC; 128 CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_CHECKED); 129 130 if (uFormat == 0) 131 { 132 Globals.uDisplayFormat = GetAutomaticClipboardFormat(); 133 } 134 else 135 { 136 Globals.uDisplayFormat = uFormat; 137 } 138 139 if (Globals.hDspBmp) 140 { 141 DeleteObject(Globals.hDspBmp); 142 } 143 144 ZeroMemory(&Scrollstate, sizeof(Scrollstate)); 145 UpdateWindowScrollState(Globals.hMainWnd, Globals.hDspBmp, &Scrollstate); 146 147 InvalidateRect(Globals.hMainWnd, NULL, TRUE); 148 } 149 150 static void InitMenuPopup(HMENU hMenu, LPARAM index) 151 { 152 if ((GetMenuItemID(hMenu, 0) == CMD_DELETE) || (GetMenuItemID(hMenu, 1) == CMD_SAVE_AS)) 153 { 154 if (CountClipboardFormats() == 0) 155 { 156 EnableMenuItem(hMenu, CMD_DELETE, MF_GRAYED); 157 EnableMenuItem(hMenu, CMD_SAVE_AS, MF_GRAYED); 158 } 159 else 160 { 161 EnableMenuItem(hMenu, CMD_DELETE, MF_ENABLED); 162 EnableMenuItem(hMenu, CMD_SAVE_AS, MF_ENABLED); 163 } 164 } 165 166 DrawMenuBar(Globals.hMainWnd); 167 } 168 169 static void UpdateDisplayMenu(void) 170 { 171 UINT uFormat; 172 WCHAR szFormatName[MAX_FMT_NAME_LEN + 1]; 173 HMENU hMenu; 174 175 hMenu = GetSubMenu(Globals.hMenu, DISPLAY_MENU_POS); 176 177 while (GetMenuItemCount(hMenu) > 1) 178 { 179 DeleteMenu(hMenu, 1, MF_BYPOSITION); 180 } 181 182 if (CountClipboardFormats() == 0) 183 return; 184 185 if (!OpenClipboard(Globals.hMainWnd)) 186 return; 187 188 AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL); 189 190 uFormat = EnumClipboardFormats(0); 191 while (uFormat) 192 { 193 RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, szFormatName, ARRAYSIZE(szFormatName)); 194 195 if (!IsClipboardFormatSupported(uFormat)) 196 { 197 AppendMenuW(hMenu, MF_STRING | MF_GRAYED, 0, szFormatName); 198 } 199 else 200 { 201 AppendMenuW(hMenu, MF_STRING, CMD_AUTOMATIC + uFormat, szFormatName); 202 } 203 204 uFormat = EnumClipboardFormats(uFormat); 205 } 206 207 CloseClipboard(); 208 } 209 210 static int ClipboardCommandHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 211 { 212 switch (LOWORD(wParam)) 213 { 214 case CMD_OPEN: 215 { 216 LoadClipboardFromFile(); 217 break; 218 } 219 220 case CMD_SAVE_AS: 221 { 222 SaveClipboardToFile(); 223 break; 224 } 225 226 case CMD_EXIT: 227 { 228 PostMessageW(Globals.hMainWnd, WM_CLOSE, 0, 0); 229 break; 230 } 231 232 case CMD_DELETE: 233 { 234 if (MessageBoxRes(Globals.hMainWnd, Globals.hInstance, 235 STRING_DELETE_MSG, STRING_DELETE_TITLE, 236 MB_ICONWARNING | MB_YESNO) != IDYES) 237 { 238 break; 239 } 240 241 DeleteClipboardContent(); 242 break; 243 } 244 245 case CMD_AUTOMATIC: 246 { 247 SetDisplayFormat(0); 248 break; 249 } 250 251 case CMD_HELP: 252 { 253 HtmlHelpW(Globals.hMainWnd, L"clipbrd.chm", 0, 0); 254 break; 255 } 256 257 case CMD_ABOUT: 258 { 259 HICON hIcon; 260 WCHAR szTitle[MAX_STRING_LEN]; 261 262 hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCE(CLIPBRD_ICON)); 263 LoadStringW(Globals.hInstance, STRING_CLIPBOARD, szTitle, ARRAYSIZE(szTitle)); 264 ShellAboutW(Globals.hMainWnd, szTitle, NULL, hIcon); 265 DeleteObject(hIcon); 266 break; 267 } 268 269 default: 270 { 271 break; 272 } 273 } 274 return 0; 275 } 276 277 static void ClipboardPaintHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 278 { 279 HDC hdc; 280 PAINTSTRUCT ps; 281 RECT rc; 282 283 if (!OpenClipboard(Globals.hMainWnd)) 284 return; 285 286 hdc = BeginPaint(hWnd, &ps); 287 GetClientRect(hWnd, &rc); 288 289 switch (Globals.uDisplayFormat) 290 { 291 case CF_NONE: 292 { 293 break; 294 } 295 296 case CF_UNICODETEXT: 297 { 298 DrawTextFromClipboard(hdc, &rc, DT_LEFT | DT_NOPREFIX); 299 break; 300 } 301 302 case CF_BITMAP: 303 { 304 BitBltFromClipboard(hdc, rc.left, rc.top, rc.right, rc.bottom, 0, 0, SRCCOPY); 305 break; 306 } 307 308 case CF_DIB: 309 { 310 SetDIBitsToDeviceFromClipboard(CF_DIB, hdc, rc.left, rc.top, 0, 0, 0, DIB_RGB_COLORS); 311 break; 312 } 313 314 case CF_DIBV5: 315 { 316 SetDIBitsToDeviceFromClipboard(CF_DIBV5, hdc, rc.left, rc.top, 0, 0, 0, DIB_RGB_COLORS); 317 break; 318 } 319 320 case CF_METAFILEPICT: 321 { 322 PlayMetaFileFromClipboard(hdc, &rc); 323 break; 324 } 325 326 case CF_ENHMETAFILE: 327 { 328 PlayEnhMetaFileFromClipboard(hdc, &rc); 329 break; 330 } 331 332 default: 333 { 334 DrawTextFromResource(Globals.hInstance, ERROR_UNSUPPORTED_FORMAT, hdc, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX); 335 break; 336 } 337 } 338 339 EndPaint(hWnd, &ps); 340 341 CloseClipboard(); 342 } 343 344 static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 345 { 346 switch(uMsg) 347 { 348 case WM_CREATE: 349 { 350 Globals.hMenu = GetMenu(hWnd); 351 Globals.hWndNext = SetClipboardViewer(hWnd); 352 UpdateDisplayMenu(); 353 SetDisplayFormat(0); 354 break; 355 } 356 357 case WM_CLOSE: 358 { 359 DestroyWindow(hWnd); 360 break; 361 } 362 363 case WM_DESTROY: 364 { 365 ChangeClipboardChain(hWnd, Globals.hWndNext); 366 PostQuitMessage(0); 367 break; 368 } 369 370 case WM_PAINT: 371 { 372 ClipboardPaintHandler(hWnd, uMsg, wParam, lParam); 373 break; 374 } 375 376 case WM_KEYDOWN: 377 { 378 HandleKeyboardScrollEvents(hWnd, uMsg, wParam, lParam); 379 break; 380 } 381 382 case WM_MOUSEWHEEL: 383 { 384 HandleMouseScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); 385 break; 386 } 387 388 case WM_HSCROLL: 389 { 390 HandleHorizontalScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); 391 break; 392 } 393 394 case WM_VSCROLL: 395 { 396 HandleVerticalScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); 397 break; 398 } 399 400 case WM_SIZE: 401 { 402 UpdateWindowScrollState(hWnd, Globals.hDspBmp, &Scrollstate); 403 404 if ((Globals.uDisplayFormat == CF_METAFILEPICT) || 405 (Globals.uDisplayFormat == CF_ENHMETAFILE) || 406 (Globals.uDisplayFormat == CF_DSPENHMETAFILE) || 407 (Globals.uDisplayFormat == CF_DSPMETAFILEPICT)) 408 { 409 InvalidateRect(Globals.hMainWnd, NULL, FALSE); 410 } 411 else if (!IsClipboardFormatSupported(Globals.uDisplayFormat)) 412 { 413 InvalidateRect(Globals.hMainWnd, NULL, TRUE); 414 } 415 416 break; 417 } 418 419 case WM_CHANGECBCHAIN: 420 { 421 if ((HWND)wParam == Globals.hWndNext) 422 { 423 Globals.hWndNext = (HWND)lParam; 424 } 425 else if (Globals.hWndNext != NULL) 426 { 427 SendMessageW(Globals.hWndNext, uMsg, wParam, lParam); 428 } 429 430 break; 431 } 432 433 case WM_DRAWCLIPBOARD: 434 { 435 UpdateDisplayMenu(); 436 SetDisplayFormat(0); 437 438 SendMessageW(Globals.hWndNext, uMsg, wParam, lParam); 439 break; 440 } 441 442 case WM_COMMAND: 443 { 444 if ((LOWORD(wParam) > CMD_AUTOMATIC)) 445 { 446 SetDisplayFormat(LOWORD(wParam) - CMD_AUTOMATIC); 447 } 448 else 449 { 450 ClipboardCommandHandler(hWnd, uMsg, wParam, lParam); 451 } 452 break; 453 } 454 455 case WM_INITMENUPOPUP: 456 { 457 InitMenuPopup((HMENU)wParam, lParam); 458 break; 459 } 460 461 case WM_DROPFILES: 462 { 463 LoadClipboardFromDrop((HDROP)wParam); 464 break; 465 } 466 467 case WM_QUERYNEWPALETTE: 468 { 469 if (RealizeClipboardPalette(hWnd) != GDI_ERROR) 470 { 471 InvalidateRect(hWnd, NULL, TRUE); 472 UpdateWindow(hWnd); 473 return TRUE; 474 } 475 return FALSE; 476 } 477 478 case WM_PALETTECHANGED: 479 { 480 if ((HWND)wParam != hWnd) 481 { 482 if (RealizeClipboardPalette(hWnd) != GDI_ERROR) 483 { 484 InvalidateRect(hWnd, NULL, TRUE); 485 UpdateWindow(hWnd); 486 } 487 } 488 break; 489 } 490 491 case WM_SYSCOLORCHANGE: 492 { 493 SetDisplayFormat(Globals.uDisplayFormat); 494 break; 495 } 496 497 case WM_SETTINGCHANGE: 498 { 499 if (wParam == SPI_SETWHEELSCROLLLINES) 500 { 501 UpdateLinesToScroll(); 502 } 503 break; 504 } 505 506 default: 507 { 508 return DefWindowProc(hWnd, uMsg, wParam, lParam); 509 } 510 } 511 return 0; 512 } 513 514 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) 515 { 516 MSG msg; 517 HACCEL hAccel; 518 HWND hPrevWindow; 519 WNDCLASSEXW wndclass; 520 WCHAR szBuffer[MAX_STRING_LEN]; 521 522 hPrevWindow = FindWindowW(szClassName, NULL); 523 if (hPrevWindow) 524 { 525 BringWindowToFront(hPrevWindow); 526 return 0; 527 } 528 529 ZeroMemory(&Globals, sizeof(Globals)); 530 Globals.hInstance = hInstance; 531 532 ZeroMemory(&wndclass, sizeof(wndclass)); 533 wndclass.cbSize = sizeof(wndclass); 534 wndclass.lpfnWndProc = MainWndProc; 535 wndclass.hInstance = hInstance; 536 wndclass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(CLIPBRD_ICON)); 537 wndclass.hCursor = LoadCursorW(0, IDC_ARROW); 538 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 539 wndclass.lpszMenuName = MAKEINTRESOURCEW(MAIN_MENU); 540 wndclass.lpszClassName = szClassName; 541 542 switch (GetUserDefaultUILanguage()) 543 { 544 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 545 SetProcessDefaultLayout(LAYOUT_RTL); 546 break; 547 548 default: 549 break; 550 } 551 552 if (!RegisterClassExW(&wndclass)) 553 { 554 ShowLastWin32Error(NULL); 555 return 0; 556 } 557 558 LoadStringW(hInstance, STRING_CLIPBOARD, szBuffer, ARRAYSIZE(szBuffer)); 559 Globals.hMainWnd = CreateWindowExW(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES, 560 szClassName, 561 szBuffer, 562 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, 563 CW_USEDEFAULT, 564 CW_USEDEFAULT, 565 CW_USEDEFAULT, 566 CW_USEDEFAULT, 567 NULL, 568 NULL, 569 Globals.hInstance, 570 NULL); 571 if (!Globals.hMainWnd) 572 { 573 ShowLastWin32Error(NULL); 574 return 0; 575 } 576 577 ShowWindow(Globals.hMainWnd, nCmdShow); 578 UpdateWindow(Globals.hMainWnd); 579 580 hAccel = LoadAcceleratorsW(Globals.hInstance, MAKEINTRESOURCEW(ID_ACCEL)); 581 if (!hAccel) 582 { 583 ShowLastWin32Error(Globals.hMainWnd); 584 } 585 586 /* If the user provided a path to a clipboard data file, try to open it */ 587 if (lpCmdLine != NULL && *lpCmdLine) 588 LoadClipboardDataFromFile(lpCmdLine); 589 590 UpdateLinesToScroll(); 591 592 while (GetMessageW(&msg, 0, 0, 0)) 593 { 594 if (!TranslateAcceleratorW(Globals.hMainWnd, hAccel, &msg)) 595 { 596 TranslateMessage(&msg); 597 DispatchMessageW(&msg); 598 } 599 } 600 601 if (Globals.hDspBmp) 602 { 603 DeleteObject(Globals.hDspBmp); 604 } 605 606 return (int)msg.wParam; 607 } 608