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