reactos/rosapps/downloader/main.c

539 lines
17 KiB
C

/* PROJECT: ReactOS Downloader
* LICENSE: GPL - See COPYING in the top level directory
* FILE: base/applications/downloader/xml.c
* PURPOSE: Main program
* PROGRAMMERS: Maarten Bosma, Lester Kortenhoeven
*/
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <stdio.h>
#include <shlwapi.h>
#include <wchar.h>
#include "resources.h"
#include "structures.h"
#define XML_PATH "tree.xml"
HWND hwnd, hCategories, hApps, hDownloadButton, hUninstallButton, hUpdateButton, hHelpButton;
HBITMAP hLogo, hUnderline;
CHAR* CmdLine;
WCHAR* DescriptionHeadline = L"";
WCHAR* DescriptionText = L"";
WCHAR ApplicationText[0xA04]; // MAX_STRING_LENGHT + Version + \n + MAX_STRING_LENGHT + Licence + \n + MAX_STRING_LENGHT + Maintainer + \n\n + Description
// 0x100 + 0x100 + 1 + 0x100 + 0x100 + 1 + 0x100 + 0x100 + 2 + 0x400 = 0xA04
struct Category Root;
struct Application* SelectedApplication;
INT_PTR CALLBACK DownloadProc (HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI InstallThreadFunc(LPVOID);
DWORD WINAPI UninstallThreadFunc(LPVOID);
BOOL ProcessXML (const char* filename, struct Category* Root);
char* addDML (const char*);
VOID FreeTree (struct Category* Node);
WCHAR Strings [STRING_COUNT][MAX_STRING_LENGHT];
BOOL getUninstaller(struct Application* CurrentApplication, WCHAR* Uninstaller) {
DWORD ArraySize = 0x100;
HKEY hKey1;
HKEY hKey2;
DWORD Type = 0;
WCHAR Value[ArraySize];
WCHAR KeyName[ArraySize];
DWORD Size = ArraySize;
LONG i = 0;
if (CurrentApplication->RegName[0] == L'\0') {
return FALSE;
}
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",0,KEY_READ,&hKey1) == ERROR_SUCCESS) {
if (RegOpenKeyExW(hKey1,CurrentApplication->RegName,0,KEY_READ,&hKey2) == ERROR_SUCCESS) {
if (RegQueryValueExW(hKey2,L"UninstallString",0,&Type,(LPBYTE)Uninstaller,&Size) == ERROR_SUCCESS) {
RegCloseKey(hKey2);
RegCloseKey(hKey1);
return TRUE;
} else {
RegCloseKey(hKey2);
RegCloseKey(hKey1);
return FALSE;
}
}
while (RegEnumKeyExW(hKey1,i,KeyName,&Size,NULL,NULL,NULL,NULL) == ERROR_SUCCESS) {
++i;
RegOpenKeyExW(hKey1,KeyName,0,KEY_READ,&hKey2);
Size = sizeof(Value);
if (RegQueryValueExW(hKey2,L"DisplayName",0,&Type,(LPBYTE)Value,&Size) == ERROR_SUCCESS) {
Size = ArraySize;
if (!wcscmp(Value,CurrentApplication->RegName)) {
if (RegQueryValueExW(hKey2,L"UninstallString",0,&Type,(LPBYTE)Uninstaller,&Size) == ERROR_SUCCESS) {
RegCloseKey(hKey2);
RegCloseKey(hKey1);
return TRUE;
} else {
RegCloseKey(hKey2);
RegCloseKey(hKey1);
return FALSE;
}
}
}
RegCloseKey(hKey2);
Size = ArraySize;
}
RegCloseKey(hKey1);
}
return FALSE;
}
void ShowMessage (WCHAR* title, WCHAR* message)
{
DescriptionHeadline = title;
DescriptionText = message;
InvalidateRect(hwnd,NULL,TRUE);
UpdateWindow(hwnd);
}
void AddItems (HWND hwnd, struct Category* Category, struct Category* Parent)
{
TV_INSERTSTRUCTW Insert;
Insert.item.lParam = (UINT)Category;
Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;;
Insert.item.pszText = Category->Name;
Insert.item.cchTextMax = lstrlenW(Category->Name);
Insert.item.iImage = Category->Icon;
Insert.item.iSelectedImage = Category->Icon;
Insert.hInsertAfter = TVI_LAST;
Insert.hParent = Category->Parent ? Category->Parent->TreeviewItem : TVI_ROOT;
Category->TreeviewItem = (HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);
if(Category->Next)
AddItems (hwnd,Category->Next,Parent);
if(Category->Children)
AddItems (hwnd,Category->Children,Category);
}
void CategoryChoosen (HWND hwnd, struct Category* Category)
{
struct Application* CurrentApplication;
TV_INSERTSTRUCTW Insert;
SelectedApplication = NULL;
if(Category->Children && !Category->Apps)
ShowMessage(Category->Name, Strings[IDS_CHOOSE_SUB]);
else if(!Category->Children && Category->Apps)
ShowMessage(Category->Name, Strings[IDS_CHOOSE_APP]);
else if(Category->Children && Category->Apps)
ShowMessage(Category->Name, Strings[IDS_CHOOSE_BOTH]);
else
ShowMessage(Category->Name, Strings[IDS_NO_APPS]);
(void)TreeView_DeleteItem(hwnd, TVI_ROOT);
(void)TreeView_DeleteItem(hwnd, TVI_ROOT); // Delete twice to bypass bug in windows
Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE;
Insert.hInsertAfter = TVI_LAST;
Insert.hParent = TVI_ROOT;
CurrentApplication = Category->Apps;
while(CurrentApplication)
{
Insert.item.lParam = (UINT)CurrentApplication;
Insert.item.pszText = CurrentApplication->Name;
Insert.item.cchTextMax = lstrlenW(CurrentApplication->Name);
if(getUninstaller(CurrentApplication, NULL)) {
Insert.item.iImage = 9;
} else {
Insert.item.iImage = 10;
}
SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);
CurrentApplication = CurrentApplication->Next;
}
}
BOOL SetupControls (HWND hwnd)
{
TV_INSERTSTRUCTW Insert = {0};
HIMAGELIST hImageList;
HINSTANCE hInstance = GetModuleHandle(NULL);
// Parse the XML file
if (ProcessXML (XML_PATH, &Root) == FALSE)
return FALSE;
// Set up the controls
hCategories = CreateWindowExW(0, WC_TREEVIEWW, L"Categories", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS,
0, 0, 0, 0, hwnd, NULL, hInstance, NULL);
hApps = CreateWindowExW(0, WC_TREEVIEWW, L"Applications", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS,
0, 0, 0, 0, hwnd, NULL, hInstance, NULL);
hLogo = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_LOGO));
hUnderline = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_UNDERLINE));
hHelpButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 550, 10, 40, 40, hwnd, 0, hInstance, NULL);
hUpdateButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 500, 10, 40, 40, hwnd, 0, hInstance, NULL);
hDownloadButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 330, 505, 140, 35, hwnd, 0, hInstance, NULL);
hUninstallButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 260, 505, 140, 35, hwnd, 0, hInstance, NULL);
SendMessageW(hHelpButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_HELP)));
SendMessageW(hUpdateButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_UPDATE)));
SendMessageW(hDownloadButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DOWNLOAD)));
SendMessageW(hUninstallButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_UNINSTALL)));
ShowWindow(hUninstallButton, SW_HIDE);
// Set deflaut entry for hApps
Insert.item.mask = TVIF_TEXT|TVIF_IMAGE;
Insert.item.pszText = Strings[IDS_CHOOSE_CATEGORY];
Insert.item.cchTextMax = lstrlenW(Strings[IDS_CHOOSE_CATEGORY]);
Insert.item.iImage = 0;
SendMessage(hApps, TVM_INSERTITEM, 0, (LPARAM)&Insert);
// Create Tree Icons
hImageList = ImageList_Create(16, 16, ILC_COLORDDB, 1, 1);
SendMessageW(hCategories, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);
SendMessageW(hApps, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_0)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_1)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_2)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_3)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_4)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_5)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_6)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_7)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_8)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_9)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_10)), NULL);
// Fill the TreeViews
AddItems (hCategories, Root.Children, NULL);
return TRUE;
}
static void ResizeControl (HWND hwnd, int x1, int y1, int x2, int y2)
{
// Make resizing a little easier
MoveWindow(hwnd, x1, y1, x2-x1, y2-y1, TRUE);
}
static void DrawBitmap (HDC hdc, int x, int y, HBITMAP hBmp)
{
BITMAP bm;
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBmp);
GetObject(hBmp, sizeof(bm), &bm);
TransparentBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, 0xFFFFFF);
DeleteDC(hdcMem);
}
static void DrawDescription (HDC hdc, RECT DescriptionRect)
{
int i;
HFONT Font;
RECT Rect = {DescriptionRect.left+5, DescriptionRect.top+3, DescriptionRect.right-2, DescriptionRect.top+22};
// Backgroud
Rectangle(hdc, DescriptionRect.left, DescriptionRect.top, DescriptionRect.right, DescriptionRect.bottom);
// Underline
for (i=DescriptionRect.left+1;i<DescriptionRect.right-1;i++)
DrawBitmap(hdc, i, DescriptionRect.top+22, hUnderline); // less code then stretching ;)
// Headline
Font = CreateFont(-16 , 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
SelectObject(hdc, Font);
DrawTextW(hdc, DescriptionHeadline, lstrlenW(DescriptionHeadline), &Rect, DT_SINGLELINE|DT_NOPREFIX);
DeleteObject(Font);
// Description
Font = CreateFont(-13 , 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
SelectObject(hdc, Font);
Rect.top += 40;
Rect.bottom = DescriptionRect.bottom-2;
DrawTextW(hdc, DescriptionText, lstrlenW(DescriptionText), &Rect, DT_WORDBREAK|DT_NOPREFIX); // ToDo: Call TabbedTextOut to draw a nice table
DeleteObject(Font);
}
void showUninstaller() {
int Split_Vertical = 200;
RECT Rect;
GetClientRect(hwnd,&Rect);
ShowWindow(hUninstallButton,SW_SHOW);
MoveWindow(hDownloadButton,(Split_Vertical+Rect.right-Rect.left)/2,Rect.bottom-Rect.top-45,140,35,TRUE);;
}
void hideUninstaller() {
int Split_Vertical = 200;
RECT Rect;
GetClientRect(hwnd,&Rect);
ShowWindow(hUninstallButton,SW_HIDE);
MoveWindow(hDownloadButton,(Split_Vertical+Rect.right-Rect.left)/2-70,Rect.bottom-Rect.top-45,140,35,TRUE);
}
void searchApp(const WCHAR* AppName, struct Category* Node) {
struct Application* CurrentApplication;
if (Node->Children)
searchApp(AppName, Node->Children);
if (Node->Next)
searchApp(AppName, Node->Next);
CurrentApplication = Node->Apps;
while((SelectedApplication == NULL) && (CurrentApplication != NULL)) {
if(wcscmp(CurrentApplication->Name,AppName)==0)
SelectedApplication = CurrentApplication;
CurrentApplication = CurrentApplication->Next;
}
}
void ShowSelectedApplication() {
ApplicationText[0]=L'\0';
if(SelectedApplication->Version[0] != L'\0') {
StrCatW(ApplicationText, Strings[IDS_VERSION]);
StrCatW(ApplicationText, SelectedApplication->Version);
StrCatW(ApplicationText, L"\n");
}
if(SelectedApplication->Licence[0] != L'\0') {
StrCatW(ApplicationText, Strings[IDS_LICENCE]);
StrCatW(ApplicationText, SelectedApplication->Licence);
StrCatW(ApplicationText, L"\n");
}
if(SelectedApplication->Maintainer[0] != L'\0') {
StrCatW(ApplicationText, Strings[IDS_MAINTAINER]);
StrCatW(ApplicationText, SelectedApplication->Maintainer);
StrCatW(ApplicationText, L"\n");
}
if((SelectedApplication->Licence[0] != L'\0') || (SelectedApplication->Version[0] != L'\0') || (SelectedApplication->Maintainer[0] != L'\0'))
StrCatW(ApplicationText, L"\n");
StrCatW(ApplicationText, SelectedApplication->Description);
ShowMessage(SelectedApplication->Name, ApplicationText);
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
static RECT DescriptionRect;
switch (Message)
{
case WM_CREATE:
{
WCHAR wAppName[0x100] = L"";
if (strncmp(CmdLine,"add ",4)==0) {
CmdLine = CmdLine+4;
if(CmdLine[0]==L'\"') {
CmdLine++;
CmdLine[strlen(CmdLine)-1]=L'\0';
}
char* aAppName = addDML(CmdLine);
MultiByteToWideChar(CP_UTF8, 0, aAppName, -1, wAppName, 0x100);
} else if (strncmp(CmdLine,"show ",5)==0) {
MultiByteToWideChar(CP_UTF8, 0, CmdLine+5, -1, wAppName, 0x100);
}
if(!SetupControls(hwnd))
return -1;
if(wAppName[0]!=L'\0')
searchApp(wAppName, &Root);
if(SelectedApplication == NULL) {
ShowMessage(Strings[IDS_WELCOME_TITLE], Strings[IDS_WELCOME]);
} else {
ShowSelectedApplication();
if(getUninstaller(SelectedApplication, NULL))
showUninstaller();
else
hideUninstaller();
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC BackbufferHdc = CreateCompatibleDC(hdc);
HBITMAP BackbufferBmp = CreateCompatibleBitmap(hdc, ps.rcPaint.right, ps.rcPaint.bottom);
SelectObject(BackbufferHdc, BackbufferBmp);
FillRect(BackbufferHdc, &ps.rcPaint, CreateSolidBrush(RGB(235,235,235)));
DrawBitmap(BackbufferHdc, 10, 12, hLogo);
DrawDescription(BackbufferHdc, DescriptionRect);
BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, BackbufferHdc, 0, 0, SRCCOPY);
DeleteObject(BackbufferBmp);
DeleteDC(BackbufferHdc);
EndPaint(hwnd, &ps);
}
break;
case WM_COMMAND:
{
if(HIWORD(wParam) == BN_CLICKED)
{
if (lParam == (LPARAM)hDownloadButton)
{
if(SelectedApplication) {
DWORD ThreadId;
CreateThread(NULL, 0, InstallThreadFunc, SelectedApplication, 0, &ThreadId);
} else
ShowMessage(Strings[IDS_NO_APP_TITLE], Strings[IDS_NO_APP]);
}
else if (lParam == (LPARAM)hUninstallButton)
{
if(SelectedApplication) {
DWORD ThreadId;
CreateThread(NULL, 0, UninstallThreadFunc, SelectedApplication, 0, &ThreadId);
hideUninstaller();
} else
ShowMessage(Strings[IDS_NO_APP_TITLE], Strings[IDS_NO_APP]);
}
else if (lParam == (LPARAM)hUpdateButton)
{
ShowMessage(Strings[IDS_UPDATE_TITLE], Strings[IDS_UPDATE]);
}
else if (lParam == (LPARAM)hHelpButton)
{
ShowMessage(Strings[IDS_HELP_TITLE], Strings[IDS_HELP]);
}
}
}
break;
case WM_NOTIFY:
{
LPNMHDR data = (LPNMHDR)lParam;
if(data->code == TVN_SELCHANGED)
{
BOOL bShowUninstaller = FALSE;
if(data->hwndFrom == hCategories)
{
struct Category* Category = (struct Category*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
CategoryChoosen (hApps, Category);
}
else if(data->hwndFrom == hApps)
{
SelectedApplication = (struct Application*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
if(SelectedApplication)
{
ShowSelectedApplication();
if(getUninstaller(SelectedApplication, NULL)) {
bShowUninstaller = TRUE;
}
}
}
if (bShowUninstaller)
showUninstaller();
else
hideUninstaller();
}
}
break;
case WM_SIZING:
{
LPRECT pRect = (LPRECT)lParam;
if (pRect->right-pRect->left < 520)
pRect->right = pRect->left + 520;
if (pRect->bottom-pRect->top < 300)
pRect->bottom = pRect->top + 300;
}
break;
case WM_SIZE:
{
int Split_Hozizontal = (HIWORD(lParam)-(45+60))/2 + 60;
int Split_Vertical = 200;
ResizeControl(hCategories, 10, 60, Split_Vertical, HIWORD(lParam)-10);
ResizeControl(hApps, Split_Vertical+5, 60, LOWORD(lParam)-10, Split_Hozizontal);
RECT Rect = {Split_Vertical+5, Split_Hozizontal+5, LOWORD(lParam)-10, HIWORD(lParam)-50};
DescriptionRect = Rect;
MoveWindow(hHelpButton, LOWORD(lParam)-50, 10, 40, 40, TRUE);
MoveWindow(hUpdateButton, LOWORD(lParam)-100, 10, 40, 40, TRUE);
if(IsWindowVisible(hUninstallButton))
MoveWindow(hDownloadButton, (Split_Vertical+LOWORD(lParam))/2, HIWORD(lParam)-45, 140, 35, TRUE);
else
MoveWindow(hDownloadButton, (Split_Vertical+LOWORD(lParam))/2-70, HIWORD(lParam)-45, 140, 35, TRUE);
MoveWindow(hUninstallButton, (Split_Vertical+LOWORD(lParam))/2-140, HIWORD(lParam)-45, 140, 35, TRUE);
}
break;
case WM_DESTROY:
{
DeleteObject(hLogo);
if(Root.Children)
FreeTree(Root.Children);
PostQuitMessage(0);
return 0;
}
break;
}
return DefWindowProc (hwnd, Message, wParam, lParam);
}
INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst,
LPSTR lpCmdLine, INT nCmdShow)
{
int i;
WNDCLASSEXW WndClass = {0};
MSG msg;
InitCommonControls();
CmdLine = lpCmdLine;
// Load strings
for(i=0; i<STRING_COUNT; i++)
LoadStringW(hInstance, i, Strings[i], MAX_STRING_LENGHT); // if you know a better method please tell me.
// Create the window
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.lpszClassName = L"Downloader";
WndClass.lpfnWndProc = WndProc;
WndClass.hInstance = hInstance;
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassExW(&WndClass);
hwnd = CreateWindowW(L"Downloader",
Strings[IDS_WINDOW_TITLE],
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
CW_USEDEFAULT,
CW_USEDEFAULT,
600, 550,
NULL, NULL,
hInstance,
NULL);
// Show it
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Message Loop
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}