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