• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

AEROGLASS - GDImage 64-bit

Started by Patrice Terrier, May 15, 2013, 08:36:00 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Patrice Terrier

AEROGLASS kept me busy for many days...

It is a major step forward into my 64-bit translation of GDImage, because many fondamentals have been done:

  • Child controls anchor within a window.
  • Completion of text and image sprite objects.
  • Alphablending.
  • Mouse handling.
  • User callback to monitor the GDImage control messages.
  • Gradient paint of the main window.
  • Easy support of Windows theme.
  • Screen capture.

Project code source:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <bitset>
using namespace std;
#include <Commctrl.h>
#include <vector>

#pragma comment(lib, "D:\\VS2010\\GDImage64.lib")
#pragma comment(lib, "D:\\VS2010\\GDImage32.lib")
#include "GDImage.h"

//Apply WinXP theme
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#define long_proc typedef long (__stdcall *zProc)

const wchar_t *$Background = L"Background";

#define IDC_STATUSBAR        100
#define IDC_CTRL             101
#define IDC_STA_Help         102
#define IDC_BTN_FIT2GLASS    103
#define IDC_BTN_CHECK        104

// Warning each overlay object must have a UNIQUE IDENTIFIER
#define ID_OBJECT_TEXT       9
#define ID_FIRST             ID_OBJECT_TEXT
#define ID_OBJECT_SPRITE     10
#define ID_AERO_GLASS        11
#define ID_LIGHT             12
#define ID_TWIN              13
#define ID_BRUSH             14
#define ID_LAST              ID_BRUSH

const wchar_t  *BUTTON = L"BUTTON";

// Global variable
HINSTANCE g_hInst; // Current instance

// Declaration of the functions being used in this module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyCallBack(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void ScreenCaptureToBackground(IN HWND hParent);

vector<HBITMAP> g_hBitmap;

HFONT zDefaultFont() {
    static HFONT hDefault;
    if (hDefault == 0) { hDefault = (HFONT) GetStockObject(ANSI_VAR_FONT); }
    return hDefault;
}

void zSetCTLFont(IN HWND hCtrl, IN HFONT hFont) {
    SendMessage(hCtrl, WM_SETFONT, (WPARAM) hFont, 0);
}

long RND (IN long nMin, IN long nMax) {
     return ((rand() * (nMax - nMin + 1) / RAND_MAX)) + nMin;
}

HFONT CaptionFont() {
    LOGFONT lf = {0};
    static HFONT CaptionFont;
    if (CaptionFont == 0) {
       lf.lfHeight = 8;
       lf.lfWidth = 0;
       lf.lfCharSet = DEFAULT_CHARSET;
       lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
       lf.lfClipPrecision = OUT_DEFAULT_PRECIS;
       lf.lfQuality = DEFAULT_QUALITY;
       lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
       lf.lfWeight = 700;
       wcscpy_s(lf.lfFaceName, L"MS Sans Serif");
       CaptionFont = CreateFontIndirect(&lf);
    }
    return CaptionFont;
}

void GetSpriteFromFile(IN HWND hCtrl) {
    RECT rc;
    long K, x = 0, y = 0, bmW = 0, bmH = 0;

    if (g_hBitmap.size() == 0) { g_hBitmap.resize(4); }
    g_hBitmap[0] = ZI_CreateBitmapFromFile(L"aero.png", bmW, bmH);
    g_hBitmap[1] = ZI_CreateBitmapFromFile(L"light.png", bmW, bmH);
    g_hBitmap[2] = ZI_CreateBitmapFromFile(L"twin.png", bmW, bmH);
    g_hBitmap[3] = ZI_CreateBitmapFromFile(L"paintbrush.png", bmW, bmH);

    GetClientRect(hCtrl, &rc);
    // Use random location to draw the sprites 
    long UB = (long) g_hBitmap.size();
    for (K = 0; K < UB; ++K) {
        x = RND(0, rc.right); y = RND(0, rc.bottom);
        ZD_DrawBitmapToCtrl(hCtrl, x, y, g_hBitmap[K], 0xFFFFFFFF, ID_OBJECT_SPRITE + K + 1, ZS_VISIBLE);
    }
   
    // Add a label for each of the sprite that will be shown on the status bar,
    // and anchor them to scroll together with the background.
    // Note: ZD_SetObjectLocked is meant to enable/disable user interaction on sprite.
    ZD_SetObjectImageLabel(ID_AERO_GLASS, L"Aero glass");
    ZD_SetObjectScroll(ID_AERO_GLASS, TRUE);

    ZD_SetObjectImageLabel(ID_LIGHT, L"Light");
    ZD_SetObjectScroll(ID_LIGHT, TRUE);
   
    ZD_SetObjectImageLabel(ID_TWIN, L"Twin");
    ZD_SetObjectScroll(ID_TWIN, TRUE);
   
    ZD_SetObjectImageLabel(ID_BRUSH, L"Paint brush");
    ZD_SetObjectScroll(ID_BRUSH, TRUE);
}

void AddChildControls (IN HWND hWnd) {
    HWND hCtrl;
    wstring sLabel;

         // ******************************************************************************
         // Helper function to create A GDImage control (with automatic scrollbar support)
         // ------------------------------------------------------------------------------
         hCtrl = ZI_CreateWindow(hWnd, 10, 10, 580, 400, (HMENU) IDC_CTRL);
         // Set the correct anchor property
         ZI_SetAnchorMode(hCtrl, ANCHOR_HEIGHT_WIDTH);
         // We use a callback to monitor the GDImage control messages
         // Create a WM_LBUTTONDOWN event
         ZI_EventMessage((LONG_PTR) MyCallBack, WM_LBUTTONDOWN, TRUE);
         // Create a WM_RBUTTONDOWN event
         ZI_EventMessage((LONG_PTR) MyCallBack, WM_RBUTTONDOWN, TRUE);
         // Create a WM_KEYDOWN event
         ZI_EventMessage((LONG_PTR) MyCallBack, WM_KEYDOWN, TRUE);
         // Create a WM_MOUSEMOVE event
         ZI_EventMessage((LONG_PTR) MyCallBack, WM_MOUSEMOVE, TRUE);

         // We capture the desktop to set the image background
         ScreenCaptureToBackground(hWnd);

         // Add the sprites
         GetSpriteFromFile(hCtrl);

         // Draw overlayed text in a GDImage Control
         // ******************************************************************************
         // Require the use of True Type Font name (TTF).
         // This type of overlay doesn't alter the image shown in the background.
         // ------------------------------------------------------------------------------
         // Draw Text overlay
         // Note: because this is the last object it is drawn on top of z-order
         ZD_DrawTextToCtrl(GetDlgItem(hWnd, IDC_CTRL), L"GDImage Aero Glass", 20, 20, ZD_ColorARGB(200,RGB(250,250,255)),
                           L"Times New Roman", 40,               // The font size in pixel
                           ID_OBJECT_TEXT,                       // The unique object ID
                           ZS_VISIBLE,                           // Show overlay
                           1,                                    // shadow effect (offset in pixel)
                           NULL);
         ZD_SetObjectScroll(ID_OBJECT_TEXT, TRUE);

         // ******************************************************************************
         // Create static help comment
         // ------------------------------------------------------------------------------
         sLabel = L"\n\nMouse use:\n\nLeft button\nto drag the object.\n\nRight button\ncusor coordinates.\n\n"
                  L"You can create events to monitor the message flow.\n___________\n\nKeyboard input:\n\n"
                  L"You can use all navigation keys.\n\nAltogether with SHIFT or CTRL or CTRL+SHIFT for faster move.";
                 
         CreateWindowEx(0, L"STATIC", (WCHAR*) sLabel.c_str(), WS_CHILD | WS_VISIBLE | SS_CENTER,
                        740 - (7 + 120 + 8), 10, 120, 340, hWnd, (HMENU) IDC_STA_Help, zInstance(), NULL);
         zSetCTLFont(GetDlgItem(hWnd, IDC_STA_Help), CaptionFont());
         // Set the correct anchor property
         ZI_SetAnchorMode(GetDlgItem(hWnd, IDC_STA_Help), ANCHOR_RIGHT);

         // ******************************************************************************
         // Create the "Center" button
         // ------------------------------------------------------------------------------
         CreateWindowEx(0, BUTTON, L"Fit sprites to glass", WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                        740 - (7 + 120 + 8), 390, 120, 22, hWnd, (HMENU) IDC_BTN_FIT2GLASS, zInstance(), NULL);
         zSetCTLFont(GetDlgItem(hWnd, IDC_BTN_FIT2GLASS), zDefaultFont());
         // Set the correct anchor property
         ZI_SetAnchorMode(GetDlgItem(hWnd, IDC_BTN_FIT2GLASS), ANCHOR_BOTTOM_RIGHT);

         CreateWindowEx(0, BUTTON, L"Glue sprites together", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX | BS_LEFT | BS_VCENTER,
                        740 - (7 + 120 + 8), 361, 120, 22, hWnd, (HMENU) IDC_BTN_CHECK, zInstance(), NULL);
         zSetCTLFont(GetDlgItem(hWnd, IDC_BTN_CHECK), zDefaultFont());
         // Set the correct anchor property
         ZI_SetAnchorMode(GetDlgItem(hWnd, IDC_BTN_CHECK), ANCHOR_BOTTOM_RIGHT);

         // ******************************************************************************
         // Create the status bar
         // ------------------------------------------------------------------------------
         hCtrl = CreateWindowEx(0, L"msctls_statusbar32", L"", WS_CHILD | WS_VISIBLE | SBARS_TOOLTIPS | SBARS_SIZEGRIP,
                                0, 423, 740, 23, hWnd, (HMENU) IDC_STATUSBAR, zInstance(), NULL);
         zSetCTLFont(hCtrl, zDefaultFont());

}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {

     UNREFERENCED_PARAMETER(hPrevInstance);
     UNREFERENCED_PARAMETER(lpCmdLine);

     // Declaration of local variables
     MSG msg;
     int nRet = 0;
     int IsInitialized = 0;
     WNDCLASSEX wcx;
     WCHAR szWindowClass[] = L"ZMAIN";
     wstring sCaption;
     sCaption = L"Aeroglass - GDImage ";
     if (sizeof(LONG_PTR) == 8) {
         sCaption += L"64-bit"; }
     else {
         sCaption += L"32-bit";
     }

     // Register the main window class
     wcx.cbSize = sizeof(WNDCLASSEX);
     IsInitialized = GetClassInfoEx(hInstance, szWindowClass, &wcx);
     if (!IsInitialized) {
        wcx.style         = CS_HREDRAW | CS_VREDRAW;
        wcx.lpfnWndProc   = WndProc;
        wcx.cbClsExtra    = 0;
        wcx.cbWndExtra    = 0;
        wcx.hInstance     = hInstance;
        wcx.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
        wcx.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wcx.lpszMenuName  = NULL;
        wcx.lpszClassName = szWindowClass;
        wcx.hIconSm       = wcx.hIcon;
        if (RegisterClassEx(&wcx)) { IsInitialized = -1; }
     }

     if (IsInitialized) {

        nRet = ZI_LoadDLL (L"");

        g_hInst = hInstance; // Stocke le handle d'instance dans la variable globale

        UINT dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
        UINT dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
        RECT rc;
        SetRect(&rc, 0, 0, 740, 443);
        AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);    // Adjust Window To True Requested Size
        int x = max((GetSystemMetrics(SM_CXSCREEN) - rc.right - rc.left) / 2, 0);
        int y = max((GetSystemMetrics(SM_CYSCREEN) - rc.bottom - rc.top) / 2, 0);
        HWND hWnd = CreateWindowEx(
                    dwExStyle,                      // Optional window styles.
                    szWindowClass,                  // Window class
                    (WCHAR*) sCaption.c_str(),      // Window text
                    dwStyle,                        // Window style
                    x, y, rc.right - rc.left, rc.bottom - rc.top,
                    NULL,                           // Parent window   
                    NULL,                           // Menu
                    hInstance,                      // Instance handle
                    NULL                            // Additional application data
                    );
   
        if (hWnd) {

           // Populate the window with all child controls
           AddChildControls(hWnd);

           ShowWindow(hWnd, nCmdShow);
           UpdateWindow(hWnd);
           SetFocus(GetDlgItem(hWnd, IDC_CTRL));

           // Main message loop
           while (GetMessage(&msg, NULL, 0, 0)) {
               TranslateMessage(&msg);
               DispatchMessage(&msg);
           }
           nRet = (int) msg.wParam;
        }

     }

     return nRet;
}

// Main winproc
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    int wmId = 0, wmEvent = 0;
    PAINTSTRUCT ps; RECT rc; HWND hCtrl = 0;
    WCHAR sFullName[MAX_PATH] = {0};
    MINMAXINFO* MinMax;
    wstring sLabel;
    HPEN hPen = 0, OldPen = 0;
    long K = 0, x = 0, y = 0, UB = 0, xCurrentScroll = 0, yCurrentScroll = 0, BoundWidthGlass = 0, BoundHeightGlass = 0,
         BoundWidth = 0, BoundHeight = 0, xGlass = 0, yGlass = 0;

    switch (uMsg) {

    case WM_GETMINMAXINFO: // Set minimum/maximum dialog size
         MinMax = (MINMAXINFO*) lParam;
         SetRect(&rc, 0, 0, 740, 443);
         AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE) ;  // Adjust Window To True Requested Size
         MinMax->ptMinTrackSize.x = rc.right - rc.left;
         MinMax->ptMinTrackSize.y = rc.bottom - rc.top;
         return 0;

    case WM_CTLCOLORSTATIC:
         switch(GetDlgCtrlID((HWND) lParam)) {
         case IDC_STA_Help:
              SetTextColor((HDC) wParam, RGB(2,77,220));
              SetBkMode((HDC) wParam, TRANSPARENT);
              // Use a custom background for the help control
              GetClientRect(GetDlgItem(hWnd, IDC_STA_Help), &rc);
              ZI_GradientPaintDC((HDC) wParam, 0, 0, rc.right, rc.bottom, RGB(228,227,255), RGB(188,187,211));
              hPen = CreatePen(0, 1, RGB(128,128,192));
              OldPen = (HPEN) SelectObject((HDC) wParam, hPen);
              SelectObject((HDC) wParam, GetStockObject(NULL_BRUSH));
              RoundRect((HDC) wParam, 0, 0, rc.right, rc.bottom, 8, 8);
              SelectObject((HDC) wParam, OldPen);
              DeleteObject(hPen);
              return 0;

         case IDC_BTN_CHECK:
              SetBkMode((HDC) wParam, TRANSPARENT);
              // Use a custom background for the Check control
              GetClientRect(GetDlgItem(hWnd, IDC_BTN_CHECK), &rc);
              ZI_GradientPaintDC((HDC) wParam, 0, 0, rc.right, rc.bottom, RGB(179,178,198), RGB(176,175,196));
              SelectObject((HDC) wParam, GetStockObject(NULL_BRUSH));
              return 0;
         }
         break;

    case WM_SIZE:
         if (wParam != SIZE_MINIMIZED) {
             // Move Status bar
             hCtrl = GetDlgItem(hWnd, IDC_STATUSBAR);
             if (hCtrl) { SendMessage(hCtrl, uMsg, wParam, lParam); }
         }
         break;

    case WM_COMMAND:
         wmId    = LOWORD(wParam);
         wmEvent = HIWORD(wParam);
         // Process menu command
         switch (wmId) {

         case IDC_BTN_FIT2GLASS:
              hCtrl = GetDlgItem(hWnd, IDC_CTRL);
              GetClientRect(hCtrl, &rc);
              xCurrentScroll = (long) ZI_GetProperty(hCtrl, ZI_Horizontal);
              yCurrentScroll = (long) ZI_GetProperty(hCtrl, ZI_Vertical);

              // ID_AERO_GLASS
              ZD_GetObjectBound(ID_AERO_GLASS, BoundWidthGlass, BoundHeightGlass);
              xGlass = max((rc.right - BoundWidthGlass) / 2 + xCurrentScroll, 0);
              yGlass = max((rc.bottom - BoundHeightGlass) / 2 + yCurrentScroll, 0);
              ZD_SetObjectXY(ID_AERO_GLASS, xGlass, yGlass, ZD_DRAW_DEFERRED);   // Move, without immediate redraw

              // ID_OBJECT_TEXT
              ZD_GetObjectBound(ID_OBJECT_TEXT, BoundWidth, BoundHeight);
              x = xGlass + max((BoundWidthGlass - BoundWidth) / 2, 0);
              y = yGlass + 10;   // max((BoundHeightGlass& - BoundHeight) \ 2, 0)
              ZD_SetObjectXY(ID_OBJECT_TEXT, x, y, ZD_DRAW_DEFERRED);   // Move, without immediate redraw

              // ID_LIGHT
              ZD_GetObjectBound(ID_LIGHT, BoundWidth, BoundHeight);
              x = (long) (xGlass + BoundWidthGlass - (BoundWidth * 0.56f));
              y = (long) (yGlass - (BoundHeight / 3.0f));
              ZD_SetObjectXY(ID_LIGHT, x, y, ZD_DRAW_DEFERRED);   // Move, without immediate redraw

              // ID_TWIN
              ZD_GetObjectBound(ID_TWIN, BoundWidth, BoundHeight);
              x = xGlass - 20;
              y = (long) (yGlass + BoundHeightGlass - (BoundHeight * 0.95f));
              ZD_SetObjectXY(ID_TWIN, x, y, ZD_DRAW_DEFERRED);   // Move, without immediate redraw

              // ID_BRUSH
              ZD_GetObjectBound(ID_BRUSH, BoundWidth, BoundHeight);
              x = (long) (xGlass + max((BoundWidthGlass - BoundWidth * 0.95f) / 2, 0));
              y = (long) (yGlass + max((BoundHeightGlass - BoundHeight * 1.10f) / 2, 0));
              ZD_SetObjectXY(ID_BRUSH, x, y, ZD_DRAW_DEFERRED); // Move, without immediate redraw

              // Request GDImage to update the control display to show the changes
              ZI_UpdateWindow(hCtrl, 0);
              break;

         }
         return 0;

    case WM_ERASEBKGND:
         GetClientRect (hWnd, &rc);
         ZI_GradientPaintDC((HDC) wParam, 0, 0, rc.right, rc.bottom, RGB(228,227,227), RGB(168,167,191));
         return 1;

    case WM_PAINT:
         BeginPaint(hWnd, &ps);
         EndPaint(hWnd, &ps);
         return 0;

    case WM_DESTROY:
         UB = (long) g_hBitmap.size();
         for (K = 0; K < UB; ++K) {
             DeleteObject(g_hBitmap[K]);
         }
         PostQuitMessage(0);
         return 0;
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

void ScreenCaptureToBackground(IN HWND hParent) {
    long SysXRes = GetSystemMetrics(SM_CXSCREEN);
    long SysYRes = GetSystemMetrics(SM_CYSCREEN);
    HWND hCtrl = GetDlgItem(hParent, IDC_CTRL);
    ZI_CreateImageBackground(hCtrl, SysXRes, SysYRes);
    HWND hDeskTop = GetDesktopWindow();
    HDC hDCSrce = GetWindowDC(hDeskTop);
    BitBlt(ZI_GetDC(hCtrl), 0, 0, SysXRes, SysYRes, hDCSrce, 0, 0, SRCCOPY);
    ReleaseDC(hDeskTop, hDCSrce);
}

LRESULT CALLBACK MyCallBack(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    LRESULT nRet = 0; // Do not stop the event processing in GDImage
    RECT rc;
    long ObjectID = 0, x = 0, y = 0, x1 = 0, y1 = 0, x2Div2 = 0, y2Div2 = 0, BoundWidth = 0, BoundHeight = 0, useWidth = 0, useHeight = 0,
         UseStep = 0, xCapture = 0, yCapture = 0, Dx = 0, Dy = 0, xCurrentScroll = 0, yCurrentScroll = 0, ID;
    wstring sMsg, sLabel;
    HWND hParent = GetParent(hWnd);
   
    if (hWnd == GetDlgItem(hParent, IDC_CTRL)) { // In case we use the same callback for several GDImage control
                                                 // make sure that we handle the good one.
       switch (uMsg) {

       case WM_LBUTTONDOWN:
            ObjectID = ZI_MouseOverObjectID();
            if (ObjectID == GetDlgCtrlID(hWnd)) {
                sLabel = $Background; }
            else {
                sLabel = ZD_GetObjectImageLabel(ObjectID);
            }
            sMsg = L"WM_LBUTTONDOWN on object "; sMsg += STRL$(ObjectID); sMsg += L" >", sMsg += sLabel; sMsg += L"<";
            SetWindowText(GetDlgItem(hParent, IDC_STATUSBAR), (WCHAR*) sMsg.c_str());
            break;

       case WM_RBUTTONDOWN:
            ObjectID = ZI_MouseOverObjectID();
            if (ObjectID == GetDlgCtrlID(hWnd)) {
                sLabel = $Background; }
            else {
                sLabel = ZD_GetObjectImageLabel(ObjectID);
            }
            sMsg = L"WM_RBUTTONDOWN on object "; sMsg += STRL$(ObjectID); sMsg += L" >";
            sMsg += sLabel; sMsg += L"< at location "; sMsg += STRL$(LOWORD(lParam)); sMsg += L","; sMsg += STRL$(HIWORD(lParam));
            SetWindowText(GetDlgItem(hParent, IDC_STATUSBAR), (WCHAR*) sMsg.c_str());
            break;

       case WM_MOUSEMOVE:
            ObjectID = ZI_GetMovingSpriteID();
            if (ObjectID) {
               // Does ID_BTN_CHECK is checked ?
               if (SendMessage(GetDlgItem(GetParent(hWnd), IDC_BTN_CHECK), BM_GETCHECK, 0, 0)) {
                  x = LOWORD(lParam); y = HIWORD(lParam);
                  xCurrentScroll = (long) ZI_GetProperty(hWnd, ZI_Horizontal);
                  yCurrentScroll = (long) ZI_GetProperty(hWnd, ZI_Vertical);
                  ZD_GetObjectXY(ObjectID, x1, y1);
                  ZD_GetObjectXYcapture(ObjectID, xCapture, yCapture);
                  Dx = (x - xCapture + xCurrentScroll) - x1;
                  Dy = (y - yCapture + yCurrentScroll) - y1;
                  for (ID = ID_FIRST; ID <= ID_LAST; ++ID) {
                      if (ID != ObjectID) {
                         ZD_GetObjectXY(ID, x, y);
                         // Add the DX,DY offset
                         ZD_SetObjectXY(ID, x + Dx, y + Dy, ZD_DRAW_DEFERRED); // Move, without immediate redraw
                      }
                  }
                  // Note: The display's refresh is yeld by the default GDImage WM_MOUSE event
               }
            }
            break;

       case WM_KEYDOWN:
            ObjectID = ZI_GetObjectFocusID();
            if (ObjectID) {
               ZD_GetObjectXY(ObjectID, x, y);
               x1 = x; // Make a copy to keep the orignal x location unchanged
               y1 = y; // Make a copy to keep the orignal y location unchanged

               // Check accelerator keys to compute the step range
               if (ZI_IsCtrlKeyPressed()) {
                  UseStep = 4; if (ZI_IsShiftKeyPressed()) { UseStep = 16; }
                  }
               else if (ZI_IsShiftKeyPressed()) {
                  UseStep = 2; }
               else {
                  UseStep = 1;
               }
               
               if (ZD_GetObjectScroll(ObjectID)) { // if (object scroll with the bitmap background
                  // Get the size of the bitmap background
                   ZI_GetBitmapSize(ZI_GetBMP(GetDlgItem(hParent, IDC_CTRL)), useWidth, useHeight); }
               else {
                  // Get the control client size 
                   GetClientRect(GetDlgItem(hParent, IDC_CTRL), &rc);
                   useWidth = rc.right; useHeight = rc.bottom;
               }
               // Get the sprite object size
               ZD_GetObjectBound(ObjectID, BoundWidth, BoundHeight);

               x2Div2 = BoundWidth / 2; y2Div2 = BoundHeight / 2;
               switch (wParam) {
               case VK_HOME:
                    x1 = 0;
                    break;

               case VK_END:
                    x1 = max(useWidth - BoundWidth, 0);
                    break;

               case VK_PRIOR:
                    y1 = 0;
                    break;

               case VK_NEXT:
                    y1 = max(useHeight - BoundHeight, 0);
                    break;

               case VK_LEFT:
               case VK_NUMPAD4:
                    if (x1 > -x2Div2) { x1 = max(x1 - UseStep, -x2Div2); }
                    break;

               case VK_UP:
               case VK_NUMPAD8:
                    if (y1 > -y2Div2) { y1 = max(y1 - UseStep, -y2Div2); }
                    break;

               case VK_RIGHT:
               case VK_NUMPAD6:
                    if (x1 < useWidth - x2Div2) { x1 = min(x1 + UseStep, useWidth - x2Div2); }
                    break;

               case VK_DOWN:
               case VK_NUMPAD2:
                    if (y1 < useHeight - y2Div2) {
                       y1 = min(y1 + UseStep, useHeight - y2Div2);
                    }
                    break;
               }
               
               if ((x != x1) || (y != y1)) {
                  x = x1; y = y1; ZD_SetObjectXY(ObjectID, x1, y1, TRUE);
               }
               sMsg = L"Object "; sMsg += STRL$(ObjectID); sMsg += L" coordinates";
               sMsg += STRL$(x); sMsg += L","; sMsg += STRL$(y);
               SetWindowText(GetDlgItem(hParent, IDC_STATUSBAR), (WCHAR*) sMsg.c_str());
               
            }
            break;
       }
    }

    return nRet;
}


The attached ZIP file comprise only the 64-bit version.
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com