1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Application view class and other classes used by it
5 * COPYRIGHT: Copyright 2020 He Yang (1160386205@qq.com)
6 * Copyright 2022,2023 Mark Jansen <mark.jansen@reactos.org>
7 */
8
9 #include "rapps.h"
10 #include "appview.h"
11 #include "gui.h"
12 #include <windowsx.h>
13
14 using namespace Gdiplus;
15
16 HICON g_hDefaultPackageIcon = NULL;
17 static int g_DefaultPackageIconILIdx = I_IMAGENONE;
18
19 // **** Menu helpers ****
20
21 BOOL
DeleteMenuEx(_In_ HMENU hMenu,_In_ UINT uPosition,_In_ UINT uFlags)22 DeleteMenuEx(
23 _In_ HMENU hMenu,
24 _In_ UINT uPosition,
25 _In_ UINT uFlags)
26 {
27 INT pos;
28 MENUITEMINFOW mii = { sizeof(mii), MIIM_FTYPE, 0 };
29 bool bIsValidItem1, bIsValidItem2;
30 bool bIsSep1, bIsSep2;
31
32 if (uFlags & MF_BYPOSITION)
33 pos = (INT)uPosition;
34 else
35 pos = ::GetMenuPosFromID(hMenu, uPosition);
36 if (pos < 0)
37 return FALSE;
38
39 bIsValidItem1 = ((pos > 0) && ::GetMenuItemInfoW(hMenu, pos - 1, TRUE, &mii));
40 bIsSep1 = bIsValidItem1 && !!(mii.fType & MFT_SEPARATOR);
41
42 bIsValidItem2 = ::GetMenuItemInfoW(hMenu, pos + 1, TRUE, &mii);
43 bIsSep2 = bIsValidItem2 && !!(mii.fType & MFT_SEPARATOR);
44
45 if (bIsSep1 && !bIsSep2 && !bIsValidItem2)
46 pos = pos - 1; // Delete separator only if pos+1 has no item
47 else if (!bIsSep1 && bIsSep2 && !bIsValidItem1)
48 pos = pos + 1; // Delete separator only if pos-1 has no item
49 else if (bIsSep1 && bIsSep2)
50 pos = pos + 1;
51 else
52 pos = -1;
53
54 // Delete one of the separators if necessary
55 if (pos != -1)
56 ::DeleteMenu(hMenu, pos, MF_BYPOSITION);
57
58 // Finally, delete the menu item itself.
59 return ::DeleteMenu(hMenu, uPosition, uFlags);
60 }
61 // **** Menu helpers ****
62
63 // **** CMainToolbar ****
64
65 VOID
AddImageToImageList(HIMAGELIST hImageList,UINT ImageIndex)66 CMainToolbar::AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex)
67 {
68 HICON hImage;
69
70 if (!(hImage =
71 (HICON)LoadImageW(hInst, MAKEINTRESOURCE(ImageIndex), IMAGE_ICON, m_iToolbarHeight, m_iToolbarHeight, 0)))
72 {
73 return;
74 }
75
76 ImageList_AddIcon(hImageList, hImage);
77 DeleteObject(hImage);
78 }
79
80 HIMAGELIST
InitImageList()81 CMainToolbar::InitImageList()
82 {
83 HIMAGELIST hImageList;
84
85 /* Create the toolbar icon image list */
86 hImageList = ImageList_Create(m_iToolbarHeight, m_iToolbarHeight, ILC_MASK | GetSystemColorDepth(), 1, 1);
87 if (!hImageList)
88 {
89 return NULL;
90 }
91
92 AddImageToImageList(hImageList, IDI_INSTALL);
93 AddImageToImageList(hImageList, IDI_UNINSTALL);
94 AddImageToImageList(hImageList, IDI_MODIFY);
95 AddImageToImageList(hImageList, IDI_CHECK_ALL);
96 AddImageToImageList(hImageList, IDI_REFRESH);
97 AddImageToImageList(hImageList, IDI_UPDATE_DB);
98 AddImageToImageList(hImageList, IDI_SETTINGS);
99 AddImageToImageList(hImageList, IDI_EXIT);
100
101 return hImageList;
102 }
103
CMainToolbar()104 CMainToolbar::CMainToolbar() : m_iToolbarHeight(24), m_dButtonsWidthMax(0)
105 {
106 }
107
108 VOID
OnGetDispInfo(LPTOOLTIPTEXT lpttt)109 CMainToolbar::OnGetDispInfo(LPTOOLTIPTEXT lpttt)
110 {
111 UINT idButton = (UINT)lpttt->hdr.idFrom;
112
113 switch (idButton)
114 {
115 case ID_EXIT:
116 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_EXIT);
117 break;
118
119 case ID_INSTALL:
120 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_INSTALL);
121 break;
122
123 case ID_UNINSTALL:
124 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL);
125 break;
126
127 case ID_MODIFY:
128 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_MODIFY);
129 break;
130
131 case ID_SETTINGS:
132 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SETTINGS);
133 break;
134
135 case ID_REFRESH:
136 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_REFRESH);
137 break;
138
139 case ID_RESETDB:
140 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE_DB);
141 break;
142 }
143 }
144
145 HWND
Create(HWND hwndParent)146 CMainToolbar::Create(HWND hwndParent)
147 {
148 CStringW szInstallBtn;
149 CStringW szUninstallBtn;
150 CStringW szModifyBtn;
151 CStringW szSelectAllBtn;
152 CStringW szRefreshBtn;
153 CStringW szUpdateDbBtn;
154
155 /* Load tooltip strings */
156 szInstallBtn.LoadStringW(IDS_TOOLTIP_INSTALL);
157 szUninstallBtn.LoadStringW(IDS_TOOLTIP_UNINSTALL);
158 szModifyBtn.LoadStringW(IDS_TOOLTIP_MODIFY);
159 szSelectAllBtn.LoadStringW(IDS_TOOLTIP_SELECT_ALL);
160 szRefreshBtn.LoadStringW(IDS_TOOLTIP_REFRESH);
161 szUpdateDbBtn.LoadStringW(IDS_TOOLTIP_UPDATE_DB);
162
163 /* Create buttons */
164 TBBUTTON Buttons[] = {
165 /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */
166 {0, ID_INSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szInstallBtn.GetString()},
167 {1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szUninstallBtn.GetString()},
168 {2, ID_MODIFY, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szModifyBtn.GetString()},
169 {3, ID_CHECK_ALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szSelectAllBtn.GetString()},
170 {-1, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},
171 {4, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szRefreshBtn.GetString()},
172 {5, ID_RESETDB, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szUpdateDbBtn.GetString()}};
173
174 m_hWnd = CreateWindowExW(
175 0, TOOLBARCLASSNAMEW, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST, 0, 0, 0, 0,
176 hwndParent, 0, hInst, NULL);
177
178 if (!m_hWnd)
179 {
180 return FALSE;
181 }
182
183 SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS);
184 SetButtonStructSize();
185
186 /* Set image list */
187 HIMAGELIST hImageList = InitImageList();
188
189 if (hImageList)
190 {
191 ImageList_Destroy(SetImageList(hImageList));
192 }
193
194 AddButtons(_countof(Buttons), Buttons);
195
196 /* Remember the ideal width to use as a max width of buttons */
197 UpdateMaxButtonsWidth();
198
199 return m_hWnd;
200 }
201
202 void
ShowButtonCaption(bool bShow)203 CMainToolbar::ShowButtonCaption(bool bShow)
204 {
205 DWORD dCurrentExStyle = (DWORD)SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0);
206 if (bShow)
207 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle & ~TBSTYLE_EX_MIXEDBUTTONS);
208 else
209 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle | TBSTYLE_EX_MIXEDBUTTONS);
210 }
211
212 void
UpdateMaxButtonsWidth()213 CMainToolbar::UpdateMaxButtonsWidth()
214 {
215 SIZE size;
216 GetIdealSize(FALSE, &size);
217 m_dButtonsWidthMax = size.cx;
218 }
219
220 DWORD
GetMaxButtonsWidth() const221 CMainToolbar::GetMaxButtonsWidth() const
222 {
223 return m_dButtonsWidthMax;
224 }
225 // **** CMainToolbar ****
226
227 // **** CSearchBar ****
228
CSearchBar()229 CSearchBar::CSearchBar() : m_Width(180), m_Height(22)
230 {
231 }
232
233 VOID
SetText(LPCWSTR lpszText)234 CSearchBar::SetText(LPCWSTR lpszText)
235 {
236 SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM)lpszText);
237 }
238
239 HWND
Create(HWND hwndParent)240 CSearchBar::Create(HWND hwndParent)
241 {
242 CStringW szBuf;
243 m_hWnd = CreateWindowExW(
244 WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, 0, 0, m_Width, m_Height,
245 hwndParent, (HMENU)NULL, hInst, 0);
246
247 SendMessageW(WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0);
248 szBuf.LoadStringW(IDS_SEARCH_TEXT);
249 SetWindowTextW(szBuf);
250 return m_hWnd;
251 }
252 // **** CSearchBar ****
253
254 // **** CComboBox ****
255
CComboBox()256 CComboBox::CComboBox() : m_Width(80), m_Height(22)
257 {
258 }
259
260 HWND
Create(HWND hwndParent)261 CComboBox::Create(HWND hwndParent)
262 {
263 m_hWnd = CreateWindowW(
264 WC_COMBOBOX, L"", CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 0, 0, m_Width,
265 m_Height, hwndParent, NULL, 0, NULL);
266
267 SendMessageW(WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0);
268
269 for (int i = 0; i < (int)_countof(m_TypeStringID); i++)
270 {
271 CStringW szBuf;
272 szBuf.LoadStringW(m_TypeStringID[i]);
273 SendMessageW(CB_ADDSTRING, 0, (LPARAM)(LPCWSTR)szBuf);
274 }
275
276 SendMessageW(CB_SETCURSEL, m_DefaultSelectType, 0); // select the first item
277
278 return m_hWnd;
279 }
280 // **** CComboBox ****
281
282 // **** CAppRichEdit ****
283
284 VOID
LoadAndInsertText(UINT uStringID,const CStringW & szText,DWORD TextFlags)285 CAppRichEdit::LoadAndInsertText(UINT uStringID, const CStringW &szText, DWORD TextFlags)
286 {
287 CStringW szLoadedText;
288 if (!szText.IsEmpty() && szLoadedText.LoadStringW(uStringID))
289 {
290 const DWORD StringFlags = CFE_BOLD;
291 InsertText(szLoadedText, StringFlags);
292 InsertText(szText, TextFlags);
293 }
294 }
295
296 VOID
LoadAndInsertText(UINT uStringID,DWORD StringFlags)297 CAppRichEdit::LoadAndInsertText(UINT uStringID, DWORD StringFlags)
298 {
299 CStringW szLoadedText;
300 if (szLoadedText.LoadStringW(uStringID))
301 {
302 InsertText(L"\n", 0);
303 InsertText(szLoadedText, StringFlags);
304 InsertText(L"\n", 0);
305 }
306 }
307
308 VOID
InsertTextWithString(UINT StringID,const CStringW & Text,DWORD TextFlags)309 CAppRichEdit::InsertTextWithString(UINT StringID, const CStringW &Text, DWORD TextFlags)
310 {
311 if (!Text.IsEmpty())
312 {
313 LoadAndInsertText(StringID, Text, TextFlags);
314 }
315 }
316 // **** CAppRichEdit ****
317
318 int
ScrnshotDownloadCallback(pASYNCINET AsyncInet,ASYNC_EVENT Event,WPARAM wParam,LPARAM lParam,VOID * Extension)319 ScrnshotDownloadCallback(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam, VOID *Extension)
320 {
321 ScrnshotDownloadParam *DownloadParam = (ScrnshotDownloadParam *)Extension;
322 switch (Event)
323 {
324 case ASYNCINET_DATA:
325 DWORD BytesWritten;
326 WriteFile(DownloadParam->hFile, (LPCVOID)wParam, (DWORD)lParam, &BytesWritten, NULL);
327 break;
328 case ASYNCINET_COMPLETE:
329 CloseHandle(DownloadParam->hFile);
330 SendMessage(
331 DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_SUCCESS, (LPARAM)DownloadParam);
332 break;
333 case ASYNCINET_CANCELLED:
334 CloseHandle(DownloadParam->hFile);
335 SendMessage(
336 DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_CANCELLED, (LPARAM)DownloadParam);
337 break;
338 case ASYNCINET_ERROR:
339 CloseHandle(DownloadParam->hFile);
340 SendMessage(DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, wParam, (LPARAM)DownloadParam);
341 break;
342 default:
343 ATLASSERT(FALSE);
344 break;
345 }
346 return 0;
347 }
348
349 // **** CAppScrnshotPreview ****
350
CAppScrnshotPreview(const CStringW & BasePath)351 CAppScrnshotPreview::CAppScrnshotPreview(const CStringW &BasePath) : m_BasePath(BasePath)
352 {
353 }
354
355 BOOL
ProcessWindowMessage(HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT & theResult,DWORD dwMapId)356 CAppScrnshotPreview::ProcessWindowMessage(
357 HWND hwnd,
358 UINT Msg,
359 WPARAM wParam,
360 LPARAM lParam,
361 LRESULT &theResult,
362 DWORD dwMapId)
363 {
364 theResult = 0;
365 switch (Msg)
366 {
367 case WM_CREATE:
368 hBrokenImgIcon =
369 (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0);
370 break;
371 case WM_SIZE:
372 {
373 if (BrokenImgSize != min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE))
374 {
375 BrokenImgSize = min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE);
376
377 if (hBrokenImgIcon)
378 {
379 DeleteObject(hBrokenImgIcon);
380 hBrokenImgIcon = (HICON)LoadImage(
381 hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0);
382 }
383 }
384 break;
385 }
386 case WM_RAPPS_DOWNLOAD_COMPLETE:
387 {
388 ScrnshotDownloadParam *DownloadParam = (ScrnshotDownloadParam *)lParam;
389 AsyncInetRelease(AsyncInet);
390 AsyncInet = NULL;
391 switch (wParam)
392 {
393 case ERROR_SUCCESS:
394 if (ContentID == DownloadParam->ID)
395 {
396 DisplayFile(DownloadParam->DownloadFileName);
397 // send a message to trigger resizing
398 ::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0);
399 InvalidateRect(0, 0);
400 TempImagePath =
401 DownloadParam->DownloadFileName; // record tmp file path in order to delete it when cleanup
402 }
403 else
404 {
405 // the picture downloaded is already outdated. delete it.
406 DeleteFileW(DownloadParam->DownloadFileName);
407 }
408 break;
409 case ERROR_CANCELLED:
410 DeleteFileW(DownloadParam->DownloadFileName);
411 break;
412 default:
413 DisplayFailed();
414 // send a message to trigger resizing
415 ::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0);
416 InvalidateRect(0, 0);
417 DeleteFileW(DownloadParam->DownloadFileName);
418 break;
419 }
420 delete DownloadParam;
421 break;
422 }
423 case WM_PAINT:
424 {
425 PAINTSTRUCT ps;
426 HDC hdc = BeginPaint(&ps);
427 CRect rect;
428 GetClientRect(&rect);
429
430 PaintOnDC(hdc, rect.Width(), rect.Height(), ps.fErase);
431
432 EndPaint(&ps);
433 break;
434 }
435 case WM_PRINTCLIENT:
436 {
437 if (lParam & PRF_CHECKVISIBLE)
438 {
439 if (!IsWindowVisible())
440 break;
441 }
442 CRect rect;
443 GetClientRect(&rect);
444
445 PaintOnDC((HDC)wParam, rect.Width(), rect.Height(), lParam & PRF_ERASEBKGND);
446 break;
447 }
448 case WM_ERASEBKGND:
449 {
450 return TRUE; // do not erase to avoid blinking
451 }
452 case WM_TIMER:
453 {
454 switch (wParam)
455 {
456 case TIMER_LOADING_ANIMATION:
457 LoadingAnimationFrame++;
458 LoadingAnimationFrame %= (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS);
459 HDC hdc = GetDC();
460 CRect rect;
461 GetClientRect(&rect);
462
463 PaintOnDC(hdc, rect.Width(), rect.Height(), TRUE);
464 ReleaseDC(hdc);
465 }
466 break;
467 }
468 case WM_DESTROY:
469 {
470 PreviousDisplayCleanup();
471 DeleteObject(hBrokenImgIcon);
472 hBrokenImgIcon = NULL;
473 break;
474 }
475 }
476 return FALSE;
477 }
478
479 VOID
DisplayLoading()480 CAppScrnshotPreview::DisplayLoading()
481 {
482 SetStatus(SCRNSHOT_PREV_LOADING);
483 if (bLoadingTimerOn)
484 {
485 KillTimer(TIMER_LOADING_ANIMATION);
486 }
487 LoadingAnimationFrame = 0;
488 bLoadingTimerOn = TRUE;
489 SetTimer(TIMER_LOADING_ANIMATION, 1000 / LOADING_ANIMATION_FPS, 0);
490 }
491
492 VOID
DisplayFailed()493 CAppScrnshotPreview::DisplayFailed()
494 {
495 InterlockedIncrement64(&ContentID);
496 SetStatus(SCRNSHOT_PREV_FAILED);
497 PreviousDisplayCleanup();
498 }
499
500 BOOL
DisplayFile(LPCWSTR lpszFileName)501 CAppScrnshotPreview::DisplayFile(LPCWSTR lpszFileName)
502 {
503 PreviousDisplayCleanup();
504 SetStatus(SCRNSHOT_PREV_IMAGE);
505 pImage = Bitmap::FromFile(lpszFileName, 0);
506 if (pImage->GetLastStatus() != Ok)
507 {
508 DisplayFailed();
509 return FALSE;
510 }
511 return TRUE;
512 }
513
514 VOID
SetStatus(SCRNSHOT_STATUS Status)515 CAppScrnshotPreview::SetStatus(SCRNSHOT_STATUS Status)
516 {
517 ScrnshotPrevStauts = Status;
518 }
519
520 VOID
PaintOnDC(HDC hdc,int width,int height,BOOL bDrawBkgnd)521 CAppScrnshotPreview::PaintOnDC(HDC hdc, int width, int height, BOOL bDrawBkgnd)
522 {
523 // use an off screen dc to avoid blinking
524 HDC hdcMem = CreateCompatibleDC(hdc);
525 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
526 SelectObject(hdcMem, hBitmap);
527
528 if (bDrawBkgnd)
529 {
530 HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcMem, (HGDIOBJ)GetSysColorBrush(COLOR_BTNFACE));
531 PatBlt(hdcMem, 0, 0, width, height, PATCOPY);
532 SelectObject(hdcMem, hOldBrush);
533 }
534
535 switch (ScrnshotPrevStauts)
536 {
537 case SCRNSHOT_PREV_EMPTY:
538 {
539 }
540 break;
541
542 case SCRNSHOT_PREV_LOADING:
543 {
544 Graphics graphics(hdcMem);
545 Color color(255, 0, 0);
546 SolidBrush dotBrush(Color(255, 100, 100, 100));
547
548 graphics.SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias);
549
550 // Paint three dot
551 float DotWidth = GetLoadingDotWidth(width, height);
552 graphics.FillEllipse(
553 (Brush *)(&dotBrush), (REAL)width / 2.0 - min(width, height) * 2.0 / 16.0 - DotWidth / 2.0,
554 (REAL)height / 2.0 -
555 GetFrameDotShift(LoadingAnimationFrame + LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0,
556 DotWidth, DotWidth);
557
558 graphics.FillEllipse(
559 (Brush *)(&dotBrush), (REAL)width / 2.0 - DotWidth / 2.0,
560 (REAL)height / 2.0 - GetFrameDotShift(LoadingAnimationFrame, width, height) - DotWidth / 2.0, DotWidth,
561 DotWidth);
562
563 graphics.FillEllipse(
564 (Brush *)(&dotBrush), (REAL)width / 2.0 + min(width, height) * 2.0 / 16.0 - DotWidth / 2.0,
565 (REAL)height / 2.0 -
566 GetFrameDotShift(LoadingAnimationFrame - LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0,
567 DotWidth, DotWidth);
568 }
569 break;
570
571 case SCRNSHOT_PREV_IMAGE:
572 {
573 if (pImage)
574 {
575 // always draw entire image inside the window.
576 Graphics graphics(hdcMem);
577 float ZoomRatio =
578 min(((float)width / (float)pImage->GetWidth()), ((float)height / (float)pImage->GetHeight()));
579 float ZoomedImgWidth = ZoomRatio * (float)pImage->GetWidth();
580 float ZoomedImgHeight = ZoomRatio * (float)pImage->GetHeight();
581
582 graphics.DrawImage(
583 pImage, ((float)width - ZoomedImgWidth) / 2.0, ((float)height - ZoomedImgHeight) / 2.0,
584 ZoomedImgWidth, ZoomedImgHeight);
585 }
586 }
587 break;
588
589 case SCRNSHOT_PREV_FAILED:
590 {
591 DrawIconEx(
592 hdcMem, (width - BrokenImgSize) / 2, (height - BrokenImgSize) / 2, hBrokenImgIcon, BrokenImgSize,
593 BrokenImgSize, NULL, NULL, DI_NORMAL | DI_COMPAT);
594 }
595 break;
596 }
597
598 // copy the content form off-screen dc to hdc
599 BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
600 DeleteDC(hdcMem);
601 DeleteObject(hBitmap);
602 }
603
604 float
GetLoadingDotWidth(int width,int height)605 CAppScrnshotPreview::GetLoadingDotWidth(int width, int height)
606 {
607 return min(width, height) / 20.0;
608 }
609
610 float
GetFrameDotShift(int Frame,int width,int height)611 CAppScrnshotPreview::GetFrameDotShift(int Frame, int width, int height)
612 {
613 return min(width, height) * (1.0 / 16.0) * (2.0 / (2.0 - sqrt(3.0))) *
614 (max(sin((float)Frame * 2 * PI / (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS)), sqrt(3.0) / 2.0) -
615 sqrt(3.0) / 2.0);
616 }
617
618 ATL::CWndClassInfo &
GetWndClassInfo()619 CAppScrnshotPreview::GetWndClassInfo()
620 {
621 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
622 static ATL::CWndClassInfo wc = {
623 {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, 0, LoadCursorW(NULL, IDC_ARROW),
624 (HBRUSH)(COLOR_BTNFACE + 1), 0, L"RAppsScrnshotPreview", NULL},
625 NULL,
626 NULL,
627 IDC_ARROW,
628 TRUE,
629 0,
630 _T("")};
631 return wc;
632 }
633
634 HWND
Create(HWND hParent)635 CAppScrnshotPreview::Create(HWND hParent)
636 {
637 RECT r = {0, 0, 0, 0};
638
639 return CWindowImpl::Create(hParent, r, L"", WS_CHILD | WS_VISIBLE);
640 }
641
642 VOID
PreviousDisplayCleanup()643 CAppScrnshotPreview::PreviousDisplayCleanup()
644 {
645 if (bLoadingTimerOn)
646 {
647 KillTimer(TIMER_LOADING_ANIMATION);
648 bLoadingTimerOn = FALSE;
649 }
650 LoadingAnimationFrame = 0;
651 if (pImage)
652 {
653 delete pImage;
654 pImage = NULL;
655 }
656 if (AsyncInet)
657 {
658 AsyncInetCancel(AsyncInet);
659 }
660 if (!TempImagePath.IsEmpty())
661 {
662 DeleteFileW(TempImagePath.GetString());
663 TempImagePath.Empty();
664 }
665 }
666
667 VOID
DisplayEmpty()668 CAppScrnshotPreview::DisplayEmpty()
669 {
670 InterlockedIncrement64(&ContentID);
671 SetStatus(SCRNSHOT_PREV_EMPTY);
672 PreviousDisplayCleanup();
673 }
674
675 BOOL
DisplayImage(LPCWSTR lpszLocation)676 CAppScrnshotPreview::DisplayImage(LPCWSTR lpszLocation)
677 {
678 LONGLONG ID = InterlockedIncrement64(&ContentID);
679 PreviousDisplayCleanup();
680
681 if (PathIsURLW(lpszLocation))
682 {
683 DisplayLoading();
684
685 ScrnshotDownloadParam *DownloadParam = new ScrnshotDownloadParam;
686 if (!DownloadParam)
687 return FALSE;
688
689 DownloadParam->hwndNotify = m_hWnd;
690 DownloadParam->ID = ID;
691 // generate a filename
692 CStringW ScrnshotFolder = m_BasePath;
693 PathAppendW(ScrnshotFolder.GetBuffer(MAX_PATH), L"screenshots");
694 ScrnshotFolder.ReleaseBuffer();
695
696 if (!PathIsDirectoryW(ScrnshotFolder.GetString()))
697 {
698 CreateDirectoryW(ScrnshotFolder.GetString(), NULL);
699 }
700
701 if (!GetTempFileNameW(
702 ScrnshotFolder.GetString(), L"img", 0, DownloadParam->DownloadFileName.GetBuffer(MAX_PATH)))
703 {
704 DownloadParam->DownloadFileName.ReleaseBuffer();
705 delete DownloadParam;
706 DisplayFailed();
707 return FALSE;
708 }
709 DownloadParam->DownloadFileName.ReleaseBuffer();
710
711 DownloadParam->hFile = CreateFileW(
712 DownloadParam->DownloadFileName.GetString(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
713 NULL);
714 if (DownloadParam->hFile == INVALID_HANDLE_VALUE)
715 {
716 delete DownloadParam;
717 DisplayFailed();
718 return FALSE;
719 }
720
721 AsyncInet = AsyncInetDownload(
722 0, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, lpszLocation, TRUE, ScrnshotDownloadCallback, DownloadParam);
723 if (!AsyncInet)
724 {
725 CloseHandle(DownloadParam->hFile);
726 DeleteFileW(DownloadParam->DownloadFileName.GetBuffer());
727 delete DownloadParam;
728 DisplayFailed();
729 return FALSE;
730 }
731 return TRUE;
732 }
733 else
734 {
735 return DisplayFile(lpszLocation);
736 }
737 }
738
739 int
GetRequestedWidth(int Height)740 CAppScrnshotPreview::GetRequestedWidth(int Height) // calculate requested window width by given height
741 {
742 switch (ScrnshotPrevStauts)
743 {
744 case SCRNSHOT_PREV_EMPTY:
745 return 0;
746 case SCRNSHOT_PREV_LOADING:
747 return 200;
748 case SCRNSHOT_PREV_IMAGE:
749 if (pImage)
750 {
751 // return the width needed to display image inside the window.
752 // and always keep window w/h ratio inside [ 1/SCRNSHOT_MAX_ASPECT_RAT, SCRNSHOT_MAX_ASPECT_RAT ]
753 return (int)floor(
754 (float)Height *
755 max(min((float)pImage->GetWidth() / (float)pImage->GetHeight(), (float)SCRNSHOT_MAX_ASPECT_RAT),
756 1.0 / (float)SCRNSHOT_MAX_ASPECT_RAT));
757 }
758 return 0;
759 case SCRNSHOT_PREV_FAILED:
760 return 200;
761 default:
762 return 0;
763 }
764 }
765
~CAppScrnshotPreview()766 CAppScrnshotPreview::~CAppScrnshotPreview()
767 {
768 PreviousDisplayCleanup();
769 }
770 // **** CAppScrnshotPreview ****
771
772 // **** CAppInfoDisplay ****
773
774 BOOL
ProcessWindowMessage(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam,LRESULT & theResult,DWORD dwMapId)775 CAppInfoDisplay::ProcessWindowMessage(
776 HWND hwnd,
777 UINT message,
778 WPARAM wParam,
779 LPARAM lParam,
780 LRESULT &theResult,
781 DWORD dwMapId)
782 {
783 theResult = 0;
784 switch (message)
785 {
786 case WM_CREATE:
787 {
788 RichEdit = new CAppRichEdit();
789 RichEdit->Create(hwnd);
790
791 CStringW Directory;
792 ::GetStorageDirectory(Directory);
793 ScrnshotPrev = new CAppScrnshotPreview(Directory);
794 ScrnshotPrev->Create(hwnd);
795 break;
796 }
797 case WM_SIZE:
798 {
799 ResizeChildren(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
800 break;
801 }
802 case WM_RAPPS_RESIZE_CHILDREN:
803 {
804 ResizeChildren();
805 break;
806 }
807 case WM_COMMAND:
808 {
809 OnCommand(wParam, lParam);
810 break;
811 }
812 case WM_NOTIFY:
813 {
814 NMHDR *NotifyHeader = (NMHDR *)lParam;
815 if (NotifyHeader->hwndFrom == RichEdit->m_hWnd)
816 {
817 switch (NotifyHeader->code)
818 {
819 case EN_LINK:
820 OnLink((ENLINK *)lParam);
821 break;
822 }
823 }
824 break;
825 }
826 case WM_SYSCOLORCHANGE:
827 {
828 RichEdit->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE));
829 break;
830 }
831 }
832
833 return FALSE;
834 }
835
836 VOID
ResizeChildren()837 CAppInfoDisplay::ResizeChildren()
838 {
839 CRect rect;
840 GetWindowRect(&rect);
841 ResizeChildren(rect.Width(), rect.Height());
842 }
843
844 VOID
ResizeChildren(int Width,int Height)845 CAppInfoDisplay::ResizeChildren(int Width, int Height)
846 {
847 int ScrnshotWidth = ScrnshotPrev->GetRequestedWidth(Height);
848
849 // make sure richedit always have room to display
850 ScrnshotWidth = min(ScrnshotWidth, Width - INFO_DISPLAY_PADDING - RICHEDIT_MIN_WIDTH);
851
852 DWORD dwError = ERROR_SUCCESS;
853 HDWP hDwp = BeginDeferWindowPos(2);
854
855 if (hDwp)
856 {
857 hDwp = ::DeferWindowPos(hDwp, ScrnshotPrev->m_hWnd, NULL, 0, 0, ScrnshotWidth, Height, 0);
858
859 if (hDwp)
860 {
861 // hide the padding if scrnshot window width == 0
862 int RicheditPosX = ScrnshotWidth ? (ScrnshotWidth + INFO_DISPLAY_PADDING) : 0;
863
864 hDwp = ::DeferWindowPos(hDwp, RichEdit->m_hWnd, NULL, RicheditPosX, 0, Width - RicheditPosX, Height, 0);
865
866 if (hDwp)
867 {
868 EndDeferWindowPos(hDwp);
869 }
870 else
871 {
872 dwError = GetLastError();
873 }
874 }
875 else
876 {
877 dwError = GetLastError();
878 }
879 }
880 else
881 {
882 dwError = GetLastError();
883 }
884
885 #if DBG
886 ATLASSERT(dwError == ERROR_SUCCESS);
887 #endif
888 UNREFERENCED_PARAMETER(dwError);
889
890 UpdateWindow();
891 }
892
893 VOID
OnLink(ENLINK * Link)894 CAppInfoDisplay::OnLink(ENLINK *Link)
895 {
896 switch (Link->msg)
897 {
898 case WM_LBUTTONUP:
899 case WM_RBUTTONUP:
900 {
901 if (pLink)
902 HeapFree(GetProcessHeap(), 0, pLink);
903
904 pLink = (LPWSTR)HeapAlloc(
905 GetProcessHeap(), 0,
906 (max(Link->chrg.cpMin, Link->chrg.cpMax) - min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) *
907 sizeof(WCHAR));
908 if (!pLink)
909 {
910 /* TODO: Error message */
911 return;
912 }
913
914 RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax);
915 RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM)pLink);
916
917 ShowPopupMenuEx(m_hWnd, m_hWnd, IDR_LINKMENU, -1);
918 }
919 break;
920 }
921 }
922
923 ATL::CWndClassInfo &
GetWndClassInfo()924 CAppInfoDisplay::GetWndClassInfo()
925 {
926 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
927 static ATL::CWndClassInfo wc = {/*.m_wc=*/
928 {/*cbSize=*/sizeof(WNDCLASSEX),
929 /*style=*/csStyle,
930 /*lpfnWndProc=*/StartWindowProc,
931 /*cbClsExtra=*/0,
932 /*cbWndExtra=*/0,
933 /*hInstance=*/NULL,
934 /*hIcon=*/NULL,
935 /*hCursor*/ NULL,
936 /*hbrBackground=*/(HBRUSH)(COLOR_BTNFACE + 1),
937 /*lpszMenuName=*/NULL,
938 /*lpszClassName=*/L"RAppsAppInfo",
939 /*hIconSm=*/NULL},
940 /*m_lpszOrigName=*/NULL,
941 /*pWndProc=*/NULL,
942 /*m_lpszCursorID=*/IDC_ARROW,
943 /*m_bSystemCursor*/ TRUE,
944 /*m_atom=*/0,
945 /*m_szAutoName=*/_T("")};
946 return wc;
947 }
948
949 HWND
Create(HWND hwndParent)950 CAppInfoDisplay::Create(HWND hwndParent)
951 {
952 RECT r = {0, 0, 0, 0};
953
954 return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
955 }
956
957 VOID
ShowAppInfo(CAppInfo * Info)958 CAppInfoDisplay::ShowAppInfo(CAppInfo *Info)
959 {
960 CStringW ScrnshotLocation;
961 if (Info->RetrieveScreenshot(ScrnshotLocation))
962 {
963 ScrnshotPrev->DisplayImage(ScrnshotLocation);
964 }
965 else
966 {
967 ScrnshotPrev->DisplayEmpty();
968 }
969 ResizeChildren();
970 Info->ShowAppInfo(RichEdit);
971 }
972
973 void
SetWelcomeText(bool bAppwiz)974 CAppInfoDisplay::SetWelcomeText(bool bAppwiz)
975 {
976 CStringW szText;
977
978 ScrnshotPrev->DisplayEmpty();
979 ResizeChildren();
980
981 // Display the standard banner in normal mode, or
982 // the specific "Add/Remove Programs" in APPWIZ-mode.
983 if (!bAppwiz)
984 {
985 szText.LoadStringW(IDS_WELCOME_TITLE);
986 RichEdit->SetText(szText, CFE_BOLD);
987 RichEdit->InsertText(L"\n\n", 0);
988
989 szText.LoadStringW(IDS_WELCOME_TEXT);
990 RichEdit->InsertText(szText, 0);
991
992 szText.LoadStringW(IDS_WELCOME_URL);
993 RichEdit->InsertText(szText, CFM_LINK);
994 }
995 else
996 {
997 szText.LoadStringW(IDS_APPWIZ_TITLE);
998 RichEdit->SetText(szText, CFE_BOLD);
999 RichEdit->InsertText(L"\n\n", 0);
1000
1001 szText.LoadStringW(IDS_APPWIZ_TEXT1);
1002 RichEdit->InsertText(szText, 0);
1003 RichEdit->InsertText(L"\n", 0);
1004
1005 szText.LoadStringW(IDS_APPWIZ_TEXT2);
1006 RichEdit->InsertText(szText, 0);
1007 }
1008 }
1009
1010 VOID
OnCommand(WPARAM wParam,LPARAM lParam)1011 CAppInfoDisplay::OnCommand(WPARAM wParam, LPARAM lParam)
1012 {
1013 WORD wCommand = LOWORD(wParam);
1014
1015 switch (wCommand)
1016 {
1017 case ID_OPEN_LINK:
1018
1019 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1020 HeapFree(GetProcessHeap(), 0, pLink);
1021 pLink = NULL;
1022 break;
1023
1024 case ID_COPY_LINK:
1025 CopyTextToClipboard(pLink);
1026 HeapFree(GetProcessHeap(), 0, pLink);
1027 pLink = NULL;
1028 break;
1029 }
1030 }
1031
~CAppInfoDisplay()1032 CAppInfoDisplay::~CAppInfoDisplay()
1033 {
1034 delete RichEdit;
1035 delete ScrnshotPrev;
1036 }
1037 // **** CAppInfoDisplay ****
1038
1039 // **** CAppsListView ****
1040
1041 struct CAsyncLoadIcon {
1042 CAsyncLoadIcon *pNext;
1043 HWND hAppsList;
1044 CAppInfo *AppInfo; // Only used to find the item in the list, do not access on background thread
1045 UINT TaskId;
1046 bool Parse;
1047 WCHAR Location[ANYSIZE_ARRAY];
1048
FreeCAsyncLoadIcon1049 void Free() { free(this); }
1050 static CAsyncLoadIcon* Queue(HWND hAppsList, CAppInfo &AppInfo, bool Parse);
1051 static void StartTasks();
1052 } *g_AsyncIconTasks = NULL;
1053 static UINT g_AsyncIconTaskId = 0;
1054
1055 static DWORD CALLBACK
AsyncLoadIconProc(LPVOID Param)1056 AsyncLoadIconProc(LPVOID Param)
1057 {
1058 for (CAsyncLoadIcon *task = (CAsyncLoadIcon*)Param, *old; task; old->Free())
1059 {
1060 if (task->TaskId == g_AsyncIconTaskId)
1061 {
1062 HICON hIcon;
1063 if (!task->Parse)
1064 hIcon = (HICON)LoadImageW(NULL, task->Location, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
1065 else if (!ExtractIconExW(task->Location, PathParseIconLocationW(task->Location), &hIcon, NULL, 1))
1066 hIcon = NULL;
1067
1068 if (hIcon)
1069 {
1070 SendMessageW(task->hAppsList, WM_RAPPSLIST_ASYNCICON, (WPARAM)hIcon, (LPARAM)task);
1071 DestroyIcon(hIcon);
1072 }
1073 }
1074 old = task;
1075 task = task->pNext;
1076 }
1077 return 0;
1078 }
1079
1080 CAsyncLoadIcon*
Queue(HWND hAppsList,CAppInfo & AppInfo,bool Parse)1081 CAsyncLoadIcon::Queue(HWND hAppsList, CAppInfo &AppInfo, bool Parse)
1082 {
1083 ATLASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(hAppsList, NULL));
1084 CStringW szIconPath;
1085 if (!AppInfo.RetrieveIcon(szIconPath))
1086 return NULL;
1087 SIZE_T cbstr = (szIconPath.GetLength() + 1) * sizeof(WCHAR);
1088 CAsyncLoadIcon *task = (CAsyncLoadIcon*)malloc(sizeof(CAsyncLoadIcon) + cbstr);
1089 if (!task)
1090 return NULL;
1091 task->hAppsList = hAppsList;
1092 task->AppInfo = &AppInfo;
1093 task->TaskId = g_AsyncIconTaskId;
1094 task->Parse = Parse;
1095 CopyMemory(task->Location, szIconPath.GetBuffer(), cbstr);
1096 szIconPath.ReleaseBuffer();
1097 task->pNext = g_AsyncIconTasks;
1098 g_AsyncIconTasks = task;
1099 return task;
1100 }
1101
1102 void
StartTasks()1103 CAsyncLoadIcon::StartTasks()
1104 {
1105 CAsyncLoadIcon *tasks = g_AsyncIconTasks;
1106 g_AsyncIconTasks = NULL;
1107 if (HANDLE hThread = CreateThread(NULL, 0, AsyncLoadIconProc, tasks, 0, NULL))
1108 CloseHandle(hThread);
1109 else
1110 AsyncLoadIconProc(tasks); // Insist so we at least free the tasks
1111 }
1112
CAppsListView()1113 CAppsListView::CAppsListView()
1114 {
1115 m_hImageListView = 0;
1116 }
1117
~CAppsListView()1118 CAppsListView::~CAppsListView()
1119 {
1120 if (m_hImageListView)
1121 ImageList_Destroy(m_hImageListView);
1122 if (g_hDefaultPackageIcon)
1123 DestroyIcon(g_hDefaultPackageIcon);
1124 }
1125
1126 LRESULT
OnEraseBackground(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1127 CAppsListView::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1128 {
1129 LRESULT lRes = this->DefWindowProc(uMsg, wParam, lParam);
1130 if (!m_Watermark.IsEmpty())
1131 {
1132 RECT rc;
1133 GetClientRect(&rc);
1134 HGDIOBJ oldFont = SelectFont(HDC(wParam), GetStockFont(DEFAULT_GUI_FONT));
1135 DrawShadowText(
1136 HDC(wParam), m_Watermark.GetString(), m_Watermark.GetLength(), &rc,
1137 DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, GetSysColor(COLOR_GRAYTEXT),
1138 GetSysColor(COLOR_GRAYTEXT), 1, 1);
1139 SelectFont(HDC(wParam), oldFont);
1140 }
1141 return lRes;
1142 }
1143
1144 LRESULT
OnAsyncIcon(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1145 CAppsListView::OnAsyncIcon(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1146 {
1147 CAsyncLoadIcon *task = (CAsyncLoadIcon*)lParam;
1148 bHandled = TRUE;
1149 if (task->TaskId == g_AsyncIconTaskId)
1150 {
1151 LVITEM lvi;
1152 LVFINDINFO lvfi;
1153 lvfi.flags = LVFI_PARAM;
1154 lvfi.lParam = (LPARAM)task->AppInfo;
1155 lvi.iItem = ListView_FindItem(m_hWnd, -1, &lvfi);
1156 if (lvi.iItem != -1)
1157 {
1158 lvi.iImage = ImageList_AddIcon(m_hImageListView, (HICON)wParam);
1159 if (lvi.iImage != -1)
1160 {
1161 lvi.mask = LVIF_IMAGE;
1162 lvi.iSubItem = 0;
1163 ListView_SetItem(m_hWnd, &lvi);
1164 }
1165 }
1166 }
1167 return 0;
1168 }
1169
1170 VOID
SetWatermark(const CStringW & Text)1171 CAppsListView::SetWatermark(const CStringW &Text)
1172 {
1173 m_Watermark = Text;
1174 }
1175
1176 void
ShowCheckboxes(bool bShow)1177 CAppsListView::ShowCheckboxes(bool bShow)
1178 {
1179 SetExtendedListViewStyle((bShow ? LVS_EX_CHECKBOXES : 0) | LVS_EX_FULLROWSELECT);
1180 bHasCheckboxes = bShow;
1181 }
1182
1183 VOID
ColumnClick(LPNMLISTVIEW pnmv)1184 CAppsListView::ColumnClick(LPNMLISTVIEW pnmv)
1185 {
1186 HWND hHeader;
1187 HDITEMW hColumn;
1188 INT nHeaderID = pnmv->iSubItem;
1189
1190 if ((GetWindowLongPtr(GWL_STYLE) & ~LVS_NOSORTHEADER) == 0)
1191 return;
1192
1193 hHeader = (HWND)SendMessage(LVM_GETHEADER, 0, 0);
1194 ZeroMemory(&hColumn, sizeof(hColumn));
1195
1196 /* If the sorting column changed, remove the sorting style from the old column */
1197 if ((nLastHeaderID != -1) && (nLastHeaderID != nHeaderID))
1198 {
1199 bIsAscending = TRUE; // also reset sorting method to ascending
1200 hColumn.mask = HDI_FORMAT;
1201 Header_GetItem(hHeader, nLastHeaderID, &hColumn);
1202 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
1203 Header_SetItem(hHeader, nLastHeaderID, &hColumn);
1204 }
1205
1206 /* Set the sorting style to the new column */
1207 hColumn.mask = HDI_FORMAT;
1208 Header_GetItem(hHeader, nHeaderID, &hColumn);
1209
1210 hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
1211 hColumn.fmt |= (bIsAscending ? HDF_SORTUP : HDF_SORTDOWN);
1212 Header_SetItem(hHeader, nHeaderID, &hColumn);
1213
1214 /* Sort the list, using the current values of nHeaderID and bIsAscending */
1215 SortContext ctx = {this, nHeaderID};
1216 SortItems(s_CompareFunc, &ctx);
1217
1218 /* Save new values */
1219 nLastHeaderID = nHeaderID;
1220 bIsAscending = !bIsAscending;
1221 }
1222
1223 BOOL
AddColumn(INT Index,CStringW & Text,INT Width,INT Format)1224 CAppsListView::AddColumn(INT Index, CStringW &Text, INT Width, INT Format)
1225 {
1226 LVCOLUMNW Column;
1227
1228 ZeroMemory(&Column, sizeof(Column));
1229
1230 Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
1231 Column.iSubItem = Index;
1232 Column.pszText = const_cast<LPWSTR>(Text.GetString());
1233 Column.cx = Width;
1234 Column.fmt = Format;
1235
1236 return SendMessage(LVM_INSERTCOLUMN, Index, (LPARAM)(&Column));
1237 }
1238
1239 void
DeleteColumn(INT Index)1240 CAppsListView::DeleteColumn(INT Index)
1241 {
1242 SendMessage(LVM_DELETECOLUMN, Index, 0);
1243 return;
1244 }
1245
1246 INT
AddItem(INT ItemIndex,INT IconIndex,LPCWSTR lpText,LPARAM lParam)1247 CAppsListView::AddItem(INT ItemIndex, INT IconIndex, LPCWSTR lpText, LPARAM lParam)
1248 {
1249 LVITEMW Item;
1250
1251 ZeroMemory(&Item, sizeof(Item));
1252
1253 Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
1254 Item.pszText = const_cast<LPWSTR>(lpText);
1255 Item.lParam = lParam;
1256 Item.iItem = ItemIndex;
1257 Item.iImage = IconIndex;
1258
1259 if (IconIndex >= 0)
1260 {
1261 Item.iImage = IconIndex;
1262 Item.mask |= LVIF_IMAGE;
1263 }
1264 return InsertItem(&Item);
1265 }
1266
1267 HIMAGELIST
GetImageList(int iImageList)1268 CAppsListView::GetImageList(int iImageList)
1269 {
1270 return (HIMAGELIST)SendMessage(LVM_GETIMAGELIST, iImageList, 0);
1271 }
1272
1273 INT CALLBACK
s_CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort)1274 CAppsListView::s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
1275 {
1276 SortContext *ctx = ((SortContext *)lParamSort);
1277 return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem);
1278 }
1279
1280 INT
CompareFunc(LPARAM lParam1,LPARAM lParam2,INT iSubItem)1281 CAppsListView::CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem)
1282 {
1283 CStringW Item1, Item2;
1284 LVFINDINFOW IndexInfo;
1285 INT Index;
1286
1287 IndexInfo.flags = LVFI_PARAM;
1288
1289 IndexInfo.lParam = lParam1;
1290 Index = FindItem(-1, &IndexInfo);
1291 GetItemText(Index, iSubItem, Item1.GetBuffer(MAX_STR_LEN), MAX_STR_LEN);
1292 Item1.ReleaseBuffer();
1293
1294 IndexInfo.lParam = lParam2;
1295 Index = FindItem(-1, &IndexInfo);
1296 GetItemText(Index, iSubItem, Item2.GetBuffer(MAX_STR_LEN), MAX_STR_LEN);
1297 Item2.ReleaseBuffer();
1298
1299 return bIsAscending ? Item1.Compare(Item2) : Item2.Compare(Item1);
1300 }
1301
1302 HWND
Create(HWND hwndParent)1303 CAppsListView::Create(HWND hwndParent)
1304 {
1305 RECT r = {205, 28, 465, 250};
1306 DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS |
1307 LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS;
1308
1309 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE);
1310
1311 if (hwnd)
1312 {
1313 ShowCheckboxes(false);
1314 }
1315
1316 #pragma push_macro("SubclassWindow")
1317 #undef SubclassWindow
1318 m_hWnd = NULL;
1319 SubclassWindow(hwnd);
1320 #pragma pop_macro("SubclassWindow")
1321
1322 return hwnd;
1323 }
1324
1325 BOOL
GetCheckState(INT item)1326 CAppsListView::GetCheckState(INT item)
1327 {
1328 return (BOOL)(GetItemState(item, LVIS_STATEIMAGEMASK) >> 12) - 1;
1329 }
1330
1331 VOID
SetCheckState(INT item,BOOL fCheck)1332 CAppsListView::SetCheckState(INT item, BOOL fCheck)
1333 {
1334 if (bHasCheckboxes)
1335 {
1336 SetItemState(item, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), LVIS_STATEIMAGEMASK);
1337 }
1338 }
1339
1340 VOID
CheckAll()1341 CAppsListView::CheckAll()
1342 {
1343 if (bHasCheckboxes)
1344 {
1345 if (CheckedItemCount == ItemCount)
1346 {
1347 // clear all
1348 SetCheckState(-1, FALSE);
1349 }
1350 else
1351 {
1352 // check all
1353 SetCheckState(-1, TRUE);
1354 }
1355 }
1356 }
1357
1358 PVOID
GetFocusedItemData()1359 CAppsListView::GetFocusedItemData()
1360 {
1361 INT item = GetSelectionMark();
1362 if (item == -1)
1363 {
1364 return (PVOID)0;
1365 }
1366 return (PVOID)GetItemData(item);
1367 }
1368
1369 BOOL
SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)1370 CAppsListView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)
1371 {
1372 ++g_AsyncIconTaskId; // Stop loading icons that are now invalid
1373 if (!DeleteAllItems())
1374 return FALSE;
1375
1376 ApplicationViewType = AppType;
1377 bIsAscending = TRUE;
1378 ItemCount = 0;
1379 CheckedItemCount = 0;
1380
1381 ListView_Scroll(m_hWnd, 0, 0x7fff * -1); // FIXME: a bug in Wine ComCtl32 where VScroll is not reset after deleting items
1382
1383 // delete old columns
1384 while (ColumnCount)
1385 {
1386 DeleteColumn(--ColumnCount);
1387 }
1388
1389 if (!g_hDefaultPackageIcon)
1390 {
1391 ImageList_Destroy(m_hImageListView);
1392 UINT IconSize = GetSystemMetrics(SM_CXICON);
1393 UINT ilc = GetSystemColorDepth() | ILC_MASK;
1394 m_hImageListView = ImageList_Create(IconSize, IconSize, ilc, 0, 1);
1395 SetImageList(m_hImageListView, LVSIL_SMALL);
1396 SetImageList(m_hImageListView, LVSIL_NORMAL);
1397 g_hDefaultPackageIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_MAIN),
1398 IMAGE_ICON, IconSize, IconSize, LR_SHARED);
1399 }
1400 ImageList_RemoveAll(m_hImageListView);
1401
1402 g_DefaultPackageIconILIdx = ImageList_AddIcon(m_hImageListView, g_hDefaultPackageIcon);
1403 if (g_DefaultPackageIconILIdx == -1)
1404 g_DefaultPackageIconILIdx = I_IMAGENONE;
1405
1406 // add new columns
1407 CStringW szText;
1408 switch (AppType)
1409 {
1410 case AppViewTypeInstalledApps:
1411
1412 /* Add columns to ListView */
1413 szText.LoadStringW(IDS_APP_NAME);
1414 AddColumn(ColumnCount++, szText, 368, LVCFMT_LEFT);
1415
1416 szText.LoadStringW(IDS_APP_INST_VERSION);
1417 AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT);
1418
1419 #if 0 // This column is not currently useful for installed apps.
1420 szText.LoadStringW(IDS_APP_DESCRIPTION);
1421 AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT);
1422 #endif
1423
1424 // Disable checkboxes
1425 ShowCheckboxes(false);
1426 break;
1427
1428 case AppViewTypeAvailableApps:
1429
1430 /* Add columns to ListView */
1431 szText.LoadStringW(IDS_APP_NAME);
1432 AddColumn(ColumnCount++, szText, 250, LVCFMT_LEFT);
1433
1434 szText.LoadStringW(IDS_APP_INST_VERSION);
1435 AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT);
1436
1437 szText.LoadStringW(IDS_APP_DESCRIPTION);
1438 AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT);
1439
1440 // Enable checkboxes
1441 ShowCheckboxes(true);
1442 break;
1443
1444 default:
1445 break;
1446 }
1447
1448 return TRUE;
1449 }
1450
1451 BOOL
SetViewMode(DWORD ViewMode)1452 CAppsListView::SetViewMode(DWORD ViewMode)
1453 {
1454 return SendMessage(LVM_SETVIEW, (WPARAM)ViewMode, 0) == 1;
1455 }
1456
1457 BOOL
AddApplication(CAppInfo * AppInfo,BOOL InitialCheckState)1458 CAppsListView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState)
1459 {
1460 if (!AppInfo)
1461 {
1462 CAsyncLoadIcon::StartTasks();
1463 return TRUE;
1464 }
1465
1466 int IconIndex = g_DefaultPackageIconILIdx;
1467 if (ApplicationViewType == AppViewTypeInstalledApps)
1468 {
1469 int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
1470 if (Index == -1)
1471 return FALSE;
1472 CAsyncLoadIcon::Queue(m_hWnd, *AppInfo, true);
1473
1474 SetItemText(Index, 1, AppInfo->szDisplayVersion.IsEmpty() ? L"---" : AppInfo->szDisplayVersion);
1475 SetItemText(Index, 2, AppInfo->szComments.IsEmpty() ? L"---" : AppInfo->szComments);
1476
1477 ItemCount++;
1478 return TRUE;
1479 }
1480 else if (ApplicationViewType == AppViewTypeAvailableApps)
1481 {
1482 int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
1483 if (Index == -1)
1484 return FALSE;
1485 CAsyncLoadIcon::Queue(m_hWnd, *AppInfo, false);
1486
1487 if (InitialCheckState)
1488 {
1489 SetCheckState(Index, TRUE);
1490 }
1491
1492 SetItemText(Index, 1, AppInfo->szDisplayVersion);
1493 SetItemText(Index, 2, AppInfo->szComments);
1494
1495 ItemCount++;
1496 return TRUE;
1497 }
1498 else
1499 {
1500 return FALSE;
1501 }
1502 }
1503
1504 // this function is called when parent window receiving an notification about checkstate changing
1505 VOID
ItemCheckStateNotify(int iItem,BOOL bCheck)1506 CAppsListView::ItemCheckStateNotify(int iItem, BOOL bCheck)
1507 {
1508 if (bCheck)
1509 {
1510 CheckedItemCount++;
1511 }
1512 else
1513 {
1514 CheckedItemCount--;
1515 }
1516 }
1517 // **** CAppsListView ****
1518
1519 // **** CApplicationView ****
1520
1521 BOOL
ProcessWindowMessage(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam,LRESULT & theResult,DWORD dwMapId)1522 CApplicationView::ProcessWindowMessage(
1523 HWND hwnd,
1524 UINT message,
1525 WPARAM wParam,
1526 LPARAM lParam,
1527 LRESULT &theResult,
1528 DWORD dwMapId)
1529 {
1530 theResult = 0;
1531 switch (message)
1532 {
1533 case WM_CREATE:
1534 {
1535 BOOL bSuccess = TRUE;
1536 m_Panel = new CUiPanel();
1537 m_Panel->m_VerticalAlignment = UiAlign_Stretch;
1538 m_Panel->m_HorizontalAlignment = UiAlign_Stretch;
1539
1540 bSuccess &= CreateToolbar();
1541 bSuccess &= CreateSearchBar();
1542 bSuccess &= CreateComboBox();
1543 bSuccess &= CreateHSplitter();
1544 bSuccess &= CreateListView();
1545 bSuccess &= CreateAppInfoDisplay();
1546
1547 /* APPWIZ-mode: Remove the unneeded menu items and toolbar buttons */
1548 if (m_MainWindow->m_bAppwizMode)
1549 {
1550 HMENU hMenu;
1551
1552 /* Delete the "Settings" item in the "File" sub-menu */
1553 hMenu = ::GetSubMenu(m_MainWindow->GetMenu(), 0);
1554 DeleteMenuEx(hMenu, ID_SETTINGS, MF_BYCOMMAND);
1555
1556 /* Remove the menu items: ID_INSTALL, ID_RESETDB */
1557 hMenu = GetMenu();
1558 DeleteMenuEx(hMenu, ID_INSTALL, MF_BYCOMMAND);
1559 DeleteMenuEx(hMenu, ID_RESETDB, MF_BYCOMMAND);
1560
1561 /* Remove the toolbar buttons:
1562 * ID_INSTALL, ID_CHECK_ALL, ID_RESETDB
1563 * We only keep:
1564 * ID_UNINSTALL, ID_MODIFY, ID_REFRESH */
1565 TBBUTTONINFO info = { sizeof(info), 0 };
1566 int index;
1567
1568 index = m_Toolbar->GetButtonInfo(ID_INSTALL, &info);
1569 if (index >= 0) m_Toolbar->DeleteButton(index);
1570
1571 index = m_Toolbar->GetButtonInfo(ID_CHECK_ALL, &info);
1572 if (index >= 0) m_Toolbar->DeleteButton(index);
1573
1574 index = m_Toolbar->GetButtonInfo(ID_RESETDB, &info);
1575 if (index >= 0) m_Toolbar->DeleteButton(index);
1576
1577 /* Update the ideal width to use as a max width of buttons */
1578 m_Toolbar->UpdateMaxButtonsWidth();
1579 }
1580
1581 /* Resize the toolbar */
1582 m_Toolbar->AutoSize();
1583
1584 RECT rTop;
1585
1586 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop);
1587 m_HSplitter->m_Margin.top = rTop.bottom - rTop.top;
1588 if (!bSuccess)
1589 {
1590 return -1; // creation failure
1591 }
1592 }
1593 break;
1594
1595 case WM_NOTIFY:
1596 {
1597 LPNMHDR pNotifyHeader = (LPNMHDR)lParam;
1598 if (pNotifyHeader->hwndFrom == m_ListView->GetWindow())
1599 {
1600 switch (pNotifyHeader->code)
1601 {
1602 case LVN_ITEMCHANGED:
1603 {
1604 LPNMLISTVIEW pnic = (LPNMLISTVIEW)lParam;
1605
1606 /* Check if this is a valid item
1607 * (technically, it can be also an unselect) */
1608 INT ItemIndex = pnic->iItem;
1609 if (ItemIndex == -1 || ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom))
1610 {
1611 break;
1612 }
1613
1614 /* Check if the focus has been moved to another item */
1615 if ((pnic->uChanged & LVIF_STATE) && (pnic->uNewState & LVIS_FOCUSED) &&
1616 !(pnic->uOldState & LVIS_FOCUSED))
1617 {
1618 ItemGetFocus((LPVOID)pnic->lParam);
1619 }
1620
1621 /* Check if the item is checked/unchecked */
1622 if (pnic->uChanged & LVIF_STATE)
1623 {
1624 int iOldState = STATEIMAGETOINDEX(pnic->uOldState);
1625 int iNewState = STATEIMAGETOINDEX(pnic->uNewState);
1626
1627 if (iOldState == STATEIMAGE_UNCHECKED && iNewState == STATEIMAGE_CHECKED)
1628 {
1629 // this item is just checked
1630 m_ListView->ItemCheckStateNotify(pnic->iItem, TRUE);
1631 ItemCheckStateChanged(TRUE, (LPVOID)pnic->lParam);
1632 }
1633 else if (iOldState == STATEIMAGE_CHECKED && iNewState == STATEIMAGE_UNCHECKED)
1634 {
1635 // this item is just unchecked
1636 m_ListView->ItemCheckStateNotify(pnic->iItem, FALSE);
1637 ItemCheckStateChanged(FALSE, (LPVOID)pnic->lParam);
1638 }
1639 }
1640 }
1641 break;
1642
1643 case LVN_COLUMNCLICK:
1644 {
1645 LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
1646
1647 m_ListView->ColumnClick(pnmv);
1648 }
1649 break;
1650
1651 case NM_DBLCLK:
1652 {
1653 LPNMITEMACTIVATE Item = (LPNMITEMACTIVATE)lParam;
1654 if (Item->iItem != -1)
1655 {
1656 /* this won't do anything if the program is already installed */
1657
1658 if (ApplicationViewType == AppViewTypeAvailableApps)
1659 {
1660 m_MainWindow->InstallApplication(
1661 (CAppInfo *)m_ListView->GetItemData(Item->iItem));
1662 }
1663 }
1664 }
1665 break;
1666 }
1667 }
1668 else if (pNotifyHeader->hwndFrom == m_Toolbar->GetWindow())
1669 {
1670 switch (pNotifyHeader->code)
1671 {
1672 case TTN_GETDISPINFO:
1673 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT)lParam);
1674 break;
1675 }
1676 }
1677 }
1678 break;
1679
1680 case WM_SYSCOLORCHANGE:
1681 {
1682 /* Forward WM_SYSCOLORCHANGE to common controls */
1683 m_ListView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
1684 m_AppsInfo->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
1685 m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
1686 m_ComboBox->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
1687 }
1688 break;
1689
1690 case WM_SIZE:
1691 {
1692 OnSize(hwnd, wParam, lParam);
1693 break;
1694 }
1695
1696 case WM_COMMAND:
1697 {
1698 OnCommand(wParam, lParam);
1699 }
1700 break;
1701
1702 case WM_CONTEXTMENU:
1703 {
1704 bool kbd = -1 == (int)(INT_PTR)lParam;
1705 if ((HWND)wParam == m_ListView->m_hWnd)
1706 {
1707 int item = m_ListView->GetNextItem(-1, LVNI_FOCUSED | LVNI_SELECTED);
1708 if (item != -1)
1709 {
1710 POINT *ppt = NULL, pt;
1711 if (kbd)
1712 {
1713 RECT r;
1714 ListView_GetItemRect((HWND)wParam, item, &r, LVIR_LABEL);
1715 pt.x = r.left + (r.right - r.left) / 2;
1716 pt.y = r.top + (r.bottom - r.top) / 2;
1717 ::ClientToScreen((HWND)wParam, ppt = &pt);
1718 }
1719 ShowPopupMenuEx(m_hWnd, m_hWnd, 0, ID_INSTALL, ppt);
1720 return TRUE;
1721 }
1722 }
1723 }
1724 break;
1725 }
1726 return FALSE;
1727 }
1728
1729 BOOL
CreateToolbar()1730 CApplicationView::CreateToolbar()
1731 {
1732 m_Toolbar = new CMainToolbar();
1733 m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop;
1734 m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch;
1735 m_Panel->Children().Append(m_Toolbar);
1736
1737 return m_Toolbar->Create(m_hWnd) != NULL;
1738 }
1739
1740 BOOL
CreateSearchBar()1741 CApplicationView::CreateSearchBar()
1742 {
1743 m_SearchBar = new CUiWindow<CSearchBar>();
1744 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop;
1745 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm;
1746 m_SearchBar->m_Margin.top = 4;
1747 m_SearchBar->m_Margin.right = TOOLBAR_PADDING;
1748
1749 return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL;
1750 }
1751
1752 BOOL
CreateComboBox()1753 CApplicationView::CreateComboBox()
1754 {
1755 m_ComboBox = new CUiWindow<CComboBox>();
1756 m_ComboBox->m_VerticalAlignment = UiAlign_LeftTop;
1757 m_ComboBox->m_HorizontalAlignment = UiAlign_RightBtm;
1758 m_ComboBox->m_Margin.top = 4;
1759
1760 return m_ComboBox->Create(m_Toolbar->m_hWnd) != NULL;
1761 }
1762
1763 BOOL
CreateHSplitter()1764 CApplicationView::CreateHSplitter()
1765 {
1766 m_HSplitter = new CUiSplitPanel();
1767 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch;
1768 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch;
1769 m_HSplitter->m_DynamicFirst = TRUE;
1770 m_HSplitter->m_Horizontal = TRUE;
1771 m_HSplitter->m_Pos = INT_MAX; // set INT_MAX to use lowest possible position (m_MinSecond)
1772 m_HSplitter->m_MinFirst = 10;
1773 m_HSplitter->m_MinSecond = 140;
1774 m_Panel->Children().Append(m_HSplitter);
1775
1776 return m_HSplitter->Create(m_hWnd) != NULL;
1777 }
1778
1779 BOOL
CreateListView()1780 CApplicationView::CreateListView()
1781 {
1782 m_ListView = new CAppsListView();
1783 m_ListView->m_VerticalAlignment = UiAlign_Stretch;
1784 m_ListView->m_HorizontalAlignment = UiAlign_Stretch;
1785 m_HSplitter->First().Append(m_ListView);
1786
1787 return m_ListView->Create(m_hWnd) != NULL;
1788 }
1789
1790 BOOL
CreateAppInfoDisplay()1791 CApplicationView::CreateAppInfoDisplay()
1792 {
1793 m_AppsInfo = new CAppInfoDisplay();
1794 m_AppsInfo->m_VerticalAlignment = UiAlign_Stretch;
1795 m_AppsInfo->m_HorizontalAlignment = UiAlign_Stretch;
1796 m_HSplitter->Second().Append(m_AppsInfo);
1797
1798 return m_AppsInfo->Create(m_hWnd) != NULL;
1799 }
1800
1801 void
SetRedraw(BOOL bRedraw)1802 CApplicationView::SetRedraw(BOOL bRedraw)
1803 {
1804 CWindow::SetRedraw(bRedraw);
1805 m_ListView->SetRedraw(bRedraw);
1806 }
1807
1808 void
SetFocusOnSearchBar()1809 CApplicationView::SetFocusOnSearchBar()
1810 {
1811 m_SearchBar->SetFocus();
1812 }
1813
1814 VOID
OnSize(HWND hwnd,WPARAM wParam,LPARAM lParam)1815 CApplicationView::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
1816 {
1817 if (wParam == SIZE_MINIMIZED)
1818 return;
1819
1820 /* Resize the toolbar */
1821 m_Toolbar->AutoSize();
1822
1823 /* Automatically hide captions */
1824 DWORD dToolbarTreshold = m_Toolbar->GetMaxButtonsWidth();
1825 DWORD dSearchbarMargin = (LOWORD(lParam) - m_SearchBar->m_Width - m_ComboBox->m_Width - TOOLBAR_PADDING * 2);
1826
1827 if (dSearchbarMargin > dToolbarTreshold)
1828 {
1829 m_Toolbar->ShowButtonCaption(true);
1830 }
1831 else if (dSearchbarMargin < dToolbarTreshold)
1832 {
1833 m_Toolbar->ShowButtonCaption(false);
1834 }
1835
1836 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)};
1837 HDWP hdwp = NULL;
1838 INT count = m_Panel->CountSizableChildren();
1839
1840 hdwp = BeginDeferWindowPos(count);
1841 if (hdwp)
1842 {
1843 hdwp = m_Panel->OnParentSize(r, hdwp);
1844 if (hdwp)
1845 {
1846 EndDeferWindowPos(hdwp);
1847 }
1848 }
1849
1850 count = m_SearchBar->CountSizableChildren();
1851 hdwp = BeginDeferWindowPos(count);
1852 if (hdwp)
1853 {
1854 hdwp = m_SearchBar->OnParentSize(r, hdwp);
1855 if (hdwp)
1856 {
1857 EndDeferWindowPos(hdwp);
1858 }
1859 }
1860
1861 m_ComboBox->m_Margin.right = m_SearchBar->m_Width + m_SearchBar->m_Margin.right + TOOLBAR_PADDING;
1862 count = m_ComboBox->CountSizableChildren();
1863 hdwp = BeginDeferWindowPos(count);
1864 if (hdwp)
1865 {
1866 hdwp = m_ComboBox->OnParentSize(r, hdwp);
1867 if (hdwp)
1868 {
1869 EndDeferWindowPos(hdwp);
1870 }
1871 }
1872 }
1873
1874 VOID
OnCommand(WPARAM wParam,LPARAM lParam)1875 CApplicationView::OnCommand(WPARAM wParam, LPARAM lParam)
1876 {
1877 if (lParam)
1878 {
1879 if ((HWND)lParam == m_SearchBar->GetWindow())
1880 {
1881 CStringW szBuf;
1882 switch (HIWORD(wParam))
1883 {
1884 case EN_SETFOCUS:
1885 {
1886 CStringW szWndText;
1887
1888 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1889 m_SearchBar->GetWindowTextW(szWndText);
1890 if (szBuf == szWndText)
1891 {
1892 m_SearchBar->SetWindowTextW(L"");
1893 }
1894 }
1895 break;
1896
1897 case EN_KILLFOCUS:
1898 {
1899 m_SearchBar->GetWindowTextW(szBuf);
1900 if (szBuf.IsEmpty())
1901 {
1902 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1903 m_SearchBar->SetWindowTextW(szBuf.GetString());
1904 }
1905 }
1906 break;
1907
1908 case EN_CHANGE:
1909 {
1910 CStringW szWndText;
1911
1912 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1913 m_SearchBar->GetWindowTextW(szWndText);
1914 if (szBuf == szWndText)
1915 {
1916 szWndText = L"";
1917 m_MainWindow->SearchTextChanged(szWndText);
1918 }
1919 else
1920 {
1921 m_MainWindow->SearchTextChanged(szWndText);
1922 }
1923 }
1924 break;
1925 }
1926
1927 return;
1928 }
1929 else if ((HWND)lParam == m_ComboBox->GetWindow())
1930 {
1931 int NotifyCode = HIWORD(wParam);
1932 switch (NotifyCode)
1933 {
1934 case CBN_SELCHANGE:
1935 int CurrSelection = m_ComboBox->SendMessageW(CB_GETCURSEL);
1936
1937 int ViewModeList[] = {LV_VIEW_DETAILS, LV_VIEW_LIST, LV_VIEW_TILE};
1938 ATLASSERT(CurrSelection < (int)_countof(ViewModeList));
1939 if (!m_ListView->SetViewMode(ViewModeList[CurrSelection]))
1940 {
1941 MessageBoxW(L"View mode invalid or unimplemented");
1942 }
1943 break;
1944 }
1945
1946 return;
1947 }
1948 else if ((HWND)lParam == m_Toolbar->GetWindow())
1949 {
1950 // the message is sent from Toolbar. fall down to continue process
1951 }
1952 else
1953 {
1954 return;
1955 }
1956 }
1957
1958 // the LOWORD of wParam contains a Menu or Control ID
1959 WORD wCommand = LOWORD(wParam);
1960
1961 switch (wCommand)
1962 {
1963 case ID_INSTALL:
1964 case ID_UNINSTALL:
1965 case ID_MODIFY:
1966 case ID_REGREMOVE:
1967 case ID_REFRESH:
1968 case ID_RESETDB:
1969 case ID_CHECK_ALL:
1970 m_MainWindow->SendMessageW(WM_COMMAND, wCommand, 0);
1971 break;
1972 }
1973 }
1974
CApplicationView(CMainWindow * MainWindow)1975 CApplicationView::CApplicationView(CMainWindow *MainWindow) : m_MainWindow(MainWindow)
1976 {
1977 }
1978
~CApplicationView()1979 CApplicationView::~CApplicationView()
1980 {
1981 delete m_Toolbar;
1982 delete m_SearchBar;
1983 delete m_ListView;
1984 delete m_AppsInfo;
1985 delete m_HSplitter;
1986 }
1987
1988 ATL::CWndClassInfo &
GetWndClassInfo()1989 CApplicationView::GetWndClassInfo()
1990 {
1991 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
1992 static ATL::CWndClassInfo wc = {
1993 {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_BTNFACE + 1), NULL,
1994 L"RAppsApplicationView", NULL},
1995 NULL,
1996 NULL,
1997 IDC_ARROW,
1998 TRUE,
1999 0,
2000 _T("")};
2001 return wc;
2002 }
2003
2004 HWND
Create(HWND hwndParent)2005 CApplicationView::Create(HWND hwndParent)
2006 {
2007 RECT r = {0, 0, 0, 0};
2008
2009 // Pick the "Programs" sub-menu for building the context menu.
2010 HMENU hMenu = ::GetSubMenu(m_MainWindow->GetMenu(), 1);
2011
2012 return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, hMenu);
2013 }
2014
2015 BOOL
SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)2016 CApplicationView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)
2017 {
2018 if (!m_ListView->SetDisplayAppType(AppType))
2019 {
2020 return FALSE;
2021 }
2022 ApplicationViewType = AppType;
2023 m_AppsInfo->SetWelcomeText(m_MainWindow->m_bAppwizMode);
2024
2025 HMENU hMenu = ::GetMenu(m_hWnd);
2026 switch (AppType)
2027 {
2028 case AppViewTypeInstalledApps:
2029 {
2030 EnableMenuItem(hMenu, ID_INSTALL, MF_GRAYED);
2031 EnableMenuItem(hMenu, ID_UNINSTALL, MF_ENABLED);
2032 EnableMenuItem(hMenu, ID_MODIFY, MF_ENABLED);
2033 EnableMenuItem(hMenu, ID_REGREMOVE, MF_ENABLED);
2034
2035 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE);
2036 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE);
2037 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE);
2038 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_CHECK_ALL, FALSE);
2039 break;
2040 }
2041
2042 case AppViewTypeAvailableApps:
2043 {
2044 // We shouldn't get there in APPWIZ-mode.
2045 ATLASSERT(!m_MainWindow->m_bAppwizMode);
2046
2047 EnableMenuItem(hMenu, ID_INSTALL, MF_ENABLED);
2048 EnableMenuItem(hMenu, ID_UNINSTALL, MF_GRAYED);
2049 EnableMenuItem(hMenu, ID_MODIFY, MF_GRAYED);
2050 EnableMenuItem(hMenu, ID_REGREMOVE, MF_GRAYED);
2051
2052 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE);
2053 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE);
2054 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE);
2055 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_CHECK_ALL, TRUE);
2056 break;
2057 }
2058 }
2059 return TRUE;
2060 }
2061
2062 BOOL
AddApplication(CAppInfo * AppInfo,BOOL InitialCheckState)2063 CApplicationView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState)
2064 {
2065 return m_ListView->AddApplication(AppInfo, InitialCheckState);
2066 }
2067
2068 VOID
SetWatermark(const CStringW & Text)2069 CApplicationView::SetWatermark(const CStringW &Text)
2070 {
2071 m_ListView->SetWatermark(Text);
2072 }
2073
2074 void
CheckAll()2075 CApplicationView::CheckAll()
2076 {
2077 m_ListView->CheckAll();
2078 }
2079
2080 PVOID
GetFocusedItemData()2081 CApplicationView::GetFocusedItemData()
2082 {
2083 return m_ListView->GetFocusedItemData();
2084 }
2085
2086 int
GetItemCount()2087 CApplicationView::GetItemCount()
2088 {
2089 return m_ListView->GetItemCount();
2090 }
2091
2092 VOID
AppendTabOrderWindow(int Direction,ATL::CSimpleArray<HWND> & TabOrderList)2093 CApplicationView::AppendTabOrderWindow(int Direction, ATL::CSimpleArray<HWND> &TabOrderList)
2094 {
2095 m_Toolbar->AppendTabOrderWindow(Direction, TabOrderList);
2096 m_ComboBox->AppendTabOrderWindow(Direction, TabOrderList);
2097 m_SearchBar->AppendTabOrderWindow(Direction, TabOrderList);
2098 m_ListView->AppendTabOrderWindow(Direction, TabOrderList);
2099 m_AppsInfo->AppendTabOrderWindow(Direction, TabOrderList);
2100 }
2101
2102 VOID
GetRestoreListSelectionData(RESTORELISTSELECTION & Restore)2103 CApplicationView::GetRestoreListSelectionData(RESTORELISTSELECTION &Restore)
2104 {
2105 LVITEMW &Item = Restore.Item;
2106 Item.mask = LVIF_TEXT|LVIF_STATE;
2107 Item.iItem = -1, Item.iSubItem = 0;
2108 Item.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2109 Item.pszText = Restore.Name, Item.cchTextMax = _countof(Restore.Name);
2110
2111 HWND hList = m_ListView ? m_ListView->m_hWnd : NULL;
2112 if (hList)
2113 {
2114 Item.iItem = ListView_GetNextItem(hList, -1, LVNI_FOCUSED);
2115 ListView_GetItem(hList, &Item);
2116 }
2117 }
2118
2119 VOID
RestoreListSelection(const RESTORELISTSELECTION & Restore)2120 CApplicationView::RestoreListSelection(const RESTORELISTSELECTION &Restore)
2121 {
2122 const LVITEMW &Item = Restore.Item;
2123 int index = Item.iItem;
2124 if (index != -1) // Was there a selected item?
2125 {
2126 LVFINDINFOW fi;
2127 fi.flags = LVFI_STRING;
2128 fi.psz = Item.pszText;
2129 index = ListView_FindItem(m_ListView->m_hWnd, -1, &fi);
2130 }
2131 if (index != -1) // Is it still in the list?
2132 {
2133 ListView_SetItemState(m_ListView->m_hWnd, index, Item.state, Item.stateMask);
2134 }
2135 }
2136
2137 // this function is called when a item of listview get focus.
2138 // CallbackParam is the param passed to listview when adding the item (the one getting focus now).
2139 VOID
ItemGetFocus(LPVOID CallbackParam)2140 CApplicationView::ItemGetFocus(LPVOID CallbackParam)
2141 {
2142 if (CallbackParam)
2143 {
2144 CAppInfo *Info = static_cast<CAppInfo *>(CallbackParam);
2145 m_AppsInfo->ShowAppInfo(Info);
2146
2147 if (ApplicationViewType == AppViewTypeInstalledApps)
2148 {
2149 HMENU hMenu = ::GetMenu(m_hWnd);
2150
2151 BOOL CanModify = Info->CanModify();
2152
2153 EnableMenuItem(hMenu, ID_MODIFY, CanModify ? MF_ENABLED : MF_GRAYED);
2154 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, CanModify);
2155 }
2156 }
2157 }
2158
2159 // this function is called when a item of listview is checked/unchecked
2160 // CallbackParam is the param passed to listview when adding the item (the one getting changed now).
2161 VOID
ItemCheckStateChanged(BOOL bChecked,LPVOID CallbackParam)2162 CApplicationView::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam)
2163 {
2164 m_MainWindow->ItemCheckStateChanged(bChecked, CallbackParam);
2165 }
2166 // **** CApplicationView ****
2167