/*
 * utility.c - general purpose utility routines
 *
 * Created by Microsoft Corporation.
 * (c) Copyright Microsoft Corp. 1990 - 1992  All Rights Reserved
 *
 */

//--- INCLUDES ---
//The order of the includes matters for compile efficiency ... /YX

#include <windows.h>
#include <ole.h>
#include "demorc.h"
#include "global.h"

#include "utility.h"
#include "object.h"
#include "dialog.h"

FARPROC lpfnTimerProc = NULL;
static  int iTimerID = 0;
static  APPITEMPTR lpaItemHold;

/****************************************************************************
 *  ErrorMessage()
 *
 *  Display a message box containing the specified string from the table.
 *
 *  id WORD       - Index into string table.
 ***************************************************************************/

VOID FAR ErrorMessage(                 //- ENTRY:
   WORD           id                   //- message ID
){                                     //- LOCAL:
   char           sz[CBMESSAGEMAX];    //- string
   HWND           hwnd;                //- parent window handle

   if (IsWindow(hwndProp))
      hwnd = hwndProp;
   else if (IsWindow(hwndFrame))
      hwnd = hwndFrame;
   else
      return;

   LoadString(hInst, id, sz, CBMESSAGEMAX);
   MessageBox(hwnd, sz, szAppName, MB_OK | MB_ICONEXCLAMATION);

}


/****************************************************************************
 *  Hourglass()
 *
 *  Put up or takes down the hourglass cursor as needed.
 *
 *  int  bToggle  - TRUE turns the hour glass on
 *                  HG_OFF turn it off
 ***************************************************************************/

VOID FAR Hourglass(                    //- ENTRY:
   BOOL           bOn                  //- hourglass on/off
){                                     //- LOCAL:
   static HCURSOR hcurWait = NULL;     //- hourglass cursor
   static HCURSOR hcurSaved;           //- old cursor
   static         iCount = 0;


   if (bOn)
   {
      iCount++;
      if (!hcurWait)
         hcurWait = LoadCursor(NULL, IDC_WAIT);
      if (!hcurSaved)
         hcurSaved = SetCursor(hcurWait);
   }
   else if (!bOn)
   {
      if (--iCount < 0 )
         iCount = 0;
      else if (!iCount)
      {
         SetCursor(hcurSaved);
         hcurSaved = NULL;
      }
   }

}


/***************************************************************************
 *  WaitForObject()
 *
 *  Dispatch messagee until the specified object is not busy.
 *  This allows asynchronous processing to occur.
 *
 *  lpObject    LPOLEOBJECT - pointer to object
 **************************************************************************/

void FAR WaitForObject(                //- ENTRY:
   APPITEMPTR    paItem                //- pointer to OLE object
){                                     //- LOCAL
   int i = 0;

   while (OleQueryReleaseStatus(paItem->lpObject) == OLE_BUSY)
   {
      if (!i)
      {
         ToggleBlockTimer(TRUE,paItem);//- set timer
         i++;
      }
      ProcessMessage(hwndFrame, hAccTable);
   }

}

/***************************************************************************
 *  WaitForAllObjects()
 *
 *  Wait for all asynchronous operations to complete.
 **************************************************************************/

VOID FAR WaitForAllObjects(VOID)
{

   while (cOleWait)
      ProcessMessage(hwndFrame, hAccTable) ;

}

/****************************************************************************
 * ProcessMessage()
 *
 * Obtain and dispatch a message. Used when in a message dispatch loop.
 *
 *  Returns BOOL - TRUE if message other than WM_QUIT retrieved
 *                 FALSE if WM_QUIT retrieved.
 ***************************************************************************/

BOOL FAR ProcessMessage(               //- ENTRY:
   HWND           hwndFrame,           //- main window handle
   HANDLE         hAccTable            //- accelerator table handle
){                                     //- LOCAL:
   BOOL           fReturn;             //- return value
   MSG            msg;                 //- message

   if (fReturn = GetMessage(&msg, NULL, NULL, NULL))
   {
      if (cOleWait || !TranslateAccelerator(hwndFrame, hAccTable, &msg))
      {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
      }
   }
   return fReturn;

}

/****************************************************************************
 *  Dirty()
 *
 *  Keep track of weather modifications have been made
 *  to the document or not.
 *
 *  iAction - action type:
 *            DOC_CLEAN set document clean flag true
 *            DOC_DIRTY the opposite
 *            DOC_UNDIRTY undo one dirty op
 *            DOC_QUERY return present state
 *
 *  Returs int - present value of fDirty; 0 is clean.
 ***************************************************************************/

int FAR Dirty(                         //- ENTRY:
   int            iAction              //- see above comment
){                                     //- LOCAL:
   static int     iDirty = 0;          //- dirty state >0 is dirty

   switch (iAction)
   {
      case DOC_CLEAN:
         iDirty = 0;
         break;
      case DOC_DIRTY:
         iDirty++;
         break;
      case DOC_UNDIRTY:
         iDirty--;
         break;
      case DOC_QUERY:
         break;
   }
   return(iDirty);

}

/***************************************************************************
 *  ObjectsBusy()
 *
 *  This function enumerates the OLE objects in the current document
 *  and displays a message box stating whether an object is busy.
 *  This function calls  the DisplayBusyMessage() function which
 *  performs most of the work. This function is only used by the macro
 *  BUSY_CHECK(), defined in object.h.
 *
 *  fSelectionOnly  BOOL -NOT USED?
 *
 *  BOOL - TRUE if one or more objects found to be busy
 *             FALSE otherwise
 *
 ***************************************************************************/

BOOL FAR ObjectsBusy ()
{
   APPITEMPTR pItem;

   if (iTimerID)
   {
      RetryMessage(NULL,RD_CANCEL);
      return TRUE;
   }

   for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem))
      if (DisplayBusyMessage(pItem))
         return TRUE;

   return FALSE;

}

/***************************************************************************
 *  DisplayBusyMessage()
 *
 *  This function determines if an object is busy and displays
 *  a message box stating this status.
 *
 *  Returns BOOL - TRUE if object is busy
 **************************************************************************/

BOOL FAR DisplayBusyMessage (          //- ENTRY:
   APPITEMPTR     paItem               //- application item pointer
){                                     //- LOCAL:

   if (OleQueryReleaseStatus(paItem->lpObject) == OLE_BUSY)
   {
      RetryMessage(paItem,RD_CANCEL);
      return TRUE;
   }
   return FALSE;

}

/***************************************************************************
 * CreateNewUniqueName()
 *
 * Create a string name unique to this document. This is done by using the
 * prefix string("OleDemo #") and appending a counter to the end of the
 * prefix string. The counter is incremented  whenever a new object is added.
 * String will be 14 bytes long.
 *
 * Return LPSTR - pointer to unique object name.
 ***************************************************************************/

LPSTR FAR CreateNewUniqueName(         //- ENTRY:
   LPSTR          lpstr                //- destination pointer
){

    wsprintf( lpstr, "%s%04d", OBJPREFIX, iObjectNumber++ );
    return( lpstr );

}

/***************************************************************************
 *  ValidateName()
 *
 *  This function ensures that the given object name is valid and unique.
 *
 *  Returns: BOOL - TRUE if object name valid
 **************************************************************************/

BOOL FAR ValidateName(                 //- ENTRY:
   LPSTR          lpstr                //- pointer to object name
){                                     //- LOCAL:
   LPSTR          lp;                  //- worker string
   int            n;
                                       //- check for "OleDemo #" prefix
   lp = OBJPREFIX;

   while( *lp )
   {
      if( *lpstr != *lp )
         return( FALSE );

      lpstr++; lp++;
   }
                                       //- convert string number to int
   for (n = 0 ; *lpstr ; n = n*10 + (*lpstr - '0'),lpstr++);

   if( n > 9999 )                      //- 9999 is largest legal number
      return FALSE;

   if( iObjectNumber <= n)             //- Make count > than any current
      iObjectNumber = n + 1;           //- object to ensure uniqueness

    return TRUE;
}

/***************************************************************************
 * FreeAppItem()
 *
 * Free application item structure and destroy the associated structure.
 **************************************************************************/

VOID FAR FreeAppItem(                  //- ENTRY:
   APPITEMPTR     pItem                //- pointer to application item
){                                     //- LOCAL:
   HANDLE         hWork;               //- handle used to free

   if (pItem)
   {                                   //- destroy the window
      if (pItem->hwnd)
         DestroyWindow(pItem->hwnd);

      hWork = LocalHandle((WORD)pItem);//- get handle from pointer

      if (pItem->aLinkName)
         DeleteAtom(pItem->aLinkName);

      if (pItem->aServer)
         DeleteAtom(pItem->aServer);

      LocalUnlock(hWork);
      LocalFree(hWork);
   }

}

/***************************************************************************
 * SizeOfLinkData()
 *
 * Find the size of a linkdata string.
 **************************************************************************/

LONG FAR SizeOfLinkData(               //- ENTRY:
   LPSTR          lpData               //- pointer to link data
){                                     //- LOCAL:
   LONG           lSize;               //- total size

   lSize = (LONG)lstrlen(lpData)+1;       //- get size of classname
   lSize += (LONG)lstrlen(lpData+lSize)+1; //- get size of doc.
   lSize += (LONG)lstrlen(lpData+lSize)+2;//- get size of item
   return lSize;

}

/****************************************************************************
 * ShowDoc()
 *
 * Display all the child windows associated with a document, or make all the
 * child windows hidden.
 ***************************************************************************/

VOID FAR ShowDoc(                      //- ENTRY:
   LHCLIENTDOC    lhcDoc,              //- document handle
   int            iShow                //- show/hide
){                                     //- LOCAL:
   APPITEMPTR     pItem;               //- application item pointer
   APPITEMPTR     pItemTop = NULL;

   for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem))
   {
      if (pItem->lhcDoc == lhcDoc)
      {
         if (!pItemTop)
            pItemTop = pItem;
         ShowWindow(pItem->hwnd,(iShow ? SW_SHOW : SW_HIDE));
         pItem->fVisible = (BOOL)iShow;
      }
   }

   if (pItemTop)
      SetTopItem(pItemTop);

}

/****************************************************************************
 * GetNextActiveItem()
 *
 * Returns HWND - the next visible window.
 ***************************************************************************/

APPITEMPTR FAR GetNextActiveItem()
{                                      //- LOCAL:
   APPITEMPTR     pItem;               //- application item pointer

   for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem))
      if (pItem->fVisible)
         break;

   return pItem;

}

/****************************************************************************
 * GetTopItem()
 ***************************************************************************/

APPITEMPTR FAR GetTopItem()
{
   HWND hwnd;

   if (hwnd = GetTopWindow(hwndFrame))
      return ((APPITEMPTR)GetWindowWord(hwnd,0));
   else
      return NULL;

}
/****************************************************************************
 * GetNextItem()
 ***************************************************************************/

APPITEMPTR FAR GetNextItem(            //- ENTRY:
   APPITEMPTR     pItem                //- application item pointer
){                                     //- LOCAL:
   HWND           hwnd;                //- next item window handle

   if (hwnd = GetNextWindow(pItem->hwnd, GW_HWNDNEXT))
      return((APPITEMPTR)GetWindowWord(hwnd,0));
   else
      return NULL;

}

/****************************************************************************
 * SetTopItem()
 ***************************************************************************/

void FAR SetTopItem(
   APPITEMPTR     pItem
){
   APPITEMPTR     pLastItem;

   pLastItem = GetTopItem();
   if (pLastItem && pLastItem != pItem)
      SendMessage(pLastItem->hwnd,WM_NCACTIVATE, 0, 0L);

   if (!pItem)
      return;

   if (pItem->fVisible)
   {
      BringWindowToTop(pItem->hwnd);
      SendMessage(pItem->hwnd,WM_NCACTIVATE, 1, 0L);
   }

}

/***************************************************************************
 * ReallocLinkData()
 *
 * Reallocate link data in order to avoid creating lots and lots of global
 * memory thunks.
 **************************************************************************/

BOOL FAR ReallocLinkData(              //- ENTRY:
   APPITEMPTR     pItem,               //- application item pointer
   long           lSize                //- new link data size
){                                     //- LOCAL:
   HANDLE         handle;              //- temporary memory handle

   handle = (HANDLE)GlobalHandle(HIWORD(pItem->lpLinkData));
   GlobalUnlock(handle);

   if (!(pItem->lpLinkData = GlobalLock(GlobalReAlloc(handle, lSize, NULL))))
   {
      ErrorMessage(E_FAILED_TO_ALLOC);
      return FALSE;
   }

   return TRUE;

}

/***************************************************************************
 * AllocLinkData()
 *
 * Allocate link data space.
 **************************************************************************/

BOOL FAR AllocLinkData(                //- ENTRY:
   APPITEMPTR     pItem,               //- application item pointer
   long           lSize                //- link data size
){

   if (!(pItem->lpLinkData = GlobalLock(
         GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT ,lSize)
      )))
   {
      ErrorMessage(E_FAILED_TO_ALLOC);
      return FALSE;
   }

   return TRUE;
}

/***************************************************************************
 * FreeLinkData()
 *
 * Free the space associated with a linkdata pointer.
 **************************************************************************/

void FAR FreeLinkData(                 //- ENTRY:
   LPSTR          lpLinkData           //- pointer to linkdata
){                                     //- LOCAL:
   HANDLE         handle;              //- temporary memory handle

   if (lpLinkData)
   {
      handle = (HANDLE)GlobalHandle(HIWORD(lpLinkData));
      GlobalUnlock(handle);
      GlobalFree(handle);
   }
}

/****************************************************************************
 * ShowNewWindow()
 *
 * Show a new application item window.
 ***************************************************************************/

void FAR ShowNewWindow(                //- ENTRY:
   APPITEMPTR     pItem
){

   if (pItem->fVisible)
   {
      pItem->fNew = TRUE;
      SetTopItem(pItem);
      ShowWindow(pItem->hwnd,SW_SHOW);
   }
   else
      ObjDelete(pItem,DELETE);

}

/****************************************************************************
 * UnqualifyPath()
 *
 * return pointer to unqualified path name.
 ***************************************************************************/

PSTR FAR UnqualifyPath(PSTR pPath)
{
   PSTR pReturn;

   for (pReturn = pPath; *pPath; pPath++)
      if (*pPath == ':' || *pPath == '\\')
         pReturn = pPath+1;

   return pReturn;

}

/****************************************************************************
 * StartBlockTimer()
 *
 * Toggle a timer used to check for blocked servers.
 ***************************************************************************/

void FAR ToggleBlockTimer(BOOL bSet, APPITEMPTR lpaItem)
{
   lpaItemHold = lpaItem;

   if (bSet && !iTimerID)
   {
      if (!lpfnTimerProc)
         lpfnTimerProc = MakeProcInstance(fnTimerBlockProc, hInst);
      iTimerID = SetTimer(hwndFrame,1, 3000,lpfnTimerProc);
   }
   else if (iTimerID)
   {
      KillTimer(hwndFrame,1);
      iTimerID = 0;
   }

}

/****************************************************************************
 *  fnTimerBlockProc()
 *
 *  Timer callback procedure
 ***************************************************************************/

BOOL FAR PASCAL __export fnTimerBlockProc(      //- ENTRY:
   HWND     hWnd,
   WORD     wMsg,
   int      iTimerID,
   DWORD    dwTime
){

   if (!hRetry)
      {
      RetryMessage(lpaItemHold, RD_RETRY | RD_CANCEL);
      return TRUE;
      }
   return FALSE;
}
