xref: /reactos/base/applications/rapps/gui.cpp (revision 3ff8adc5)
1 /*
2  * PROJECT:     ReactOS Applications Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     GUI classes for RAPPS
5  * COPYRIGHT:   Copyright 2015 David Quintana (gigaherz@gmail.com)
6  *              Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
7  *              Copyright 2020 He Yang (1160386205@qq.com)
8  */
9 
10 #include "rapps.h"
11 #include "rosui.h"
12 #include "crichedit.h"
13 #include "asyncinet.h"
14 #include "gui.h"
15 #include <shlobj_undoc.h>
16 #include <shlguid_undoc.h>
17 
18 #include <atlbase.h>
19 #include <atlcom.h>
20 #include <atltypes.h>
21 #include <atlwin.h>
22 #include <wininet.h>
23 #include <shellutils.h>
24 #include <ui/rosctrls.h>
25 #include <gdiplus.h>
26 #include <math.h>
27 
28 #define SEARCH_TIMER_ID 'SR'
29 #define TREEVIEW_ICON_SIZE 24
30 
31 // **** CSideTreeView ****
32 
CSideTreeView()33 CSideTreeView::CSideTreeView()
34     : CUiWindow(),
35       hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1))
36 {
37 }
38 
39 HTREEITEM
AddItem(HTREEITEM hParent,CStringW & Text,INT Image,INT SelectedImage,LPARAM lParam)40 CSideTreeView::AddItem(HTREEITEM hParent, CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam)
41 {
42     return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam);
43 }
44 
45 HTREEITEM
AddCategory(HTREEITEM hRootItem,UINT TextIndex,UINT IconIndex)46 CSideTreeView::AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
47 {
48     CStringW szText;
49     INT Index = 0;
50     HICON hIcon;
51 
52     hIcon = (HICON)LoadImageW(
53         hInst, MAKEINTRESOURCE(IconIndex), IMAGE_ICON, TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, LR_CREATEDIBSECTION);
54     if (hIcon)
55     {
56         Index = ImageList_AddIcon(hImageTreeView, hIcon);
57         DestroyIcon(hIcon);
58     }
59 
60     szText.LoadStringW(TextIndex);
61     return AddItem(hRootItem, szText, Index, Index, TextIndex);
62 }
63 
64 HIMAGELIST
SetImageList()65 CSideTreeView::SetImageList()
66 {
67     return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL);
68 }
69 
70 VOID
DestroyImageList()71 CSideTreeView::DestroyImageList()
72 {
73     if (hImageTreeView)
74         ImageList_Destroy(hImageTreeView);
75 }
76 
~CSideTreeView()77 CSideTreeView::~CSideTreeView()
78 {
79     DestroyImageList();
80 }
81 // **** CSideTreeView ****
82 
83 // **** CMainWindow ****
84 
CMainWindow(CAppDB * db,BOOL bAppwiz)85 CMainWindow::CMainWindow(CAppDB *db, BOOL bAppwiz) : m_ClientPanel(NULL), m_Db(db), m_bAppwizMode(bAppwiz), SelectedEnumType(ENUM_ALL_INSTALLED)
86 {
87 }
88 
~CMainWindow()89 CMainWindow::~CMainWindow()
90 {
91     LayoutCleanup();
92 }
93 
94 VOID
InitCategoriesList()95 CMainWindow::InitCategoriesList()
96 {
97     HTREEITEM hRootItemAvailable;
98 
99     hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY);
100     m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS);
101     m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD);
102 
103     // Do not show any other categories in APPWIZ-mode.
104     if (m_bAppwizMode)
105         goto Finish;
106 
107     m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST);
108 
109     hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
110     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
111     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
112     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
113     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES);
114     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
115     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
116     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
117     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU);
118     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
119     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
120     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
121     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
122     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
123     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS);
124     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_THEMES, IDI_CAT_THEMES);
125     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER);
126 
127 Finish:
128     m_TreeView->SetImageList();
129     m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND);
130     if (!m_bAppwizMode)
131         m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND);
132     m_TreeView->SelectItem(m_bAppwizMode ? hRootItemInstalled : hRootItemAvailable);
133 }
134 
135 BOOL
CreateStatusBar()136 CMainWindow::CreateStatusBar()
137 {
138     m_StatusBar = new CUiWindow<CStatusBar>();
139     m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm;
140     m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch;
141     m_ClientPanel->Children().Append(m_StatusBar);
142 
143     return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL;
144 }
145 
146 BOOL
CreateTreeView()147 CMainWindow::CreateTreeView()
148 {
149     m_TreeView = new CSideTreeView();
150     m_TreeView->m_VerticalAlignment = UiAlign_Stretch;
151     m_TreeView->m_HorizontalAlignment = UiAlign_Stretch;
152     m_VSplitter->First().Append(m_TreeView);
153 
154     return m_TreeView->Create(m_hWnd) != NULL;
155 }
156 
157 BOOL
CreateApplicationView()158 CMainWindow::CreateApplicationView()
159 {
160     m_ApplicationView = new CApplicationView(this); // pass this to ApplicationView for callback purpose
161     m_ApplicationView->m_VerticalAlignment = UiAlign_Stretch;
162     m_ApplicationView->m_HorizontalAlignment = UiAlign_Stretch;
163     m_VSplitter->Second().Append(m_ApplicationView);
164 
165     return m_ApplicationView->Create(m_hWnd) != NULL;
166 }
167 
168 BOOL
CreateVSplitter()169 CMainWindow::CreateVSplitter()
170 {
171     m_VSplitter = new CUiSplitPanel();
172     m_VSplitter->m_VerticalAlignment = UiAlign_Stretch;
173     m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch;
174     m_VSplitter->m_DynamicFirst = FALSE;
175     m_VSplitter->m_Horizontal = FALSE;
176     m_VSplitter->m_MinFirst = 0;
177 
178     // TODO: m_MinSecond should be calculate dynamically instead of hard-coded
179     m_VSplitter->m_MinSecond = 480;
180     m_VSplitter->m_Pos = 240;
181     m_ClientPanel->Children().Append(m_VSplitter);
182 
183     return m_VSplitter->Create(m_hWnd) != NULL;
184 }
185 
186 BOOL
CreateLayout()187 CMainWindow::CreateLayout()
188 {
189     BOOL b = TRUE;
190     bUpdating = TRUE;
191 
192     m_ClientPanel = new CUiPanel();
193     m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch;
194     m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch;
195 
196     // Top level
197     b = b && CreateStatusBar();
198     b = b && CreateVSplitter();
199 
200     // Inside V Splitter
201     b = b && CreateTreeView();
202     b = b && CreateApplicationView();
203 
204     if (b)
205     {
206         RECT rBottom;
207 
208         /* Size status bar */
209         m_StatusBar->SendMessageW(WM_SIZE, 0, 0);
210 
211         ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom);
212 
213         m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top;
214     }
215 
216     bUpdating = FALSE;
217     return b;
218 }
219 
220 VOID
LayoutCleanup()221 CMainWindow::LayoutCleanup()
222 {
223     delete m_TreeView;
224     delete m_ApplicationView;
225     delete m_VSplitter;
226     delete m_StatusBar;
227     return;
228 }
229 
230 BOOL
InitControls()231 CMainWindow::InitControls()
232 {
233     if (CreateLayout())
234     {
235         InitCategoriesList();
236         UpdateStatusBarText();
237 
238         return TRUE;
239     }
240 
241     return FALSE;
242 }
243 
244 VOID
OnSize(HWND hwnd,WPARAM wParam,LPARAM lParam)245 CMainWindow::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
246 {
247     if (wParam == SIZE_MINIMIZED)
248         return;
249 
250     /* Size status bar */
251     m_StatusBar->SendMessage(WM_SIZE, 0, 0);
252 
253     RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)};
254     HDWP hdwp = NULL;
255     INT count = m_ClientPanel->CountSizableChildren();
256 
257     hdwp = BeginDeferWindowPos(count);
258     if (hdwp)
259     {
260         hdwp = m_ClientPanel->OnParentSize(r, hdwp);
261         if (hdwp)
262         {
263             EndDeferWindowPos(hdwp);
264         }
265     }
266 }
267 
268 BOOL
RemoveSelectedAppFromRegistry()269 CMainWindow::RemoveSelectedAppFromRegistry()
270 {
271     if (!IsInstalledEnum(SelectedEnumType))
272         return FALSE;
273 
274     CStringW szMsgText, szMsgTitle;
275 
276     if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) || !szMsgTitle.LoadStringW(IDS_INFORMATION))
277         return FALSE;
278 
279     CAppInfo *InstalledApp = (CAppInfo *)m_ApplicationView->GetFocusedItemData();
280     if (!InstalledApp)
281         return FALSE;
282 
283     if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
284         return m_Db->RemoveInstalledAppFromRegistry(InstalledApp) == ERROR_SUCCESS;
285 
286     return FALSE;
287 }
288 
289 BOOL
UninstallSelectedApp(BOOL bModify)290 CMainWindow::UninstallSelectedApp(BOOL bModify)
291 {
292     if (!IsInstalledEnum(SelectedEnumType))
293         return FALSE;
294 
295     CAppInfo *InstalledApp = (CAppInfo *)m_ApplicationView->GetFocusedItemData();
296     if (!InstalledApp)
297         return FALSE;
298 
299     return InstalledApp->UninstallApplication(bModify ? UCF_MODIFY : UCF_NONE);
300 }
301 
302 VOID
CheckAvailable()303 CMainWindow::CheckAvailable()
304 {
305     if (m_Db->GetAvailableCount() == 0)
306     {
307         CUpdateDatabaseMutex lock;
308         m_Db->RemoveCached();
309         m_Db->UpdateAvailable();
310     }
311 }
312 
313 BOOL
ProcessWindowMessage(HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT & theResult,DWORD dwMapId)314 CMainWindow::ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId)
315 {
316     theResult = 0;
317     switch (Msg)
318     {
319         case WM_CREATE:
320             if (!InitControls())
321                 ::PostMessageW(hwnd, WM_CLOSE, 0, 0);
322             ::PostMessageW(hwnd, DM_REPOSITION, 0, 0);
323             break;
324 
325         case WM_DESTROY:
326         {
327             hMainWnd = NULL;
328             SaveSettings(hwnd, &SettingsInfo);
329             FreeLogs();
330 
331             delete m_ClientPanel;
332 
333             PostQuitMessage(0);
334             return 0;
335         }
336 
337         case WM_CLOSE:
338             ShowWindow(SW_HIDE);
339             return g_Busy;
340 
341         case WM_NOTIFY_OPERATIONCOMPLETED:
342             if (!g_Busy && !IsWindowVisible())
343                 SendMessage(WM_CLOSE, 0, 0);
344             break;
345 
346         case DM_REPOSITION:
347             EmulateDialogReposition(hwnd); // We are not a real dialog, we need help from a real one
348             break;
349 
350         case WM_COMMAND:
351             OnCommand(wParam, lParam);
352             break;
353 
354         case WM_NOTIFY:
355         {
356             LPNMHDR data = (LPNMHDR)lParam;
357 
358             switch (data->code)
359             {
360                 case TVN_ITEMEXPANDING:
361                 {
362                     if (data->hwndFrom == m_TreeView->m_hWnd)
363                     {
364                         // APPWIZ-mode: forbid item collapse.
365                         // FIXME: Prevent collapse (COMCTL32 is buggy)
366                         // https://bugs.winehq.org/show_bug.cgi?id=53727
367                         if (m_bAppwizMode && (((LPNMTREEVIEW)lParam)->action & TVE_TOGGLE) == TVE_COLLAPSE)
368                         {
369                             theResult = TRUE;
370                             return TRUE; // Handled
371                         }
372                     }
373                     break;
374                 }
375 
376                 case TVN_SELCHANGED:
377                 {
378                     if (data->hwndFrom == m_TreeView->m_hWnd)
379                     {
380                         switch (((LPNMTREEVIEW)lParam)->itemNew.lParam)
381                         {
382                             case IDS_INSTALLED:
383                                 UpdateApplicationsList(ENUM_ALL_INSTALLED);
384                                 break;
385 
386                             case IDS_APPLICATIONS:
387                                 UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS);
388                                 break;
389 
390                             case IDS_UPDATES:
391                                 UpdateApplicationsList(ENUM_UPDATES);
392                                 break;
393 
394                             case IDS_AVAILABLEFORINST:
395                                 UpdateApplicationsList(ENUM_ALL_AVAILABLE, FALSE, TRUE);
396                                 break;
397 
398                             case IDS_CAT_AUDIO:
399                                 UpdateApplicationsList(ENUM_CAT_AUDIO, FALSE, TRUE);
400                                 break;
401 
402                             case IDS_CAT_DEVEL:
403                                 UpdateApplicationsList(ENUM_CAT_DEVEL, FALSE, TRUE);
404                                 break;
405 
406                             case IDS_CAT_DRIVERS:
407                                 UpdateApplicationsList(ENUM_CAT_DRIVERS, FALSE, TRUE);
408                                 break;
409 
410                             case IDS_CAT_EDU:
411                                 UpdateApplicationsList(ENUM_CAT_EDU, FALSE, TRUE);
412                                 break;
413 
414                             case IDS_CAT_ENGINEER:
415                                 UpdateApplicationsList(ENUM_CAT_ENGINEER, FALSE, TRUE);
416                                 break;
417 
418                             case IDS_CAT_FINANCE:
419                                 UpdateApplicationsList(ENUM_CAT_FINANCE, FALSE, TRUE);
420                                 break;
421 
422                             case IDS_CAT_GAMES:
423                                 UpdateApplicationsList(ENUM_CAT_GAMES, FALSE, TRUE);
424                                 break;
425 
426                             case IDS_CAT_GRAPHICS:
427                                 UpdateApplicationsList(ENUM_CAT_GRAPHICS, FALSE, TRUE);
428                                 break;
429 
430                             case IDS_CAT_INTERNET:
431                                 UpdateApplicationsList(ENUM_CAT_INTERNET, FALSE, TRUE);
432                                 break;
433 
434                             case IDS_CAT_LIBS:
435                                 UpdateApplicationsList(ENUM_CAT_LIBS, FALSE, TRUE);
436                                 break;
437 
438                             case IDS_CAT_OFFICE:
439                                 UpdateApplicationsList(ENUM_CAT_OFFICE, FALSE, TRUE);
440                                 break;
441 
442                             case IDS_CAT_OTHER:
443                                 UpdateApplicationsList(ENUM_CAT_OTHER, FALSE, TRUE);
444                                 break;
445 
446                             case IDS_CAT_SCIENCE:
447                                 UpdateApplicationsList(ENUM_CAT_SCIENCE, FALSE, TRUE);
448                                 break;
449 
450                             case IDS_CAT_TOOLS:
451                                 UpdateApplicationsList(ENUM_CAT_TOOLS, FALSE, TRUE);
452                                 break;
453 
454                             case IDS_CAT_VIDEO:
455                                 UpdateApplicationsList(ENUM_CAT_VIDEO, FALSE, TRUE);
456                                 break;
457 
458                             case IDS_CAT_THEMES:
459                                 UpdateApplicationsList(ENUM_CAT_THEMES, FALSE, TRUE);
460                                 break;
461 
462                             case IDS_SELECTEDFORINST:
463                                 UpdateApplicationsList(ENUM_CAT_SELECTED);
464                                 break;
465                         }
466                     }
467                 }
468                 break;
469             }
470         }
471         break;
472 
473         case WM_SIZE:
474             OnSize(hwnd, wParam, lParam);
475             break;
476 
477         case WM_SIZING:
478         {
479             LPRECT pRect = (LPRECT)lParam;
480 
481             if (pRect->right - pRect->left < 565)
482                 pRect->right = pRect->left + 565;
483 
484             if (pRect->bottom - pRect->top < 300)
485                 pRect->bottom = pRect->top + 300;
486 
487             return TRUE;
488         }
489 
490         case WM_SYSCOLORCHANGE:
491         {
492             /* Forward WM_SYSCOLORCHANGE to common controls */
493             m_ApplicationView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
494             m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
495         }
496         break;
497 
498         case WM_SETTINGCHANGE:
499             if (wParam == SPI_SETNONCLIENTMETRICS || wParam == SPI_SETICONMETRICS)
500             {
501                 DestroyIcon(g_hDefaultPackageIcon);
502                 g_hDefaultPackageIcon = NULL; // Trigger imagelist recreation on next load
503             }
504             break;
505 
506         case WM_TIMER:
507             if (wParam == SEARCH_TIMER_ID)
508             {
509                 ::KillTimer(hwnd, SEARCH_TIMER_ID);
510 
511                 UpdateApplicationsList(SelectedEnumType);
512             }
513             break;
514     }
515 
516     return FALSE;
517 }
518 
519 VOID
ShowAboutDlg()520 CMainWindow::ShowAboutDlg()
521 {
522     CStringW szApp;
523     CStringW szAuthors;
524 
525     szApp.LoadStringW(IDS_APPTITLE);
526     szAuthors.LoadStringW(IDS_APP_AUTHORS);
527     ShellAboutW(m_hWnd, szApp, szAuthors,
528                 LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)));
529 }
530 
531 VOID
OnCommand(WPARAM wParam,LPARAM lParam)532 CMainWindow::OnCommand(WPARAM wParam, LPARAM lParam)
533 {
534     const BOOL bReload = TRUE;
535     WORD wCommand = LOWORD(wParam);
536 
537     if (!lParam)
538     {
539         switch (wCommand)
540         {
541             case ID_SETTINGS:
542                 CreateSettingsDlg(m_hWnd);
543                 break;
544 
545             case ID_EXIT:
546                 PostMessageW(WM_CLOSE, 0, 0);
547                 break;
548 
549             case ID_SEARCH:
550                 m_ApplicationView->SetFocusOnSearchBar();
551                 break;
552 
553             case ID_INSTALL:
554                 if (IsAvailableEnum(SelectedEnumType))
555                 {
556                     if (!m_Selected.IsEmpty())
557                     {
558                         if (DownloadListOfApplications(m_Selected, FALSE))
559                         {
560                             m_Selected.RemoveAll();
561                             UpdateApplicationsList(SelectedEnumType);
562                         }
563                     }
564                     else
565                     {
566                         CAppInfo *App = (CAppInfo *)m_ApplicationView->GetFocusedItemData();
567                         if (App)
568                         {
569                             InstallApplication(App);
570                         }
571                     }
572                 }
573                 break;
574 
575             case ID_UNINSTALL:
576                 if (UninstallSelectedApp(FALSE))
577                     UpdateApplicationsList(SelectedEnumType, bReload);
578                 break;
579 
580             case ID_MODIFY:
581                 if (UninstallSelectedApp(TRUE))
582                     UpdateApplicationsList(SelectedEnumType, bReload);
583                 break;
584 
585             case ID_REGREMOVE:
586                 if (RemoveSelectedAppFromRegistry())
587                     UpdateApplicationsList(SelectedEnumType, bReload);
588                 break;
589 
590             case ID_REFRESH:
591                 UpdateApplicationsList(SelectedEnumType, bReload);
592                 break;
593 
594             case ID_RESETDB:
595             {
596                 CUpdateDatabaseMutex lock;
597                 m_Db->RemoveCached();
598                 UpdateApplicationsList(SelectedEnumType, bReload);
599                 break;
600             }
601 
602             case ID_HELP:
603                 MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
604                 break;
605 
606             case ID_ABOUT:
607                 ShowAboutDlg();
608                 break;
609 
610             case ID_CHECK_ALL:
611                 m_ApplicationView->CheckAll();
612                 break;
613 
614             case ID_ACTIVATE_APPWIZ:
615                 if (hRootItemInstalled)
616                     m_TreeView->SelectItem(hRootItemInstalled);
617                 break;
618         }
619     }
620 }
621 
622 VOID
UpdateStatusBarText()623 CMainWindow::UpdateStatusBarText()
624 {
625     if (m_StatusBar)
626     {
627         CStringW szBuffer;
628         szBuffer.Format(IDS_APPS_COUNT, m_ApplicationView->GetItemCount());
629 
630         // Append the number of selected apps if not in APPWIZ-mode.
631         if (!m_bAppwizMode)
632         {
633             CStringW szBuffer2;
634             szBuffer2.Format(IDS_APPS_SELECT_COUNT, m_Selected.GetCount());
635             szBuffer += szBuffer2;
636         }
637 
638         m_StatusBar->SetText(szBuffer);
639     }
640 }
641 
642 VOID
AddApplicationsToView(CAtlList<CAppInfo * > & List)643 CMainWindow::AddApplicationsToView(CAtlList<CAppInfo *> &List)
644 {
645     POSITION CurrentListPosition = List.GetHeadPosition();
646     while (CurrentListPosition)
647     {
648         CAppInfo *Info = List.GetNext(CurrentListPosition);
649         if (szSearchPattern.IsEmpty() || SearchPatternMatch(Info->szDisplayName, szSearchPattern) ||
650             SearchPatternMatch(Info->szComments, szSearchPattern))
651         {
652             BOOL bSelected = m_Selected.Find(Info) != NULL;
653             m_ApplicationView->AddApplication(Info, bSelected);
654         }
655     }
656     m_ApplicationView->AddApplication(NULL, FALSE); // Tell the list we are done
657 }
658 
659 VOID
UpdateApplicationsList(AppsCategories EnumType,BOOL bReload,BOOL bCheckAvailable)660 CMainWindow::UpdateApplicationsList(AppsCategories EnumType, BOOL bReload, BOOL bCheckAvailable)
661 {
662     // Only installed applications should be enumerated in APPWIZ-mode.
663     if (m_bAppwizMode && !IsInstalledEnum(EnumType))
664     {
665         ATLASSERT(FALSE && "Should not be called in APPWIZ-mode");
666         return;
667     }
668 
669     bUpdating = TRUE;
670 
671     if (HCURSOR hCursor = LoadCursor(NULL, IDC_APPSTARTING))
672     {
673         // The database (.ini files) is parsed on the UI thread, let the user know we are busy
674         SetCursor(hCursor);
675         PostMessage(WM_SETCURSOR, (WPARAM)m_hWnd, MAKELONG(HTCLIENT, WM_MOUSEMOVE));
676     }
677 
678     if (bCheckAvailable)
679         CheckAvailable();
680 
681     BOOL TryRestoreSelection = SelectedEnumType == EnumType;
682     if (SelectedEnumType != EnumType)
683         SelectedEnumType = EnumType;
684 
685     CApplicationView::RESTORELISTSELECTION RestoreSelection;
686     if (TryRestoreSelection)
687         m_ApplicationView->GetRestoreListSelectionData(RestoreSelection);
688 
689     if (bReload)
690         m_Selected.RemoveAll();
691 
692     m_ApplicationView->SetRedraw(FALSE);
693     if (IsInstalledEnum(EnumType))
694     {
695         if (bReload)
696             m_Db->UpdateInstalled();
697 
698         // set the display type of application-view. this will remove all the item in application-view too.
699         m_ApplicationView->SetDisplayAppType(AppViewTypeInstalledApps);
700 
701         CAtlList<CAppInfo *> List;
702         m_Db->GetApps(List, EnumType);
703         AddApplicationsToView(List);
704     }
705     else if (IsAvailableEnum(EnumType))
706     {
707         // We shouldn't get there in APPWIZ-mode.
708         ATLASSERT(!m_bAppwizMode);
709 
710         if (bReload)
711             m_Db->UpdateAvailable();
712 
713         // set the display type of application-view. this will remove all the item in application-view too.
714         m_ApplicationView->SetDisplayAppType(AppViewTypeAvailableApps);
715 
716         // enum available softwares
717         if (EnumType == ENUM_CAT_SELECTED)
718         {
719             AddApplicationsToView(m_Selected);
720         }
721         else
722         {
723             CAtlList<CAppInfo *> List;
724             m_Db->GetApps(List, EnumType);
725             AddApplicationsToView(List);
726         }
727     }
728     else
729     {
730         ATLASSERT(0 && "This should be unreachable!");
731     }
732 
733     if (TryRestoreSelection)
734         m_ApplicationView->RestoreListSelection(RestoreSelection);
735     m_ApplicationView->SetRedraw(TRUE);
736     m_ApplicationView->RedrawWindow(0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); // force the child window to repaint
737     UpdateStatusBarText();
738 
739     CStringW text;
740     if (m_ApplicationView->GetItemCount() == 0 && !szSearchPattern.IsEmpty())
741     {
742         text.LoadString(IDS_NO_SEARCH_RESULTS);
743     }
744     m_ApplicationView->SetWatermark(text);
745 
746     bUpdating = FALSE;
747 }
748 
749 ATL::CWndClassInfo &
GetWndClassInfo()750 CMainWindow::GetWndClassInfo()
751 {
752     DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
753     static ATL::CWndClassInfo wc = {
754         {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL,
755          LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)), LoadCursorW(NULL, IDC_ARROW),
756          (HBRUSH)(COLOR_BTNFACE + 1), MAKEINTRESOURCEW(IDR_MAINMENU), szWindowClass, NULL},
757         NULL,
758         NULL,
759         IDC_ARROW,
760         TRUE,
761         0,
762         _T("")};
763     return wc;
764 }
765 
766 HWND
Create()767 CMainWindow::Create()
768 {
769     const CStringW szWindowName(MAKEINTRESOURCEW(m_bAppwizMode ? IDS_APPWIZ_TITLE : IDS_APPTITLE));
770 
771     RECT r = {
772         (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
773         (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
774         (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)};
775     r.right += r.left;
776     r.bottom += r.top;
777 
778     return CWindowImpl::Create(
779         NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
780 }
781 
782 // this function is called when a item of application-view is checked/unchecked
783 // CallbackParam is the param passed to application-view when adding the item (the one getting focus now).
784 VOID
ItemCheckStateChanged(BOOL bChecked,LPVOID CallbackParam)785 CMainWindow::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam)
786 {
787     if (bUpdating)
788         return;
789 
790     CAppInfo *Info = (CAppInfo *)CallbackParam;
791 
792     if (bChecked)
793     {
794         m_Selected.AddTail(Info);
795     }
796     else
797     {
798         POSITION Pos = m_Selected.Find(Info);
799         ATLASSERT(Pos != NULL);
800 
801         if (Pos != NULL)
802         {
803             m_Selected.RemoveAt(Pos);
804         }
805     }
806 
807     UpdateStatusBarText();
808 }
809 
810 // this function is called when one or more application(s) should be installed
811 // if Info is not zero, this app should be installed. otherwise those checked apps should be installed
812 BOOL
InstallApplication(CAppInfo * Info)813 CMainWindow::InstallApplication(CAppInfo *Info)
814 {
815     if (Info)
816     {
817         if (DownloadApplication(Info))
818         {
819             UpdateApplicationsList(SelectedEnumType);
820             return TRUE;
821         }
822     }
823 
824     return FALSE;
825 }
826 
827 BOOL
SearchTextChanged(CStringW & SearchText)828 CMainWindow::SearchTextChanged(CStringW &SearchText)
829 {
830     if (szSearchPattern == SearchText)
831     {
832         return FALSE;
833     }
834 
835     szSearchPattern = SearchText;
836 
837     DWORD dwDelay = 0;
838     SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
839     SetTimer(SEARCH_TIMER_ID, dwDelay);
840 
841     return TRUE;
842 }
843 
844 void
HandleTabOrder(int direction)845 CMainWindow::HandleTabOrder(int direction)
846 {
847     ATL::CSimpleArray<HWND> TabOrderHwndList;
848 
849     m_TreeView->AppendTabOrderWindow(direction, TabOrderHwndList);
850     m_ApplicationView->AppendTabOrderWindow(direction, TabOrderHwndList);
851 
852     if (TabOrderHwndList.GetSize() == 0)
853     {
854         // in case the list is empty
855         return;
856     }
857 
858     int FocusIndex;
859 
860     if ((FocusIndex = TabOrderHwndList.Find(GetFocus())) == -1)
861     {
862         FocusIndex = 0; // focus the first window in the list
863     }
864     else
865     {
866         FocusIndex += direction;
867         FocusIndex +=
868             TabOrderHwndList.GetSize(); // FocusIndex might be negative. we don't want to mod a negative number
869         FocusIndex %= TabOrderHwndList.GetSize();
870     }
871 
872     ::SetFocus(TabOrderHwndList[FocusIndex]);
873     return;
874 }
875 // **** CMainWindow ****
876 
877 VOID
MainWindowLoop(CMainWindow * wnd,INT nShowCmd)878 MainWindowLoop(CMainWindow *wnd, INT nShowCmd)
879 {
880     HACCEL KeyBrd;
881     MSG Msg;
882 
883     hMainWnd = wnd->Create();
884     if (!hMainWnd)
885         return;
886 
887     /* Maximize it if we must */
888     wnd->ShowWindow((SettingsInfo.bSaveWndPos && SettingsInfo.Maximized) ? SW_MAXIMIZE : nShowCmd);
889     wnd->UpdateWindow();
890 
891     /* Load the menu hotkeys */
892     KeyBrd = LoadAcceleratorsW(NULL, MAKEINTRESOURCEW(HOTKEYS));
893 
894     /* Message Loop */
895     while (GetMessageW(&Msg, NULL, 0, 0))
896     {
897         if (!TranslateAcceleratorW(hMainWnd, KeyBrd, &Msg))
898         {
899             if (Msg.message == WM_CHAR && Msg.wParam == VK_TAB)
900             {
901                 // Move backwards if shift is held down
902                 int direction = (GetKeyState(VK_SHIFT) & 0x8000) ? -1 : 1;
903 
904                 wnd->HandleTabOrder(direction);
905                 continue;
906             }
907 
908             TranslateMessage(&Msg);
909             DispatchMessageW(&Msg);
910         }
911     }
912 }
913