MAG Disk (Jun 1990) : source / PopUpMenu.c

#include "PopUpMenu.h"

/****************************************
 * PopUpMenu(InputSignals)              *
 *					*
 * Input:				*
 *   InputSignals  - Allocated signals. *
 * Output:				*
 *   none				*
 ****************************************/
VOID PopUpMenu(InputSignals)
  struct SignalData *const InputSignals;
{
  IMPORT struct WindowData MenuWindow, ItemWindow, SubWindow;
  IMPORT struct RastPort Rp;
  IMPORT struct Window *ActiveWindow;
  IMPORT struct Screen *Screen;
  IMPORT struct Menu *Menues;
  IMPORT UWORD	CurrentMenuNr;
  IMPORT UWORD	MenuFontSize;
  IMPORT WORD	MouseX,MouseY;
  IMPORT struct SignalSemaphore PopUpSemaphore;
  IMPORT struct timerequest  *TimerReqBlock;
  IMPORT struct Window *LastWindow;
  IMPORT WORDBITS LastSelected;
  IMPORT struct IntuitionBase *IntuitionBase;

  WORDBITS MenuNumber;

  ObtainSemaphore(&PopUpSemaphore);

  /* be sure that the window to use is still active */
  if (IntuitionBase->ActiveWindow != ActiveWindow)
    goto Abort;

#ifndef NOLOCKING
  LockLayers(&Screen->LayerInfo); /* puh! now we are safe (I hope) */
#endif
  InitGlobals();

  /* try to open menues */
  if ((Menues) AND
      (OpenMenuWindow((ActiveWindow == LastWindow)?
		       (UWORD)(MENUNUM(LastSelected)) * MenuFontSize: 0))) {

    const LONGBITS MenuButtonSignals = InputSignals->MenuUpSig |
				       InputSignals->MenuDownSig;
    const LONGBITS MouseMovedSignal  = InputSignals->MouseMovedSig;
    const LONGBITS ButtonSignals     = MenuButtonSignals |
				       InputSignals->SelectDownSig;

    BOOL Waiting = FALSE;
    struct MsgPort *const TimerPort =
			  TimerReqBlock->tr_node.io_Message.mn_ReplyPort;
    const LONGBITS TimerSignal	     = 1L << TimerPort->mp_SigBit;

    /* menuwindow opened ok */
    MenuNumber = LastSelected = MENUNULL;

    /* start timer */

    SetSignal(0,TimerSignal); /* clear old timermessages */
    if (CheckIO((struct IORequest *)TimerReqBlock) == NULL) {
      WaitIO((struct IORequest *)TimerReqBlock);
      GetMsg(TimerPort); /* get rid of the message */
    }
    QueueTimer();

    FOREVER {
      const LONGBITS SignalBits = Wait(MouseMovedSignal |
				       ButtonSignals |
				       TimerSignal);

      if (SignalBits & MouseMovedSignal) {
	Waiting = FALSE;
	if ((MouseX != Screen->MouseX) OR (MouseY != Screen->MouseY)) {
	  MouseX = Screen->MouseX;
	  MouseY = Screen->MouseY;
	  SelectItem();
	}
      }
      if (SignalBits & ButtonSignals) {
	const WORDBITS Selected = FinalSelect();

	Waiting = FALSE;
	/* do multiple select */
	if (Selected != LastSelected) { /* same => do nothing */
	  if (MenuNumber == MENUNULL)
	    MenuNumber = Selected;   /* first selectiom */
	  else
	    (ItemAddress(Menues,(LONG)LastSelected))->NextSelect = Selected;
	  LastSelected = Selected;
	}

	if (SignalBits & MenuButtonSignals) /* Menubutton -> final selection */
	  break;
      }

      if (SignalBits & TimerSignal) {
	GetMsg(TimerPort); /* get rid of the message */

	if (Waiting) {

	  /* the input.device has stopped, unlock the screen and wait */

	  SwapBits(&SubWindow);
	  SwapBits(&ItemWindow);
	  SwapBits(&MenuWindow);

#ifndef NOLOCKING
	  UnlockLayers(&Screen->LayerInfo);
#endif
	  Wait(MouseMovedSignal);
#ifndef NOLOCKING
	  LockLayers(&Screen->LayerInfo); /* let's hope the screen is still here */
#endif
	  SwapBits(&MenuWindow);
	  SwapBits(&ItemWindow);
	  SwapBits(&SubWindow);
	}

	/* start timer again */
	QueueTimer();
	Waiting = !Waiting;
      }
    } /* FOREVER */

    /* close all windows */
    CloseItemWindow(&SubWindow);
    CloseItemWindow(&ItemWindow);
    CloseItemWindow(&MenuWindow);
  }

  /* remember last menu selected */
  LastWindow = ActiveWindow;

#ifndef NOLOCKING
  UnlockLayers(&Screen->LayerInfo);
#endif
  /* tell the window the good news (MENUPICK) */
  TellWindow(MenuNumber);
Abort:
  ReleaseSemaphore(&PopUpSemaphore);
}

/*****************************************
 *  TellWindow() - Send Fake Menu Event. *
 *					 *
 * Input:				 *
 *   none				 *
 * Output:				 *
 *   none				 *
 *****************************************/
VOID TellWindow(MenuNumber)
  WORDBITS MenuNumber;
{
  IMPORT struct Window	      *const ActiveWindow;
  IMPORT struct IOStdReq      *const InputReqBlock;
  IMPORT struct IntuitionBase *const IntuitionBase;

  STATIC struct InputEvent MyFakeEvent;

  /* get the current time */
  CurrentTime(&MyFakeEvent.ie_TimeStamp.tv_secs,
	      &MyFakeEvent.ie_TimeStamp.tv_micro);

  MyFakeEvent.ie_Class	   = IECLASS_MENULIST;
  MyFakeEvent.ie_Code	   = MenuNumber;
  MyFakeEvent.ie_Qualifier = 0;
  MyFakeEvent.ie_NextEvent = NULL;
  MyFakeEvent.ie_EventAddress = NULL;

  InputReqBlock->io_Command = IND_WRITEEVENT;
  InputReqBlock->io_Flags   = 0;
  InputReqBlock->io_Length  = sizeof(struct InputEvent);
  InputReqBlock->io_Data    = (APTR)&MyFakeEvent;

  /* be sure the right window will get the message. */
  ActivateWindow(ActiveWindow);

  DoIO(InputReqBlock);
}
/****************************************************
 * InitGlobals() - Init the needed global variables *
 *						    *
 * Input:					    *
 *   none					    *
 * Output:					    *
 *   none					    *
 ****************************************************/
VOID InitGlobals()
{
  IMPORT struct WindowData MenuWindow, ItemWindow, SubWindow;
  IMPORT UWORD	CurrentMenuNr;
  IMPORT struct Menu	 *CurrentMenuPtr;
  IMPORT struct MenuItem *CurrentItem, *CurrentSubItem;

  IMPORT struct Screen	 *Screen;
  IMPORT struct Menu	 *Menues;
  IMPORT struct RastPort Rp;
  IMPORT struct Window	 *const ActiveWindow;
  IMPORT UWORD	MenuFontSize;
  IMPORT BOOL	ScreenType;

  /* no window is open */
  ItemWindow.BitMapOk = FALSE;
  SubWindow.BitMapOk = FALSE;
  MenuWindow.BitMapOk = FALSE;

  /* no menu is selected */
  CurrentMenuNr  = 0;
  CurrentMenuPtr = NULL;
  CurrentItem	 = NULL;
  CurrentSubItem = NULL;

  Menues = ActiveWindow->MenuStrip;

  /* set up a rastport for the screen */
  InitRastPort(&Rp);

  SetFont(&Rp,Screen->RastPort.Font);
  Rp.BitMap = &Screen->BitMap;

  /* default font height */
  MenuFontSize = Rp.TxHeight + 2;

  /* Size of screen */
  ScreenType = (Screen->ViewPort.Modes & HIRES) == 0;
}

/******************************************************
 * QueueTimer() - Queue the timer to go of after 0.2s *
 *						      *
 * Input:					      *
 *   none					      *
 * Output:					      *
 *   none:					      *
 ******************************************************/
VOID QueueTimer()
{
  IMPORT struct timerequest  *TimerReqBlock;

  TimerReqBlock->tr_node.io_Command = TR_ADDREQUEST;
  TimerReqBlock->tr_time.tv_secs    = 0;
  TimerReqBlock->tr_time.tv_micro   = 200000L;
  SendIO((struct IORequest *)TimerReqBlock);
}