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