/*
 *  SWINDOWS    A program to allow you to open windows on any screen by
 *              supplying the screen name in thw window title
 *
 *              Copyright 1989 by Davide P. Cervone.
 *  You may use this code, provided this copywrite notice is kept intact.
 */

#include "swHandler.h"

static char *program   = PROGRAM;
static char *copyright = COPYRIGHT;

/* don't want to have to include LIB:lc.lib in the linking */

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


/*
 *  FindScreen()
 *
 *  FindScreen looks through the IntuitionBase linked list of screens
 *  for a screen whose title matches the one supplied by the user.
 *  If no title was supplied, then the active screen is returned.
 *  A case-insensitive prefix match is performed.  If the screen is
 *  not found, FindScreen returns NULL.
 */

static struct Screen *FindScreen(OldTitle,Title)
UBYTE *OldTitle,*Title;
{
   register struct Screen *theScreen = IntuitionBase->ActiveScreen;
   register UBYTE *TName,*SName;
   int NotDone = TRUE;
   ULONG ILock;
   
   if (OldTitle != Title)
   {
      ILock = LockIBase(0L);
      theScreen = IntuitionBase->FirstScreen;
      while (theScreen && NotDone)
      {
         TName = theScreen->Title;
         if (TName)
         {
            SName = OldTitle;
            while (SName < Title && toupper(*TName) == toupper(*SName))
            {
               SName++;
               TName++;
            }
            if (SName == Title) NotDone = FALSE;
         }
         if (NotDone) theScreen = theScreen->NextScreen;
      }
      UnlockIBase(ILock);
   }
   return(theScreen);
}


/*
 *  OpenOnScreen()
 *
 *  The NewWindow structure is modified temporarily to incorporate the 
 *  correct screen pointer and type.  The title is shortened so that
 *  the screen name and "::" are note included in the window title.
 *  Once the window is opened, the NewWindow structure is restored in case
 *  it needs to be used by the calling program again.
 */

static struct Window *OpenOnScreen(theScreen,NewWindow,Title,OldTitle)
struct Screen *theScreen;
struct NewWindow *NewWindow;
UBYTE *Title,*OldTitle;
{
   struct Window *theWindow;
   struct Screen *OldScreen;
   USHORT OldType;
   
   OldScreen = NewWindow->Screen;
   OldType   = NewWindow->Type;

   NewWindow->Screen = theScreen;
   NewWindow->Type   = (theScreen->Flags & SCREENTYPE);
   NewWindow->Title  = (*Title)? Title: NULL;
   theWindow = aOldOpenWindow(NewWindow);

   NewWindow->Title  = OldTitle;
   NewWindow->Screen = OldScreen;
   NewWindow->Type   = OldType;

   return(theWindow);
}


/*
 *  cOpenWindow()
 *
 *  This is the replacement for OpenWindow.
 *
 *  If a NewWindow structure was supplied and it has a title string, then
 *  Search the title string for "::" (change CHAR1 and CHAR2 if you want to 
 *    use different characters for the separators)
 *  If "::" was found, then
 *     Look for the screen with the given title.
 *     If one was found, then
 *        Get the ScreenlistItem associated with that screen
 *        If one was found (or newly created) then
 *          Make a new WindowListItem for this window
 *          If successfull, then
 *            Save the ScreenItem pointer
 *            Open the window on the specified screen, and
 *            If window opened OK, then add it to the linked list
 *            Otherwise free the WindowListItem, and maybe the ScreenItem too
 *           Otherwise
 *            Free the ScreenItem if it was newly created.
 *  If for any reason, the old OpenWindow was not called, 
 *    Try to open the window without changes to the NewWindow structure.
 *  return the window pointer produced.
 */

struct Window *cOpenWindow(NewWindow)
struct NewWindow *NewWindow;
{
   UBYTE *Title,*OldTitle;
   struct Window *theWindow = NOWINDOW;
   struct Screen *theScreen;
   SLISTITEM ScreenItem;
   WLISTITEM WindowItem;

   if (NewWindow && NewWindow->Title)
   {
      Title = OldTitle = NewWindow->Title;
      while (*Title && (*Title != CHAR1 || *(Title+1) != CHAR2)) Title++;
      if (*Title)
      {
         theScreen = FindScreen(OldTitle,Title);
         if (theScreen)
         {
            ScreenItem = FindScreenListItem(theScreen);
            if (ScreenItem)
            {
               if (NEW(WindowListItem,WindowItem))
               {
                  WindowItem->Screen = ScreenItem;
                  theWindow = 
                     OpenOnScreen(theScreen,NewWindow,Title+2,OldTitle);
                  if (theWindow)
                  {
                     AddWindowListItem(WindowItem,theWindow);
                  } else {
                     FREE(WindowListItem,WindowItem);
                     UnuseScreenListItem(ScreenItem);
                  }
               } else {
                  UnuseScreenListItem(ScreenItem);
               }
            }
         }
      }
   }
   if (theWindow == NOWINDOW) theWindow = aOldOpenWindow(NewWindow);
   return(theWindow);
}


/*
 *  cCloseWindow()
 *
 *  This is the replacement for CloseWindow().
 *  (the old CloseWindow is called by the stub before calling this routine)
 *  
 *  Look through the window item list for this window.
 *  If the window was found (i.e., it was opened by sWindows) then
 *    Free the window item and remove it from the list.
 *    Check to see whether the screen can now be closed
 */

void cCloseWindow(theWindow)
struct Window *theWindow;
{
   WLISTITEM WindowItem;
   SLISTITEM ScreenItem;
   
   Forbid();
   WindowItem = WindowList;
   while (WindowItem && WindowItem->Window != theWindow)
      WindowItem = WindowItem->Next;
   if (WindowItem)
   {
      ScreenItem = WindowItem->Screen;
      FreeWindowListItem(WindowItem);
      UnuseScreenListItem(ScreenItem);
   }
   Permit();
}


/*
 *  cCloseScreen()
 *
 *  This is the replacement for CloseScreen().
 *  (the old CloseScreen is called by the stub after this routine returns)
 *
 *  Look through the screen list to for this screen.
 *  If found (i.e., there is a window opened by sWindows open on it), then
 *    Allocate a signal so so that we can wait for all the windows to close
 *    If one could not be allocated, use the default signal.
 *    Clear the close signal.
 *    Save the old task pointer and signal (in case some other task also 
 *       called CloseScreen for this screen).
 *    Set the task pointer and signal in the ScreenItem so that we can get 
 *      signaled when all the windows are closed.
 *    Wait for the close signal to arrive.
 *    (time passes ... finally all the windows are closed)
 *    If there was another task was waiting, signal it.
 *    Free the allocated signal bit.
 */
 
void cCloseScreen(theScreen)
struct Screen *theScreen;
{
   SLISTITEM ScreenItem;
   APTR NextTask;
   ULONG NextSignal,mySignal;
   UBYTE mySigBit;
   extern APTR FindTask();
   extern ULONG AllocSignal();

   Forbid();
   ScreenItem = ScreenList;
   while (ScreenItem && ScreenItem->Screen != theScreen)
      ScreenItem = ScreenItem->Next;
   if (ScreenItem)
   {
      mySigBit = AllocSignal(-ONE);
      mySignal = (mySigBit == -ONE)? DEFAULTSIGNAL: (ONE<<mySigBit);
      SetSignal(mySignal,0L);
      NextSignal = ScreenItem->CloseSignal;
      NextTask   = ScreenItem->CloseTask;
      ScreenItem->CloseSignal = mySignal;
      ScreenItem->CloseTask   = FindTask(NULL);
   }
   Permit();
   if (ScreenItem)
   {
      Wait(mySignal);
      if (NextTask) Signal(NextTask,NextSignal);
      if (mySigBit != -ONE) FreeSignal(mySigBit);
   }
}
