/*
 *  WICONIFY    A utility that allows you to iconify any Intuition window
 *              on any screen, and to open WB windows on any screen.
 *
 *  wIconCalls.c    The programmer's interface to wIconify.  This module
 *                  handles the inter-process communication between
 *                  wIconify and client programs.
 *
 *  Copyright 1990 by Davide P. Cervone, all rights reserved.
 *  You may use this code, provided this copyright notice is kept intact.
 */

#define INTUITION_PREFERENCES_H             /* don't need 'em */
#include <intuition/intuition.h>
#include "wStructs.h"

/*
 *  The version of wIconCalls.c
 */

#define MAJVERSION      1
#define MINVERSION      4

/*
 *  The expected version of wIconify itself
 */

#define MAJLIBVERS      3
#define MINLIBVERS      7


#define NEWICONIFY      ((struct MsgPort *)-1)

static struct MsgPort *wIconPort;           /* the wIconify message port */
extern struct MsgPort *FindPort();

extern struct Window *wBackDropOf();        /* defined later on */



/*
 *  ContactIconify()
 *
 *  If we have already got a pointer to wIconify, and the new one is not
 *    the same as the old one, indicate that a new wIconify is running.
 *  Otherwise, if we found a pointer to wIconify,
 *    Get a reply port.
 *    Set up a contact message for the new wIconify, and make contact.
 *    Wait for a reply and then remove the port.
 *    If there is a version mismatch, clear the pointer to wIconify.
 *  Return the (possibly modified) pointer to wIconify.
 */

static void ContactIconify(NewIconPort)
struct MsgPort *NewIconPort;
{
   struct wIconMessage theMessage;
   struct MsgPort *thePort;
   extern struct wIconMessage *WaitPort();
   extern struct MsgPort *CreatePort();

   if (wIconPort && NewIconPort != wIconPort)
   {
      NewIconPort = NEWICONIFY;
   } else if (NewIconPort) {
      thePort = CreatePort(NULL,0);
      if (thePort)
      {
         theMessage.Window = NULL;
         theMessage.Icon   = NULL;
         theMessage.Action = WI_MAKECONTACT;
         theMessage.Flags  = 0;
         theMessage.Message.mn_ReplyPort = thePort;
         theMessage.Message.mn_Length = sizeof(struct wIconMessage);
         PutMsg(NewIconPort,&theMessage);
         WaitPort(thePort);
         DeletePort(thePort);
         if (theMessage.Action != WI_VERSIONOK ||
             theMessage.Data.Version.Maj < MAJLIBVERS ||
            (theMessage.Data.Version.Maj == MAJLIBVERS &&
             theMessage.Data.Version.Min < MINLIBVERS))
                  NewIconPort = NULL;
      }
   }
   wIconPort = NewIconPort;
}


/*
 *  wIconifyActive()
 *
 *  Get a pointer to the current wIconify port.
 *  If it's not the one we used to have, try to contact the new wIconify.
 *  return a success status.
 */

int wIconifyActive()
{
   struct MsgPort *NewIconPort;

   NewIconPort = FindPort(WICONPORT);
   if (NewIconPort != wIconPort) ContactIconify(NewIconPort);
   return(wIconPort && wIconPort != NEWICONIFY);
}


/*
 *  *SendMessage()
 *
 *  Initializes and sends a message to wIconify.  The data items are
 *  stored in the proper union fields, and the result from wIconify is
 *  returned.
 *
 *  If wIconify is running,
 *    Create a reply port, and initialize the message.
 *    Set the data field for the proper message class.
 *    Send the message to wIconify and wait for the reply.
 *    Remove the reply port.
 *  Massage the return data, and return the icon.
 */

static WICONREF *SendMessage(theAction,theWindow,theIcon,Data1,Data2)
struct Window *theWindow;
WICONREF *theIcon;
long Data1,Data2;
{
   struct wIconMessage theMessage;
   struct MsgPort *thePort;
   extern struct wIconMessage *WaitPort();
   extern struct MsgPort *CreatePort();
   extern APTR FindTask();

   theMessage.Icon = NULL;
   if (wIconifyActive())
   {
      thePort = CreatePort(NULL,0);
      if (thePort)
      {
         theMessage.Window = theWindow;
         theMessage.Icon   = theIcon;
         theMessage.Action = theAction;
         theMessage.Flags  = 0;
         switch(theAction)
         {
#ifndef WINDOW_CALLS
#ifndef ICONSETTER_CALLS
#ifndef SCREEN_CALLS
            case WI_SELECTICON:
               if (Data1) theMessage.Flags = WI_ADDTOSELECT;
               break;

            case WI_MOVEICON:
               theMessage.Data.Position.x = (WORD) Data1;
               theMessage.Data.Position.y = (WORD) Data2;
               break;

            case WI_REDRAW:
               theMessage.Data.wScreen = (WSCREEN *) Data1;
               break;

            case WI_UPDATEICON:
               theMessage.Data.Icon = (WICON *) Data1;
               break;

            case WI_ADDICON:
            case WI_SELECTNEXT:
            case WI_OPENSELECT:
            case WI_CLOSESELECT:
#endif
            case WI_MAKEWB:
#endif
#endif
            case WI_BACKDROPOF:
               theMessage.Data.Screen = (struct Screen *) Data1;
               break;

#ifndef WINDOW_CALLS
#ifndef ICONSETTER_CALLS
            case WI_NEWSCREEN:
               theMessage.Data.NewScreen.Depth = Data1;
               theMessage.Data.NewScreen.Modes = Data2;
               break;

            case WI_OPENON:
               theMessage.Data.OpenOn.ScreenType = Data1;
               theMessage.Data.OpenOn.SizeToFit  = Data2;
               break;

#ifdef WICONIFY_PRIVATE
            case WI_ENDICONIFY:
               theMessage.Data.DataPtr = FindTask(NULL);
               break;
#endif
#endif
#endif
         }
         theMessage.Message.mn_ReplyPort = thePort;
         theMessage.Message.mn_Length = sizeof(struct wIconMessage);
         PutMsg(wIconPort,&theMessage);
         WaitPort(thePort);
         DeletePort(thePort);
      }
   }
   if (theAction == WI_BACKDROPOF)
      theMessage.Icon = (WICONREF *)theMessage.Window;
#ifndef WINDOW_CALLS
#ifndef ICONSETTER_CALLS
   else if (theAction == WI_NEWSCREEN)
      theMessage.Icon = (WICONREF *)theMessage.Data.Screen;
#endif
#endif
   return(theMessage.Icon);
}


/*
 *  The remainder of the routines simply call SendMessage to send
 *  the appropriate message type with the appropriate data, and
 *  return a pointer to a wIconRef when necessary
 */


#ifndef SCREEN_CALLS
/*
 *  wIconify()
 *  wIconifyScreen()
 *
 *  Attempt to iconify the specified window or screen (iconifying
 *  the backdrop window iconifies the screen).
 */

WICONREF *wIconify(theWindow)
struct Window *theWindow;
{
   return(SendMessage(WI_ICONIFY,theWindow,NULL));
}

WICONREF *wIconifyScreen(theScreen)
struct Screen *theScreen;
{
   return(wIconify(wBackDropOf(theScreen)));
}


/*
 *  wIconOf()
 *  wIconOfScreen()
 *
 *  Returns the wIconRef for the icon of the specified window or screen
 *  (the screen icon is stored as the icon of the backdrop window).
 */

WICONREF *wIconOf(theWindow)
struct Window *theWindow;
{
   return(SendMessage(WI_ICONOF,theWindow,NULL));
}

#ifndef ICONSETTER_CALLS

WICONREF *wIconOfScreen(theScreen)
struct Screen *theScreen;
{
   return(wIconOf(wBackDropOf(theScreen)));
}


/*
 *  wRestore()
 *
 *  Attempt to restore the window of the given wIconRef.
 */

void wRestore(theIcon)
WICONREF *theIcon;
{
   SendMessage(WI_RESTORE,NULL,theIcon);
}


/*
 *  wGetIconData()
 *
 *  Copy the data from the wIconRef into the wIcon structure.
 *  (Use with wUpdateIcon() to modify an icon).
 */

void wGetIconData(theIcon,theIconRef)
WICON *theIcon;
WICONREF *theIconRef;
{
   if (theIcon && theIconRef)
      Forbid(), *theIcon = theIconRef->Icon, Permit();
}
#endif


/*
 *  *wSetIcon()
 *  *wSetScreenIcon()
 *
 *  Assign the given icon definition to the specified window or screen, and
 *  return the wIconRef pointer of the resulting icon.
 */

WICONREF *wSetIcon(theWindow,theIcon)
struct Window *theWindow;
WICON *theIcon;
{
   return(SendMessage(WI_SETICON,theWindow,theIcon));
}

WICONREF *wSetScreenIcon(theScreen,theIcon)
struct Screen *theScreen;
WICON *theIcon;
{
   return(wSetIcon(wBackDropOf(theScreen),theIcon));
}


#ifndef WINDOW_CALLS
#ifndef ICONSETTER_CALLS
/*
 *  wUnSetIcon()
 *  wUnSetScreenIcon()
 *
 *  Clear any icon associated with the window or screen.
 */

void wUnSetIcon(theWindow)
struct Window *theWindow;
{
   SendMessage(WI_UNSETICON,theWindow,NULL);
}

void wUnSetScreenIcon(theScreen)
struct Screen *theScreen;
{
   wUnSetIcon(wBackDropOf(theScreen));
}


/*
 *  wUpdateIcon()
 *
 *  Update the values of the wIconRef to those specified in the wIcon.
 */

void wUpdateIcon(theIcon,WIcon)
WICONREF *theIcon;
WICON *WIcon;
{
   SendMessage(WI_UPDATEICON,NULL,theIcon,WIcon);
}

/*
 *  wSelectIcon()
 *
 *  Attempt to select the given icon.  If AddToSelect is TRUE, this is
 *  a multiple selection, otherwise, any previously selected items are
 *  deselected first.
 */

void wSelectIcon(theIcon,AddToSelect)
WICONREF *theIcon;
long AddToSelect;
{
   SendMessage(WI_SELECTICON,NULL,theIcon,AddToSelect);
}


/*
 *  wUnSelectIcon()
 *
 *  Make the given icon become unselected.
 */

void wUnSelectIcon(theIcon)
WICONREF *theIcon;
{
   SendMessage(WI_UNSELECT,NULL,theIcon);
}


/*
 *  wMoveIcon()
 *
 *  Move the icon to the specified location.
 */

void wMoveIcon(theIcon,x,y)
WICONREF *theIcon;
WORD x,y;
{
   SendMessage(WI_MOVEICON,NULL,theIcon,x,y);
}
#endif

/*
 *  wIconXY()
 *
 *  Gets the position of the given icon and places them in X and Y.
 */

void wIconXY(theIcon,X,Y)
WICONREF *theIcon;
WORD *X,*Y;
{
   Forbid();
   if (theIcon)
   {
      if (theIcon->Gadget)
      {
         *X = theIcon->Gadget->Gadget.LeftEdge;
         *Y = theIcon->Gadget->Gadget.TopEdge;
      } else {
         *X = theIcon->Icon.x;
         *Y = theIcon->Icon.y;
      }
   }
   Permit();
}

#ifndef ICONSETTER_CALLS
/*
 *  wModifyFlags()
 *
 *  Update the icon's Flags field to the given flags.
 */

void wModifyFlags(theIcon,Flags)
WICONREF *theIcon;
ULONG Flags;
{
   if (theIcon)
   {
      Forbid();
      theIcon->Icon.Flags &= WI_SYSTEMFLAGS;
      theIcon->Icon.Flags |= (Flags & ~WI_SYSTEMFLAGS);
      Permit();
   }
}


/*
 *  wModifyReport()
 *
 *  Modify the icon's Report field to the given classes.
 */

void wModifyReport(theIcon,Report)
WICONREF *theIcon;
ULONG Report;
{
   if (theIcon) Forbid(), theIcon->Icon.Report = Report, Permit();
}


/*
 *  wRefreshIcon()
 *
 *  Redraw all the icons on the screen containing the given icon.
 */

void wRefreshIcon(theIcon)
WICONREF *theIcon;
{
   if (theIcon) SendMessage(WI_REDRAW,NULL,NULL,theIcon->Screen);
}


/*
 *  *wAddIcon()
 *
 *  Add an icon (with no associated window) to the given screen.
 *  Returns the wIconRef for the newly created icon.
 */

WICONREF *wAddIcon(theScreen,theIcon)
struct Screen *theScreen;
WICON *theIcon;
{
   return(SendMessage(WI_ADDICON,NULL,theIcon,theScreen));
}


/*
 *  wRemoveIcon()
 *
 *  Remove an icon created by wAddIcon().
 */

void wRemoveIcon(theIcon)
WICONREF *theIcon;
{
   SendMessage(WI_REMOVEICON,NULL,theIcon);
}


/*
 *  wCloseIcon()
 *
 *  Close the window associated with a given icon.
 *  The same as choosing CLOSE from the ICON menu.
 */

void wCloseIcon(theIcon)
WICONREF *theIcon;
{
   SendMessage(WI_CLOSEICON,NULL,theIcon);
}


/*
 *  wNoIconify()
 *
 *  Mark the given window as not-iconifiable.  Any attempt by the user
 *  to iconify the window will fail.
 */

void wNoIconify(theWindow)
struct Window *theWindow;
{
   WICONREF *theIcon;
   
   theIcon = wIconOf(theWindow);
   if (theIcon == NULL) theIcon = wSetIcon(theWindow,NULL);
   if (theIcon) Forbid(), theIcon->Icon.Flags |= WI_NOICONIFY, Permit();
}


/*
 *  wIconReport()
 *
 *  Returns the current settings of the icon's Report field.
 */

ULONG wIconReport(theIcon)
WICONREF *theIcon;
{
   ULONG Report = 0;

   if (theIcon) Forbid(), Report = theIcon->Icon.Report, Permit();
   return(Report);
}
#endif
#endif


#ifndef ICONSETTER_CALLS
/*
 *  wIconFlags()
 *
 *  Returns the current contents of the icon's Flags field.
 */

ULONG wIconFlags(theIcon)
WICONREF *theIcon;
{
   ULONG Flags = 0;

   if (theIcon) Forbid(), Flags = theIcon->Icon.Flags, Permit();
   return(Flags);
}


/*
 *  wIsIconified()
 *  wIsScreenIconified()
 *
 *  Returns TRUE if the given window or screen is iconified,
 *  and FALSE otherwise.
 */

int wIsIconified(theWindow)
struct Window *theWindow;
{
   return((wIconFlags(wIconOf(theWindow)) & WI_ICONIFIED) != FALSE);
}

int wIsScreenIconified(theScreen)
struct Screen *theScreen;
{
   return(wIsIconified(wBackDropOf(theScreen)));
}


#ifndef WINDOW_CALLS
/*
 *  wWindowOf()
 *
 *  Returns a pointer to the window associated with a given icon.
 */

struct Window *wWindowOf(theIcon)
WICONREF *theIcon;
{
   struct Window *theWindow = NULL;
   
   if (theIcon) Forbid(), theWindow = theIcon->Window, Permit();
   return(theWindow);
}
#endif
#endif
#endif


/*
 *  *wBackDropOf()
 *
 *  Returns a pointer to the wIconify window of the given screen.  If there
 *  is no wIconify window on the screen, NULL is returned.
 */

struct Window *wBackDropOf(theScreen)
struct Screen *theScreen;
{
   return((struct Window *)SendMessage(WI_BACKDROPOF,NULL,NULL,theScreen));
}


/*
 *  These routines are used by the wIconify utilities, such as wIconifyWindow,
 *  wIconScreen, wOpenOn, and wMakeWB, to do their thing.  User programs
 *  should not use these routines, in general.
 */


#ifdef WKEYS_CALLS
/*
 *  wSelectNext()
 *
 *  Attempt to select the next icon on the given screen.
 */

void wSelectNext(theScreen)
struct Screen *theScreen;
{
   SendMessage(WI_SELECTNEXT,NULL,NULL,theScreen);
}


/*
 *  wOpenSelected()
 *
 *  Open the selected icons on the given screen.
 *  Same as choosing OPEN from the ICON menu.
 */

void wOpenSelected(theScreen)
struct Screen *theScreen;
{
   SendMessage(WI_OPENSELECT,NULL,NULL,theScreen);
}


/*
 *  wCloseSelected()
 *
 *  Close the selected icons on the given screen.
 *  Same as choosing CLOSE from the ICON menu.
 */

void wCloseSelected(theScreen)
struct Screen *theScreen;
{
   SendMessage(WI_CLOSESELECT,NULL,NULL,theScreen);
}
#endif


#ifdef SCREEN_CALLS
/*
 *  *wNewScreen()
 *
 *  Open a new screen of the given depth an modes, and return a pointer
 *  to the new screen, or NULL if none created.
 *  (For use by wIconScreen).
 */

struct Screen *wNewScreen(Depth,Modes)
UWORD Depth,Modes;
{
   return((struct Screen *)SendMessage(WI_NEWSCREEN,NULL,NULL,Depth,Modes));
}


/*
 *  wMakeWB()
 *
 *  Change the current WB screen to the given screen.
 *  (For use by wMakeWB).
 */

void wMakeWB(theScreen)
struct Screen *theScreen;
{
   SendMessage(WI_MAKEWB,NULL,NULL,theScreen);
}


/*
 *  wSetOpenOn()
 *
 *  Set the OpenOn submenu to the specified type of window and sizing option.
 *  (For use by wOpenOn).
 */

void wSetOpenOn(ScreenType,SizeToFit)
int ScreenType,SizeToFit;
{
   SendMessage(WI_OPENON,NULL,NULL,ScreenType,SizeToFit);
}
#endif


#ifdef WICONIFY_PRIVATE
/*
 *  wEndIconify()
 *
 *  Ask wIconify to terminate.  This is for the internal use of wIconify ONLY!
 */

APTR wEndIconify()
{
   return((APTR)SendMessage(WI_ENDICONIFY,NULL,NULL));
}
#endif
