xref: /reactos/base/applications/clipbrd/clipbrd.c (revision bae2bac6)
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