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

//*** INCLUDES ****

#include <windows.h>
#include <ole.h>

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

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:
   DWORD          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
   BOOL bTimerOn = FALSE;

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

   if (bTimerOn)
       ToggleBlockTimer(FALSE);//* toggle timer off
}

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

VOID FAR WaitForAllObjects(VOID)
{
   BOOL bTimerOn = FALSE;

   while (cOleWait) 
   {
      if (!bTimerOn)
         bTimerOn = ToggleBlockTimer(TRUE);//* set timer

      ProcessMessage(hwndFrame, hAccTable) ;
   }

   if (bTimerOn)
       ToggleBlockTimer(FALSE);//* toggle timer off
     
}

/****************************************************************************
 * 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, 0, 0)) 
   {
      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((LPSTR)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)GetWindowLong(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)GetWindowLong(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 = GlobalHandle(pItem->lpLinkData);
   GlobalUnlock(handle);

   if (!(pItem->lpLinkData = GlobalLock(GlobalReAlloc(handle, lSize, 0)))) 
   {
      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 = GlobalHandle(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,OLE_OBJ_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;

}

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

BOOL FAR ToggleBlockTimer(BOOL bSet)
{     
   if (bSet && !iTimerID)
   {
      if (iTimerID = SetTimer(hwndFrame,1, 3000, (TIMERPROC) fnTimerBlockProc))
          return TRUE;
   }
   else if (iTimerID)
   {
      KillTimer(hwndFrame,1);
      iTimerID = 0;
      return TRUE;
   }
   
   return FALSE;
}

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

VOID CALLBACK fnTimerBlockProc(      //* ENTRY: 
   HWND     hWnd,
   UINT     wMsg,
   UINT     iTimerID,
   DWORD    dwTime
){

   if (!hRetry)
      RetryMessage(lpaItemHold, RD_RETRY | RD_CANCEL);

}