//+--------------------------------------------------------------------------+
//|                                                                          |
//|                               HUDcontrols                                |
//|                                                                          |
//|                         GDImage/WinLIFT project                          |
//|                                                                          |
//+--------------------------------------------------------------------------+
//|                                                                          |
//|                         Author Patrice TERRIER                           |
//|                            copyright(c) 2013                             |
//|                           www.zapsolution.com                            |
//|                        pterrier@zapsolution.com                          |
//|                                                                          |
//+--------------------------------------------------------------------------+
//|                  Project started on : 11-01-2013 (MM-DD-YYYY)            |
//|                        Last revised : 11-23-2013 (MM-DD-YYYY)            |
//+--------------------------------------------------------------------------+

#include "stdafx.h"
#include <windows.h>
#include <Commctrl.h>
#include <vector>
#include <time.h>

//#include "zTrace.h"   // Search in the same directory of the current file.
#pragma comment(lib, "..\\GDImage64.lib")
#pragma comment(lib, "..\\GDImage32.lib")
#include "GDImage.h"
#pragma comment(lib, "..\\WinLIFT64.lib")
#pragma comment(lib, "..\\WinLIFT32.lib")
#include "WinLIFT.h"  // Search in the same directory of the current file.

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

#define LOINT(a) ((SHORT)(a))
#define HIINT(a) ((SHORT)(((DWORD)(a) >> 16) & 0xFFFF))
#define MAKLNG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16)) 
#define UBOUND(T) (T.size())
#define strSize(s) ( sizeof(s) / sizeof(s[0]) )
#define stringW(str) ((WCHAR*) wstring(str.begin(), str.end()).c_str())
#define long_proc typedef long (__stdcall *zProc)

#define MyWindowWidth       1024
#define MyWindowHeight      574

// Child controls ID
#define IDC_MAIN_GDIMAGE    -999
#define IDC_BUT_WARNING     -1
#define IDC_BUT_STATUS      -2
#define IDC_BUT_STASIS      -3
#define IDC_BUT_NEUTRAL     -4

// GDImage constant to identify each sprite image.
#define ID_GREENGRID        1
#define ID_PEPPER           2
#define ID_SPARKLING        3
#define ID_GAUGE_BACK       4
#define ID_GAUGE_FRONT      5
#define ID_BOTTOM_RED       6
#define ID_BOTTOM_GREEN     7
#define ID_SIDE_GREEN       8
#define ID_LABEL_RANGE      9
#define ID_TRIANGLE         10
#define ID_HORZ_LINE        11
#define ID_IRONMAN          12
#define ID_LASER            13
#define ID_ROCKET           14
#define ID_TORPEDO          15
#define ID_POLYLINE         16
#define ID_TIME             17
#define ID_CLOCK            18
#define ID_HUD_01           19
#define ID_HUD_02           20
#define ID_HUD_03           21
#define ID_HUD_04           22

#define ID_IMAGE_BUTTON     1000
#define ID_BUTTON_LASER     1001
#define ID_BUTTON_ROCKET    1002
#define ID_BUTTON_TORPEDO   1003

#define ID_BUTTON_IN        1004
#define ID_BUTTON_OUT       1005

#define ID_DOTS_BUTTON      1100
#define ID_DOTS_BUTTON_01   1101
#define ID_DOTS_BUTTON_02   1102
#define ID_DOTS_BUTTON_03   1103
#define ID_DOTS_BUTTON_04   1104
#define ID_DOTS_BUTTON_05   1105
#define ID_DOTS_BUTTON_06   1106
#define ID_DOTS_BUTTON_07   1107
#define ID_DOTS_BUTTON_08   1108
#define ID_DOTS_BUTTON_09   1109
#define ID_DOTS_BUTTON_10   1110
#define ID_DOTS_BUTTON_11   1111
#define ID_DOTS_BUTTON_MAX  ID_DOTS_BUTTON_11

#define TIMER_DELAY         33 // Initial animation value.

#define DOCK_IN_RANGE      -24 // Pixel range of dock in out
#define DOCK_OUT_RANGE     208 // Pixel range of dock in out

// Global variables
HWND gh_GDImageCtrl; wstring gs_ResourcePath;
long gn_SeedAngle, gn_FocusID, gn_PrevAngle, gn_WasAngle, gn_DockInOut, gn_TimeX, gn_TimeY;
DWORD gh_StartDelay;

long zTrace (IN wstring sPtr) {
    long nRet = 0;
    static HMODULE hDll;
    if (hDll == 0) { 
        if (sizeof(LONG_PTR) == 8) {
            hDll = LoadLibrary(L"zTrace64"); }
        else {
            hDll = LoadLibrary(L"zTrace32");
        }
    }
    if (hDll) {
        long_proc (WCHAR*);
        zProc hProc = (zProc) GetProcAddress(hDll, "zTrace");
        if (hProc) { nRet = hProc((WCHAR*) sPtr.c_str()); }
    }
    return nRet;
}

wstring STRL$(IN long N) {
    WCHAR longstr[33] = {0};
    _ltow_s(N, longstr, 10);
    return (WCHAR*) longstr;
}

wstring RIGHT$(IN wstring sBuf, IN long nRight) {
    wstring sResult = L"";
    LONG_PTR nLength = sBuf.length();
    if (nLength) { sResult = sBuf.substr(nLength - nRight, nRight); }
    return sResult;
}

//wstring STRPTR$(IN LONG_PTR N) {
//    WCHAR longstr[33] = {0};
//    _i64tow_s(N, longstr, 33, 10);
//    return (WCHAR*) longstr;
//}

wstring TIME$() {
    time_t now = time(0);
    tm tstruct;
    WCHAR buf[16];
    if (localtime_s(&tstruct, &now) == 0) {;
        wcsftime(buf, strSize(buf), L"%X", &tstruct);
    }
    return buf;
}

void SoundFX(IN wstring ws, IN long nID) {
    long nFrame = ZD_GetObjectFrameToUse(nID) + 1;
    if (nFrame == 6) { PlaySound(ws.c_str(), NULL, SND_FILENAME | SND_ASYNC); }
    if (nFrame > ZD_GetObjectFrameCount(nID)) { ZD_ShowObject (nID, ZD_HIDE); }
    ZD_SetObjectFrameToUse(nID, nFrame, FALSE);
}

void InitTime() {
    gh_StartDelay = GetTickCount() + 2000; SetFocus(gh_GDImageCtrl);
}

void UpdateTimeXY() {
    long x = 0, y = 0;
    ZD_GetObjectXY(ID_BUTTON_IN, x, y); ZD_SetObjectXY(ID_TIME, x - gn_TimeX, y - gn_TimeY, FALSE);
}

void Animate (IN LPVOID nDelay) {
    DWORD nWait = 0, nTick = 0, nSecond = 0;
    long nFrame, nStepInOut = 8, nFlip = 0, x = 0, y = 0, nAngle = 0;
    wstring ws;

    for (;;) {

        nTick = GetTickCount(); if (nSecond == 0) { nSecond = nTick + 1000; }
        if (gh_StartDelay) {
            if (nTick > gh_StartDelay) { gh_StartDelay = 0; PostMessage(gh_GDImageCtrl, WM_COMMAND, MAKLNG(ID_BUTTON_OUT, 1), (LPARAM) gh_GDImageCtrl); }
        }

        nFrame = ZD_GetObjectFrameToUse(ID_TRIANGLE) + 1; if (nFrame > ZD_GetObjectFrameCount(ID_TRIANGLE)) { nFrame = 1; }
        ZD_SetObjectFrameToUse(ID_TRIANGLE, nFrame, FALSE);

        nFlip++;
        if (nFlip > 1) {
            nFlip = 0;
            nFrame = ZD_GetObjectFrameToUse(ID_IRONMAN) + 1; if (nFrame > ZD_GetObjectFrameCount(ID_IRONMAN)) { nFrame = 1; }
            ZD_SetObjectFrameToUse(ID_IRONMAN, nFrame, FALSE);
        }

        if (ZD_IsObjectVisible(ID_LASER)) {
            ws = gs_ResourcePath; ws.append(L"lockedin.wav"); SoundFX(ws, ID_LASER);
        }

        if (ZD_IsObjectVisible(ID_ROCKET)) {
            ws = gs_ResourcePath; ws.append(L"rocket.wav"); SoundFX(ws, ID_ROCKET);
        }

        if (ZD_IsObjectVisible(ID_TORPEDO)) {
            ws = gs_ResourcePath; ws.append(L"blast.wav"); SoundFX(ws, ID_TORPEDO);
        }

        if (nTick > nSecond) {
            nSecond = 0;
            if (ZD_IsObjectVisible (ID_TIME)) { ws = TIME$(); ZD_SetObjectTextEx(ID_TIME, (WCHAR*) ws.c_str(), FALSE); }
            //if (ZD_IsObjectVisible (ID_TIME) == FALSE) {ZD_SetObjectVisibility (ID_TIME, TRUE); }
        }

        if (gn_DockInOut) {
            if (ZD_IsObjectVisible(ID_BUTTON_IN)) {
                ZD_GetObjectXY(ID_BUTTON_IN, x, y);
                if (x > DOCK_IN_RANGE) {
                    ZD_SetObjectXY(ID_BUTTON_IN, x - nStepInOut, y, FALSE);
                    ZD_SetObjectXY(ID_BUTTON_OUT, x - nStepInOut, y, FALSE);
                    UpdateTimeXY();
                    }
                else {
                    ZD_SetObjectVisibility (ID_BUTTON_IN, FALSE);
                    ZD_SetObjectVisibility (ID_BUTTON_OUT, TRUE);
                    gn_DockInOut = 0; }
                }
            else {
                ZD_GetObjectXY(ID_BUTTON_OUT, x, y);
                if (x < DOCK_OUT_RANGE) {
                    ZD_SetObjectXY(ID_BUTTON_OUT, x + nStepInOut, y, FALSE);
                    ZD_SetObjectXY(ID_BUTTON_IN, x + nStepInOut, y, FALSE);
                    UpdateTimeXY();
                    }
                else {
                    ZD_SetObjectVisibility (ID_BUTTON_OUT, FALSE);
                    ZD_SetObjectVisibility (ID_BUTTON_IN, TRUE);
                    gn_DockInOut = 0;
                }                
            }
        }

nAngle += 8; if (nAngle > 360) { nAngle = 0; }
ZD_SetObjectAngle(ID_HUD_01, nAngle, FALSE);
ZD_SetObjectAngle(ID_HUD_02, nAngle, FALSE);
ZD_SetObjectAngle(ID_HUD_03, nAngle, FALSE);
ZD_SetObjectAngle(ID_HUD_04, nAngle/2, FALSE);
ZD_SetObjectScale(ID_HUD_04, 1.0f - ( (float) nAngle / 180.0f));

        nFrame = ZD_GetObjectFrameToUse(ID_SPARKLING) + 1; if (nFrame > ZD_GetObjectFrameCount(ID_SPARKLING)) { nFrame = 1; }
        ZD_SetObjectFrameToUse(ID_SPARKLING, nFrame, TRUE);

        Sleep((long) nDelay);
    }
}

long StartAnimation (IN long Delay) {
    long nRet = LB_ERR;
    DWORD dwThreadId = 0;
    HANDLE hThread = CreateThread(NULL,                                  // default security attributes
                                  0,                                     // use default stack size  
                                  (LPTHREAD_START_ROUTINE ) Animate,     // thread function name
                                  (LPVOID) Delay,                        // argument to thread function 
                                  0,                                     // use default creation flags 
                                  &dwThreadId);                          // returns the thread identifier
    if (hThread) { nRet = 0; Sleep(100); }
    CloseHandle(hThread);
    return nRet;
}

wstring EXEpath() {
    wstring sResult;
    WCHAR exepath[MAX_PATH] = {0};
    if (GetModuleFileName(0, exepath, sizeof(exepath))) {
       sResult = exepath;
       sResult = sResult.substr( 0, sResult.rfind(L"\\"));
       sResult += L"\\";
    }
    return sResult;
}

void SET_LABEL_RANGE_COLOR(IN long nAngle) {
    long nR = min((long) (nAngle * 0.7f), 255);
    long nG = max(360 - nAngle, 0); nG = min(nG, 255);
    ZD_SetObjectARGB(ID_LABEL_RANGE, (BYTE) 255, (BYTE) nR, (BYTE) nG, (BYTE) 0);
}

void RestoreMainGauge() {
     // Restore the default rotation angle
     if (ZD_GetObjectAngle(ID_GAUGE_FRONT) != 180) {
         gn_SeedAngle = 0; gn_PrevAngle = 180;

         RECT lpr = {0}; long x = 0, y = 0;
         GetClientRect(gh_GDImageCtrl, &lpr);
         long BoundWidth = 0, BoundHeight = 0;
         ZD_GetObjectBound(ID_HORZ_LINE, BoundWidth, BoundHeight);
         ZD_GetObjectXY(ID_HORZ_LINE, x, y);
         y = (lpr.bottom - BoundHeight) / 2;
         ZD_SetObjectXY(ID_HORZ_LINE, x, y, FALSE);
         ZD_SetObjectXY(ID_LABEL_RANGE, x + 16, y + 3, FALSE);
         ZD_SetObjectTextEx(ID_LABEL_RANGE, L"180", FALSE);
         SET_LABEL_RANGE_COLOR(gn_PrevAngle);
         gn_WasAngle = gn_PrevAngle; ZD_SetObjectAngle(ID_GAUGE_FRONT, gn_WasAngle, TRUE);
     }
}

void RestorePolyline() {
     POINTS* pXY = new POINTS[ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON]; // Create our POLYLINE array
     long K, x, y, nID, nW, nH;
     for (K = 1; K <= ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON; K++) {
          nID = ID_DOTS_BUTTON + K;
          ZD_GetObjectBound(nID, nW, nH); nH /= 2;
          if (K == 1) { x = 249 - nH; y = 56 - nH; }
          ZD_SetObjectXY(nID, x, y, FALSE);
          pXY[K - 1].x = (SHORT) (x + nH); pXY[K - 1].y = (SHORT) (y + nH);
          x += 51;
     }
     ZD_UpdatePoints(OBJECT_POLYLINE, gh_GDImageCtrl, ID_POLYLINE, &pXY[0], ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON);
     delete [] pXY;
}

void RedrawPolyline() {
     long K, x, y, nID, nW, nH;
     POINTS* pXY = new POINTS[ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON]; // Our POLYLINE array
     for (K = 1; K <= ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON; K++) {
          nID = ID_DOTS_BUTTON + K;
          ZD_GetObjectXY(nID, x, y); ZD_GetObjectBound(nID, nW, nH); nH /= 2;
          pXY[K - 1].x = (SHORT) (x + nH); pXY[K - 1].y = (SHORT) (y + nH);
     }
     ZD_UpdatePoints(OBJECT_POLYLINE, gh_GDImageCtrl, ID_POLYLINE, &pXY[0], ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON);
     delete [] pXY;
}

// Main winproc
LRESULT CALLBACK WndProc(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam) {

    long wmId = 0, wmEvent = 0;

    switch (uMsg) {

    case WM_SIZE:
         if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) { 
            RedrawPolyline(); // Redraw the polyline
            UpdateTimeXY();
         }
         break;

    case WM_MOVE:
    case WM_WINDOWPOSCHANGED:
    case WM_IME_NOTIFY:
         break;

    case WM_COMMAND:
         wmId    = LOWORD(wParam);
         wmEvent = HIWORD(wParam);
         break;

    case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
    }

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

void AnimFX(IN HWND hWnd, IN wstring ws, IN long nID) {
     RECT lpr = {0};
     long x, y, BoundWidth = 0, BoundHeight = 0;
     PlaySound(ws.c_str(), NULL, SND_FILENAME | SND_ASYNC);
     GetClientRect(hWnd, &lpr);
     ZD_GetObjectBound (nID, BoundWidth, BoundHeight);
     x = (lpr.right - (rand() % 600)) / 2 + BoundHeight / 3; y = -BoundHeight / 4 + rand() % 80;
     ZD_SetObjectXY(nID, x, y, FALSE);
     ZD_SetObjectFrameToUse(nID, 1, FALSE);
     ZD_ShowObject(nID, ZD_SHOW);
}

// This is the magic place to control what is going on inside of the GDImage graphic container!
LRESULT CALLBACK GDImageCallBack(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam) {
    RECT lpr;
    long x, y, x1, y1, useWidth, useHeight, BoundWidth, BoundHeight,
         x2Div2, y2Div2, useStep, useAngle, nAngle, wmID, nCount = 0;
    wstring ws, sLabel;
    static wstring wasLabel;

    LRESULT nRet = FALSE; // Do not stop the event processing in GDImage

    static long nHoverID, nPushID;

    long nObjectID = ZI_MouseOverObjectID(); if (nObjectID) { nCount = ZD_GetObjectFrameCount(nObjectID); }

    switch (uMsg) {

    case WM_COMMAND:
         wmID = LOINT(wParam);
         switch (wmID) {
         case IDC_BUT_WARNING:
              ZD_SetObjectRed(ID_PEPPER, 255, FALSE);
              ZD_SetObjectGreen(ID_PEPPER, 128, FALSE);
              ZD_SetObjectBlue(ID_PEPPER, 8, TRUE);
              break;
         case IDC_BUT_STATUS:
              ZD_SetObjectRed(ID_PEPPER, 92, FALSE);
              ZD_SetObjectGreen(ID_PEPPER, 255, FALSE);
              ZD_SetObjectBlue(ID_PEPPER, 92, TRUE);
              break;
         case IDC_BUT_STASIS:
              ZD_SetObjectRed(ID_PEPPER, 92, FALSE);
              ZD_SetObjectGreen(ID_PEPPER, 92, FALSE);
              ZD_SetObjectBlue(ID_PEPPER, 255, TRUE);
              break;
         case IDC_BUT_NEUTRAL:
              ZD_SetObjectRed(ID_PEPPER, 0, FALSE);
              ZD_SetObjectGreen(ID_PEPPER, 0, FALSE);
              ZD_SetObjectBlue(ID_PEPPER, 0, TRUE);
              RestorePolyline();
              RestoreMainGauge();
              break;
         case ID_BUTTON_LASER:
              ws = gs_ResourcePath; ws.append(L"laser.wav"); AnimFX(hWnd, ws, ID_LASER);
              break;
         case ID_BUTTON_ROCKET:
              ws = gs_ResourcePath; ws.append(L"faser.wav"); AnimFX(hWnd, ws, ID_ROCKET);
              break;
         case ID_BUTTON_TORPEDO:
              ws = gs_ResourcePath; ws.append(L"torpedo.wav"); AnimFX(hWnd, ws, ID_TORPEDO);
              break;
         case ID_BUTTON_IN:
              ZD_SetObjectVisibility (ID_BUTTON_IN, TRUE);
              ZD_SetObjectVisibility (ID_BUTTON_OUT, FALSE);
              ws = gs_ResourcePath; ws.append(L"blup.wav"); PlaySound(ws.c_str(), NULL, SND_FILENAME | SND_ASYNC);
              gn_DockInOut = -1;
              break;
         case ID_BUTTON_OUT:
              ZD_SetObjectVisibility (ID_BUTTON_OUT, TRUE);
              ZD_SetObjectVisibility (ID_BUTTON_IN, FALSE);
              ws = gs_ResourcePath; ws.append(L"furtiv.wav"); PlaySound(ws.c_str(), NULL, SND_FILENAME | SND_ASYNC);
              gn_DockInOut = -1;
              break;
         }
         break;

    case WM_LBUTTONDOWN:
    case WM_LBUTTONDBLCLK:
         if (nObjectID > 0) {
             if ((nObjectID > ID_IMAGE_BUTTON) && (nCount == 5)) {
                 if (ZD_GetObjectFrameToUse(nObjectID) != 2) { ZD_SetObjectFrameToUse(nObjectID, 2, TRUE); nPushID = nObjectID; }
                 }
             // Save angle value
             else if (nObjectID == ID_GAUGE_FRONT) {
                 gn_SeedAngle = ZD_ComputeObjectAngleFromPoint(nObjectID, LOINT(lParam), HIINT(lParam));
                 gn_FocusID = nObjectID;
             }
         }
         break;

    case WM_LBUTTONUP:
         if ((nObjectID > ID_IMAGE_BUTTON) && (nCount == 5)) {
             if (nPushID) { 
                 ZD_SetObjectFrameToUse(nPushID, 5, TRUE), nHoverID = nPushID;
                 nPushID = 0;
                 // Fire WM_COMMAND event there
                 PostMessage(hWnd, WM_COMMAND, MAKLNG(nHoverID, 1), (LPARAM) hWnd); }
             }
         else if (gn_FocusID) { 
            if (gn_FocusID == ID_GAUGE_FRONT) {
               gn_PrevAngle = ZD_GetObjectAngle(gn_FocusID);
               gn_FocusID = 0;
            }
         }
         break;

    case WM_RBUTTONDOWN:
         if (nObjectID == ID_GAUGE_FRONT) { RestoreMainGauge(); }
         break;

    case WM_MOUSEMOVE:
         if (gn_FocusID) {
            if (ZI_IsLButtonDown()) {

               // Compute the compass rotation
               if (gn_FocusID == ID_GAUGE_FRONT) {
                  useAngle = ZD_ComputeObjectAngleFromPoint(gn_FocusID, LOINT(lParam), HIINT(lParam));
                  if (useAngle != gn_WasAngle) {
                     nAngle = gn_PrevAngle + useAngle - gn_SeedAngle;
                     if (nAngle < 0) {  nAngle += 360; }
                     if (nAngle > 359) { nAngle -= 360; }

                     GetClientRect(hWnd, &lpr);
                     ZD_GetObjectBound(ID_HORZ_LINE, BoundWidth, BoundHeight);
                     ZD_GetObjectXY(ID_HORZ_LINE, x, y);
                     y = ((lpr.bottom - BoundHeight) / 2) - 144 + (long) (nAngle * 0.8f); // Limit y value to the scale range
                     ZD_SetObjectXY(ID_HORZ_LINE, x, y, FALSE);
                     ZD_SetObjectXY(ID_LABEL_RANGE, x + 16, y + 3, FALSE);
                     ws = L"00"; ws.append(STRL$(nAngle)); ws = RIGHT$(ws, 3);
                     ZD_SetObjectTextEx(ID_LABEL_RANGE, (WCHAR*) ws.c_str(), FALSE);
                     SET_LABEL_RANGE_COLOR(nAngle);

                     ZD_SetObjectAngle(ID_GAUGE_FRONT, nAngle, TRUE);
                  }
                  gn_WasAngle = useAngle;
               }

            }
         }
         if (nObjectID > ID_IMAGE_BUTTON) { // && (nCount == 5)) {
             if (nCount == 5) {
                 if (nPushID != 0 && nPushID != nObjectID) { ZD_SetObjectFrameToUse(nPushID, 1, TRUE); }
                 if (nHoverID != 0 && nObjectID != nHoverID) { ZD_SetObjectFrameToUse(nHoverID, 1, TRUE); }
                 if (ZD_GetObjectFrameToUse(nObjectID) == 1) { ZD_SetObjectFrameToUse(nObjectID, 5, TRUE); nHoverID = nObjectID; }
                 }
             else {
                 nHoverID = nObjectID;
             }
             sLabel = ZD_GetObjectImageLabel(nObjectID);
             if (sLabel.compare(wasLabel) !=0) { ZI_SetToolTipText(hWnd, (WCHAR*) sLabel.c_str()); }
             wasLabel = sLabel;
             }
         else {
             if (nHoverID || nPushID) { 
                 if (nHoverID) {
                     if (ZD_GetObjectFrameCount(nHoverID) == 5) { ZD_SetObjectFrameToUse(nHoverID, 1, TRUE); }
                 }
                 nHoverID = 0; nPushID = 0;
                 // Clear tooltip if any
                 if (wasLabel.length()) { wasLabel = L"";  ZI_SetToolTipText(hWnd, (WCHAR*) wasLabel.c_str()); }
             }
         }

         // Shall we redraw the polyline
         if (nObjectID > ID_DOTS_BUTTON && ZI_IsLButtonDown()) { RedrawPolyline(); }

         break;

    case WM_KEYDOWN:
         nObjectID = ZI_GetObjectFocusID(); // Move the sprite that has the focus
         if (nObjectID > 0) {

            ZD_GetObjectXY(nObjectID, 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(nObjectID)) { // If object scroll with the bitmap background
                // Get the size of the bitmap background
                ZI_GetBitmapSize(ZI_GetBMP(hWnd), useWidth, useHeight); }
            else {
                // Get the control client size
                GetClientRect(hWnd, &lpr);
                useWidth = lpr.right; useHeight = lpr.bottom;
            }
            // Get the sprite object size
            ZD_GetObjectBound(nObjectID, 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(nObjectID, x1, y1, TRUE);
               ZD_MoveLinkedObject(nObjectID, x1, y1, TRUE);
            }
         }
         break;
    }
    return nRet;
}

void GDImageEnableEvents(IN HWND hGDImageCtrl) {
    LONG_PTR lpCallBack = (LONG_PTR) GDImageCallBack;
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_LBUTTONDOWN, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_LBUTTONUP, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_LBUTTONDBLCLK, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_RBUTTONDOWN, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_KEYDOWN, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_KEYUP, TRUE);  
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_MOUSEMOVE, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_NCHITTEST, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_MOUSEWHEEL, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_SIZE, TRUE);
    ZI_EventMessageEx(hGDImageCtrl, lpCallBack, WM_COMMAND, TRUE);
}

void CreateWidget_GreenGrid () {
     long imgW = 0, imgH = 0;
     HBITMAP hBitmap; wstring ws;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     // Add a transparent grid.
     ws = gs_ResourcePath; ws.append(L"grid.png");
     // Load image and retrieve the width and height of its bitmap.
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, (rc.right - imgW) / 2, (rc.bottom - imgH) / 2, hBitmap, ZD_ColorARGB(255, 0), ID_GREENGRID, ZS_VISIBLE);
     // Anchor the grid to center.
     ZD_SetObjectAnchorMode(ID_GREENGRID, ANCHOR_CENTER);
     // Lock and disable the sprite image.
     ZD_SetObjectLocked(ID_GREENGRID, TRUE);
}

void CreateWidget_RightPepper () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws; HWND hCtrl;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     // Add a transparent logo on the top right corner.
     ws = gs_ResourcePath; ws.append(L"pepper.png");
     // Load image and retrieve the width and height of its bitmap.
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, rc.right - (imgW), 0, hBitmap, ZD_ColorARGB(255, 0), ID_PEPPER, ZS_VISIBLE);
     // Anchor the logo to the top right corner.
     ZD_SetObjectAnchorMode(ID_PEPPER, ANCHOR_RIGHT);
     // Lock and disable the sprite image.
     ZD_SetObjectLocked(ID_PEPPER, TRUE);

     // Here we put the WinLIFT button's controls directly onto the GDImage control.
     long K, nStep = 0, nID; wstring sLabel;
     for (K = 1; K < 5; K++) {
         switch (K) {
         case 1:
              sLabel = L"WARNING";
              break;
         case 2:
              sLabel = L"STATUS";
              break;
         case 3:
              sLabel = L"STASIS";
              break;
         case 4:
              sLabel = L"NEUTRAL";
              break;
         default:
              sLabel = L"BUTTON_0"; sLabel.append(STRL$(K));
              break;
         }
         nID = K * -1;
         hCtrl = skPushButtonImage(gh_GDImageCtrl, L"", (WCHAR*) sLabel.c_str(), rc.right - (imgW), imgH + nStep, imgW, 28, nID, BS_CENTER | BS_BOTTOM);
         skSetAnchorCtrl(hCtrl, ANCHOR_RIGHT);
         nStep += 29;
     }
}

void CreateWidget_BottomLines () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws;

     // Add bottom_red.
     ws = gs_ResourcePath; ws.append(L"bottom_red.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 0, 507, hBitmap, ZD_ColorARGB(255, 0), ID_BOTTOM_RED, ZS_VISIBLE);
     // Anchor the logo to the top right corner.
     ZD_SetObjectAnchorMode(ID_BOTTOM_RED, ANCHOR_CENTER_HORZ_BOTTOM);
     // Lock and disable the sprite image.
     ZD_SetObjectLocked(ID_BOTTOM_RED, TRUE);

     // Add bottom_green.
     ws = gs_ResourcePath; ws.append(L"bottom_green.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 0, 468, hBitmap, ZD_ColorARGB(255, 0), ID_BOTTOM_GREEN, ZS_VISIBLE);
     // Anchor horz_bottom.
     ZD_SetObjectAnchorMode(ID_BOTTOM_GREEN, ANCHOR_CENTER_HORZ_BOTTOM);
     // Lock and disable the sprite image.
     ZD_SetObjectLocked(ID_BOTTOM_GREEN, TRUE);
}

void CreateWidget_LeftScale () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws;

     // Add side_green.
     ws = gs_ResourcePath; ws.append(L"side_green.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 56, 0, hBitmap, ZD_ColorARGB(255, 0), ID_SIDE_GREEN, ZS_VISIBLE);
     // Anchor side_green.
     ZD_SetObjectAnchorMode(ID_SIDE_GREEN, ANCHOR_CENTER);
     // Lock and disable the sprite image.
     ZD_SetObjectLocked(ID_SIDE_GREEN, TRUE);

     // The animated green triangle.
     ws = gs_ResourcePath; ws.append(L"triangle.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 69, 18, hBitmap, ZD_ColorARGB(255, 0), ID_TRIANGLE, ZS_VISIBLE);
     // Anchor side_green.
     ZD_SetObjectAnchorMode(ID_TRIANGLE, ANCHOR_CENTER);
     ZD_SetObjectFrameCount(ID_TRIANGLE, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_TRIANGLE, 1, FALSE);
     // Lock and disable the sprite animation.
     ZD_SetObjectLocked(ID_TRIANGLE, TRUE);
}

void CreateWidget_HorzLine () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws;

     // Add ID_HORZ_LINE.
     ws = gs_ResourcePath; ws.append(L"horz_line.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     // Use the new hBitmap to create a sprite image on the top right corner.
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 4, 253, hBitmap, ZD_ColorARGB(255, 0), ID_HORZ_LINE, ZS_VISIBLE);
     ZD_SetObjectLinked(ID_HORZ_LINE, ID_HORZ_LINE); // This is a linked object
     // Anchor ID_HORZ_LINE.
     ZD_SetObjectAnchorMode(ID_HORZ_LINE, ANCHOR_CENTER);
     // Lock and disable ID_HORZ_LINE.
     ZD_SetObjectLocked(ID_HORZ_LINE, TRUE);
     // Add ID_LABEL_RANGE
     short Use3D = 0; long UseStrFormat = 0;
     ZD_DrawTextToCtrlEx(gh_GDImageCtrl, L"180", 20, 256, 0, 0, ZD_ARGB(255, 0,255,0), L"ARIAL BLACK", 17, ID_LABEL_RANGE, ZS_VISIBLE, Use3D, UseStrFormat);
     ZD_SetObjectLinked(ID_LABEL_RANGE, ID_HORZ_LINE); // Link it to HORZ_LINE.
     ZD_SetObjectAnchorMode(ID_LABEL_RANGE, ANCHOR_CENTER);
     SET_LABEL_RANGE_COLOR(180);
     // Lock and disable ID_LABEL_RANGE.
     ZD_SetObjectLocked(ID_LABEL_RANGE, TRUE);  
}

void CreateWidget_Main () {
     long imgW = 0, imgH = 0, nReserved = 0, x = 0, y = 0;
     HBITMAP hBitmap; wstring ws;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     // The animated ID_SPARKLING.
     ws = gs_ResourcePath; ws.append(L"sparkling.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     x =(rc.right - imgH) / 2; y = (rc.bottom - imgH) / 2;
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, y, hBitmap, ZD_ColorARGB(255, 0), ID_SPARKLING, ZS_VISIBLE);
     ZD_SetObjectFrameCount(ID_SPARKLING, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_SPARKLING, 1, FALSE);
     // Anchor ID_SPARKLING.
     ZD_SetObjectAnchorMode(ID_SPARKLING, ANCHOR_CENTER);

     //// The main rotating ID_HUD_GAUGE
     ws = gs_ResourcePath; ws.append(L"HUD01.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
     x =(rc.right - imgH) / 2; y = (rc.bottom - imgH) / 2;
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, y, hBitmap, ZD_ColorARGB(255, 0), ID_GAUGE_BACK, ZS_VISIBLE);
     ZD_SetObjectLinked(ID_GAUGE_BACK, ID_GAUGE_BACK);
     ZD_SetObjectFrameCount(ID_GAUGE_BACK, 2);
     ZD_SetObjectFrameToUse(ID_GAUGE_BACK, 1, FALSE);
     ZD_SetObjectAnchorMode(ID_GAUGE_BACK, ANCHOR_CENTER);
     // The static GAUGE_FRONT sprite.
     ZD_CloneObject(ID_GAUGE_BACK, ID_GAUGE_FRONT, x, y);
     ZD_SetObjectLinked(ID_GAUGE_FRONT, ID_GAUGE_BACK);
     ZD_SetObjectFrameToUse(ID_GAUGE_FRONT, 2, FALSE);
     ZD_SetObjectLocked(ID_GAUGE_FRONT, TRUE); // We lock the GAUGE_FRONT, because we want to rotate it with the mouse

     gn_SeedAngle = 0; gn_PrevAngle = 180; gn_WasAngle = gn_PrevAngle; 
     ZD_SetObjectAngle(ID_GAUGE_FRONT, gn_WasAngle, FALSE);

     // Link the SPARKLING animation to HUD_GAUGE
     ZD_SetObjectLinked(ID_SPARKLING, ID_GAUGE_BACK);
}

void CreateWidget_Button () {
     long K, imgW = 0, imgH = 0, x = 660, nID, nReserved = 0;
     HBITMAP hBitmap; wstring ws;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     for (K = 1; K < 4; K++) {
          
          ws = gs_ResourcePath; ws.append(L"BUTTON_0"); ws.append(STRL$(K)); ws.append(L".png");
          hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), imgW, imgH);
          //ws = gs_ResourcePath; ws.append(L"BUTTON_0"); ws.append(STRL$(K)); ws.append(L".ski");
          //hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved); ZI_GetBitmapSize(hBitmap, imgW, imgH);

          nID = ID_IMAGE_BUTTON + K;
          ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, 389, hBitmap, ZD_ColorARGB(255, 0), nID, ZS_VISIBLE);
          ZD_SetObjectFrameCount(nID, 5);
          ZD_SetObjectFrameToUse(nID, 1, FALSE);
          ZD_SetObjectLocked(nID, TRUE);
          ZD_SetObjectAnchorMode(nID, ANCHOR_CENTER);
          switch (K) {
          case 1: ZD_SetObjectImageLabel(nID, L"Laser"); break;
          case 2: ZD_SetObjectImageLabel(nID, L"Rocket"); break;
          case 3: ZD_SetObjectImageLabel(nID, L"Torpedo"); break;
          }
          x += 66;
     }
}

void CreateWidget_IronMan () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     // The animated IRONMAN.
     ws = gs_ResourcePath; ws.append(L"iron_man.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 820, 240, hBitmap, ZD_ColorARGB(255, 0), ID_IRONMAN, ZS_VISIBLE);
     // Anchor IronMan.
     ZD_SetObjectAnchorMode(ID_IRONMAN, ANCHOR_CENTER);
     ZD_SetObjectFrameCount(ID_IRONMAN, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_IRONMAN, 1, FALSE);
     ZD_SetObjectLocked(ID_IRONMAN, TRUE);
}

void CreateWidget_Explode () {
     long imgW = 0, imgH = 0, nReserved = 0;
     HBITMAP hBitmap; wstring ws;
     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     // The animated GREENLIGHT.
     ws = gs_ResourcePath; ws.append(L"greenlight.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 820, 240, hBitmap, ZD_ColorARGB(255, 0), ID_LASER, ZS_HIDDEN);
     // Anchor Explode.
     ZD_SetObjectAnchorMode(ID_LASER, ANCHOR_CENTER);
     ZD_SetObjectFrameCount(ID_LASER, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_LASER, 1, FALSE);
     ZD_SetObjectLocked(ID_LASER, TRUE);

     // The animated EXPLODE.
     ws = gs_ResourcePath; ws.append(L"explode.ski");
     ws = gs_ResourcePath; ws.append(L"boom.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 820, 240, hBitmap, ZD_ColorARGB(255, 0), ID_ROCKET, ZS_HIDDEN);
     // Anchor Explode.
     ZD_SetObjectAnchorMode(ID_ROCKET, ANCHOR_CENTER);
     ZD_SetObjectFrameCount(ID_ROCKET, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_ROCKET, 1, FALSE);
     ZD_SetObjectLocked(ID_ROCKET, TRUE);

     // The animated GREENLIGHT.
     ws = gs_ResourcePath; ws.append(L"flashlight.ski");
     hBitmap = skSkiToDib ((WCHAR*) ws.c_str(), nReserved);
     ZI_GetBitmapSize(hBitmap, imgW, imgH);
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 820, 240, hBitmap, ZD_ColorARGB(255, 0), ID_TORPEDO, ZS_HIDDEN);
     // Anchor Explode.
     ZD_SetObjectAnchorMode(ID_TORPEDO, ANCHOR_CENTER);
     ZD_SetObjectFrameCount(ID_TORPEDO, (BYTE) (imgW / imgH));
     ZD_SetObjectFrameToUse(ID_TORPEDO, 1, FALSE);
     ZD_SetObjectLocked(ID_TORPEDO, TRUE);
}

void CreateWidget_Time () {
     HBITMAP hBitmap; long x = 0, y = 0, nW = 0, nH = 0, nFrameCount = 0; wstring ws;

     RECT rc = {0}; GetClientRect(gh_GDImageCtrl, &rc);

     ws = gs_ResourcePath; ws.append(L"in.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), nW, nH); nFrameCount = nW / nH; nH /= 2;
     x = -31; y = 429;
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, y, hBitmap, ZD_ColorARGB(255, 0), ID_BUTTON_IN, ZS_VISIBLE);
     ZD_SetObjectFrameCount(ID_BUTTON_IN, (BYTE) nFrameCount);
     ZD_SetObjectFrameToUse(ID_BUTTON_IN, 1, FALSE);
     ZD_SetObjectAnchorMode(ID_BUTTON_IN, ANCHOR_BOTTOM);
     ZD_SetObjectLocked(ID_BUTTON_IN, TRUE);
     ZD_SetObjectImageLabel(ID_BUTTON_IN, L"Hide time");
     ZD_SetObjectVisibility(ID_BUTTON_IN, FALSE);

     ws = gs_ResourcePath; ws.append(L"out.png");
     hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), nW, nH); nFrameCount = nW / nH; nH /= 2;
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, y, hBitmap, ZD_ColorARGB(255, 0), ID_BUTTON_OUT, ZS_VISIBLE);
     ZD_SetObjectFrameCount(ID_BUTTON_OUT, (BYTE) nFrameCount);
     ZD_SetObjectFrameToUse(ID_BUTTON_OUT, 1, FALSE);
     ZD_SetObjectAnchorMode(ID_BUTTON_OUT, ANCHOR_BOTTOM);
     ZD_SetObjectLocked(ID_BUTTON_OUT, TRUE);
     ZD_SetObjectImageLabel(ID_BUTTON_OUT, L"Show time");

     gn_TimeX = 208, gn_TimeY = -4; ws = gs_ResourcePath; ws.append(L"lcd.ttf"); // Here we are using a private font (no need to register it first)
     //gn_TimeX = 208, gn_TimeY = -4; ws = gs_ResourcePath; ws.append(L"doergon.ttf"); // Here we are using a private font (no need to register it first)
     //gn_TimeX = 208, gn_TimeY = -4; ws = gs_ResourcePath; ws.append(L"AlienRegular.ttf"); // Here we are using a private font (no need to register it first)
     ZD_DrawTextToCtrlEx(gh_GDImageCtrl, (WCHAR*) TIME$().c_str(), x - gn_TimeX, y - gn_TimeY, 238, 42, ZD_ColorARGB(128,RGB(255,255,64)),
                        (WCHAR*) ws.c_str(), 40, ID_TIME, ZS_VISIBLE, 0, StringAlignmentCenter);
     ZD_SetObjectLocked(ID_TIME, TRUE);
}

void CreateWidget_Dots () {
     long K, nW = 0, nH = 0, x = 0, y = 0, nOrder = 0, nFrameCount, nID;
     HBITMAP hBitmap; wstring ws;

     POINTS* pXY = new POINTS[ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON]; // Create our POLYLINE array

     ws = gs_ResourcePath; ws.append(L"planets.png");
     for (K = 1; K <= ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON; K++) {
          nID = ID_DOTS_BUTTON + K;
          if (K == 1) {
              hBitmap = ZI_CreateBitmapFromFile((WCHAR*) ws.c_str(), nW, nH); nFrameCount = nW / nH; nH /= 2;
              x = 249 - nH; y = 56 - nH;
              ZD_DrawBitmapToCtrl(gh_GDImageCtrl, x, y, hBitmap, ZD_ColorARGB(255, 0), nID, ZS_VISIBLE);
              nOrder = ZD_GetObjectZorder (nID); }
          else { // Reuse the same bitmap for all dots (save RAM)
              ZD_CloneObject(ID_DOTS_BUTTON + 1, nID, x, y);
          }
          pXY[K - 1].x = (SHORT)(x + nH); pXY[K - 1].y = (SHORT) (y + nH);
          ZD_SetObjectFrameCount(nID, (BYTE) nFrameCount);
          ZD_SetObjectFrameToUse(nID, K, FALSE);
          //ZD_SetObjectLocked(ID_DOTS_BUTTON + K, TRUE);
          ZD_SetObjectAnchorMode(nID, ANCHOR_CENTER);
          switch (K) { // Set planet labels
          case 1:  ZD_SetObjectImageLabel(nID, L"Sun"); break;
          case 2:  ZD_SetObjectImageLabel(nID, L"Mercury"); break;
          case 3:  ZD_SetObjectImageLabel(nID, L"Venus"); break;
          case 4:  ZD_SetObjectImageLabel(nID, L"Earth"); break;
          case 5:  ZD_SetObjectImageLabel(nID, L"Mars"); break;
          case 6:  ZD_SetObjectImageLabel(nID, L"Jupiter"); break;
          case 7:  ZD_SetObjectImageLabel(nID, L"Saturn"); break;
          case 8:  ZD_SetObjectImageLabel(nID, L"Uranus"); break;
          case 9:  ZD_SetObjectImageLabel(nID, L"Neptune"); break;
          case 10: ZD_SetObjectImageLabel(nID, L"Asteroid"); break;
          case 11: ZD_SetObjectImageLabel(nID, L"Pluto"); break;
          }
          x += 51;
     }

     ZD_DrawPolyLineToCtrl(gh_GDImageCtrl, &pXY[0], ID_DOTS_BUTTON_MAX - ID_DOTS_BUTTON, ZD_ARGB(255, 100,250,150),
                           1, ID_POLYLINE, ZS_VISIBLE, ZD_DRAW_OUTLINE | ZD_DRAW_OPEN, 0);
     ZD_SetObjectAnchorMode(ID_POLYLINE, ANCHOR_CENTER);
     ZD_SetObjectZorder (ID_POLYLINE, nOrder);
     ZD_SetObjectLocked(ID_POLYLINE, TRUE);

     // Keep the Sun hover the polyline
     ZD_SetObjectZorder(ID_DOTS_BUTTON + 1, ZD_ORDER_TOP);

     delete [] pXY;
}

void CreateWidget_HUD_FONT () {
     HBITMAP hBitmap; wstring ws;
     ws = gs_ResourcePath; ws.append(L"circle.ttf");
     hBitmap = ZD_CreateBitmapFromText(L",",                            // Text to render
                                       (WCHAR*) ws.c_str(),             // Font name
                                       120,                             // Font size (abs), use negative value to avoid creation of rectangular background region.
                                       ZD_ColorARGB(255,RGB(255,0,0)),  // ARGB color
                                       0,                               // Shadow/3D
                                       0);                              // String format
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 206, 117, hBitmap, 0xFFFFFFFF, ID_HUD_01, ZS_VISIBLE);
     ZD_SetObjectAnchorMode(ID_HUD_01, ANCHOR_CENTER);
//     ZD_SetObjectLocked(ID_HUD_01, TRUE);

     hBitmap = ZD_CreateBitmapFromText(L"?",                            // Text to render
                                       (WCHAR*) ws.c_str(),             // Font name
                                       120,                             // Font size (abs), use negative value to avoid creation of rectangular background region.
                                       ZD_ColorARGB(255,RGB(255,255,0)),  // ARGB color
                                       0,                               // Shadow/3D
                                       0);                              // String format
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 206, 117+100, hBitmap, 0xFFFFFFFF, ID_HUD_02, ZS_VISIBLE);
     ZD_SetObjectAnchorMode(ID_HUD_02, ANCHOR_CENTER);
//     ZD_SetObjectLocked(ID_HUD_02, TRUE);

     hBitmap = ZD_CreateBitmapFromText(L"G",                            // Text to render
                                       (WCHAR*) ws.c_str(),             // Font name
                                       240,                             // Font size (abs), use negative value to avoid creation of rectangular background region.
                                       ZD_ARGB(255, 0,192,0),          // ARGB color
                                       1,                               // Shadow/3D
                                       0);                              // String format
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 206+465, 117+65, hBitmap, 0xFFFFFFFF, ID_HUD_03, ZS_VISIBLE);
     ZD_SetObjectAnchorMode(ID_HUD_03, ANCHOR_CENTER);
//     ZD_SetObjectLocked(ID_HUD_03, TRUE);

     hBitmap = ZD_CreateBitmapFromText(L"U",                            // Text to render
                                       (WCHAR*) ws.c_str(),             // Font name
                                       120,                             // Font size (abs), use negative value to avoid creation of rectangular background region.
                                       ZD_ARGB(255, 255,255,255),       // ARGB color
                                       0,                               // Shadow/3D
                                       0);                              // String format
     ZD_DrawBitmapToCtrl(gh_GDImageCtrl, 204, 117 + 198, hBitmap, 0xFFFFFFFF, ID_HUD_04, ZS_VISIBLE);
     ZD_SetObjectAnchorMode(ID_HUD_04, ANCHOR_CENTER);
//     ZD_SetObjectLocked(ID_HUD_04, TRUE);

}

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

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Declaration of local variables
    MSG msg;
    long nRet = 0, IsInitialized = 0;
    WNDCLASSEX wcx;
    WCHAR szWindowClass[] = L"ZHUDCTRLS";
    WCHAR szTitle[20] = {0};
    if (sizeof(LONG_PTR) == 8) {
        wcscpy_s(szTitle, L"HUD controls 64-bit");} 
    else {
        wcscpy_s(szTitle, L"HUD controls 32-bit");
    }
    WCHAR szFullpathToFile[MAX_PATH] = {0};

    // Register the main window class
    wcx.cbSize = sizeof(wcx);
    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       = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
       if (RegisterClassEx(&wcx)) { IsInitialized = -1; }
    }

    if (IsInitialized) {

       long w = MyWindowWidth;
       long h = MyWindowHeight;
       long x = max((GetSystemMetrics(SM_CXSCREEN) - w) / 2, 0);
       long y = max((GetSystemMetrics(SM_CYSCREEN) - h) / 2, 0);

       DWORD dwStyle = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
                       WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // | WS_VISIBLE

       HWND hMain = CreateWindowEx(0, szWindowClass, szTitle, dwStyle, x, y, w, h, NULL, NULL, hInstance, NULL);

       if (hMain) {

           StartAnimation(TIMER_DELAY); // Fire the animation thread

           WCHAR szExePath[MAX_PATH] = {0}; wcscpy_s(szExePath, (WCHAR*) EXEpath().c_str());
           wstring wS = (WCHAR*) szExePath;
           RECT rc; GetClientRect(hMain, &rc);
           //dwStyle = WS_CHILD | WS_VISIBLE; // Default GDImage control style
           // We use this dwStyle because we want to combine WinLIFT controls inside of the GDImage control
           dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
           gh_GDImageCtrl = CreateWindowEx(0, GDImageClassName, NULL, dwStyle,
                                           rc.left, rc.top, rc.right, rc.bottom,
                                           hMain, (HMENU) IDC_MAIN_GDIMAGE, hInstance, NULL);

           wstring ws = (WCHAR*) szExePath; ws.append(L"IronMan.sks");
           if (skInitEngine ((WCHAR*) ws.c_str(), L"")) {

               skSkinWindow(hMain, L"Dock|Undock|Minimize|Maximize|Restore|Close");
               wstring sResource;
               if (gh_GDImageCtrl) {
                   // Add tooltip support to the GDImage control.
                   ZI_CreateToolTip(gh_GDImageCtrl, L"");
                   // Anchor the GDImage control.
                   skSetAnchorCtrl(gh_GDImageCtrl, ANCHOR_HEIGHT_WIDTH);
                   gs_ResourcePath = (WCHAR*) szExePath; gs_ResourcePath.append(L"Resource\\");

                   // Create the widgets
                   CreateWidget_Explode();
                   CreateWidget_GreenGrid();
                   CreateWidget_Dots();
                   CreateWidget_RightPepper();
                   CreateWidget_BottomLines();
CreateWidget_HUD_FONT();
                   CreateWidget_LeftScale();
                   CreateWidget_HorzLine();
                   CreateWidget_Main();
                   CreateWidget_Button();
                   CreateWidget_IronMan();
                   CreateWidget_Time();

                   // Make sure we keep those on top of the z-order.
                   ZD_SetObjectZorder(ID_LABEL_RANGE, ZD_ORDER_TOP);

                   ZD_SetObjectZorder(ID_PEPPER, ZD_ORDER_TOP);
                   ZD_SetObjectZorder(ID_GAUGE_BACK, ZD_ORDER_TOP);
                   ZD_SetObjectZorder(ID_GAUGE_FRONT, ZD_ORDER_TOP);

                   // Tell GDImage to use the WinLIFT background.
                   ZI_UseWinLIFTbackground(gh_GDImageCtrl, TRUE, FALSE);

                   // VERY IMPORTANT, we must enable GDImage Events to process mousing and sprite monitoring.
                   GDImageEnableEvents(gh_GDImageCtrl);

               }
           }

           ShowWindow(hMain, SW_SHOW);
           InitTime();

           // Main message loop
           while (GetMessage(&msg, NULL, 0, 0)) {
               TranslateMessage(&msg);
               DispatchMessage(&msg);
           }

           nRet = (long) msg.wParam;
       }

    }
    return nRet;
}
