https://git.reactos.org/?p=reactos.git;a=commitdiff;h=fbf119fde1fe00f0e5920070d37b73983c7cfb5a

commit fbf119fde1fe00f0e5920070d37b73983c7cfb5a
Author:     He Yang <[email protected]>
AuthorDate: Sun Jun 14 21:22:58 2020 +0800
Commit:     Mark Jansen <[email protected]>
CommitDate: Sun Sep 6 17:09:19 2020 +0200

    [RAPPS] Screenshot preview and other trivial fixes (#2894)
    
    * add one more layer of window
    * using Path* API to operate path
    * always use rappmgr.cab as file name when downloading db. ignore the URL
    * add snapshot preview window
    * show a broken-image icon when failed to load image
    * add a padding between image and content, and make sure always some space 
is reserved for richedit
    * hide the padding if snapshot window does not have a width
    * some work to avoid blinking when window resizing
    * add WM_PRINTCLIENT handling
---
 base/applications/rapps/CMakeLists.txt      |   2 +-
 base/applications/rapps/available.cpp       |  67 +++-
 base/applications/rapps/gui.cpp             | 491 ++++++++++++++++++++++++++--
 base/applications/rapps/include/available.h |  38 ++-
 base/applications/rapps/include/misc.h      |   1 -
 base/applications/rapps/include/resource.h  |   1 +
 base/applications/rapps/loaddlg.cpp         |  15 +-
 base/applications/rapps/misc.cpp            |   5 +-
 base/applications/rapps/rapps.rc            |   3 +-
 base/applications/rapps/res/brokenimg.ico   | Bin 0 -> 152126 bytes
 base/applications/rapps/winmain.cpp         |  20 ++
 11 files changed, 574 insertions(+), 69 deletions(-)

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 <atlstr.h>
 
  // 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 <atlbase.h>
 #include <atlcom.h>
+#include <atltypes.h>
 #include <atlwin.h>
 #include <wininet.h>
 #include <shellutils.h>
 #include <rosctrls.h>
+#include <gdiplus.h>
+#include <math.h>
+
+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<CAppSnapshotPreview>
+{
+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<CWindowImpl<CAppInfoDisplay>>
 {
@@ -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<LCID> m_LanguageLCIDs;
+    ATL::CSimpleArray<ATL::CStringW> 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 <atlcom.h>
 
+#include <gdiplus.h>
+
 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;

Reply via email to