xref: /reactos/base/applications/rapps/gui.cpp (revision 03422451)
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 "appview.h"
14 #include "asyncinet.h"
15 #include "misc.h"
16 #include "gui.h"
17 #include "appview.h"
18 #include "winmain.h"
19 #include <shlobj_undoc.h>
20 #include <shlguid_undoc.h>
21 
22 #include <atlbase.h>
23 #include <atlcom.h>
24 #include <atltypes.h>
25 #include <atlwin.h>
26 #include <wininet.h>
27 #include <shellutils.h>
28 #include <ui/rosctrls.h>
29 #include <gdiplus.h>
30 #include <math.h>
31 
32 #define SEARCH_TIMER_ID 'SR'
33 #define TREEVIEW_ICON_SIZE 24
34 
35 
36 
37 // **** CSideTreeView ****
38 
39 CSideTreeView::CSideTreeView() :
40     CUiWindow(),
41     hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE,
42         GetSystemColorDepth() | ILC_MASK,
43         0, 1))
44 {
45 }
46 
47 HTREEITEM CSideTreeView::AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam)
48 {
49     return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam);
50 }
51 
52 HTREEITEM CSideTreeView::AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
53 {
54     ATL::CStringW szText;
55     INT Index = 0;
56     HICON hIcon;
57 
58     hIcon = (HICON)LoadImageW(hInst,
59         MAKEINTRESOURCE(IconIndex),
60         IMAGE_ICON,
61         TREEVIEW_ICON_SIZE,
62         TREEVIEW_ICON_SIZE,
63         LR_CREATEDIBSECTION);
64     if (hIcon)
65     {
66         Index = ImageList_AddIcon(hImageTreeView, hIcon);
67         DestroyIcon(hIcon);
68     }
69 
70     szText.LoadStringW(TextIndex);
71     return AddItem(hRootItem, szText, Index, Index, TextIndex);
72 }
73 
74 HIMAGELIST CSideTreeView::SetImageList()
75 {
76     return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL);
77 }
78 
79 VOID CSideTreeView::DestroyImageList()
80 {
81     if (hImageTreeView)
82         ImageList_Destroy(hImageTreeView);
83 }
84 
85 CSideTreeView::~CSideTreeView()
86 {
87     DestroyImageList();
88 }
89 // **** CSideTreeView ****
90 
91 
92 
93 // **** CMainWindow ****
94 
95 CMainWindow::CMainWindow() :
96     m_ClientPanel(NULL),
97     SelectedEnumType(ENUM_ALL_INSTALLED)
98 {
99 }
100 
101 CMainWindow::~CMainWindow()
102 {
103     LayoutCleanup();
104 }
105 
106 VOID CMainWindow::InitCategoriesList()
107 {
108     HTREEITEM hRootItemInstalled, hRootItemAvailable;
109 
110     hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY);
111     m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS);
112     m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD);
113 
114     m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST);
115 
116     hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
117     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
118     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
119     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
120     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES);
121     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
122     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
123     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
124     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU);
125     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
126     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
127     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
128     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
129     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
130     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS);
131     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_THEMES, IDI_CAT_THEMES);
132     m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER);
133 
134     m_TreeView->SetImageList();
135     m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND);
136     m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND);
137     m_TreeView->SelectItem(hRootItemAvailable);
138 }
139 
140 BOOL CMainWindow::CreateStatusBar()
141 {
142     m_StatusBar = new CUiWindow<CStatusBar>();
143     m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm;
144     m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch;
145     m_ClientPanel->Children().Append(m_StatusBar);
146 
147     return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL;
148 }
149 
150 BOOL CMainWindow::CreateTreeView()
151 {
152     m_TreeView = new CSideTreeView();
153     m_TreeView->m_VerticalAlignment = UiAlign_Stretch;
154     m_TreeView->m_HorizontalAlignment = UiAlign_Stretch;
155     m_VSplitter->First().Append(m_TreeView);
156 
157     return m_TreeView->Create(m_hWnd) != NULL;
158 }
159 
160 BOOL CMainWindow::CreateApplicationView()
161 {
162     m_ApplicationView = new CApplicationView(this); // pass this to ApplicationView for callback purpose
163     m_ApplicationView->m_VerticalAlignment = UiAlign_Stretch;
164     m_ApplicationView->m_HorizontalAlignment = UiAlign_Stretch;
165     m_VSplitter->Second().Append(m_ApplicationView);
166 
167     return m_ApplicationView->Create(m_hWnd) != NULL;
168 }
169 
170 BOOL CMainWindow::CreateVSplitter()
171 {
172     m_VSplitter = new CUiSplitPanel();
173     m_VSplitter->m_VerticalAlignment = UiAlign_Stretch;
174     m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch;
175     m_VSplitter->m_DynamicFirst = FALSE;
176     m_VSplitter->m_Horizontal = FALSE;
177     m_VSplitter->m_MinFirst = 0;
178 
179     // TODO: m_MinSecond should be calculate dynamically instead of hard-coded
180     m_VSplitter->m_MinSecond = 480;
181     m_VSplitter->m_Pos = 240;
182     m_ClientPanel->Children().Append(m_VSplitter);
183 
184     return m_VSplitter->Create(m_hWnd) != NULL;
185 }
186 
187 BOOL 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 CMainWindow::LayoutCleanup()
221 {
222     delete m_TreeView;
223     delete m_ApplicationView;
224     delete m_VSplitter;
225     delete m_StatusBar;
226     return;
227 }
228 
229 BOOL CMainWindow::InitControls()
230 {
231     if (CreateLayout())
232     {
233         InitCategoriesList();
234 
235         UpdateStatusBarText();
236 
237         return TRUE;
238     }
239 
240     return FALSE;
241 }
242 
243 VOID CMainWindow::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
244 {
245     if (wParam == SIZE_MINIMIZED)
246         return;
247 
248     /* Size status bar */
249     m_StatusBar->SendMessage(WM_SIZE, 0, 0);
250 
251 
252     RECT r = { 0, 0, LOWORD(lParam), HIWORD(lParam) };
253     HDWP hdwp = NULL;
254     INT count = m_ClientPanel->CountSizableChildren();
255 
256     hdwp = BeginDeferWindowPos(count);
257     if (hdwp)
258     {
259         hdwp = m_ClientPanel->OnParentSize(r, hdwp);
260         if (hdwp)
261         {
262             EndDeferWindowPos(hdwp);
263         }
264     }
265 }
266 
267 BOOL CMainWindow::RemoveSelectedAppFromRegistry()
268 {
269     if (!IsInstalledEnum(SelectedEnumType))
270         return FALSE;
271 
272     ATL::CStringW szMsgText, szMsgTitle;
273 
274     if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) ||
275         !szMsgTitle.LoadStringW(IDS_INFORMATION))
276         return FALSE;
277 
278     if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
279     {
280         CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData();
281         if (!InstalledApp)
282             return FALSE;
283 
284         LSTATUS Result = InstalledApp->RemoveFromRegistry();
285         if (Result != ERROR_SUCCESS)
286         {
287             // TODO: popup a messagebox telling user it fails somehow
288             return FALSE;
289         }
290 
291         // as it's already removed form registry, this will also remove it from the list
292         UpdateApplicationsList(-1);
293         return TRUE;
294     }
295 
296     return FALSE;
297 }
298 
299 BOOL CMainWindow::UninstallSelectedApp(BOOL bModify)
300 {
301     if (!IsInstalledEnum(SelectedEnumType))
302         return FALSE;
303 
304     CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData();
305     if (!InstalledApp)
306         return FALSE;
307 
308     return InstalledApp->UninstallApplication(bModify);
309 }
310 
311 BOOL CMainWindow::ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId)
312 {
313     theResult = 0;
314     switch (Msg)
315     {
316     case WM_CREATE:
317         if (!InitControls())
318             ::PostMessageW(hwnd, WM_CLOSE, 0, 0);
319         break;
320 
321     case WM_DESTROY:
322     {
323         ShowWindow(SW_HIDE);
324         SaveSettings(hwnd, &SettingsInfo);
325 
326         FreeLogs();
327         m_AvailableApps.FreeCachedEntries();
328         m_InstalledApps.FreeCachedEntries();
329 
330         delete m_ClientPanel;
331 
332         PostQuitMessage(0);
333         return 0;
334     }
335 
336     case WM_COMMAND:
337         OnCommand(wParam, lParam);
338         break;
339 
340     case WM_NOTIFY:
341     {
342         LPNMHDR data = (LPNMHDR)lParam;
343 
344         switch (data->code)
345         {
346         case TVN_SELCHANGED:
347         {
348             if (data->hwndFrom == m_TreeView->m_hWnd)
349             {
350                 switch (((LPNMTREEVIEW)lParam)->itemNew.lParam)
351                 {
352                 case IDS_INSTALLED:
353                     UpdateApplicationsList(ENUM_ALL_INSTALLED);
354                     break;
355 
356                 case IDS_APPLICATIONS:
357                     UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS);
358                     break;
359 
360                 case IDS_UPDATES:
361                     UpdateApplicationsList(ENUM_UPDATES);
362                     break;
363 
364                 case IDS_AVAILABLEFORINST:
365                     UpdateApplicationsList(ENUM_ALL_AVAILABLE);
366                     break;
367 
368                 case IDS_CAT_AUDIO:
369                     UpdateApplicationsList(ENUM_CAT_AUDIO);
370                     break;
371 
372                 case IDS_CAT_DEVEL:
373                     UpdateApplicationsList(ENUM_CAT_DEVEL);
374                     break;
375 
376                 case IDS_CAT_DRIVERS:
377                     UpdateApplicationsList(ENUM_CAT_DRIVERS);
378                     break;
379 
380                 case IDS_CAT_EDU:
381                     UpdateApplicationsList(ENUM_CAT_EDU);
382                     break;
383 
384                 case IDS_CAT_ENGINEER:
385                     UpdateApplicationsList(ENUM_CAT_ENGINEER);
386                     break;
387 
388                 case IDS_CAT_FINANCE:
389                     UpdateApplicationsList(ENUM_CAT_FINANCE);
390                     break;
391 
392                 case IDS_CAT_GAMES:
393                     UpdateApplicationsList(ENUM_CAT_GAMES);
394                     break;
395 
396                 case IDS_CAT_GRAPHICS:
397                     UpdateApplicationsList(ENUM_CAT_GRAPHICS);
398                     break;
399 
400                 case IDS_CAT_INTERNET:
401                     UpdateApplicationsList(ENUM_CAT_INTERNET);
402                     break;
403 
404                 case IDS_CAT_LIBS:
405                     UpdateApplicationsList(ENUM_CAT_LIBS);
406                     break;
407 
408                 case IDS_CAT_OFFICE:
409                     UpdateApplicationsList(ENUM_CAT_OFFICE);
410                     break;
411 
412                 case IDS_CAT_OTHER:
413                     UpdateApplicationsList(ENUM_CAT_OTHER);
414                     break;
415 
416                 case IDS_CAT_SCIENCE:
417                     UpdateApplicationsList(ENUM_CAT_SCIENCE);
418                     break;
419 
420                 case IDS_CAT_TOOLS:
421                     UpdateApplicationsList(ENUM_CAT_TOOLS);
422                     break;
423 
424                 case IDS_CAT_VIDEO:
425                     UpdateApplicationsList(ENUM_CAT_VIDEO);
426                     break;
427 
428                 case IDS_CAT_THEMES:
429                     UpdateApplicationsList(ENUM_CAT_THEMES);
430                     break;
431 
432                 case IDS_SELECTEDFORINST:
433                     UpdateApplicationsList(ENUM_CAT_SELECTED);
434                     break;
435                 }
436             }
437 
438             HMENU mainMenu = ::GetMenu(hwnd);
439 
440             /* Disable/enable items based on treeview selection */
441             if (IsSelectedNodeInstalled())
442             {
443                 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED);
444                 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED);
445                 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED);
446                 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
447             }
448             else
449             {
450                 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED);
451                 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED);
452                 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED);
453                 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
454             }
455         }
456         break;
457 
458         }
459     }
460     break;
461 
462     case WM_SIZE:
463         OnSize(hwnd, wParam, lParam);
464         break;
465 
466     case WM_SIZING:
467     {
468         LPRECT pRect = (LPRECT)lParam;
469 
470         if (pRect->right - pRect->left < 565)
471             pRect->right = pRect->left + 565;
472 
473         if (pRect->bottom - pRect->top < 300)
474             pRect->bottom = pRect->top + 300;
475 
476         return TRUE;
477     }
478 
479     case WM_SYSCOLORCHANGE:
480     {
481         /* Forward WM_SYSCOLORCHANGE to common controls */
482         m_ApplicationView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
483         m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam);
484     }
485     break;
486 
487     case WM_TIMER:
488         if (wParam == SEARCH_TIMER_ID)
489         {
490             ::KillTimer(hwnd, SEARCH_TIMER_ID);
491 
492             UpdateApplicationsList(-1);
493         }
494         break;
495     }
496 
497     return FALSE;
498 }
499 
500 BOOL CMainWindow::IsSelectedNodeInstalled()
501 {
502     HTREEITEM hSelectedItem = m_TreeView->GetSelection();
503     TV_ITEM tItem;
504 
505     tItem.mask = TVIF_PARAM | TVIF_HANDLE;
506     tItem.hItem = hSelectedItem;
507     m_TreeView->GetItem(&tItem);
508     switch (tItem.lParam)
509     {
510     case IDS_INSTALLED:
511     case IDS_APPLICATIONS:
512     case IDS_UPDATES:
513         return TRUE;
514     default:
515         return FALSE;
516     }
517 }
518 
519 VOID CMainWindow::ShowAboutDlg()
520 {
521     ATL::CStringW szApp;
522     ATL::CStringW szAuthors;
523     HICON hIcon;
524 
525     szApp.LoadStringW(IDS_APPTITLE);
526     szAuthors.LoadStringW(IDS_APP_AUTHORS);
527     hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
528     ShellAboutW(m_hWnd, szApp, szAuthors, hIcon);
529     DestroyIcon(hIcon);
530 }
531 
532 VOID CMainWindow::OnCommand(WPARAM wParam, LPARAM lParam)
533 {
534     WORD wCommand = LOWORD(wParam);
535 
536     if (!lParam)
537     {
538         switch (wCommand)
539         {
540         case ID_SETTINGS:
541             CreateSettingsDlg(m_hWnd);
542             break;
543 
544         case ID_EXIT:
545             PostMessageW(WM_CLOSE, 0, 0);
546             break;
547 
548         case ID_SEARCH:
549             m_ApplicationView->SetFocusOnSearchBar();
550             break;
551 
552         case ID_INSTALL:
553             if (IsAvailableEnum(SelectedEnumType))
554             {
555                 ATL::CSimpleArray<CAvailableApplicationInfo> AppsList;
556 
557                 // enum all selected apps
558                 m_AvailableApps.Enum(ENUM_CAT_SELECTED, s_EnumSelectedAppForDownloadProc, (PVOID)&AppsList);
559 
560                 if (AppsList.GetSize())
561                 {
562                     if (DownloadListOfApplications(AppsList, FALSE))
563                     {
564                         m_AvailableApps.RemoveAllSelected();
565                         UpdateApplicationsList(-1);
566                     }
567                 }
568                 else
569                 {
570                     // use the currently focused item in application-view
571                     CAvailableApplicationInfo *FocusedApps = (CAvailableApplicationInfo *)m_ApplicationView->GetFocusedItemData();
572                     if (FocusedApps)
573                     {
574                         if (DownloadApplication(FocusedApps))
575                         {
576                             UpdateApplicationsList(-1);
577                         }
578                     }
579                     else
580                     {
581                         // TODO: in this case, Install button in toolbar (and all other places) should be disabled
582                         // or at least popup a messagebox telling user to select/check some app first
583                     }
584                 }
585             }
586             break;
587 
588         case ID_UNINSTALL:
589             if (UninstallSelectedApp(FALSE))
590                 UpdateApplicationsList(-1);
591             break;
592 
593         case ID_MODIFY:
594             if (UninstallSelectedApp(TRUE))
595                 UpdateApplicationsList(-1);
596             break;
597 
598         case ID_REGREMOVE:
599             RemoveSelectedAppFromRegistry();
600             break;
601 
602         case ID_REFRESH:
603             UpdateApplicationsList(-1);
604             break;
605 
606         case ID_RESETDB:
607             CAvailableApps::ForceUpdateAppsDB();
608             UpdateApplicationsList(-1);
609             break;
610 
611         case ID_HELP:
612             MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
613             break;
614 
615         case ID_ABOUT:
616             ShowAboutDlg();
617             break;
618 
619         case ID_CHECK_ALL:
620             m_ApplicationView->CheckAll();
621             break;
622         }
623     }
624 }
625 
626 BOOL CALLBACK CMainWindow::EnumInstalledAppProc(CInstalledApplicationInfo *Info)
627 {
628     if (!SearchPatternMatch(Info->szDisplayName.GetString(), szSearchPattern))
629     {
630         return TRUE;
631     }
632     return m_ApplicationView->AddInstalledApplication(Info, Info); // currently, the callback param is Info itself
633 }
634 
635 BOOL CALLBACK CMainWindow::EnumAvailableAppProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState)
636 {
637     if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) &&
638         !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern))
639     {
640         return TRUE;
641     }
642     return m_ApplicationView->AddAvailableApplication(Info, bInitialCheckState, Info); // currently, the callback param is Info itself
643 }
644 
645 BOOL CALLBACK CMainWindow::s_EnumInstalledAppProc(CInstalledApplicationInfo *Info, PVOID param)
646 {
647     CMainWindow *pThis = (CMainWindow *)param;
648     return pThis->EnumInstalledAppProc(Info);
649 }
650 
651 BOOL CALLBACK CMainWindow::s_EnumAvailableAppProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param)
652 {
653     CMainWindow *pThis = (CMainWindow *)param;
654     return pThis->EnumAvailableAppProc(Info, bInitialCheckState);
655 }
656 
657 BOOL CALLBACK CMainWindow::s_EnumSelectedAppForDownloadProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param)
658 {
659     ATL::CSimpleArray<CAvailableApplicationInfo> *pAppList = (ATL::CSimpleArray<CAvailableApplicationInfo> *)param;
660     pAppList->Add(*Info);
661     return TRUE;
662 }
663 
664 VOID CMainWindow::UpdateStatusBarText()
665 {
666     if (m_StatusBar)
667     {
668         ATL::CStringW szBuffer;
669 
670         szBuffer.Format(IDS_APPS_COUNT, m_ApplicationView->GetItemCount(), m_AvailableApps.GetSelectedCount());
671         m_StatusBar->SetText(szBuffer);
672     }
673 }
674 
675 VOID CMainWindow::UpdateApplicationsList(INT EnumType)
676 {
677     bUpdating = TRUE;
678 
679     if (EnumType == -1)
680     {
681         // keep the old enum type
682         EnumType = SelectedEnumType;
683     }
684     else
685     {
686         SelectedEnumType = EnumType;
687     }
688 
689     m_ApplicationView->SetRedraw(FALSE);
690     if (IsInstalledEnum(EnumType))
691     {
692         // set the display type of application-view. this will remove all the item in application-view too.
693         m_ApplicationView->SetDisplayAppType(AppViewTypeInstalledApps);
694 
695         // enum installed softwares
696         m_InstalledApps.Enum(EnumType, s_EnumInstalledAppProc, this);
697     }
698     else if (IsAvailableEnum(EnumType))
699     {
700         // set the display type of application-view. this will remove all the item in application-view too.
701         m_ApplicationView->SetDisplayAppType(AppViewTypeAvailableApps);
702 
703         // enum available softwares
704         m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc, this);
705     }
706     m_ApplicationView->SetRedraw(TRUE);
707     m_ApplicationView->RedrawWindow(0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); // force the child window to repaint
708     UpdateStatusBarText();
709 
710     CStringW text;
711     if (m_ApplicationView->GetItemCount() == 0 && !szSearchPattern.IsEmpty())
712     {
713         text.LoadString(IDS_NO_SEARCH_RESULTS);
714     }
715     m_ApplicationView->SetWatermark(text);
716 
717     bUpdating = FALSE;
718 }
719 
720 ATL::CWndClassInfo &CMainWindow::GetWndClassInfo()
721 {
722     DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
723     static ATL::CWndClassInfo wc =
724     {
725         {
726             sizeof(WNDCLASSEX),
727             csStyle,
728             StartWindowProc,
729             0,
730             0,
731             NULL,
732             LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)),
733             LoadCursorW(NULL, IDC_ARROW),
734             (HBRUSH)(COLOR_BTNFACE + 1),
735             MAKEINTRESOURCEW(IDR_MAINMENU),
736             szWindowClass,
737             NULL
738         },
739         NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
740     };
741     return wc;
742 }
743 
744 HWND CMainWindow::Create()
745 {
746     ATL::CStringW szWindowName;
747     szWindowName.LoadStringW(IDS_APPTITLE);
748 
749     RECT r = {
750         (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
751         (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
752         (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680),
753         (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)
754     };
755     r.right += r.left;
756     r.bottom += r.top;
757 
758     return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
759 }
760 
761 // this function is called when a item of application-view is checked/unchecked
762 // CallbackParam is the param passed to application-view when adding the item (the one getting focus now).
763 BOOL CMainWindow::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam)
764 {
765     if (!bUpdating)
766     {
767         if (bChecked)
768         {
769             if (!m_AvailableApps.AddSelected((CAvailableApplicationInfo *)CallbackParam))
770             {
771                 return FALSE;
772             }
773         }
774         else
775         {
776             if (!m_AvailableApps.RemoveSelected((CAvailableApplicationInfo *)CallbackParam))
777             {
778                 return FALSE;
779             }
780         }
781 
782         UpdateStatusBarText();
783         return TRUE;
784     }
785     else
786     {
787         return TRUE;
788     }
789 }
790 
791 // this function is called when one or more application(s) should be installed install
792 // if Info is not zero, this app should be installed. otherwise those checked apps should be installed
793 BOOL CMainWindow::InstallApplication(CAvailableApplicationInfo *Info)
794 {
795     if (Info)
796     {
797         if (DownloadApplication(Info))
798         {
799             UpdateApplicationsList(-1);
800             return TRUE;
801         }
802     }
803     else
804     {
805         ATL::CSimpleArray<CAvailableApplicationInfo> AppsList;
806 
807         // enum all selected apps
808         m_AvailableApps.Enum(ENUM_CAT_SELECTED, s_EnumSelectedAppForDownloadProc, (PVOID)&AppsList);
809 
810         if (AppsList.GetSize())
811         {
812             if (DownloadListOfApplications(AppsList, FALSE))
813             {
814                 m_AvailableApps.RemoveAllSelected();
815                 UpdateApplicationsList(-1);
816                 return TRUE;
817             }
818         }
819     }
820 
821     return FALSE;
822 }
823 
824 BOOL CMainWindow::SearchTextChanged(ATL::CStringW &SearchText)
825 {
826     if (szSearchPattern == SearchText)
827     {
828         return FALSE;
829     }
830 
831     szSearchPattern = SearchText;
832 
833     DWORD dwDelay;
834     SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
835     SetTimer(SEARCH_TIMER_ID, dwDelay);
836 
837     return TRUE;
838 }
839 
840 void CMainWindow::HandleTabOrder(int direction)
841 {
842     ATL::CSimpleArray<HWND> TabOrderHwndList;
843 
844     m_TreeView->AppendTabOrderWindow(direction, TabOrderHwndList);
845     m_ApplicationView->AppendTabOrderWindow(direction, TabOrderHwndList);
846 
847 
848     if (TabOrderHwndList.GetSize() == 0)
849     {
850         // in case the list is empty
851         return;
852     }
853 
854     int FocusIndex;
855 
856     if ((FocusIndex = TabOrderHwndList.Find(GetFocus())) == -1)
857     {
858         FocusIndex = 0; // focus the first window in the list
859     }
860     else
861     {
862         FocusIndex += direction;
863         FocusIndex += TabOrderHwndList.GetSize(); // FocusIndex might be negative. we don't want to mod a negative number
864         FocusIndex %= TabOrderHwndList.GetSize();
865     }
866 
867     ::SetFocus(TabOrderHwndList[FocusIndex]);
868     return;
869 }
870 // **** CMainWindow ****
871 
872 
873 
874 VOID MainWindowLoop(INT nShowCmd)
875 {
876     HACCEL KeyBrd;
877     MSG Msg;
878 
879     CMainWindow* wnd = new CMainWindow();
880     if (!wnd)
881         return;
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 &&
900                 Msg.wParam == VK_TAB)
901             {
902                 // Move backwards if shift is held down
903                 int direction = (GetKeyState(VK_SHIFT) & 0x8000) ? -1 : 1;
904 
905                 wnd->HandleTabOrder(direction);
906                 continue;
907             }
908 
909             TranslateMessage(&Msg);
910             DispatchMessageW(&Msg);
911         }
912     }
913 
914     delete wnd;
915 }
916