[RAPPS]
[reactos.git] / reactos / base / applications / rapps / gui.cpp
1 /* PROJECT: ReactOS CE Applications Manager
2 * LICENSE: GPL - See COPYING in the top level directory
3 * AUTHORS: David Quintana <gigaherz@gmail.com>
4 */
5
6 #include "rapps.h"
7
8 #include <shlobj_undoc.h>
9 #include <shlguid_undoc.h>
10
11 #include <atlbase.h>
12 #include <atlcom.h>
13 #include <atlwin.h>
14 #include <wininet.h>
15 #include <shellutils.h>
16
17 #include <rosctrls.h>
18
19 #include "rosui.h"
20 #include "crichedit.h"
21
22 #define SEARCH_TIMER_ID 'SR'
23
24 HWND hListView = NULL;
25
26 class CMainToolbar :
27 public CUiWindow< CToolbar<> >
28 {
29 #define TOOLBAR_HEIGHT 24
30
31 WCHAR szInstallBtn[MAX_STR_LEN];
32 WCHAR szUninstallBtn[MAX_STR_LEN];
33 WCHAR szModifyBtn[MAX_STR_LEN];
34
35 VOID AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex)
36 {
37 HICON hImage;
38
39 if (!(hImage = (HICON) LoadImage(hInst,
40 MAKEINTRESOURCE(ImageIndex),
41 IMAGE_ICON,
42 TOOLBAR_HEIGHT,
43 TOOLBAR_HEIGHT,
44 0)))
45 {
46 /* TODO: Error message */
47 }
48
49 ImageList_AddIcon(hImageList, hImage);
50 DeleteObject(hImage);
51 }
52
53 HIMAGELIST InitImageList(VOID)
54 {
55 HIMAGELIST hImageList;
56
57 /* Create the toolbar icon image list */
58 hImageList = ImageList_Create(TOOLBAR_HEIGHT,//GetSystemMetrics(SM_CXSMICON),
59 TOOLBAR_HEIGHT,//GetSystemMetrics(SM_CYSMICON),
60 ILC_MASK | GetSystemColorDepth(),
61 1,
62 1);
63 if (!hImageList)
64 {
65 /* TODO: Error message */
66 return NULL;
67 }
68
69 AddImageToImageList(hImageList, IDI_INSTALL);
70 AddImageToImageList(hImageList, IDI_UNINSTALL);
71 AddImageToImageList(hImageList, IDI_MODIFY);
72 AddImageToImageList(hImageList, IDI_REFRESH);
73 AddImageToImageList(hImageList, IDI_UPDATE_DB);
74 AddImageToImageList(hImageList, IDI_SETTINGS);
75 AddImageToImageList(hImageList, IDI_EXIT);
76
77 return hImageList;
78 }
79
80 public:
81 VOID OnGetDispInfo(LPTOOLTIPTEXT lpttt)
82 {
83 UINT idButton = (UINT) lpttt->hdr.idFrom;
84
85 switch (idButton)
86 {
87 case ID_EXIT:
88 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_EXIT);
89 break;
90
91 case ID_INSTALL:
92 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_INSTALL);
93 break;
94
95 case ID_UNINSTALL:
96 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_UNINSTALL);
97 break;
98
99 case ID_MODIFY:
100 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_MODIFY);
101 break;
102
103 case ID_SETTINGS:
104 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_SETTINGS);
105 break;
106
107 case ID_REFRESH:
108 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_REFRESH);
109 break;
110
111 case ID_RESETDB:
112 lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_UPDATE_DB);
113 break;
114 }
115 }
116
117 HWND Create(HWND hwndParent)
118 {
119 static TBBUTTON Buttons [] =
120 { /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */
121 { 0, ID_INSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szInstallBtn },
122 { 1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szUninstallBtn },
123 { 2, ID_MODIFY, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szModifyBtn },
124 { 5, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 },
125 { 3, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 },
126 { 4, ID_RESETDB, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, 0},
127 { 5, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 },
128 { 5, ID_SETTINGS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 },
129 { 6, ID_EXIT, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }
130 };
131
132 INT NumButtons = _countof(Buttons);
133 HIMAGELIST hImageList;
134
135 LoadStringW(hInst, IDS_INSTALL, szInstallBtn, _countof(szInstallBtn));
136 LoadStringW(hInst, IDS_UNINSTALL, szUninstallBtn, _countof(szUninstallBtn));
137 LoadStringW(hInst, IDS_MODIFY, szModifyBtn, _countof(szModifyBtn));
138
139 m_hWnd = CreateWindowExW(0,
140 TOOLBARCLASSNAMEW,
141 NULL,
142 WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST,
143 0, 0, 0, 0,
144 hwndParent,
145 0,
146 hInst,
147 NULL);
148
149 if (!m_hWnd)
150 {
151 /* TODO: Show error message */
152 return FALSE;
153 }
154
155 SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS);
156 SetButtonStructSize();
157
158 hImageList = InitImageList();
159
160 if (!hImageList)
161 {
162 /* TODO: Show error message */
163 return FALSE;
164 }
165
166 ImageList_Destroy((HIMAGELIST) SetImageList(hImageList));
167
168 AddButtons(NumButtons, Buttons);
169
170 return m_hWnd;
171 }
172 };
173
174 class CAppsListView :
175 public CUiWindow<CListView>
176 {
177 struct SortContext
178 {
179 CAppsListView * lvw;
180 int iSubItem;
181 };
182
183 public:
184 BOOL bAscending;
185
186 CAppsListView()
187 {
188 bAscending = TRUE;
189 }
190
191 VOID ColumnClick(LPNMLISTVIEW pnmv)
192 {
193 SortContext ctx = { this, pnmv->iSubItem };
194
195 SortItems(s_CompareFunc, &ctx);
196
197 bAscending = !bAscending;
198 }
199
200 PVOID GetLParam(INT Index)
201 {
202 INT ItemIndex;
203 LVITEM Item;
204
205 if (Index == -1)
206 {
207 ItemIndex = (INT) SendMessage(LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
208 if (ItemIndex == -1)
209 return NULL;
210 }
211 else
212 {
213 ItemIndex = Index;
214 }
215
216 ZeroMemory(&Item, sizeof(Item));
217
218 Item.mask = LVIF_PARAM;
219 Item.iItem = ItemIndex;
220 if (!GetItem(&Item))
221 return NULL;
222
223 return (PVOID) Item.lParam;
224 }
225
226 BOOL AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format)
227 {
228 LV_COLUMN Column;
229
230 ZeroMemory(&Column, sizeof(Column));
231
232 Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
233 Column.iSubItem = Index;
234 Column.pszText = (LPTSTR) lpText;
235 Column.cx = Width;
236 Column.fmt = Format;
237
238 return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE;
239 }
240
241 INT AddItem(INT ItemIndex, INT IconIndex, LPWSTR lpText, LPARAM lParam)
242 {
243 LV_ITEMW Item;
244
245 ZeroMemory(&Item, sizeof(Item));
246
247 Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
248 Item.pszText = lpText;
249 Item.lParam = lParam;
250 Item.iItem = ItemIndex;
251 Item.iImage = IconIndex;
252
253 return InsertItem(&Item);
254 }
255
256 static INT CALLBACK s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
257 {
258 SortContext * ctx = ((SortContext*) lParamSort);
259 return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem);
260 }
261
262 INT CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem)
263 {
264 WCHAR Item1[MAX_STR_LEN], Item2[MAX_STR_LEN];
265 LVFINDINFO IndexInfo;
266 INT Index;
267
268 IndexInfo.flags = LVFI_PARAM;
269
270 IndexInfo.lParam = lParam1;
271 Index = FindItem(-1, &IndexInfo);
272 GetItemText(Index, iSubItem, Item1, _countof(Item1));
273
274 IndexInfo.lParam = lParam2;
275 Index = FindItem(-1, &IndexInfo);
276 GetItemText(Index, iSubItem, Item2, _countof(Item2));
277
278 if (bAscending)
279 return wcscmp(Item2, Item1);
280 else
281 return wcscmp(Item1, Item2);
282
283 return 0;
284 }
285
286 HWND Create(HWND hwndParent)
287 {
288 RECT r = { 205, 28, 465, 250 };
289 DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS;
290 HMENU menu = GetSubMenu(LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_APPLICATIONMENU)), 0);
291
292 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE, menu);
293
294 if (hwnd)
295 SetExtendedListViewStyle(LVS_EX_FULLROWSELECT);
296
297 return hwnd;
298 }
299
300 };
301
302 class CMainWindow :
303 public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>
304 {
305 CUiPanel * m_ClientPanel;
306 CUiSplitPanel * m_VSplitter;
307 CUiSplitPanel * m_HSplitter;
308
309 CMainToolbar * m_Toolbar;
310 CAppsListView * m_ListView;
311
312 CUiWindow<CTreeView> * m_TreeView;
313 CUiWindow<CStatusBar> * m_StatusBar;
314 CUiWindow<CRichEdit> * m_RichEdit;
315
316 CUiWindow<> * m_SearchBar;
317
318 HIMAGELIST hImageTreeView;
319
320 PWSTR pLink;
321
322 BOOL SearchEnabled;
323
324 public:
325 CMainWindow() :
326 m_ClientPanel(NULL),
327 hImageTreeView(NULL),
328 pLink(NULL),
329 SearchEnabled(TRUE)
330 {
331 }
332
333 private:
334
335 VOID InitApplicationsList(VOID)
336 {
337 WCHAR szText[MAX_STR_LEN];
338
339 /* Add columns to ListView */
340 LoadStringW(hInst, IDS_APP_NAME, szText, _countof(szText));
341 m_ListView->AddColumn(0, szText, 200, LVCFMT_LEFT);
342
343 LoadStringW(hInst, IDS_APP_INST_VERSION, szText, _countof(szText));
344 m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT);
345
346 LoadStringW(hInst, IDS_APP_DESCRIPTION, szText, _countof(szText));
347 m_ListView->AddColumn(3, szText, 250, LVCFMT_LEFT);
348
349 UpdateApplicationsList(ENUM_ALL_COMPONENTS);
350 }
351
352 HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
353 {
354 WCHAR szText[MAX_STR_LEN];
355 INT Index;
356 HICON hIcon;
357
358 hIcon = (HICON) LoadImage(hInst,
359 MAKEINTRESOURCE(IconIndex),
360 IMAGE_ICON,
361 TREEVIEW_ICON_SIZE,
362 TREEVIEW_ICON_SIZE,
363 LR_CREATEDIBSECTION);
364
365 Index = ImageList_AddIcon(hImageTreeView, hIcon);
366 DestroyIcon(hIcon);
367
368 LoadStringW(hInst, TextIndex, szText, _countof(szText));
369
370 return m_TreeView->AddItem(hRootItem, szText, Index, Index, TextIndex);
371 }
372
373 VOID InitCategoriesList(VOID)
374 {
375 HTREEITEM hRootItem;
376
377 hRootItem = AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
378 AddCategory(hRootItem, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
379 AddCategory(hRootItem, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
380 AddCategory(hRootItem, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
381 AddCategory(hRootItem, IDS_CAT_GAMES, IDI_CAT_GAMES);
382 AddCategory(hRootItem, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
383 AddCategory(hRootItem, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
384 AddCategory(hRootItem, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
385 AddCategory(hRootItem, IDS_CAT_EDU, IDI_CAT_EDU);
386 AddCategory(hRootItem, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
387 AddCategory(hRootItem, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
388 AddCategory(hRootItem, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
389 AddCategory(hRootItem, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
390 AddCategory(hRootItem, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
391 AddCategory(hRootItem, IDS_CAT_LIBS, IDI_CAT_LIBS);
392 AddCategory(hRootItem, IDS_CAT_OTHER, IDI_CAT_OTHER);
393
394 m_TreeView->SetImageList(hImageTreeView, TVSIL_NORMAL);
395 m_TreeView->Expand(hRootItem, TVE_EXPAND);
396 m_TreeView->SelectItem(hRootItem);
397 }
398
399 BOOL CreateStatusBar()
400 {
401 m_StatusBar = new CUiWindow<CStatusBar>();
402 m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm;
403 m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch;
404 m_ClientPanel->Children().Append(m_StatusBar);
405
406 return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL;
407 }
408
409 BOOL CreateToolbar()
410 {
411 m_Toolbar = new CMainToolbar();
412 m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop;
413 m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch;
414 m_ClientPanel->Children().Append(m_Toolbar);
415
416 return m_Toolbar->Create(m_hWnd) != NULL;
417 }
418
419 BOOL CreateTreeView()
420 {
421 m_TreeView = new CUiWindow<CTreeView>();
422 m_TreeView->m_VerticalAlignment = UiAlign_Stretch;
423 m_TreeView->m_HorizontalAlignment = UiAlign_Stretch;
424 m_VSplitter->First().Append(m_TreeView);
425
426 return m_TreeView->Create(m_hWnd) != NULL;
427 }
428
429 BOOL CreateListView()
430 {
431 m_ListView = new CAppsListView();
432 m_ListView->m_VerticalAlignment = UiAlign_Stretch;
433 m_ListView->m_HorizontalAlignment = UiAlign_Stretch;
434 m_HSplitter->First().Append(m_ListView);
435
436 hListView = m_ListView->Create(m_hWnd);
437 return hListView != NULL;
438 }
439
440 BOOL CreateRichEdit()
441 {
442 m_RichEdit = new CUiWindow<CRichEdit>();
443 m_RichEdit->m_VerticalAlignment = UiAlign_Stretch;
444 m_RichEdit->m_HorizontalAlignment = UiAlign_Stretch;
445 m_HSplitter->Second().Append(m_RichEdit);
446
447 return m_RichEdit->Create(m_hWnd) != NULL;
448 }
449
450 BOOL CreateVSplitter()
451 {
452 m_VSplitter = new CUiSplitPanel();
453 m_VSplitter->m_VerticalAlignment = UiAlign_Stretch;
454 m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch;
455 m_VSplitter->m_DynamicFirst = FALSE;
456 m_VSplitter->m_Horizontal = FALSE;
457 m_VSplitter->m_MinFirst = 240;
458 m_VSplitter->m_MinSecond = 300;
459 m_ClientPanel->Children().Append(m_VSplitter);
460
461 return m_VSplitter->Create(m_hWnd) != NULL;
462 }
463
464 BOOL CreateHSplitter()
465 {
466 m_HSplitter = new CUiSplitPanel();
467 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch;
468 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch;
469 m_HSplitter->m_DynamicFirst = TRUE;
470 m_HSplitter->m_Horizontal = TRUE;
471 m_HSplitter->m_Pos = 32768;
472 m_HSplitter->m_MinFirst = 300;
473 m_HSplitter->m_MinSecond = 80;
474 m_VSplitter->Second().Append(m_HSplitter);
475
476 return m_HSplitter->Create(m_hWnd) != NULL;
477 }
478
479 BOOL CreateSearchBar(VOID)
480 {
481 WCHAR szBuf[MAX_STR_LEN];
482
483 // TODO: WRAPPER
484 m_SearchBar = new CUiWindow<>();
485 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop;
486 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm;
487 m_SearchBar->m_Margin.top = 6;
488 m_SearchBar->m_Margin.right = 6;
489 //m_ClientPanel->Children().Append(m_SearchBar);
490
491 HWND hwnd = CreateWindowExW(WS_EX_CLIENTEDGE,
492 L"Edit",
493 NULL,
494 WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,
495 0,
496 0,
497 200,
498 22,
499 m_Toolbar->m_hWnd,
500 (HMENU) 0,
501 hInst,
502 0);
503
504 m_SearchBar->m_hWnd = hwnd;
505
506 m_SearchBar->SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
507
508 LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf));
509 m_SearchBar->SetWindowTextW(szBuf);
510
511 return hwnd != NULL;
512 }
513
514 BOOL CreateLayout()
515 {
516 bool b = TRUE;
517
518 m_ClientPanel = new CUiPanel();
519 m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch;
520 m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch;
521
522 // Top level
523 b = b && CreateStatusBar();
524 b = b && CreateToolbar();
525 b = b && CreateSearchBar();
526 b = b && CreateVSplitter();
527
528 // Inside V Splitter
529 b = b && CreateHSplitter();
530 b = b && CreateTreeView();
531
532 // Inside H Splitter
533 b = b && CreateListView();
534 b = b && CreateRichEdit();
535
536 if (b)
537 {
538 RECT rTop;
539 RECT rBottom;
540
541 /* Size status bar */
542 m_StatusBar->SendMessage(WM_SIZE, 0, 0);
543
544 /* Size tool bar */
545 m_Toolbar->AutoSize();
546
547 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop);
548 ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom);
549
550 m_VSplitter->m_Margin.top = rTop.bottom - rTop.top;
551 m_VSplitter->m_Margin.bottom = rBottom.bottom-rBottom.top;
552 }
553
554 return b;
555 }
556
557 BOOL InitControls()
558 {
559 /* Create image list */
560 hImageTreeView = ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE,
561 GetSystemColorDepth() | ILC_MASK,
562 0, 1);
563
564 if (CreateLayout())
565 {
566 WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN];
567
568 InitApplicationsList();
569
570 InitCategoriesList();
571
572 LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, _countof(szBuffer2));
573 StringCbPrintfW(szBuffer1, sizeof(szBuffer1),
574 szBuffer2,
575 m_ListView->GetItemCount());
576 m_StatusBar->SetText(szBuffer1);
577 return TRUE;
578 }
579
580 return FALSE;
581 }
582
583 VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
584 {
585 /* Size status bar */
586 m_StatusBar->SendMessage(WM_SIZE, 0, 0);
587
588 /* Size tool bar */
589 m_Toolbar->AutoSize();
590
591
592 RECT r = { 0, 0, LOWORD(lParam), HIWORD(lParam) };
593
594 HDWP hdwp = NULL;
595
596 int count = m_ClientPanel->CountSizableChildren();
597 hdwp = BeginDeferWindowPos(count);
598 if (hdwp) hdwp = m_ClientPanel->OnParentSize(r, hdwp);
599 if (hdwp) EndDeferWindowPos(hdwp);
600
601 // TODO: Sub-layouts for children of children
602 count = m_SearchBar->CountSizableChildren();
603 hdwp = BeginDeferWindowPos(count);
604 if (hdwp) hdwp = m_SearchBar->OnParentSize(r, hdwp);
605 if (hdwp) EndDeferWindowPos(hdwp);
606 }
607
608 BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
609 {
610 theResult = 0;
611 switch (Msg)
612 {
613 case WM_CREATE:
614 if (!InitControls())
615 ::PostMessage(hwnd, WM_CLOSE, 0, 0);
616 break;
617
618 case WM_DESTROY:
619 {
620 ShowWindow(SW_HIDE);
621 SaveSettings(hwnd);
622
623 FreeLogs();
624
625 FreeCachedAvailableEntries();
626
627 if (IS_INSTALLED_ENUM(SelectedEnumType))
628 FreeInstalledAppList();
629
630 if (hImageTreeView)
631 ImageList_Destroy(hImageTreeView);
632
633 delete m_ClientPanel;
634
635 PostQuitMessage(0);
636 return 0;
637 }
638
639 case WM_COMMAND:
640 OnCommand(wParam, lParam);
641 break;
642
643 case WM_NOTIFY:
644 {
645 LPNMHDR data = (LPNMHDR) lParam;
646
647 switch (data->code)
648 {
649 case TVN_SELCHANGED:
650 {
651 if (data->hwndFrom == m_TreeView->m_hWnd)
652 {
653 switch (((LPNMTREEVIEW) lParam)->itemNew.lParam)
654 {
655 case IDS_INSTALLED:
656 UpdateApplicationsList(ENUM_ALL_COMPONENTS);
657 break;
658
659 case IDS_APPLICATIONS:
660 UpdateApplicationsList(ENUM_APPLICATIONS);
661 break;
662
663 case IDS_UPDATES:
664 UpdateApplicationsList(ENUM_UPDATES);
665 break;
666
667 case IDS_AVAILABLEFORINST:
668 UpdateApplicationsList(ENUM_ALL_AVAILABLE);
669 break;
670
671 case IDS_CAT_AUDIO:
672 UpdateApplicationsList(ENUM_CAT_AUDIO);
673 break;
674
675 case IDS_CAT_DEVEL:
676 UpdateApplicationsList(ENUM_CAT_DEVEL);
677 break;
678
679 case IDS_CAT_DRIVERS:
680 UpdateApplicationsList(ENUM_CAT_DRIVERS);
681 break;
682
683 case IDS_CAT_EDU:
684 UpdateApplicationsList(ENUM_CAT_EDU);
685 break;
686
687 case IDS_CAT_ENGINEER:
688 UpdateApplicationsList(ENUM_CAT_ENGINEER);
689 break;
690
691 case IDS_CAT_FINANCE:
692 UpdateApplicationsList(ENUM_CAT_FINANCE);
693 break;
694
695 case IDS_CAT_GAMES:
696 UpdateApplicationsList(ENUM_CAT_GAMES);
697 break;
698
699 case IDS_CAT_GRAPHICS:
700 UpdateApplicationsList(ENUM_CAT_GRAPHICS);
701 break;
702
703 case IDS_CAT_INTERNET:
704 UpdateApplicationsList(ENUM_CAT_INTERNET);
705 break;
706
707 case IDS_CAT_LIBS:
708 UpdateApplicationsList(ENUM_CAT_LIBS);
709 break;
710
711 case IDS_CAT_OFFICE:
712 UpdateApplicationsList(ENUM_CAT_OFFICE);
713 break;
714
715 case IDS_CAT_OTHER:
716 UpdateApplicationsList(ENUM_CAT_OTHER);
717 break;
718
719 case IDS_CAT_SCIENCE:
720 UpdateApplicationsList(ENUM_CAT_SCIENCE);
721 break;
722
723 case IDS_CAT_TOOLS:
724 UpdateApplicationsList(ENUM_CAT_TOOLS);
725 break;
726
727 case IDS_CAT_VIDEO:
728 UpdateApplicationsList(ENUM_CAT_VIDEO);
729 break;
730 }
731 }
732
733 HMENU mainMenu = ::GetMenu(hwnd);
734 HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd);
735
736 /* Disable/enable items based on treeview selection */
737 if (IsSelectedNodeInstalled())
738 {
739 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED);
740 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED);
741 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED);
742 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
743
744 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED);
745 EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED);
746 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED);
747 EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED);
748
749 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE);
750 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_INSTALL, FALSE);
751 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE);
752 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_MODIFY, TRUE);
753 }
754 else
755 {
756 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED);
757 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED);
758 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED);
759 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
760
761 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED);
762 EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED);
763 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED);
764 EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED);
765
766 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE);
767 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_INSTALL, TRUE);
768 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE);
769 m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_MODIFY, FALSE);
770 }
771 }
772 break;
773
774 case LVN_ITEMCHANGED:
775 {
776 LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam;
777
778 if (pnic->hdr.hwndFrom == m_ListView->m_hWnd)
779 {
780 /* Check if this is a valid item
781 * (technically, it can be also an unselect) */
782 INT ItemIndex = pnic->iItem;
783 if (ItemIndex == -1 ||
784 ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom))
785 {
786 break;
787 }
788
789 /* Check if the focus has been moved to another item */
790 if ((pnic->uChanged & LVIF_STATE) &&
791 (pnic->uNewState & LVIS_FOCUSED) &&
792 !(pnic->uOldState & LVIS_FOCUSED))
793 {
794 if (IS_INSTALLED_ENUM(SelectedEnumType))
795 ShowInstalledAppInfo(ItemIndex);
796 if (IS_AVAILABLE_ENUM(SelectedEnumType))
797 ShowAvailableAppInfo(ItemIndex);
798 }
799 }
800 }
801 break;
802
803 case LVN_COLUMNCLICK:
804 {
805 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
806
807 m_ListView->ColumnClick(pnmv);
808 }
809 break;
810
811 case NM_CLICK:
812 {
813 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
814 {
815 if (IS_INSTALLED_ENUM(SelectedEnumType))
816 ShowInstalledAppInfo(-1);
817 if (IS_AVAILABLE_ENUM(SelectedEnumType))
818 ShowAvailableAppInfo(-1);
819 }
820 }
821 break;
822
823 case NM_DBLCLK:
824 {
825 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
826 {
827 /* this won't do anything if the program is already installed */
828 SendMessage(hwnd, WM_COMMAND, ID_INSTALL, 0);
829 }
830 }
831 break;
832
833 case NM_RCLICK:
834 {
835 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
836 {
837 ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL);
838 }
839 }
840 break;
841
842 case EN_LINK:
843 OnLink((ENLINK*) lParam);
844 break;
845
846 case TTN_GETDISPINFO:
847 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam);
848 break;
849 }
850 }
851 break;
852
853 case WM_SIZE:
854 OnSize(hwnd, wParam, lParam);
855 break;
856
857 case WM_SIZING:
858 {
859 LPRECT pRect = (LPRECT) lParam;
860
861 if (pRect->right - pRect->left < 565)
862 pRect->right = pRect->left + 565;
863
864 if (pRect->bottom - pRect->top < 300)
865 pRect->bottom = pRect->top + 300;
866
867 return TRUE;
868 }
869
870 case WM_SYSCOLORCHANGE:
871 {
872 /* Forward WM_SYSCOLORCHANGE to common controls */
873 m_ListView->SendMessage(WM_SYSCOLORCHANGE, 0, 0);
874 m_TreeView->SendMessage(WM_SYSCOLORCHANGE, 0, 0);
875 m_Toolbar->SendMessage(WM_SYSCOLORCHANGE, 0, 0);
876 m_ListView->SendMessage(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE));
877 }
878 break;
879
880 case WM_TIMER:
881 if (wParam == SEARCH_TIMER_ID)
882 {
883 ::KillTimer(hwnd, SEARCH_TIMER_ID);
884 UpdateApplicationsList(-1);
885 }
886 break;
887 }
888
889 return FALSE;
890 }
891
892 virtual VOID OnLink(ENLINK *Link)
893 {
894 switch (Link->msg)
895 {
896 case WM_LBUTTONUP:
897 case WM_RBUTTONUP:
898 {
899 if (pLink) HeapFree(GetProcessHeap(), 0, pLink);
900
901 pLink = (PWSTR) HeapAlloc(GetProcessHeap(), 0,
902 (max(Link->chrg.cpMin, Link->chrg.cpMax) -
903 min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR));
904 if (!pLink)
905 {
906 /* TODO: Error message */
907 return;
908 }
909
910 m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax);
911 m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink);
912
913 ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1);
914 }
915 break;
916 }
917 }
918
919 BOOL IsSelectedNodeInstalled(void)
920 {
921 HTREEITEM hSelectedItem = m_TreeView->GetSelection();
922 TV_ITEM tItem;
923
924 tItem.mask = TVIF_PARAM | TVIF_HANDLE;
925 tItem.hItem = hSelectedItem;
926 m_TreeView->GetItem(&tItem);
927 switch (tItem.lParam)
928 {
929 case IDS_INSTALLED:
930 case IDS_APPLICATIONS:
931 case IDS_UPDATES:
932 return TRUE;
933 default:
934 return FALSE;
935 }
936 }
937
938 VOID OnCommand(WPARAM wParam, LPARAM lParam)
939 {
940 WORD wCommand = LOWORD(wParam);
941
942 if (lParam == (LPARAM) m_SearchBar->m_hWnd)
943 {
944 WCHAR szBuf[MAX_STR_LEN];
945
946 switch (HIWORD(wParam))
947 {
948 case EN_SETFOCUS:
949 {
950 WCHAR szWndText[MAX_STR_LEN];
951
952 LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf));
953 m_SearchBar->GetWindowTextW(szWndText, MAX_STR_LEN);
954 if (wcscmp(szBuf, szWndText) == 0)
955 {
956 SearchEnabled = FALSE;
957 m_SearchBar->SetWindowTextW(L"");
958 }
959 }
960 break;
961
962 case EN_KILLFOCUS:
963 {
964 m_SearchBar->GetWindowTextW(szBuf, MAX_STR_LEN);
965 if (wcslen(szBuf) < 1)
966 {
967 LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf));
968 SearchEnabled = FALSE;
969 m_SearchBar->SetWindowTextW(szBuf);
970 }
971 }
972 break;
973
974 case EN_CHANGE:
975 {
976 WCHAR szWndText[MAX_STR_LEN];
977
978 if (!SearchEnabled)
979 {
980 SearchEnabled = TRUE;
981 break;
982 }
983
984 LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf));
985 m_SearchBar->GetWindowTextW(szWndText, MAX_STR_LEN);
986 if (wcscmp(szBuf, szWndText) != 0)
987 {
988 StringCbCopy(szSearchPattern, sizeof(szSearchPattern),
989 szWndText);
990 }
991 else
992 {
993 szSearchPattern[0] = UNICODE_NULL;
994 }
995
996 DWORD dwDelay;
997 SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
998 SetTimer(SEARCH_TIMER_ID, dwDelay);
999 }
1000 break;
1001 }
1002
1003 return;
1004 }
1005
1006 switch (wCommand)
1007 {
1008 case ID_OPEN_LINK:
1009 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1010 HeapFree(GetProcessHeap(), 0, pLink);
1011 break;
1012
1013 case ID_COPY_LINK:
1014 CopyTextToClipboard(pLink);
1015 HeapFree(GetProcessHeap(), 0, pLink);
1016 break;
1017
1018 case ID_SETTINGS:
1019 CreateSettingsDlg(m_hWnd);
1020 break;
1021
1022 case ID_EXIT:
1023 PostMessageW(WM_CLOSE, 0, 0);
1024 break;
1025
1026 case ID_INSTALL:
1027 if (DownloadApplication(-1))
1028 /* TODO: Implement install dialog
1029 * if (InstallApplication(-1))
1030 */
1031 UpdateApplicationsList(-1);
1032 break;
1033
1034 case ID_UNINSTALL:
1035 if (UninstallApplication(-1, FALSE))
1036 UpdateApplicationsList(-1);
1037 break;
1038
1039 case ID_MODIFY:
1040 if (UninstallApplication(-1, TRUE))
1041 UpdateApplicationsList(-1);
1042 break;
1043
1044 case ID_REGREMOVE:
1045 RemoveAppFromRegistry(-1);
1046 break;
1047
1048 case ID_REFRESH:
1049 UpdateApplicationsList(-1);
1050 break;
1051
1052 case ID_RESETDB:
1053 UpdateAppsDB();
1054 UpdateApplicationsList(-1);
1055 break;
1056
1057 case ID_HELP:
1058 MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
1059 break;
1060
1061 case ID_ABOUT:
1062 ShowAboutDialog();
1063 break;
1064 }
1065 }
1066
1067 VOID FreeInstalledAppList(VOID)
1068 {
1069 INT Count = ListView_GetItemCount(hListView) - 1;
1070 PINSTALLED_INFO Info;
1071
1072 while (Count >= 0)
1073 {
1074 Info = (PINSTALLED_INFO) ListViewGetlParam(Count);
1075 if (Info)
1076 {
1077 RegCloseKey(Info->hSubKey);
1078 HeapFree(GetProcessHeap(), 0, Info);
1079 }
1080 Count--;
1081 }
1082 }
1083
1084 static BOOL SearchPatternMatch(PCWSTR szHaystack, PCWSTR szNeedle)
1085 {
1086 if (!*szNeedle)
1087 return TRUE;
1088 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
1089 return StrStrIW(szHaystack, szNeedle) != NULL;
1090 }
1091
1092 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, LPWSTR lpName, PINSTALLED_INFO Info)
1093 {
1094 PINSTALLED_INFO ItemInfo;
1095 WCHAR szText[MAX_PATH];
1096 INT Index;
1097
1098 if (!SearchPatternMatch(lpName, szSearchPattern))
1099 {
1100 RegCloseKey(Info->hSubKey);
1101 return TRUE;
1102 }
1103
1104 ItemInfo = (PINSTALLED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(INSTALLED_INFO));
1105 if (!ItemInfo)
1106 {
1107 RegCloseKey(Info->hSubKey);
1108 return FALSE;
1109 }
1110
1111 RtlCopyMemory(ItemInfo, Info, sizeof(INSTALLED_INFO));
1112
1113 Index = ListViewAddItem(ItemIndex, 0, lpName, (LPARAM) ItemInfo);
1114
1115 /* Get version info */
1116 GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText);
1117 ListView_SetItemText(hListView, Index, 1, szText);
1118
1119 /* Get comments */
1120 GetApplicationString(ItemInfo->hSubKey, L"Comments", szText);
1121 ListView_SetItemText(hListView, Index, 2, szText);
1122
1123 return TRUE;
1124 }
1125
1126 static BOOL CALLBACK s_EnumAvailableAppProc(PAPPLICATION_INFO Info)
1127 {
1128 INT Index;
1129 HICON hIcon = NULL;
1130 bool failed = false;
1131 WCHAR szIconPath[MAX_PATH] = L"";
1132 HIMAGELIST hImageListView = NULL;
1133 hImageListView = ListView_GetImageList(hListView, LVSIL_SMALL);
1134
1135 if (!SearchPatternMatch(Info->szName, szSearchPattern) &&
1136 !SearchPatternMatch(Info->szDesc, szSearchPattern))
1137 {
1138 return TRUE;
1139 }
1140 //TODO: Define another field for icon name in the DB
1141 failed = !GetStorageDirectory(szIconPath, _countof(szIconPath))
1142 || FAILED(StringCbPrintfW(szIconPath, sizeof(szIconPath), L"%ls\\rapps\\icons\\%ls.ico", szIconPath, Info->szName));
1143 if (!failed) {
1144 //Load icon from file
1145 hIcon = (HICON)LoadImage(NULL,
1146 szIconPath,
1147 IMAGE_ICON,
1148 LISTVIEW_ICON_SIZE,
1149 LISTVIEW_ICON_SIZE,
1150 LR_LOADFROMFILE);
1151 }
1152 if (GetLastError() != ERROR_SUCCESS) {
1153 //Load default icon
1154 hIcon = (HICON)LoadIcon(hInst, MAKEINTRESOURCE(IDI_MAIN));
1155 }
1156 Index = ImageList_AddIcon(hImageListView, hIcon);
1157 DestroyIcon(hIcon);
1158
1159 Index = ListViewAddItem(Info->Category, Index, Info->szName, (LPARAM) Info);
1160 hImageListView = ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);
1161
1162 ListView_SetItemText(hListView, Index, 1, Info->szVersion);
1163 ListView_SetItemText(hListView, Index, 2, Info->szDesc);
1164 return TRUE;
1165 }
1166
1167
1168 VOID UpdateApplicationsList(INT EnumType)
1169 {
1170 WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN];
1171 HIMAGELIST hImageListView = NULL;
1172
1173 m_ListView->SendMessage(WM_SETREDRAW, FALSE, 0);
1174
1175 if (EnumType < 0) EnumType = SelectedEnumType;
1176
1177 if (IS_INSTALLED_ENUM(SelectedEnumType))
1178 FreeInstalledAppList();
1179
1180 (VOID) ListView_DeleteAllItems(hListView);
1181 //Create new ImageList
1182 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
1183 LISTVIEW_ICON_SIZE,
1184 GetSystemColorDepth() | ILC_MASK,
1185 0, 1000);
1186 hImageListView = ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);
1187
1188 if (hImageListView)
1189 ImageList_Destroy(hImageListView);
1190
1191 if (IS_AVAILABLE_ENUM(EnumType)) {
1192 /* Enum available applications */
1193 EnumAvailableApplications(EnumType, s_EnumAvailableAppProc);
1194 }
1195
1196 SelectedEnumType = EnumType;
1197
1198 LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, _countof(szBuffer2));
1199 StringCbPrintfW(szBuffer1, sizeof(szBuffer1),
1200 szBuffer2,
1201 ListView_GetItemCount(hListView));
1202 SetStatusBarText(szBuffer1);
1203
1204 SetWelcomeText();
1205
1206 /* set automatic column width for program names if the list is not empty */
1207 if (ListView_GetItemCount(hListView) > 0)
1208 ListView_SetColumnWidth(hListView, 0, LVSCW_AUTOSIZE);
1209
1210 SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
1211 }
1212
1213 public:
1214 static ATL::CWndClassInfo& GetWndClassInfo()
1215 {
1216 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
1217 static ATL::CWndClassInfo wc =
1218 {
1219 { sizeof(WNDCLASSEX), csStyle, StartWindowProc,
1220 0, 0, NULL,
1221 LoadIcon(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCE(IDI_MAIN)),
1222 LoadCursor(NULL, IDC_ARROW),
1223 (HBRUSH) (COLOR_BTNFACE + 1), MAKEINTRESOURCE(IDR_MAINMENU),
1224 L"RAppsWnd", NULL },
1225 NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
1226 };
1227 return wc;
1228 }
1229
1230 HWND Create()
1231 {
1232 WCHAR szWindowName[MAX_STR_LEN];
1233
1234 LoadStringW(hInst, IDS_APPTITLE, szWindowName, _countof(szWindowName));
1235
1236 RECT r = {
1237 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
1238 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
1239 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680),
1240 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)
1241 };
1242 r.right += r.left;
1243 r.bottom += r.top;
1244
1245 return CWindowImpl::Create(NULL, r, szWindowName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
1246 }
1247
1248 CStatusBar * GetStatusBar()
1249 {
1250 return m_StatusBar;
1251 }
1252
1253 CAppsListView * GetListView()
1254 {
1255 return m_ListView;
1256 }
1257
1258 CRichEdit * GetRichEdit()
1259 {
1260 return m_RichEdit;
1261 }
1262 };
1263
1264 CMainWindow * g_MainWindow;
1265
1266 HWND CreateMainWindow()
1267 {
1268 g_MainWindow = new CMainWindow();
1269 return g_MainWindow->Create();
1270 }
1271
1272 DWORD_PTR ListViewGetlParam(INT item)
1273 {
1274 if (item < 0)
1275 {
1276 item = g_MainWindow->GetListView()->GetSelectionMark();
1277 }
1278 return g_MainWindow->GetListView()->GetItemData(item);
1279 }
1280
1281 VOID SetStatusBarText(PCWSTR szText)
1282 {
1283 g_MainWindow->GetStatusBar()->SetText(szText);
1284 }
1285
1286 INT ListViewAddItem(INT ItemIndex, INT IconIndex, PWSTR lpName, LPARAM lParam)
1287 {
1288 return g_MainWindow->GetListView()->AddItem(ItemIndex, IconIndex, lpName, lParam);
1289 }
1290
1291 VOID NewRichEditText(PCWSTR szText, DWORD flags)
1292 {
1293 g_MainWindow->GetRichEdit()->SetText(szText, flags);
1294 }
1295
1296 VOID InsertRichEditText(PCWSTR szText, DWORD flags)
1297 {
1298 g_MainWindow->GetRichEdit()->InsertText(szText, flags);
1299 }