/*
 *  Click-Handler.c     Input Handler for ClickUpFront, which brings a
 *                      window to the front when you double-click in it.
 *
 *              Copyright (c) 1987 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

#include <exec/types.h>
#include <devices/inputevent.h>
#include <intuition/intuitionbase.h>

static char *version = "Click-Handler v2.1 (October 1987)";
static char *author  = "Copyright (c) 1987 by Davide P. Cervone";

extern struct Layer *WhichLayer();
extern void myHandlerStub();

#define WINDOW(layer)       ((struct Window *)((layer)->Window))
#define TOPLAYER(screen)    ((screen)->LayerInfo.top_layer)

#define ie_Secs         ie_TimeStamp.tv_secs
#define ie_Mics         ie_TimeStamp.tv_micro

static UBYTE LastCode = 0;     /* the last ie_Code for a button event */
static long LastSecs = 0;      /* seconds field from last mouse click */
static long LastMics = 0;      /* micros  field from last mouse click */
static long StayMask;          /* qualifier keys that allow you to click */
                               /* a window without bringing it to the front */


struct IntuitionBase *IntuitionBase = NULL;
struct LayersBase    *LayersBase    = NULL;
struct SysBase       *SysBase       = NULL;


/*
 *  Setup()
 *
 *  ClickUpFront calls LoadSeg() to get this handler into memory.  The segment
 *  that it gets points to this routine.  ClickUpFront calls Setup() and 
 *  passes the IntuitionBase, LayersBase and SysBase pointers that it
 *  has initialized (with OpenLibrary()).  Setup returns a pointer to
 *  the actual input handler, which ClickUpFront installs.
 */

static long Setup(Ibase,Lbase,Sbase,flags)
struct IntuitionBase *Ibase;
struct LayersBase *Lbase;
struct SysBase *Sbase;
long flags;
{
   IntuitionBase = Ibase;
   LayersBase = Lbase;
   SysBase = Sbase;
   StayMask = flags;
   return((long) &myHandlerStub);
}


/*
 *  myHandler()
 *
 *  This is the input handler.  For each event in the event list:
 *  If it is a raw mouse event:
 *    Ignore button-up events,
 *    For button down events, if it is the same as the last button, and
 *      the mouse has not moved, and the button was a double click, then
 *      Find the active window and screen (where the button press occured)
 *      for SELECTDOWN buttons:
 *        If the screen is not the first one, then bring it to the front. 
 *        If a window is selected, then find the top layer with a window.
 *        If the window where the click ocurred is not the top, and
 *          the stay-put qualifier key is not pressed, then 
 *            bring the window to the front.
 *      for MENUDOWN buttons:
 *        If a window is selected, and the window is not the bottom window
 *          already, and the window below it is not a backdrop window, then
 *          if the selected window has no double-menu requester and 
 *            the stay-put qualifier is not present (if there IS a 
 *            DM request and the stay-put qualifier IS present), then 
 *              send the window to the back.
 *              change the event into a mouse move with zero offsets
 *              (that is, remove one of the right button clicks).
 *         otherwise (there is no window, or it is already at the bottom,
 *          or the one below is a backdrop), then
 *          find the first window on the next screen and activate it.
 *          Send the active screen to the back.
 *     Otherwise (the mouse was moved, or it was not a double click, etc.)
 *       record this click and the time it ocurred.
 *    For mouse moves, clear the last code (ie, don't match a double click)
 *  If the event was a keyboard event, then set the times to zero.
 *  Ignore all other event types (timer, disk).
 *
 *  Finally, return the event list so that Intuition can do its thing.
 *
 */

struct InputEvent *myHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   register struct InputEvent *theEvent = EventList;
   register struct Window *theWindow;
   register struct Screen *theScreen;
   register struct Layer *topLayer;

   Forbid();
   while(theEvent)
   {
      switch(theEvent->ie_Class)
      {
         case IECLASS_RAWMOUSE:
            if ((theEvent->ie_Code & IECODE_UP_PREFIX) == 0)
            {
               if (theEvent->ie_Code == LastCode &&
                   theEvent->ie_X == 0 && theEvent->ie_Y == 0 &&
                   DoubleClick(LastSecs,LastMics,
                              theEvent->ie_Secs,theEvent->ie_Mics))
               {
                  theWindow = IntuitionBase->ActiveWindow;
                  theScreen = IntuitionBase->ActiveScreen;
                  switch(LastCode)
                  {
                     case SELECTDOWN:
                        if (theScreen != IntuitionBase->FirstScreen)
                           ScreenToFront(theScreen);
                        if (theWindow)
                        {
                           topLayer = TOPLAYER(theScreen);
                           while (topLayer && WINDOW(topLayer) == NULL)
                              topLayer = topLayer->back;
                           if (theWindow != WINDOW(topLayer) &&
                              (theEvent->ie_Qualifier & StayMask) == 0)
                                 WindowToFront(theWindow);
                        }
                        break;

                     case MENUDOWN:
                        if (theWindow && theWindow->WLayer->back &&
                           (theWindow->Flags & BACKDROP) == 0 &&
                           (theWindow->WLayer->back->Flags &
                            LAYERBACKDROP) == 0)
                        {
                           if (theWindow && (theWindow->DMRequest == NULL) ==
                              ((theEvent->ie_Qualifier & StayMask) == 0))
                           {
                              WindowToBack(theWindow);
                              theEvent->ie_Code = IECODE_NOBUTTON;
                              theEvent->ie_X = theEvent->ie_Y = 0;
                           }
                        } else {
                           if (theScreen->NextScreen)
                           {
                              topLayer = TOPLAYER(theScreen->NextScreen);
                              while (topLayer && WINDOW(topLayer) == NULL)
                                 topLayer = topLayer->back;
                              if (topLayer && WINDOW(topLayer))
                                 ActivateWindow(WINDOW(topLayer));
                              ScreenToBack(theScreen);
                           }
                        }
                        break;
                  }
                  LastCode = 0;
               } else {
                  LastCode = theEvent->ie_Code;
                  LastSecs = theEvent->ie_Secs;
                  LastMics = theEvent->ie_Mics;
               }
            } else {
               if (theEvent->ie_Code == IECODE_NOBUTTON) LastCode = 0;
            }
            break;

         case IECLASS_RAWKEY:
            LastSecs = LastMics = 0;
            break;
      }
      theEvent = theEvent->ie_NextEvent;
   }
   Permit();
   return(EventList);
}
