/*
 *  WICONIFY    A utility that allows you to iconify any Intuition window
 *              on any screen, and to open WB windows on any screen.
 *
 *  wHandler.c  The Input Handler and replacement routines for the
 *              Intuition functions trapped by SetFunction().
 *
 *  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/intuitionbase.h>
#include "wHandler.h"
#include "wMenu.h"
#include <libraries/dosextens.h> 
#include <graphics/gfxbase.h>


/*
 *  The definition for wIconify BackDrop windows
 *  (the IDCMPFlags are added later, since they share a UserPort)
 */
static struct NewWindow BDWindow =
{
   0,0, 0,0, -1,-1, NULL,
   SIMPLE_REFRESH| NOCAREREFRESH| BACKDROP| BORDERLESS| REPORTMOUSE,
   NULL, NULL, NULL, NULL, NULL, 20,20, -1,-1, WBENCHSCREEN
};

extern struct wMenuItem OpenWindowMenu[];

/*
 *  Tells whether a screen is iconified (i.e., pushed off the bottom)
 */
#define ISICONIFIED(s)  ((s)->TopEdge > GfxBase->NormalDisplayRows)



/*
 *  CheckIconifiedScreen()
 *
 *  If the front screen is iconified, move it to the back.
 *  If there is an active window
 *    If its screen is iconified
 *      Look for the next uniconified screen
 *      (Start over from the front of the list if we hit the end)
 *      If we found an uniconified screen and it has a window
 *        Activate the window
 *  (i.e, try to make sure that windows on iconified screens never become
 *  active.  Give preference to forward screens rather than backward ones.
 *  This may effect the use of some hot-key screen shufflers that activate
 *  windows on the new front screen:  pushing screens to the back will
 *  still work fine, but pulling the back one to the front will fail).
 */

static void CheckIconifiedScreen()
{
   struct Screen *iScreen;
   
   Forbid();
   iScreen = IntuitionBase->FirstScreen;
   if (ISICONIFIED(iScreen)) ScreenToBack(iScreen);
   if (IntuitionBase->ActiveWindow)
   {
      iScreen = IntuitionBase->ActiveWindow->WScreen;
      if (ISICONIFIED(iScreen))
      {
         while (iScreen && ISICONIFIED(iScreen)) iScreen = iScreen->NextScreen;
         if (iScreen == NULL)
         {
            iScreen = IntuitionBase->FirstScreen;
            while (iScreen && ISICONIFIED(iScreen))
               iScreen = iScreen->NextScreen;
         }
         if (iScreen && iScreen->FirstWindow)
            ActivateWindow(iScreen->FirstWindow);
      }
   }
   Permit();
}


/*
 *  wHandler()
 *
 *  If we are in the process of ending, just return the list
 *  Check for iconified screens
 *  While there are events in the list
 *    If they are mouse or key events
 *      If the event matches the iconify-key specification (up or down)
 *        If the event is the key or button up event
 *          Get a new message to send to the wIconify process
 *          Set up the values to pass to it
 *          Send it to the handler process
 *        Remove this event from the list
 *        (ie, process key-up events, but just remove key-down events)
 *      Otherwise, if the event matches the activation-key specification
 *        If it's the key up event
 *          Get the current active screen
 *          Activate its wIconify window, if any
 *        Remove this event from the list (as above)
 *    If we are removing the event from the list, do so
 *    Go on to the next event in the list
 *  Return the (modified) list.
 */

struct InputEvent *wHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   struct InputEvent **EventPtr = &EventList;
   struct InputEvent *theEvent;
   struct wIconMessage *theMessage;
   WSCREEN *theScreen;
   int RemoveEvent = FALSE;


   if (EndSignal) return(EventList);

   CheckIconifiedScreen();
   while (theEvent = *EventPtr)
   {
      if (theEvent->ie_Class == IECLASS_RAWMOUSE ||
          theEvent->ie_Class == IECLASS_RAWKEY)
      {
         if ((theEvent->ie_Code | IECODE_UP_PREFIX) == IconifyKey &&
            (theEvent->ie_Qualifier & (IconifyQuals | IconifyDisquals))
                   == IconifyQuals)
         {
            if (theEvent->ie_Code == IconifyKey)
            {
               if (NEWSTRUCT(wIconMessage,theMessage))
               {
                  theMessage->Window = IntuitionBase->ActiveWindow;
                  theMessage->Action = WI_ICONIFY;
                  theMessage->Flags  = WI_NOREPLY;
                  if ((theEvent->ie_Qualifier & IconifyChange) == IconifyChange)
                     theMessage->Flags |= WI_CHANGE;
                  theMessage->Message.mn_ReplyPort = NULL;
                  theMessage->Message.mn_Length = sizeof(struct wIconMessage);
                  PutMsg(wIconPort,theMessage);
               }
            }
            RemoveEvent = TRUE;
         } else if ((theEvent->ie_Code | IECODE_UP_PREFIX) == ActivateKey &&
                   (theEvent->ie_Qualifier & ActivateQuals) == ActivateQuals) {
            if (theEvent->ie_Code == ActivateKey)
            {
               Forbid();
               theScreen = FindScreen(IntuitionBase->ActiveScreen);
               if (theScreen && theScreen->BackDrop)
                  ActivateWindow(theScreen->BackDrop);
               Permit();
            }
            RemoveEvent = TRUE;
         }
      }
      if (RemoveEvent)
         *EventPtr = theEvent->ie_NextEvent;
        else
          EventPtr = &(theEvent->ie_NextEvent);
      RemoveEvent = FALSE;
   }
   return(EventList);
}


#define TOUPPER(c)      (((c)>='a'&&(c)<='z')?(c)-'a'+'A':c)


/*
 *  PrefixMatch()
 *
 *  Case-insensitive prefix match.  The empty string will match NULL pointers
 *  and empty strings, but are not considered prefixes to any string.
 *
 *  Return <0 if the first is smaller than the second,
 *         =0 if the first is a prefix of the second,
 *         >1 if the first is larger than the second.
 */

int PrefixMatch(s1,s2)
char *s1,*s2;
{
   int match = -1;
   
   if (*s1)
   {
      if (s2)
      {
         while (TOUPPER(*s1) == TOUPPER(*s2) && *s2 != 0) s1++,s2++;
         if (*s1 == 0) match = 0; else match = *s1 - *s2;
      } else match = 1;
   } else if (s2 == NULL || *s2 == 0) match = 0;
   return(match);
}


/*
 *  GetScreen()
 *
 *  Find the screen where the window will open via the following rules:
 *    If we are supposed to open on the active screen, get the active screen
 *    Otherwise, if we are supposed to open on the current WB, get it
 *    If we have found a screen
 *       If the screen is the real WB, forget it (no changes needed)
 *       If the screen is closing or iconified, forget it
 *       If the screen we are opening on is not currently active
 *  Get the actual screen pointer of the screen we are interested in
 *  If it is not active, we should bring it to the front
 *  return the WSCREEN we shoulod use (or NULL if no action)
 */

static WSCREEN *GetScreen(BringToFront)
int *BringToFront;
{
   WSCREEN *theScreen = NULL;
   struct Screen *chkScreen = NULL;

   if (OpenWindowMenu[OW_ACTIVESCRN].Item.Flags & CHECKED)
      theScreen = FindScreen(IntuitionBase->ActiveScreen); else
   if (OpenWindowMenu[OW_CURRENTWB].Item.Flags & CHECKED)
      theScreen = WBScreen;
   if (theScreen)
   {
      if (theScreen == RealWB) theScreen = NULL; else
      if ((theScreen->Flags & WI_CLOSING) ||
          (theScreen->Icon.Icon.Flags & WI_ICONIFIED))
              theScreen = NULL;
   }
   if (theScreen) chkScreen = theScreen->Screen;
   else if (RealWB) chkScreen = RealWB->Screen;
   *BringToFront = (chkScreen != IntuitionBase->ActiveScreen);
   return(theScreen);
}


/*
 *  CheckWindowSize()
 *
 *  If the window has a drag bar
 *    Check if it fits on the screen; if not, move it to a better spot
 *  If the window has a sizing gadget or accepts NEWSIZE messages
 *    If the window doesn't fint on the screen, shrink it so that it does
 *  Return an indication of whether the size was modified
 */

static int CheckWindowSize(newWindow)
struct NewWindow *newWindow;
{
   short x = newWindow->LeftEdge;
   short y = newWindow->TopEdge;
   short w = newWindow->Width;
   short h = newWindow->Height;
   short Sw = newWindow->Screen->Width;
   short Sh = newWindow->Screen->Height;
   int Sized = FALSE;
   
   if (newWindow->Flags & WINDOWDRAG)
   {
      if (x+w > Sw) x = newWindow->LeftEdge = (Sw > w)? Sw - w: 0;
      if (y+h > Sh) y = newWindow->TopEdge  = (Sh > h)? Sh - h: 0;
   }
   if ((newWindow->Flags & WINDOWSIZING) || (newWindow->IDCMPFlags & NEWSIZE))
   {
      if (x < Sw && x+w > Sw) newWindow->Width  = Sw - x, Sized = TRUE;
      if (y < Sh && y+h > Sh) newWindow->Height = Sh - y, Sized = TRUE;
   }
   return(Sized);
}


/*
 *  cOpenWindow()
 *
 *  If we have a new window and we are not in the process of ending Iconify
 *    Get the screen where we should open, and the bring-to-front flag
 *    If we have a screen to open on, and the window was opening on the WB
 *      Get a new wbWindow structure for it
 *      Copy the NewWindow info so we can change it
 *      Change the window specification so that it opens on the right screen
 *      If we are allowed to resize the window, check if it needs it
 *      Try to open the window
 *      If successful
 *        Link the window into the screen's list of foreign windows
 *        Send a resize message if the window was sized
 *      Otherwise
 *        Free the allocated structure (we won't be needing it)
 *    Otherwise (it's not a window we're supposed to change)
 *    If the real WB screen is open and this window is the WB backdrop
 *        window (ie, LoadWB is trying to start the WB)
 *      Copy the NewWindow structure so we can "fix" it
 *      Set the width to something smaller
 *      Move the window to the right side of the screen
 *      Make it a regular non-backdrop window with a border
 *      Add the sizing, dragging and depth gadgets
 *      Then open the window
 *  If the window was not opened yet, open it as a normal window
 *  If we are supposed to bring a screen to the front and the window
 *    was actually opened, bring the screen to front
 *  Return the pointer to the opened window
 */

struct Window *cOpenWindow(newWindow)
struct NewWindow *newWindow;
{
   struct Window *theWindow = NOWINDOW;
   struct NewWindow tmpWindow;
   WBWINDOW *WBWindow;
   WSCREEN *theScreen = NULL;
   int BringToFront = FALSE;
   int Sized = FALSE;

   if (newWindow && EndSignal == 0)
   {
      Forbid();
      theScreen = GetScreen(&BringToFront);
      if (theScreen && (newWindow->Type & SCREENTYPE) == WBENCHSCREEN)
      {
         if (NEWSTRUCT(wbWindow,WBWindow))
         {
            tmpWindow = *newWindow;
            tmpWindow.Screen = theScreen->Screen;
            tmpWindow.Type = CUSTOMSCREEN;
            if (OpenWindowMenu[OW_AUTORESIZE].Item.Flags & CHECKED)
               Sized = CheckWindowSize(&tmpWindow);
            theWindow = aOldOpenWindow(&tmpWindow);
            if (theWindow)
            {
               WBWindow->Window = theWindow;
               LinkWindow(WBWindow,theScreen);
               if (Sized) SendIntuiMessage(NEWSIZE,theWindow);
            } else {
               FREESTRUCT(wbWindow,WBWindow);
            }
         }
      } else if (RealWB && (newWindow->Flags & WBFLAGS) == WBFLAGS) {
         tmpWindow = *newWindow;
         tmpWindow.Width = WBWIDTH;
         tmpWindow.LeftEdge = RealWB->Screen->Width - WBWIDTH;
         tmpWindow.Flags &= ~(BORDERLESS | BACKDROP);
         tmpWindow.Flags |= WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH;
         theWindow = aOldOpenWindow(&tmpWindow);
      }
      Permit();
   }
   if (theWindow == NOWINDOW) theWindow = aOldOpenWindow(newWindow);
   if (BringToFront && theWindow != NOWINDOW) ScreenToFront(theWindow->WScreen);
   return(theWindow);
}


/*
 *  ParentWindow()
 *
 *  Get the parent window of the current one
 *  If it has one, then
 *     Find its icon
 *     While the window we are looking at is iconified
 *       Go to its parent and get its icon
 *  If we did not find a window, use the screen's backdrop window
 *  Return the window that we found
 */

static struct Window *ParentWindow(theWindow)
struct Window *theWindow;
{
   WICONREF *theIcon;
   WSCREEN *theScreen = NULL;
   
   theWindow = theWindow->Parent;
   if (theWindow)
   {
      theIcon = FindIcon(theWindow,&theScreen);
      while (theIcon && (theIcon->Icon.Flags & WI_ICONIFIED))
      {
         theWindow = theWindow->Parent;
         theIcon = (theWindow)? FindIcon(theWindow,&theScreen): NULL;
      }
   }
   if (theWindow == NULL && theScreen) theWindow = theScreen->BackDrop;
   return(theWindow);
}


/*
 *  FinishWindow()
 *
 *  Find the icon of the window that is closing
 *  If it has one, tell the Iconify process to remove it
 *  Find the wbWindow structure of this window (if any)
 *  If it has one (ie, it is opened on a foreign screen)
 *    Unlink the window from the screen
 *    If it was the last one on the screen, let the Iconify process know
 *      (it may want to update the menu to include CLOSESCREEN)
 *    Free the window structure
 *    If the Iconify process was waiting to end, let it try now
 *  Find the (first non-iconified) parent of this window
 *  Close the window
 *  If the screen was waiting to close, signal it that it can
 *  Of Iconify wants to hear about this window, signal it
 *  If Intuition would attempt to activate an iconified window,
 *    Activate the first non-iconified parent window
 */

static void FinishWindow(oldWindow,RemoveWindow)
struct Window *oldWindow;
void (*RemoveWindow)();
{
   WBWINDOW *theWindow;
   APTR  ScreenTask = NULL;
   ULONG ScreenSignal;
   ULONG IconSignal = 0;
   WICONREF *theIcon;
   WSCREEN *theScreen;
   struct Window *Parent,*OldParent;

   Forbid();
   theIcon = FindIcon(oldWindow,&theScreen);
   if (theIcon) SendIconMessage(WI_REMOVEICON,NULL,theScreen,theIcon);
   theWindow = FindWindow(oldWindow);
   if (theWindow)
   {
      ScreenTask   = theWindow->Screen->CloseTask;
      ScreenSignal = theWindow->Screen->CloseSignal;
      UnLinkWindow(theWindow);
      if (theWindow->Screen->Window == NULL) IconSignal = CloseSignal;
      FREESTRUCT(wbWindow,theWindow);
      IconSignal |= EndSignal;
   }
   Parent = ParentWindow(oldWindow); OldParent = oldWindow->Parent;
   Permit();
   (*RemoveWindow)(oldWindow);
   if (ScreenTask) Signal(ScreenTask,ScreenSignal);
   if (IconSignal) Signal(IconTask,IconSignal);
   if (Parent && Parent != OldParent) ActivateWindow(Parent);
}


/*
 *  cCloseWindow()
 *
 *  We are finished with this window; use the old CloseWindow() routine
 *  to remove it from the screen.
 */

void cCloseWindow(oldWindow)
struct Window *oldWindow;
{
   extern void aOldCloseWindow();

   FinishWindow(oldWindow,&aOldCloseWindow);
}


/*
 *  cOpenScreen()
 *
 *  If the screen was opened OK, and the Iconify process is not ending
 *    Look through the ignore-screen list for this screen
 *    If it does not appear in the list
 *      Get a new Screen sctructure for this screen
 *      Open a BackDrop window of the correct size on this screen
 *      Set up the screen's icon
 *      If the BackDrop window opened OK
 *        Link the screen into the list
 *        Set the new window as the current WB window
 *       else
 *        Free the structure, we won't be needing it
 */

void cOpenScreen(newScreen)
struct Screen *newScreen;
{
   WSCREEN *theScreen;
   struct Ignore *igScreen = IgnoreScreen;
   extern struct Window *OpenWindow();
   extern struct Menu wMenu[];

   if (newScreen && EndSignal == 0)
   {
      while (igScreen && PrefixMatch(igScreen->Title,newScreen->DefaultTitle))
         igScreen = igScreen->Next;
      if (igScreen == NULL)
      {
         if (NEWSTRUCT(wScreen,theScreen))
         {
            theScreen->Screen = newScreen;
            BDWindow.Screen = newScreen;
            BDWindow.Type   = newScreen->Flags & SCREENTYPE;
            BDWindow.Width  = newScreen->Width;
            BDWindow.Height = newScreen->Height;
            theScreen->BackDrop = OpenWindow(&BDWindow);
            theScreen->Icon.Icon.Name  = newScreen->DefaultTitle;
            theScreen->Icon.Icon.Flags = WI_SCREENICON| DefaultScreenFlags;
            theScreen->Icon.Screen     = theScreen;
            theScreen->Icon.Window     = theScreen->BackDrop;
            if (theScreen->BackDrop)
            {
               LinkScreen(theScreen);
               NewIconWindow(theScreen);
            } else {
               FREESTRUCT(wScreen,theScreen);
            }
         }
      }
   }
}


/*
 *  cCloseScreen()
 *
 *  Look to see if the screen is in the list of screens with wIconify windows
 *  If so,
 *    Mark the screen as closing so we don't allow any more icons on it
 *    If the screen is the current WB, clear the current WB
 *    If the screen is the real WB screen (should never happen), forget it
 *    Restore the screen's icon if it is iconified
 *    Tell anyone who's interested that the screen is closing
 *    Remove any icons we're allowed to remove
 *    Wait for any foreign windows or other icons to be removed
 *    If the current task is the wIconify handler process, unlink the screen
 *    Otherwise send wIconify the message to remove the screen
 *    Close the wIconify backdrop window
 *    Close the screen itself and free the screen structure
 *  Otherwise
 *    Just close the screen
 */

void cCloseScreen(oldScreen)
struct Screen *oldScreen;
{
   WSCREEN *theScreen;

   Forbid();
   theScreen = FindScreen(oldScreen);
   if (theScreen)
   {
      theScreen->Flags |= WI_CLOSING;
      if (theScreen == WBScreen) NewWBScreen(NULL);
      if (theScreen == RealWB)   RealWB = NULL;
      Permit();
      Restore(&(theScreen->Icon),TRUE);
      ReportMulti(WI_REPORTSCREENCLOSE,theScreen->Screen,theScreen);
      RemoveAutoIcons(theScreen);
      WaitForWindows(theScreen);
      if (FindTask(NULL) == IconTask)
         UnLinkScreen(theScreen);
        else
         SendIconMessage(WI_REMSCREEN,NULL,theScreen,NULL);
      CloseIconWindow(theScreen);
      aOldCloseScreen(oldScreen);
      FREESTRUCT(wScreen,theScreen);
   } else {
      Permit();
      aOldCloseScreen(oldScreen);
   }
}


/*
 *  cSetWindowTitles()
 *
 *  If a window was specified and it is getting a new title
 *     Look for the window's icon
 *     If found, and if the icon has a gadget (ie, it is iconified)
 *       Set the icon's title and center it
 *       Tell wIconify to update the screen
 */

void cSetWindowTitles(theWindow,wTitle,sTitle)
struct Window *theWindow;
UBYTE *wTitle,*sTitle;
{
   WICONREF *theIcon;
   WSCREEN *theScreen;

   if (theWindow && wTitle != (UBYTE *)-ONE)
   {
      Forbid();
      theIcon = FindIcon(theWindow,&theScreen);
      if (theIcon && theIcon->Gadget)
      {
         theIcon->Gadget->IText.IText = (wTitle)? wTitle: "[No Title]";
         theIcon->Gadget->IText.LeftEdge =
            (((struct Image *)(theIcon->Gadget->Gadget.GadgetRender))->Width -
               IntuiTextLength(&theIcon->Gadget->IText)) / 2;
         SendIconMessage(WI_REDRAW,NULL,theIcon->Screen,NULL);
      }
      Permit();
   }
}


/*
 *  cWindowToFront()
 *
 *  If a window was specified and it is a BACKDROP window
 *    If the current task is not the wIconify handler process
 *      Find the icon for this window
 *      If it was found and it is iconified, or if its screen was found
 *        and the window is the wIconify backdrop window, don't allow the
 *        window to be moved
 *  Return TRUE if the window can be moved, FLASE otherwise
 */

int cWindowToFront(theWindow)
struct Window *theWindow;
{
   int MoveIt = TRUE;
   WICONREF *theIcon;
   WSCREEN *theScreen;

   if (theWindow && (theWindow->Flags & BACKDROP))
   {
      if (FindTask(NULL) != IconTask)
      {
         theIcon = FindIcon(theWindow,&theScreen);
         if ((theIcon && (theIcon->Icon.Flags && WI_ICONIFIED)) ||
             (theScreen && theScreen->BackDrop == theWindow)) MoveIt = FALSE;
      }
   }
   return(MoveIt);
}

/*
 *  cWindowToBack()
 *
 *  If a window was specified and it is a BACKDROP window
 *    If the current task is not the wIconify handler process
 *      Find the icon for this window
 *      If it was found and it is iconified, or if its screen was found
 *        and the window is the wIconify backdrop window, don't allow the
 *        window to be moved
 *      Otherwise, the window is a BACKDROP window that is being
 *        sent to the back, so it will fall behind the wIconify window
 *        were the user will not be able to get at it, so iconify it
 *        so it can be retrieved.
 *  Return TRUE if the window can be moved, FLASE otherwise
 */

int cWindowToBack(theWindow)
struct Window *theWindow;
{
   int MoveIt = TRUE;
   WICONREF *theIcon;
   WSCREEN *theScreen;

   if (theWindow && (theWindow->Flags & BACKDROP))
   {
      if (FindTask(NULL) != IconTask)
      {
         theIcon = FindIcon(theWindow,&theScreen);
         if ((theIcon && (theIcon->Icon.Flags & WI_ICONIFIED)) ||
             (theScreen && theScreen->BackDrop == theWindow)) MoveIt = FALSE;
           else SendIconMessage(WI_ICONIFY,theWindow,NULL,NULL);
      }
   }
   return(MoveIt);
}


/*
 *  cActivateWindow()
 *
 *  If a window was specified and it is a BACKDROP window
 *    If the current process is not the wIconify process
 *      Find the icon for this window
 *      If found and it is iconified, don't all the window to be activated
 *  Return TRUE if it can be activated, FALSE otherwise
 */

int cActivateWindow(theWindow)
struct Window *theWindow;
{
   int Activate = TRUE;
   WICONREF *theIcon;
   WSCREEN *theScreen;

   if (theWindow && (theWindow->Flags & BACKDROP))
   {
      if (FindTask(NULL) != IconTask)
      {
         theIcon = FindIcon(theWindow,&theScreen);
         if (theIcon && (theIcon->Icon.Flags & WI_ICONIFIED)) Activate = FALSE;
      }
   }
   return(Activate);
}


/*
 *  cBuildSysRequest()
 *
 *  If wIconify is stil running
 *    If a window was not specified
 *      Get the process that is calling BuildSysRequest()
 *      If it is really a process and it has an error window, use it
 *      If there is still no window
 *        Get the screen and bring-to-front flag as for normal windows
 *        If it is for a different screen, use it's backdrop window
 *    If we have a window to work with
 *      If we don't already have a screen, get it
 *      If the screen is the real WB, ignore it
 *      If we got a screen
 *        Allocate a new wbWindow structure
 *        If this fails, use the original window (ie, don't switch its screen)
 *  Get the system requester on the proper screen
 *  If we actually got a window (not TRUE or FALSE)
 *    If we have a wbWindow for this window, link it in
 *    Bring the screen to the front, if necessary
 *    If the process has a name, use it as the window title
 *      Otherwise use "System Request" as usual
 *  Otherwise, free the wbWindow, if it was allocated
 *  Return the window created by the original BuildSysRequest
 */

struct Window *cBuildSysRequest(theWindow,Body,PosText,NegText,Flags,w,h)
struct Window *theWindow;
struct IntuiText *Body,*PosText,*NegText;
ULONG Flags;
long w,h;
{
   extern struct Window *aOldBuildSysRequest();
   struct Process *theProc = NULL;
   struct Window *onWindow = theWindow;
   struct Window *newWindow;
   WBWINDOW *WBWindow = NULL;
   WSCREEN *theScreen = NULL;
   int BringToFront = FALSE;
   char *theTitle;

   Forbid();
   if (EndSignal == 0)
   {
      if (onWindow == NULL)
      {
         theProc = (struct Process *)FindTask(NULL);
         if (theProc->pr_Task.tc_Node.ln_Type == NT_PROCESS &&
             theProc->pr_WindowPtr != (APTR)-1)
                 onWindow = (struct Window *)theProc->pr_WindowPtr;
         if (onWindow == NULL)
         {
            theScreen = GetScreen(&BringToFront);
            if (theScreen) onWindow = theScreen->BackDrop;
         }
      }
      if (onWindow)
      {
         if (theScreen == NULL) theScreen = FindScreen(onWindow->WScreen);
         if (theScreen == RealWB) theScreen = NULL;
         if (theScreen)
            if (NEWSTRUCT(wbWindow,WBWindow) == NULL) onWindow = theWindow;
      }
   }
   newWindow = aOldBuildSysRequest(onWindow,Body,PosText,NegText,Flags,w,h);
   if (newWindow && newWindow != (struct Window *)TRUE)
   {
      if (WBWindow)
      {
         WBWindow->Window = newWindow;
         LinkWindow(WBWindow,theScreen);
      }
      if (BringToFront) ScreenToFront(newWindow->WScreen);
      if (theProc && theProc->pr_Task.tc_Node.ln_Name)
         theTitle = theProc->pr_Task.tc_Node.ln_Name;
        else
         theTitle = "System Request";
      SetWindowTitles(newWindow,theTitle,-1L);
   } else {
      if (WBWindow) FREESTRUCT(wbWindow,WBWindow);
   }
   Permit();
   return(newWindow);
}


/*
 *  cFreeSysRequest()
 *
 *  We are finished with this requester; use the old FreeSysRequest() routine
 *  to remove it from the screen.
 */

void cFreeSysRequest(oldWindow)
struct Window *oldWindow;
{
   extern void aOldFreeSysRequest();

   FinishWindow(oldWindow,&aOldFreeSysRequest);
}


/*
 *  cAutoRequest()
 *
 *  Use (the modified) BuildSysRequest() to get a system requester
 *  If we got a window (not TRUE or FALSE)
 *    Get the signal bit for the window's port
 *    While we are not done with the requester
 *      While there are more messages in the port
 *        Get the message info and reply to the message
 *        If the message is one of the positive ones, end with TRUE
 *        If it is one of the negative ones, end with FALSE
 *        If it is a key event and the left amiga key id down
 *          If it is left-amiga-B, cancel the requester
 *          If it is left-amiga-V, retry the requester
 *        If it is a gadget event
 *          If the gadget is the negative one, end with FALSE
 *          If it is the positive one, end with TRUE
 *        If we are not done yet, Wait() for more messages
 *    Finish up the system requester
 *  Otherwise return the answer obtained from BuildSysRequest()
 *  Return the result (TRUE or FALSE)
 */

int cAutoRequest(theWindow,Body,PosText,NegText,PFlags,NFlags,w,h)
struct Window *theWindow;
struct IntuiText *Body,*PosText,*NegText;
ULONG PFlags,NFlags;
long w,h;
{
   struct Window *newWindow;
   struct IntuiMessage *theMessage;
   struct Gadget *theGadget;
   ULONG Signal,Class;
   USHORT Code,Qual;
   int Result = NOTDONE;
   extern struct IntuiMessage *GetMsg();
   extern struct Window *BuildSysRequest();
   
   newWindow = BuildSysRequest(theWindow,Body,PosText,NegText,
      GADGETUP|RAWKEY|PFlags|NFlags,w,h);
   if (newWindow && newWindow != (struct Window *)TRUE)
   {
      Signal = (1 << newWindow->UserPort->mp_SigBit);
      while (Result == NOTDONE)
      {
         while (theMessage = GetMsg(newWindow->UserPort))
         {
            Class = theMessage->Class;
            Code  = theMessage->Code;
            Qual  = theMessage->Qualifier;
            theGadget = (struct Gadget *)theMessage->IAddress;
            ReplyMsg(theMessage);
            if (Class & PFlags) Result = TRUE; else
            if (Class & NFlags) Result = FALSE; else
            if (Class == RAWKEY && (Qual & AMIGALEFT))
            {
               if (Code == KEYCODE_B) Result = FALSE; else
               if (Code == KEYCODE_V) Result = TRUE;
            } else if (Class == GADGETUP) {
               if (theGadget->GadgetText == NegText) Result = FALSE; else
               if (theGadget->GadgetText == PosText) Result = TRUE;
            }
         }
         if (Result == NOTDONE) Wait(Signal);
      }
      FreeSysRequest(newWindow);
   } else Result = (int) newWindow;
   return(Result);
}




/*
 *  SendIconMessage()
 *
 *  Set up the data for the IconMessage
 *  If the current task is the wIconify process
 *    Call the event routine with the dummy message
 *  Otherwise
 *    Create a reply port
 *    If successful
 *      Set the message reply port
 *      Send the message to the wIconify process
 *      Wait for the reply
 *      Remove the port
 */

void SendIconMessage(theAction,theWindow,theScreen,theIcon)
int theAction;
struct Window *theWindow;
WSCREEN *theScreen;
WICONREF *theIcon;
{
   struct wIconMessage theMessage;
   struct MsgPort *thePort;
   extern struct wIconMessage *WaitPort();
   extern struct MsgPort *CreatePort();
   
   theMessage.Window = theWindow;
   theMessage.Icon   = theIcon;
   theMessage.Action = theAction;
   theMessage.Flags  = 0;
   theMessage.Data.wScreen = theScreen;
   if (FindTask(NULL) == IconTask)
   {
      DoIconEvent(&theMessage,TRUE);
   } else {
      thePort = CreatePort(NULL);
      if (thePort)
      {
         theMessage.Message.mn_ReplyPort = thePort;
         theMessage.Message.mn_Length = sizeof(struct wIconMessage);
         PutMsg(wIconPort,&theMessage);
         WaitPort(thePort);
         DeletePort(thePort);
      }
   }
}


/*
 *  SendIntuiMessage()
 *
 *  If the window has a message port and it accepts this type of message
 *    Allocate a new IntuiMessage structure
 *    If successful
 *      Set the important IntuiMessage fields
 *      Use the wIconify UserPort as the reply port (the wIconify process
 *        will clean up after us)
 *      Send the message to the window
 */

void SendIntuiMessage(theClass,theWindow)
ULONG theClass;
struct Window *theWindow;
{
   struct IntuiMessage *theMessage;

   if (theWindow && theWindow->UserPort && (theWindow->IDCMPFlags & theClass))
   {
      if (NEWSTRUCT(IntuiMessage,theMessage))
      {
         theMessage->Class = theClass;
         theMessage->Qualifier = 0;
         theMessage->Code = 0;
         theMessage->MouseX = theMessage->MouseY = 0;
         theMessage->IAddress = NULL;
         theMessage->IDCMPWindow = theWindow;
         theMessage->SpecialLink = NULL;
         theMessage->ExecMessage.mn_ReplyPort = wUserPort;
         theMessage->ExecMessage.mn_Length = sizeof(struct IntuiMessage);
         PutMsg(theWindow->UserPort,theMessage);
      }
   }
}
