/******************************************************************************
 *
 *  TBHELP_C.C
 *
 *  Author: Thomas Braun, released as freeware
 *
 * How to Build the C source:
 *
 * compile ( Visual C++ 5.x ) :
 *
 *      cl tbhelp_c.c -c -Zl
 *
 *  or use
 *
 *      cl.bat  (you may need to modify the batch file
 *               so it matches your C compiler installation)
 *
 * link :
 *
 *      pbuild tbhelp
 *
 * result is TASKBAR.DLL and TASKBAR.LIB
 *
 * History:
 *
 * 15.02.2003 changed static linked shell32.dll to dynamic linking, so you do
 *            not need to include shell32.lib anymore into your Xbase++ project
 *
 ******************************************************************************/
#include <windows.h>
#include <shellapi.h>
#include <xpppar.h>
#include <xppcon.h>
#include <error.ch>

// Definition for dynamically loaded Shell_NotifyIcon function
typedef BOOL (*MYPROC)(DWORD, PNOTIFYICONDATA);

// Xbase++ Event-Codes
#define xbeP_User              134217728
#define xbeB_Event		       1048576

// Generic messages
#define xbeP_None                  (001 + xbeB_Event)

// Mouse messages
#define xbeM_LbUp                  (10 + xbeB_Event)
#define xbeM_LbDown                ( 7 + xbeB_Event)
#define xbeM_LbDblClick            (16 + xbeB_Event)

#define xbeM_RbUp                  (12 + xbeB_Event)
#define xbeM_RbDown                ( 9 + xbeB_Event)
#define xbeM_RbDblClick            (18 + xbeB_Event)

#define xbeM_MbUp                  (11 + xbeB_Event)
#define xbeM_MbDown                ( 8 + xbeB_Event)
#define xbeM_MbDblClick            (17 + xbeB_Event)

#define xbeM_LbMotion              (19 + xbeB_Event)
#define xbeM_MbMotion              (20 + xbeB_Event)
#define xbeM_RbMotion              (21 + xbeB_Event)

#define xbeM_Motion                (22 + xbeB_Event)


static WNDPROC pWinProc=NULL;              /* Pointer to window procedure */
static short   Counter=0;             /* Counter for SetWindowLong calls */
static ULONG   UserMessage;           /* Value of userdefined message */
static ContainerHandle EventObject;   /* Xbase++ object that receives message via PostAppEvent */

/*--------------------------------------------------------------------------------------*/
LRESULT CALLBACK TbHelpWinProc( HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2 )
// Creates a userdefined event whenever a taskbar event occurs
// Eintritt.
// The message is only handled if msg = WM_USER + 1966 -> see XADDICON function
//   mp1 = numerische ID of the taskbar icons
//   mp2 = contains the MW_ constant that is the mouse message
{
   POINT MousePosition;                 /* mouse coordinates */
   XPPAPIRET xr;                        /* general return value for Xbase++ C-API calls */
   LONG lMsg;                           /* Xbase++ mouse message constant */
   ContainerHandle chResult, chMousePos, chNum, chNull;  /* containers for various purposes */

   // Translate taskbar events into Xbase++ events and
   // post the via PostAppEvent
   if ( msg == WM_USER + 1966 ) {

      /* Get the mouse position */
      GetCursorPos( &MousePosition );

      /* Transform the Y-coordinate into Xbase++ coordinates*/
      MousePosition.y = GetSystemMetrics( SM_CYSCREEN ) - MousePosition.y;

      /* Translate the Windows mouse messages into Xbase++ messages
         mp2 ist the Windows-API message constant */
      lMsg = xbeP_None;
      switch( mp2 )
      {
        case WM_LBUTTONUP:
            lMsg = xbeM_LbUp;
            break;

        case WM_LBUTTONDOWN:
            lMsg = xbeM_LbDown;
            break;

        case WM_LBUTTONDBLCLK:
            lMsg = xbeM_LbDblClick;
            break;

        case WM_RBUTTONUP:
            lMsg = xbeM_RbUp;
            break;

        case WM_RBUTTONDOWN:
            lMsg = xbeM_RbDown;
            break;

        case WM_RBUTTONDBLCLK:
            lMsg = xbeM_RbDblClick;
            break;

        case WM_MBUTTONUP:
            lMsg = xbeM_MbUp;
            break;

        case WM_MBUTTONDOWN:
            lMsg = xbeM_MbDown;
            break;

        case WM_MBUTTONDBLCLK:
            lMsg = xbeM_MbDblClick;
            break;

        case WM_MOUSEMOVE:
            lMsg = xbeM_Motion;
            break;
      }

      /* Put the data into an Array :
       * [1] = Icon ID (parameter mp1)
       * [2] = Xbase++ mouse message constant
       * [3] = x-coordinate of the mouse pointer
       * [4] = y-coordinate of the mouse pointer
       */

      // create new array container
      chMousePos = _conNewArray(1,4);

      // Icon ID into element
      chNum = _conPutNL( NULLCONTAINER, mp1 );
      xr    = _conArrayPut( chMousePos, chNum, 1, 0);

      // Xbase++ message into element 2
      chNum = _conPutNL( chNum, lMsg );
      xr    = _conArrayPut( chMousePos, chNum, 2, 0);

      // x-position into element 3
      chNum = _conPutNL( chNum, MousePosition.x );
      xr    = _conArrayPut( chMousePos, chNum, 3, 0);

      // y-position into element 3
      chNum = _conPutNL( chNum, MousePosition.y );
      xr    = _conArrayPut( chMousePos, chNum, 4, 0);

      // use temporäry container chNum for Xbase++ userdefined message
      _conPutNL( chNum, (LONG)( UserMessage ) );

      // new container for "0" parameter for PostAppEvent
      chNull = _conPutNL( NULLCONTAINER, 0 );

      // container for _conCall return value (not used)
      chResult  = _conNew(NULLCONTAINER);

      // Send event with PostAppEvent() to the XBase++ application
      xr        = _conCall( chResult, "PostAppEvent", 4, chNum, chMousePos, chNull, EventObject );

      // Free all containers for garbage collection
      xr = _conRelease( chNum );
      xr = _conRelease( chNull );
      xr = _conRelease( chResult );
      xr = _conRelease( chMousePos );
      return 0;
   }

   /* If the message has not been handled above, just forward it to
    * the original windows message procedure */
   return CallWindowProc( pWinProc, hWnd, (ULONG)msg, (WPARAM)mp1, (LPARAM)mp2 );
}



/*--------------------------------------------------------------------------------------*/
XPPRET XPPENTRY XADDICON(XppParamList paramList)
// TBADDICON - Add an icon to the taskbar
{
    HWND hwnd;                          // Window handle
    UINT uID;                           // Id of the icon
    HICON hicon;                        // Icon resource ID
    LPSTR lpszTip;                      // Tooltip text
    NOTIFYICONDATA tnid;                // Parameter for Shell_NotifyIcon()
    CHAR buffer[64];                    // Buffer for _parc() call

    HINSTANCE hinstLib;                 // DLL handle
    MYPROC ProcAdd;                     // procedure address
    BOOL fFreeResult;                   // dummy

    // Get the DLL module handle.
    hinstLib = LoadLibrary("shell32.dll");

    // If the handle is valid, try to get the function address.
     if (hinstLib != NULL)
    {
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "Shell_NotifyIcon");

        // If the function address is valid, call the function.
        if (NULL != ProcAdd)
        {

            // Get the window handle
            hwnd = (HWND)  _parnl( paramList, 1 );

            // Get the icon ID also used for changing icons "on-the-fly"
            uID = (UINT)  _parnl( paramList, 2 );

            // Get the resource handle for the icon
            hicon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE(_parnl( paramList, 3 )) );

            // Get the Tooltip Text
            _parc(buffer, sizeof(buffer), paramList, 4);
            lpszTip = buffer;

            // Setup the structure for SHell_NotifyIcon
            tnid.cbSize           = sizeof(NOTIFYICONDATA);
            tnid.hWnd             = hwnd;
            tnid.uID              = uID;
            tnid.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP;
            tnid.uCallbackMessage = WM_USER + 1966;
            tnid.hIcon            = hicon;
            if (lpszTip)
                lstrcpyn(tnid.szTip, lpszTip, sizeof(tnid.szTip));
            else
                tnid.szTip[0] = '\0';

            /* If the window procedure has not yet been redirected to
             * TbHelpWinProc, do it now and store old procedure address
             * in pWinProc
             * */
            if ( ! pWinProc)
                pWinProc = (WNDPROC)SetWindowLong( hwnd, GWL_WNDPROC, (LONG)TbHelpWinProc );

            // Register icon with the taskbar
            if ( (ProcAdd)(NIM_ADD, &tnid) ){
                // Increase icon counter
                Counter++;
                _retl(paramList, TRUE );
                }
            else
                _retl(paramList, FALSE );

            // Free icon resource
            if (hicon)
                DestroyIcon(hicon);
        }

        // Free the DLL module.
        fFreeResult = FreeLibrary(hinstLib);

    }
    return;
}

/*--------------------------------------------------------------------------------------*/
XPPRET XPPENTRY XDELICON(XppParamList paramList)
// remove Icon from the taskbar
{
    HWND hwnd;                          // Windowhandle
    UINT uID;                           // Icon-Id
    NOTIFYICONDATA tnid;                // struct for Shell_NotifyIcon()

    HINSTANCE hinstLib;                 // DLL-Handle
    MYPROC ProcAdd;                     // Procedure address
    BOOL fFreeResult;                   // Dummy

    // Get a handle to the DLL module.
    hinstLib = LoadLibrary("shell32.dll");

    // If the handle is valid, try to get the function address.
     if (hinstLib != NULL)
    {
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "Shell_NotifyIcon");

        // If the function address is valid, call the function.
        if (NULL != ProcAdd)
        {

            // Get parameters
            hwnd = (HWND) _parnl( paramList, 1 );
            uID  = (UINT) _parnl( paramList, 2 );

            // Setup struct
            tnid.cbSize = sizeof(NOTIFYICONDATA);
            tnid.hWnd   = hwnd;
            tnid.uID    = uID;

            // Remove icon
            if ( (ProcAdd)(NIM_DELETE, &tnid) ){
                // decrease counter
                Counter--;
                _retl(paramList, TRUE );
                }
            else
                _retl(paramList, FALSE );

            // If the counter is decreased to 0, restore the old window procedure
            if ( ! Counter )
                if ( pWinProc){
                    SetWindowLong( hwnd, GWL_WNDPROC, (LONG)pWinProc );
                    pWinProc = NULL;
                    }

        }

        // Free the DLL module.
        fFreeResult = FreeLibrary(hinstLib);

    }

    return;
}


/*--------------------------------------------------------------------------------------*/
XPPRET XPPENTRY XCHANGEICON(XppParamList paramList)
// TBCHANGEICON - ändert ein Icon im Taskbar
{
    HWND hwnd;                          // Windowhandle
    UINT uID;                           // Icon ID
    HICON hicon;                        // Resource handle
    LPSTR lpszTip;                      // Tooltip text
    NOTIFYICONDATA tnid;                // Struct for Shell_NotifyIcon()
    CHAR buffer[64];                    // Puffer for _parc()

    HINSTANCE hinstLib;                 // DLL-Handle
    MYPROC ProcAdd;                     // Procedure address
    BOOL fFreeResult;                   // Dummy


    // Only do something if any icons are active
    if ( Counter ) {

        // Get a handle to the DLL module.
        hinstLib = LoadLibrary("shell32.dll");

        // If the handle is valid, try to get the function address.
        if (hinstLib != NULL)
        {
            ProcAdd = (MYPROC) GetProcAddress(hinstLib, "Shell_NotifyIcon");

            // If the function address is valid, call the function.
            if (NULL != ProcAdd)
            {
                // Get the window handle
                hwnd = (HWND) _parnl( paramList, 1 );

                // Get the icon ID
                uID = (UINT) _parnl( paramList, 2 );

                // Get the icons resource handle
                hicon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE(_parnl( paramList, 3 )) );

                // Get the tooltip text
                _parc(buffer, sizeof(buffer), paramList, 4);
                lpszTip = buffer;

                tnid.cbSize           = sizeof(NOTIFYICONDATA);
                tnid.hWnd             = hwnd;
                tnid.uID              = uID;
                tnid.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP;
                tnid.uCallbackMessage = WM_USER + 1966;
                tnid.hIcon            = hicon;
                if (lpszTip)
                    lstrcpyn(tnid.szTip, lpszTip, sizeof(tnid.szTip));
                else
                    tnid.szTip[0] = '\0';

                // Change the icon properties
                if ( (ProcAdd)(NIM_MODIFY, &tnid) )
                    _retl( paramList, TRUE );
                else
                    _retl( paramList, FALSE );

                // Free the icon resource
                if (hicon)
                    DestroyIcon(hicon);
            }

            // Free the DLL module.
            fFreeResult = FreeLibrary(hinstLib);

        }

    }
    else {
        _retl(paramList, FALSE);
    }
    return;
}

/*--------------------------------------------------------------------------------------*/
XPPRET XPPENTRY XICONINIT(XppParamList paramList)
// XICONINIT - initialize user message and event object variables
{
    if ( _partype( paramList, 0) == 2 ){
        UserMessage = (ULONG) _parnl( paramList, 1 );     /* User-Nachricht */
        EventObject = _conParam( paramList, 2, NULL );  /* Fenster-Objekt */
    }

    _ret( paramList );
    return;
}

/*--------------------------------------------------------------------------------------*/
XPPRET XPPENTRY XICONEXIT(XppParamList paramList)
// XICONEXIT - clean up before the taskbar object is destroyed
{
    HWND hwnd;

    // Get the window handle
    hwnd = (HWND)_parnl( paramList, 1 );  /* Fenster-Handle */

    // Reset the window procedure if neccesary
    if ( pWinProc){
        SetWindowLong( hwnd, GWL_WNDPROC, (LONG)pWinProc );
        pWinProc = NULL;
        }

    // Free the container for garbage collection
    if ( EventObject ){
        _conRelease( EventObject );
    }
    _ret( paramList );
    return;
}
