diff --git a/base/applications/rapps/CMakeLists.txt b/base/applications/rapps/CMakeLists.txt index 33225ae5c04..226a8badcbd 100644 --- a/base/applications/rapps/CMakeLists.txt +++ b/base/applications/rapps/CMakeLists.txt @@ -39,7 +39,7 @@ add_rc_deps(rapps.rc ${rapps_rc_deps}) add_executable(rapps ${SOURCE} rapps.rc) set_module_type(rapps win32gui UNICODE) target_link_libraries(rapps uuid wine) -add_importlibs(rapps advapi32 comctl32 gdi32 wininet user32 shell32 shlwapi ole32 setupapi msvcrt kernel32 ntdll) +add_importlibs(rapps advapi32 comctl32 gdi32 wininet user32 shell32 shlwapi ole32 setupapi gdiplus msvcrt kernel32 ntdll) add_pch(rapps include/rapps.h SOURCE) add_dependencies(rapps rappsmsg) add_message_headers(ANSI rappsmsg.mc) diff --git a/base/applications/rapps/available.cpp b/base/applications/rapps/available.cpp index c7c78e180a9..be376c1c05b 100644 --- a/base/applications/rapps/available.cpp +++ b/base/applications/rapps/available.cpp @@ -18,23 +18,23 @@ #include // CAvailableApplicationInfo -CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam) +CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam, AvailableStrings& AvlbStrings) : m_IsSelected(FALSE), m_LicenseType(LICENSE_NONE), m_SizeBytes(0), m_sFileName(sFileNameParam), m_IsInstalled(FALSE), m_HasLanguageInfo(FALSE), m_HasInstalledVersion(FALSE) { - RetrieveGeneralInfo(); + RetrieveGeneralInfo(AvlbStrings); } -VOID CAvailableApplicationInfo::RefreshAppInfo() +VOID CAvailableApplicationInfo::RefreshAppInfo(AvailableStrings& AvlbStrings) { if (m_szUrlDownload.IsEmpty()) { - RetrieveGeneralInfo(); + RetrieveGeneralInfo(AvlbStrings); } } // Lazily load general info from the file -VOID CAvailableApplicationInfo::RetrieveGeneralInfo() +VOID CAvailableApplicationInfo::RetrieveGeneralInfo(AvailableStrings& AvlbStrings) { m_Parser = new CConfigParser(m_sFileName); @@ -52,10 +52,31 @@ VOID CAvailableApplicationInfo::RetrieveGeneralInfo() GetString(L"License", m_szLicense); GetString(L"Description", m_szDesc); GetString(L"URLSite", m_szUrlSite); - GetString(L"CDPath", m_szCDPath); - GetString(L"Language", m_szRegName); GetString(L"SHA1", m_szSHA1); + static_assert(MAX_SNAPSHOT_NUM < 10000, "MAX_SNAPSHOT_NUM is too big"); + for (int i = 0; i < MAX_SNAPSHOT_NUM; i++) + { + WCHAR SnapshotField[sizeof("Snapshot") + 4]; + wsprintfW(SnapshotField, L"Snapshot%d", i + 1); + ATL::CStringW SnapshotFileName; + if (!GetString(SnapshotField, SnapshotFileName)) + { + continue; + } + + // TODO: Add URL Support + + // TODO: Does the filename contain anything stuff like "\\" ".." ":" "<" ">" ? + // these stuff may lead to security issues + + ATL::CStringW SnapshotName = AvlbStrings.szAppsPath; + PathAppendW(SnapshotName.GetBuffer(MAX_PATH), L"snapshots"); + PathAppendW(SnapshotName.GetBuffer(), SnapshotFileName.GetString()); + SnapshotName.ReleaseBuffer(); + m_szSnapshotFilename.Add(SnapshotName); + } + RetrieveSize(); RetrieveLicenseType(); RetrieveLanguages(); @@ -209,6 +230,16 @@ BOOL CAvailableApplicationInfo::HasUpdate() const return (m_szInstalledVersion.Compare(m_szVersion) < 0) ? TRUE : FALSE; } +BOOL CAvailableApplicationInfo::RetrieveSnapshot(UINT Index,ATL::CStringW& SnapshotFileName) const +{ + if (Index >= (UINT)m_szSnapshotFilename.GetSize()) + { + return FALSE; + } + SnapshotFileName = m_szSnapshotFilename[Index]; + return TRUE; +} + VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime) { RtlCopyMemory(&m_ftCacheStamp, ftTime, sizeof(FILETIME)); @@ -231,11 +262,19 @@ AvailableStrings::AvailableStrings() //FIXME: maybe provide a fallback? if (GetStorageDirectory(szPath)) { - szAppsPath = szPath + L"\\rapps\\"; + szAppsPath = szPath; + PathAppendW(szAppsPath.GetBuffer(MAX_PATH), L"rapps"); + szAppsPath.ReleaseBuffer(); + szCabName = L"rappmgr.cab"; szCabDir = szPath; - szCabPath = (szCabDir + L"\\") + szCabName; - szSearchPath = szAppsPath + L"*.txt"; + szCabPath = szCabDir; + PathAppendW(szCabPath.GetBuffer(MAX_PATH), szCabName); + szCabPath.ReleaseBuffer(); + + szSearchPath = szAppsPath; + PathAppendW(szSearchPath.GetBuffer(MAX_PATH), L"*.txt"); + szSearchPath.ReleaseBuffer(); } } // AvailableStrings @@ -273,7 +312,9 @@ VOID CAvailableApps::DeleteCurrentAppsDB() ATL::CStringW szTmp; do { - szTmp = m_Strings.szAppsPath + FindFileData.cFileName; + szTmp = m_Strings.szAppsPath; + PathAppendW(szTmp.GetBuffer(MAX_PATH), FindFileData.cFileName); + szTmp.ReleaseBuffer(); DeleteFileW(szTmp.GetString()); } while (FindNextFileW(hFind, &FindFileData) != 0); FindClose(hFind); @@ -369,7 +410,7 @@ BOOL CAvailableApps::Enum(INT EnumType, AVAILENUMPROC lpEnumProc, PVOID param) } // create a new entry - Info = new CAvailableApplicationInfo(FindFileData.cFileName); + Info = new CAvailableApplicationInfo(FindFileData.cFileName, m_Strings); // set a timestamp for the next time Info->SetLastWriteTime(&FindFileData.ftLastWriteTime); @@ -380,7 +421,7 @@ skip_if_cached: || EnumType == ENUM_ALL_AVAILABLE || (EnumType == ENUM_CAT_SELECTED && Info->m_IsSelected)) { - Info->RefreshAppInfo(); + Info->RefreshAppInfo(m_Strings); if (lpEnumProc) lpEnumProc(Info, m_Strings.szAppsPath.GetString(), param); diff --git a/base/applications/rapps/gui.cpp b/base/applications/rapps/gui.cpp index a1d1e0075c8..e8d2e4057de 100644 --- a/base/applications/rapps/gui.cpp +++ b/base/applications/rapps/gui.cpp @@ -17,15 +17,48 @@ #include #include +#include #include #include #include #include +#include +#include + +using namespace Gdiplus; #define SEARCH_TIMER_ID 'SR' #define LISTVIEW_ICON_SIZE 24 #define TREEVIEW_ICON_SIZE 24 +// default broken-image icon size +#define BROKENIMG_ICON_SIZE 96 + +// the boundary of w/h ratio of snapshot preview window +#define SNPSHT_MAX_ASPECT_RAT 2.5 + +// padding between snapshot preview and richedit (in pixel) +#define INFO_DISPLAY_PADDING 10 + +// minimum width of richedit +#define RICHEDIT_MIN_WIDTH 160 + +enum SNPSHT_STATUS +{ + SNPSHTPREV_EMPTY, // show nothing + SNPSHTPREV_LOADING, // image is loading (most likely downloading) + SNPSHTPREV_FILE, // display image from a file + SNPSHTPREV_FAILED // image can not be shown (download failure or wrong image) +}; + +#define TIMER_LOADING_ANIMATION 1 // Timer ID + +#define LOADING_ANIMATION_PERIOD 3 // Animation cycling period (in seconds) +#define LOADING_ANIMATION_FPS 18 // Animation Frame Per Second + + +#define PI 3.1415927 + INT GetSystemColorDepth() { DEVMODEW pDevMode; @@ -257,6 +290,322 @@ public: } }; +class CAppSnapshotPreview : + public CWindowImpl +{ +private: + + SNPSHT_STATUS SnpshtPrevStauts = SNPSHTPREV_EMPTY; + Image* pImage = NULL; + HICON hBrokenImgIcon = NULL; + BOOL bLoadingTimerOn = FALSE; + int LoadingAnimationFrame = 0; + int BrokenImgSize = BROKENIMG_ICON_SIZE; + + BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) + { + theResult = 0; + switch (Msg) + { + case WM_CREATE: + hBrokenImgIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0); + break; + case WM_SIZE: + { + if (BrokenImgSize != min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE)) + { + BrokenImgSize = min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE); + + if (hBrokenImgIcon) + { + DeleteObject(hBrokenImgIcon); + hBrokenImgIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0); + } + } + break; + } + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(&ps); + CRect rect; + GetClientRect(&rect); + + PaintOnDC(hdc, + rect.Width(), + rect.Height(), + ps.fErase); + + EndPaint(&ps); + break; + } + case WM_PRINTCLIENT: + { + if (lParam & PRF_CHECKVISIBLE) + { + if (!IsWindowVisible()) break; + } + CRect rect; + GetClientRect(&rect); + + PaintOnDC((HDC)wParam, + rect.Width(), + rect.Height(), + lParam & PRF_ERASEBKGND); + break; + } + case WM_ERASEBKGND: + { + return TRUE; // do not erase to avoid blinking + } + case WM_TIMER: + { + switch (wParam) + { + case TIMER_LOADING_ANIMATION: + LoadingAnimationFrame++; + LoadingAnimationFrame %= (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS); + HDC hdc = GetDC(); + CRect rect; + GetClientRect(&rect); + + PaintOnDC(hdc, + rect.Width(), + rect.Height(), + TRUE); + ReleaseDC(hdc); + } + break; + } + case WM_DESTROY: + { + PreviousDisplayCleanup(); + DeleteObject(hBrokenImgIcon); + hBrokenImgIcon = NULL; + break; + } + } + return FALSE; + } + + VOID SetStatus(SNPSHT_STATUS Status) + { + SnpshtPrevStauts = Status; + } + + VOID PaintOnDC(HDC hdc, int width, int height, BOOL bDrawBkgnd) + { + // use an off screen dc to avoid blinking + HDC hdcMem = CreateCompatibleDC(hdc); + HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height); + SelectObject(hdcMem, hBitmap); + + if (bDrawBkgnd) + { + HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcMem, (HGDIOBJ)GetSysColorBrush(COLOR_BTNFACE)); + PatBlt(hdcMem, 0, 0, width, height, PATCOPY); + SelectObject(hdcMem, hOldBrush); + } + + switch (SnpshtPrevStauts) + { + case SNPSHTPREV_EMPTY: + { + + } + break; + + case SNPSHTPREV_LOADING: + { + Graphics graphics(hdcMem); + Color color(255, 0, 0); + SolidBrush dotBrush(Color(255, 100, 100, 100)); + + graphics.SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias); + + // Paint three dot + float DotWidth = GetLoadingDotWidth(width, height); + graphics.FillEllipse((Brush*)(&dotBrush), + (REAL)width / 2.0 - min(width, height) * 2.0 / 16.0 - DotWidth / 2.0, + (REAL)height / 2.0 - GetFrameDotShift(LoadingAnimationFrame + LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0, + DotWidth, + DotWidth); + + graphics.FillEllipse((Brush*)(&dotBrush), + (REAL)width / 2.0 - DotWidth / 2.0, + (REAL)height / 2.0 - GetFrameDotShift(LoadingAnimationFrame, width, height) - DotWidth / 2.0, + DotWidth, + DotWidth); + + graphics.FillEllipse((Brush*)(&dotBrush), + (REAL)width / 2.0 + min(width, height) * 2.0 / 16.0 - DotWidth / 2.0, + (REAL)height / 2.0 - GetFrameDotShift(LoadingAnimationFrame - LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0, + DotWidth, + DotWidth); + } + break; + + case SNPSHTPREV_FILE: + { + if (pImage) + { + // always draw entire image inside the window. + Graphics graphics(hdcMem); + float ZoomRatio = min(((float)width / (float)pImage->GetWidth()), ((float)height / (float)pImage->GetHeight())); + float ZoomedImgWidth = ZoomRatio * (float)pImage->GetWidth(); + float ZoomedImgHeight = ZoomRatio * (float)pImage->GetHeight(); + + graphics.DrawImage(pImage, + ((float)width - ZoomedImgWidth) / 2.0, ((float)height - ZoomedImgHeight) / 2.0, + ZoomedImgWidth, ZoomedImgHeight); + } + } + break; + + case SNPSHTPREV_FAILED: + { + DrawIconEx(hdcMem, + (width - BrokenImgSize) / 2, + (height - BrokenImgSize) / 2, + hBrokenImgIcon, + BrokenImgSize, + BrokenImgSize, + NULL, + NULL, + DI_NORMAL | DI_COMPAT); + } + break; + } + + // copy the content form off-screen dc to hdc + BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY); + DeleteDC(hdcMem); + DeleteObject(hBitmap); + } + + float GetLoadingDotWidth(int width, int height) + { + return min(width, height) / 20.0; + } + + float GetFrameDotShift(int Frame, int width, int height) + { + return min(width, height) * + (1.0 / 16.0) * + (2.0 / (2.0 - sqrt(3.0))) * + (max(sin((float)Frame * 2 * PI / (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS)), sqrt(3.0) / 2.0) - sqrt(3.0) / 2.0); + } + +public: + static ATL::CWndClassInfo& GetWndClassInfo() + { + DWORD csStyle = CS_VREDRAW | CS_HREDRAW; + static ATL::CWndClassInfo wc = + { + { + sizeof(WNDCLASSEX), + csStyle, + StartWindowProc, + 0, + 0, + NULL, + 0, + LoadCursorW(NULL, IDC_ARROW), + (HBRUSH)(COLOR_BTNFACE + 1), + 0, + L"RAppsSnapshotPreview", + NULL + }, + NULL, NULL, IDC_ARROW, TRUE, 0, _T("") + }; + return wc; + } + + HWND Create(HWND hParent) + { + RECT r = { 0,0,0,0 }; + + return CWindowImpl::Create(hParent, r, L"", WS_CHILD | WS_VISIBLE); + } + + VOID PreviousDisplayCleanup() + { + if (bLoadingTimerOn) + { + KillTimer(TIMER_LOADING_ANIMATION); + bLoadingTimerOn = FALSE; + } + LoadingAnimationFrame = 0; + if (pImage) + { + delete pImage; + pImage = NULL; + } + } + + VOID DisplayEmpty() + { + SetStatus(SNPSHTPREV_EMPTY); + PreviousDisplayCleanup(); + } + + VOID DisplayLoading() + { + SetStatus(SNPSHTPREV_LOADING); + PreviousDisplayCleanup(); + bLoadingTimerOn = TRUE; + SetTimer(TIMER_LOADING_ANIMATION, 1000 / LOADING_ANIMATION_FPS, 0); + } + + BOOL DisplayFile(LPCWSTR lpszFileName) + { + SetStatus(SNPSHTPREV_FILE); + PreviousDisplayCleanup(); + pImage = Bitmap::FromFile(lpszFileName, 0); + if (pImage->GetLastStatus() != Ok) + { + DisplayFailed(); + return FALSE; + } + return TRUE; + } + + VOID DisplayFailed() + { + SetStatus(SNPSHTPREV_FAILED); + PreviousDisplayCleanup(); + } + + int GetRequestedWidth(int Height) // calculate requested window width by given height + { + switch (SnpshtPrevStauts) + { + case SNPSHTPREV_EMPTY: + return 0; + case SNPSHTPREV_LOADING: + return 200; + case SNPSHTPREV_FILE: + if (pImage) + { + // return the width needed to display image inside the window. + // and always keep window w/h ratio inside [ 1/SNPSHT_MAX_ASPECT_RAT, SNPSHT_MAX_ASPECT_RAT ] + return (int)floor((float)Height * + max(min((float)pImage->GetWidth() / (float)pImage->GetHeight(), (float)SNPSHT_MAX_ASPECT_RAT), 1.0/ (float)SNPSHT_MAX_ASPECT_RAT)); + } + return 0; + case SNPSHTPREV_FAILED: + return 200; + default: + return 0; + } + } + + ~CAppSnapshotPreview() + { + PreviousDisplayCleanup(); + } +}; + class CAppInfoDisplay : public CUiWindow> { @@ -272,16 +621,18 @@ private: { RichEdit = new CAppRichEdit(); RichEdit->Create(hwnd); + + SnpshtPrev = new CAppSnapshotPreview(); + SnpshtPrev->Create(hwnd); break; } case WM_SIZE: { - ::MoveWindow(RichEdit->m_hWnd, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), TRUE); + ResizeChildren(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; } case WM_COMMAND: { - OnCommand(wParam, lParam); break; } @@ -304,9 +655,94 @@ private: return FALSE; } + VOID ResizeChildren() + { + CRect rect; + GetWindowRect(&rect); + ResizeChildren(rect.Width(), rect.Height()); + } + + VOID ResizeChildren(int Width, int Height) + { + int SnpshtWidth = SnpshtPrev->GetRequestedWidth(Height); + + // make sure richedit always have room to display + SnpshtWidth = min(SnpshtWidth, Width - INFO_DISPLAY_PADDING - RICHEDIT_MIN_WIDTH); + + DWORD dwError = ERROR_SUCCESS; + HDWP hDwp = BeginDeferWindowPos(2); + + if (hDwp) + { + hDwp = ::DeferWindowPos(hDwp, SnpshtPrev->m_hWnd, NULL, + 0, 0, SnpshtWidth, Height, 0); + + if (hDwp) + { + // hide the padding if snapshot window width == 0 + int RicheditPosX = SnpshtWidth ? (SnpshtWidth + INFO_DISPLAY_PADDING) : 0; + + hDwp = ::DeferWindowPos(hDwp, RichEdit->m_hWnd, NULL, + RicheditPosX, 0, Width - RicheditPosX, Height, 0); + + if (hDwp) + { + EndDeferWindowPos(hDwp); + } + else + { + dwError = GetLastError(); + } + } + else + { + dwError = GetLastError(); + } + } + else + { + dwError = GetLastError(); + } + + +#if DBG + ATLASSERT(dwError == ERROR_SUCCESS); +#endif + + UpdateWindow(); + } + + VOID OnLink(ENLINK* Link) + { + switch (Link->msg) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + if (pLink) HeapFree(GetProcessHeap(), 0, pLink); + + pLink = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, + (max(Link->chrg.cpMin, Link->chrg.cpMax) - + min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR)); + if (!pLink) + { + /* TODO: Error message */ + return; + } + + RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax); + RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM)pLink); + + ShowPopupMenuEx(m_hWnd, m_hWnd, IDR_LINKMENU, -1); + } + break; + } + } + public: CAppRichEdit * RichEdit; + CAppSnapshotPreview * SnpshtPrev; static ATL::CWndClassInfo& GetWndClassInfo() { @@ -336,51 +772,38 @@ public: { RECT r = { 0,0,0,0 }; - return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE); + return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); } BOOL ShowAvailableAppInfo(CAvailableApplicationInfo* Info) { + ATL::CStringW SnapshotFilename; + if (Info->RetrieveSnapshot(0, SnapshotFilename)) + { + SnpshtPrev->DisplayFile(SnapshotFilename); + } + else + { + SnpshtPrev->DisplayEmpty(); + } + ResizeChildren(); return RichEdit->ShowAvailableAppInfo(Info); } BOOL ShowInstalledAppInfo(PINSTALLED_INFO Info) { + SnpshtPrev->DisplayEmpty(); + ResizeChildren(); return RichEdit->ShowInstalledAppInfo(Info); } VOID SetWelcomeText() { + SnpshtPrev->DisplayEmpty(); + ResizeChildren(); RichEdit->SetWelcomeText(); } - VOID OnLink(ENLINK* Link) - { - switch (Link->msg) - { - case WM_LBUTTONUP: - case WM_RBUTTONUP: - { - if (pLink) HeapFree(GetProcessHeap(), 0, pLink); - - pLink = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, - (max(Link->chrg.cpMin, Link->chrg.cpMax) - - min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR)); - if (!pLink) - { - /* TODO: Error message */ - return; - } - - RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax); - RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM)pLink); - - ShowPopupMenuEx(m_hWnd, m_hWnd, IDR_LINKMENU, -1); - } - break; - } - } - VOID OnCommand(WPARAM wParam, LPARAM lParam) { WORD wCommand = LOWORD(wParam); @@ -1804,8 +2227,12 @@ private: } /* Load icon from file */ - ATL::CStringW szIconPath; - szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString()); + ATL::CStringW szIconPath = szFolderPath; + PathAppendW(szIconPath.GetBuffer(MAX_PATH), L"icons"); + PathAppendW(szIconPath.GetBuffer(), Info->m_szName.GetString()); + PathAddExtensionW(szIconPath.GetBuffer(), L".ico"); + szIconPath.ReleaseBuffer(); + hIcon = (HICON) LoadImageW(NULL, szIconPath.GetString(), IMAGE_ICON, diff --git a/base/applications/rapps/include/available.h b/base/applications/rapps/include/available.h index 5e3a464fc03..e52c1970a30 100644 --- a/base/applications/rapps/include/available.h +++ b/base/applications/rapps/include/available.h @@ -7,6 +7,9 @@ #include "misc.h" + +#define MAX_SNAPSHOT_NUM 16 + enum LicenseType { LICENSE_NONE, @@ -22,6 +25,18 @@ inline BOOL IsLicenseType(INT x) return (x >= LICENSE_MIN && x <= LICENSE_MAX); } +struct AvailableStrings +{ + ATL::CStringW szPath; + ATL::CStringW szCabPath; + ATL::CStringW szAppsPath; + ATL::CStringW szSearchPath; + ATL::CStringW szCabName; + ATL::CStringW szCabDir; + + AvailableStrings(); +}; + struct CAvailableApplicationInfo { INT m_Category; @@ -35,8 +50,9 @@ struct CAvailableApplicationInfo ATL::CStringW m_szSize; ATL::CStringW m_szUrlSite; ATL::CStringW m_szUrlDownload; - ATL::CStringW m_szCDPath; ATL::CSimpleArray m_LanguageLCIDs; + ATL::CSimpleArray m_szSnapshotFilename; + ULONG m_SizeBytes; // Caching mechanism related entries @@ -48,17 +64,17 @@ struct CAvailableApplicationInfo ATL::CStringW m_szInstalledVersion; // Create an object from file - CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam); + CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam, AvailableStrings& m_Strings); // Load all info from the file - VOID RefreshAppInfo(); + VOID RefreshAppInfo(AvailableStrings& m_Strings); BOOL HasLanguageInfo() const; BOOL HasNativeLanguage() const; BOOL HasEnglishLanguage() const; BOOL IsInstalled() const; BOOL HasInstalledVersion() const; BOOL HasUpdate() const; - + BOOL RetrieveSnapshot(UINT Index, ATL::CStringW& SnapshotFileName) const; // Set a timestamp VOID SetLastWriteTime(FILETIME* ftTime); @@ -71,7 +87,7 @@ private: inline BOOL GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString); // Lazily load general info from the file - VOID RetrieveGeneralInfo(); + VOID RetrieveGeneralInfo(AvailableStrings& m_Strings); VOID RetrieveInstalledStatus(); VOID RetrieveInstalledVersion(); VOID RetrieveLanguages(); @@ -82,18 +98,6 @@ private: typedef BOOL(CALLBACK *AVAILENUMPROC)(CAvailableApplicationInfo *Info, LPCWSTR szFolderPath, PVOID param); -struct AvailableStrings -{ - ATL::CStringW szPath; - ATL::CStringW szCabPath; - ATL::CStringW szAppsPath; - ATL::CStringW szSearchPath; - ATL::CStringW szCabName; - ATL::CStringW szCabDir; - - AvailableStrings(); -}; - class CAvailableApps { static AvailableStrings m_Strings; diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index fd914c4c468..50354c0f7ff 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -9,7 +9,6 @@ INT GetClientWindowWidth(HWND hwnd); INT GetClientWindowHeight(HWND hwnd); VOID CopyTextToClipboard(LPCWSTR lpszText); -VOID SetWelcomeText(); VOID ShowPopupMenuEx(HWND hwnd, HWND hwndOwner, UINT MenuID, UINT DefaultItem); VOID ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem); BOOL StartProcess(ATL::CStringW &Path, BOOL Wait); diff --git a/base/applications/rapps/include/resource.h b/base/applications/rapps/include/resource.h index 49dc2105b4f..26b6cf706db 100644 --- a/base/applications/rapps/include/resource.h +++ b/base/applications/rapps/include/resource.h @@ -14,6 +14,7 @@ #define IDI_UPDATE_DB 20 #define IDI_CHECK_ALL 21 #define IDI_SELECTEDFORINST 22 +#define IDI_BROKEN_IMAGE 23 /* Icons for categories */ #define IDI_CAT_AUDIO 50 diff --git a/base/applications/rapps/loaddlg.cpp b/base/applications/rapps/loaddlg.cpp index cc88af6cc16..60f999967cb 100644 --- a/base/applications/rapps/loaddlg.cpp +++ b/base/applications/rapps/loaddlg.cpp @@ -650,8 +650,19 @@ unsigned int WINAPI CDownloadManager::ThreadFunc(LPVOID param) } // append a \ to the provided file system path, and the filename portion from the URL after that - Path += L"\\"; - Path += (LPWSTR) (p + 1); + + PathAddBackslashW(Path.GetBuffer(MAX_PATH)); + switch (InfoArray[iAppId].DLType) + { + case DLTYPE_DBUPDATE: + case DLTYPE_DBUPDATE_UNOFFICIAL: + PathAppendW(Path.GetBuffer(), L"rappmgr.cab"); // whatever the URL is, use the file name L"rappmgr.cab" + break; + case DLTYPE_APPLICATION: + PathAppendW(Path.GetBuffer(), (LPWSTR)(p + 1)); // use the filename retrieved from URL + break; + } + Path.ReleaseBuffer(); if ((InfoArray[iAppId].DLType == DLTYPE_APPLICATION) && InfoArray[iAppId].szSHA1[0] && GetFileAttributesW(Path.GetString()) != INVALID_FILE_ATTRIBUTES) { diff --git a/base/applications/rapps/misc.cpp b/base/applications/rapps/misc.cpp index d0f13cac232..9ee6a391f63 100644 --- a/base/applications/rapps/misc.cpp +++ b/base/applications/rapps/misc.cpp @@ -177,14 +177,15 @@ BOOL StartProcess(LPWSTR lpPath, BOOL Wait) BOOL GetStorageDirectory(ATL::CStringW& Directory) { - if (!SHGetSpecialFolderPathW(NULL, Directory.GetBuffer(MAX_PATH), CSIDL_LOCAL_APPDATA, TRUE)) + LPWSTR DirectoryStr = Directory.GetBuffer(MAX_PATH); + if (!SHGetSpecialFolderPathW(NULL, DirectoryStr, CSIDL_LOCAL_APPDATA, TRUE)) { Directory.ReleaseBuffer(); return FALSE; } + PathAppendW(DirectoryStr, L"rapps"); Directory.ReleaseBuffer(); - Directory += L"\\rapps"; return (CreateDirectoryW(Directory.GetString(), NULL) || GetLastError() == ERROR_ALREADY_EXISTS); } diff --git a/base/applications/rapps/rapps.rc b/base/applications/rapps/rapps.rc index c67e1de5442..5debb642bdd 100644 --- a/base/applications/rapps/rapps.rc +++ b/base/applications/rapps/rapps.rc @@ -23,6 +23,8 @@ IDI_APPUPD ICON "res/appupd.ico" IDI_CATEGORY ICON "res/cat.ico" IDI_UPDATE_DB ICON "res/updatedb.ico" IDI_CHECK_ALL ICON "res/select.ico" +IDI_SELECTEDFORINST ICON "res/select.ico" +IDI_BROKEN_IMAGE ICON "res/brokenimg.ico" /* Categories */ IDI_CAT_AUDIO ICON "res/cats/audio.ico" @@ -41,7 +43,6 @@ IDI_CAT_SCIENCE ICON "res/cats/science.ico" IDI_CAT_TOOLS ICON "res/cats/tools.ico" IDI_CAT_VIDEO ICON "res/cats/video.ico" IDI_CAT_THEMES ICON "res/cats/themes.ico" -IDI_SELECTEDFORINST ICON "res/select.ico" /* Accelerators -- key bindings */ HOTKEYS ACCELERATORS diff --git a/base/applications/rapps/res/brokenimg.ico b/base/applications/rapps/res/brokenimg.ico new file mode 100644 index 00000000000..58454f74041 Binary files /dev/null and b/base/applications/rapps/res/brokenimg.ico differ diff --git a/base/applications/rapps/winmain.cpp b/base/applications/rapps/winmain.cpp index d55edca46fa..ad14c9fd80b 100644 --- a/base/applications/rapps/winmain.cpp +++ b/base/applications/rapps/winmain.cpp @@ -13,6 +13,8 @@ #include +#include + HWND hMainWnd; HINSTANCE hInst; SETTINGS_INFO SettingsInfo; @@ -28,6 +30,10 @@ END_OBJECT_MAP() CRAppsModule gModule; CAtlWinModule gWinModule; +Gdiplus::GdiplusStartupInput gdiplusStartupInput; +ULONG_PTR gdiplusToken; + + static VOID InitializeAtlModule(HINSTANCE hInstance, BOOL bInitialize) { if (bInitialize) @@ -40,6 +46,18 @@ static VOID InitializeAtlModule(HINSTANCE hInstance, BOOL bInitialize) } } +VOID InitializeGDIPlus(BOOL bInitialize) +{ + if (bInitialize) + { + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + } + else + { + Gdiplus::GdiplusShutdown(gdiplusToken); + } +} + VOID FillDefaultSettings(PSETTINGS_INFO pSettingsInfo) { ATL::CStringW szDownloadDir; @@ -129,6 +147,7 @@ INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi BOOL bIsFirstLaunch; InitializeAtlModule(hInstance, TRUE); + InitializeGDIPlus(TRUE); if (GetUserDefaultUILanguage() == MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT)) { @@ -169,6 +188,7 @@ INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi if (hMutex) CloseHandle(hMutex); + InitializeGDIPlus(FALSE); InitializeAtlModule(hInstance, FALSE); return 0;