home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / boopsi / popupmenuclass / lib_source / popupmenuclass.c
C/C++ Source or Header  |  1995-10-09  |  31KB  |  986 lines

  1. /** ** *** MakeRev Header *** **
  2. **
  3. **  ** PopUpMenu Gadget Class - BOOPSI gadget class. **
  4. **
  5. **  Copyright © 1993 Markus Aalto
  6. **
  7. **  Creation date: 09-Dec-93
  8. **
  9. **  ------------------------------------------------------------------
  10. **  $Filename: PopUpMenuClass.c $
  11. **  $Revision: 1.1 $
  12. **  $Date: 29-Dec-93 $
  13. **
  14. **  $Author: Markus_Aalto $
  15. **  $Comment: Freely distributable. $
  16. **
  17. */
  18.  
  19. /* Make sure everything is added from PopUpMenuClass.h */
  20. #define POPUPMENUCLASS_PRIVATE  1
  21.  
  22. #include    "PopUpMenuClass.h"
  23. #include    <string.h>
  24. #include    <math.h>
  25.  
  26. /* Version. */
  27. static UBYTE popupmenuclass_version[] = "$VER: PopUpMenuClass 1.1 (29.12.93)";
  28.  
  29. /* STATIC Protos for real Method functions and for class dispatcher. */
  30. static ULONG    __saveds __asm PUMG_Dispatcher( register __a0 Class *cl,
  31.                                                 register __a2 Object *o,
  32.                                                 register __a1 Msg msg);
  33.  
  34. static ULONG    PUMG_NEW( Class *cl,
  35.                           Object *o,
  36.                           struct opSet *ops );
  37.  
  38. static ULONG    PUMG_DISPOSE( Class *cl,
  39.                               Object *o,
  40.                               Msg msg );
  41.  
  42. static ULONG    PUMG_SET( Class *cl,
  43.                           Object *o,
  44.                           struct opSet *ops );
  45.  
  46. static ULONG    PUMG_GET( Class *cl,
  47.                           Object *o,
  48.                           struct opGet *opg );
  49.  
  50. static ULONG    PUMG_UPDATE( Class *cl,
  51.                              Object *o,
  52.                              struct opUpdate *opu );
  53.  
  54. static ULONG    PUMG_NOTIFY( Class *cl,
  55.                              Object *o,
  56.                              struct opUpdate *opu );
  57.  
  58. static ULONG    PUMG_RENDER( Class *cl,
  59.                              Object *o,
  60.                              struct gpRender *gpr );
  61.  
  62. static ULONG    PUMG_GOACTIVE( Class *cl,
  63.                                Object *o,
  64.                                struct gpInput *gpi );
  65.  
  66. static ULONG    PUMG_HANDLEINPUT( Class *cl,
  67.                                   Object *o,
  68.                                   struct gpInput *gpi );
  69.  
  70. static ULONG    PUMG_GOINACTIVE( Class *cl,
  71.                                  Object *o,
  72.                                  struct gpGoInactive *gpgi );
  73.  
  74. /* Protos for static help functions. */
  75.  
  76. static void     PUMG_MakeCheckings( PUMGData *PD );
  77.  
  78. static void     PUMG_GetGadgetRect( Object *o,
  79.                                     struct GadgetInfo *gi,
  80.                                     struct Rectangle *rect );
  81.  
  82. static void     PUMG_DrawPopupWindow( PUMGData *PD,
  83.                                       struct DrawInfo *dri,
  84.                                       ULONG From,
  85.                                       LONG Count );
  86.  
  87. static void     PUMG_DrawFrame( struct Window *win,
  88.                                 int order,
  89.                                 struct DrawInfo *dri,
  90.                                 UBYTE *name,
  91.                                 struct TextFont *tf,
  92.                                 BOOL Active,
  93.                                 BOOL NewLook,
  94.                                 ULONG ItemHeight );
  95.  
  96. static void     PUMG_DrawPopupMenuImage( struct RastPort *RP,
  97.                                          ULONG Pen1,
  98.                                          ULONG Pen2,
  99.                                          ULONG Left,
  100.                                          ULONG Top,
  101.                                          LONG Height );
  102.  
  103. /*******************************************************************/
  104. /*******************************************************************/
  105. /* The real code starts here.                                      */
  106. /*******************************************************************/
  107. /*******************************************************************/
  108.  
  109. /* Global Create PopUpMenuClass function. */
  110.  
  111. Class *CreatePopUpMenuClass()
  112. {
  113.     Class *cl;
  114.  
  115.     cl = MakeClass( NULL, GADGETCLASS, NULL, sizeof(PUMGData), 0);
  116.     if( cl ) {
  117.         cl->cl_Dispatcher.h_Entry = (HookFunction)PUMG_Dispatcher;
  118.     }
  119.  
  120.     return( cl );
  121. }
  122.  
  123. /* Global Free PopUpMenuClass function. */
  124. BOOL DisposePopUpMenuClass( Class * cl )
  125. {
  126.     return( FreeClass( cl ) ) ;
  127. }
  128.  
  129.  
  130. /*******************************************************************/
  131. /*******************************************************************/
  132. /* PopUpMenu specific class code.                                  */
  133. /*******************************************************************/
  134. /*******************************************************************/
  135.  
  136. static ULONG __saveds __asm PUMG_Dispatcher( register __a0 Class *cl,
  137.                                              register __a2 Object *o,
  138.                                              register __a1 Msg msg)
  139. {
  140.     ULONG retval;
  141.  
  142.     switch( msg->MethodID )
  143.     {
  144.     case OM_NEW:
  145.         retval = PUMG_NEW(cl, o, (struct opSet *)msg );
  146.         break;
  147.     case OM_DISPOSE:
  148.         retval = PUMG_DISPOSE( cl, o, msg );
  149.         break;
  150.     case OM_SET:
  151.         retval = PUMG_SET(cl, o, (struct opSet *)msg );
  152.         break;
  153.     case OM_GET:
  154.         retval = PUMG_GET( cl, o, (struct opGet *)msg );
  155.         break;
  156.     case OM_UPDATE:
  157.         retval = PUMG_UPDATE( cl, o, (struct opUpdate *)msg );
  158.         break;
  159.     case OM_NOTIFY:
  160.         retval = PUMG_NOTIFY( cl, o, (struct opUpdate *)msg );
  161.         break;
  162.     case GM_RENDER:
  163.         retval = PUMG_RENDER( cl, o, (struct gpRender *)msg );
  164.         break;
  165.     case GM_GOACTIVE:
  166.         retval = PUMG_GOACTIVE( cl, o, (struct gpInput *)msg );
  167.         break;
  168.     case GM_HANDLEINPUT:
  169.         retval = PUMG_HANDLEINPUT( cl, o, (struct gpInput *)msg );
  170.         break;
  171.     case GM_GOINACTIVE:
  172.         retval = PUMG_GOINACTIVE( cl, o, (struct gpGoInactive *)msg );
  173.         break;
  174.     default:
  175.         retval = DoSuperMethodA(cl, o, msg);
  176.         break;
  177.     }
  178.  
  179.  
  180.     return(retval);
  181. }
  182.  
  183. static ULONG PUMG_NEW( Class *cl,
  184.                        Object *o,
  185.                        struct opSet *ops )
  186. {
  187.     Object *object;
  188.     PUMGData *PD;
  189.  
  190.     object = (Object *)DoSuperMethodA( cl, o, (Msg)ops );
  191.     if( object ) {
  192.         PD = INST_DATA( cl, object );
  193.  
  194.         PD->Labels = (struct List *)GetTagData( PUMG_Labels, NULL, ops->ops_AttrList );
  195.         PD->Active = GetTagData( PUMG_Active, 0, ops->ops_AttrList );
  196.         PD->NewLook = (BOOL)GetTagData( PUMG_NewLook, FALSE, ops->ops_AttrList);
  197.  
  198.         PUMG_MakeCheckings( PD );
  199.  
  200.         PD->Font = (struct TextFont *)GetTagData( PUMG_TextFont, NULL,
  201.             ops->ops_AttrList);
  202.  
  203.         PD->FrameImage = (struct Image *)NewObject(NULL,"frameiclass",
  204.                 IA_Recessed,    FALSE,
  205.                 IA_EdgesOnly,   FALSE,
  206.                 IA_FrameType,   FRAME_BUTTON,
  207.                 TAG_END);
  208.         if( PD->FrameImage == NULL ) {
  209.             CoerceMethod(cl, o, OM_DISPOSE);
  210.             object = NULL;
  211.         }
  212.     }
  213.  
  214.     return( (ULONG)object );
  215. }
  216.  
  217.  
  218. static ULONG PUMG_DISPOSE( Class *cl,
  219.                            Object *o,
  220.                            Msg msg )
  221. {
  222.     PUMGData *PD = INST_DATA( cl, o );
  223.  
  224.     if( PD->popup_window ) {
  225.         CloseWindow( PD->popup_window );
  226.     }
  227.  
  228.     if( PD->FrameImage ) DisposeObject( PD->FrameImage );
  229.  
  230.     return( DoSuperMethodA(cl, o, msg) );
  231. }
  232.  
  233.  
  234. static ULONG PUMG_SET( Class *cl,
  235.                        Object *o,
  236.                        struct opSet *ops )
  237. {
  238.     ULONG retval;
  239.     PUMGData *PD = INST_DATA( cl, o );
  240.     struct TagItem *tag, notify;
  241.     UWORD old_active;
  242.  
  243.     retval = DoSuperMethodA( cl, o, (Msg)ops );
  244.  
  245.     /* I decided that it would be best that the values which are
  246.     ** specific to this class, could bot be changed when we have
  247.     ** our PopUpMenu window opened. */
  248.     if( (ops->ops_AttrList != NULL) && (PD->popup_window == NULL) ) {
  249.         tag = FindTagItem( PUMG_Labels, ops->ops_AttrList );
  250.         if( tag ) {
  251.             PD->Labels = (struct List *)tag->ti_Data;
  252.             retval = TRUE;
  253.         }
  254.  
  255.         old_active = PD->Active;
  256.  
  257.         tag = FindTagItem( PUMG_Active, ops->ops_AttrList );
  258.         if( tag ) {
  259.             PD->Active = tag->ti_Data;
  260.             retval = TRUE;
  261.         }
  262.  
  263.         PUMG_MakeCheckings( PD );
  264.  
  265.         tag = FindTagItem( PUMG_TextFont, ops->ops_AttrList );
  266.         if( tag ) {
  267.             PD->Font = (struct TextFont *)tag->ti_Data;
  268.             retval = TRUE;
  269.         }
  270.  
  271.         if( old_active != PD->Active ) {
  272.             /* We send ourselves a OM_NOTIFY message, which will
  273.             ** eventually be broadcasted as OM_UPDATE message
  274.             ** to the target object. Note that we don't send it
  275.             ** simply to our parent, but to ourselves, so if
  276.             ** we have a children which needs to add it's own
  277.             ** data it will be added. */
  278.             PUMG_SetTagArg( notify, TAG_END, NULL );
  279.             (VOID)DoMethod( o, OM_NOTIFY, ¬ify, ops->ops_GInfo, 0 );
  280.         }
  281.     }
  282.  
  283.     return( retval );
  284. }
  285.  
  286. static ULONG PUMG_GET( Class *cl,
  287.                        Object *o,
  288.                        struct opGet *opg )
  289. {
  290.     ULONG retval;
  291.     PUMGData *PD = INST_DATA( cl, o );
  292.  
  293.     switch( opg->opg_AttrID )
  294.     {
  295.     case PUMG_Labels:
  296.         *opg->opg_Storage = (ULONG)PD->Labels;
  297.         retval = TRUE;
  298.         break;
  299.     case PUMG_Active:
  300.         *opg->opg_Storage = (ULONG)PD->Active;
  301.         retval = TRUE;
  302.         break;
  303.     case PUMG_TextFont:
  304.         *opg->opg_Storage = (ULONG)PD->Font;
  305.         retval = TRUE;
  306.         break;
  307.     case PUMG_NewLook:
  308.         *opg->opg_Storage = (ULONG)PD->NewLook;
  309.         retval = TRUE;
  310.         break;
  311.     default:
  312.         retval = DoSuperMethodA(cl, o, (Msg)opg);
  313.         break;
  314.     }
  315.  
  316.     return( retval );
  317. }
  318.  
  319.  
  320. static ULONG PUMG_UPDATE( Class *cl,
  321.                           Object *o,
  322.                           struct opUpdate *opu )
  323. {
  324.     ULONG retval;
  325.     PUMGData *PD = INST_DATA( cl, o );
  326.     struct TagItem *tag, notify;
  327.     struct RastPort *rp;
  328.  
  329.     retval = DoSuperMethodA(cl, o, opu);
  330.  
  331.     /* Update only if gadget isn't currently manipulated. */
  332.     if( PD->popup_window == NULL ) {
  333.         if( opu->opu_AttrList ) {
  334.             tag = FindTagItem( PUMG_Active, opu->opu_AttrList );
  335.             if( tag ) {
  336.                 if( tag->ti_Data != PD->Active ) {
  337.                     PD->Active = tag->ti_Data;
  338.  
  339.                     PUMG_MakeCheckings( PD );
  340.  
  341.                     rp = ObtainGIRPort( opu->opu_GInfo );
  342.                     if( rp ) {
  343.                         DoMethod( o, GM_RENDER, opu->opu_GInfo, rp, GREDRAW_UPDATE );
  344.                         ReleaseGIRPort( rp );
  345.                     }
  346.  
  347.                     /* Notify the change. */
  348.                     PUMG_SetTagArg( notify, TAG_END, NULL );
  349.                     (void)DoMethod( o, OM_NOTIFY, ¬ify, opu->opu_GInfo, 0 );
  350.                 }
  351.             }
  352.         }
  353.     }
  354.  
  355.     return( retval );
  356. }
  357.  
  358.  
  359. static ULONG PUMG_NOTIFY( Class *cl,
  360.                           Object *o,
  361.                           struct opUpdate *opu )
  362. {
  363.     struct TagItem tags[3];
  364.     PUMGData *PD = INST_DATA( cl, o );
  365.  
  366.     PUMG_SetTagArg(tags[0], GA_ID, ((struct Gadget *)o)->GadgetID);
  367.     PUMG_SetTagArg(tags[1], PUMG_Active, PD->Active );
  368.  
  369.     /* If there are no previous tags in OM_NOTIFY message, we
  370.     ** add them there as only ones. Otherwise we tag previous
  371.     ** tags to the end of our tags. Got it? :')
  372.     */
  373.     if( opu->opu_AttrList == NULL ) {
  374.         PUMG_SetTagArg(tags[2], TAG_END, NULL);
  375.     }
  376.     else PUMG_SetTagArg(tags[2], TAG_MORE, opu->opu_AttrList );
  377.  
  378.     return( DoSuperMethod(cl, o, OM_NOTIFY, tags, opu->opu_GInfo, opu->opu_Flags) );
  379. }
  380.  
  381. static ULONG PUMG_RENDER( Class *cl,
  382.                           Object *o,
  383.                           struct gpRender *gpr )
  384. {
  385.     ULONG retval, State;
  386.     struct Gadget *gad = (struct Gadget *)o;
  387.     struct Rectangle rect;
  388.     struct DrawInfo *dri;
  389.     struct IBox container;
  390.     struct Node *node;
  391.     struct TextExtent temp_te;
  392.     struct RastPort *RP = gpr->gpr_RPort;
  393.     UWORD BorderWidth, BorderHeight, TextWidth;
  394.     UWORD patterndata[2] = { 0x2222, 0x8888 };
  395.     ULONG TextPen, ImagePen1, ImagePen2;
  396.     PUMGData *PD = INST_DATA( cl, o );
  397.  
  398.     retval = DoSuperMethodA(cl, o, gpr);
  399.  
  400.     /* Get real Min and Max positions. */
  401.     PUMG_GetGadgetRect( o, gpr->gpr_GInfo, &rect );
  402.  
  403.     /* Calculate real dimensions. */
  404.     container.Left = rect.MinX; container.Top = rect.MinY;
  405.     container.Width = 1 + rect.MaxX - rect.MinX;
  406.     container.Height = 1 + rect.MaxY - rect.MinY;
  407.  
  408.     dri = gpr->gpr_GInfo->gi_DrInfo;
  409.  
  410.     if( gad->Flags & GFLG_DISABLED ) {
  411.         State = IDS_DISABLED;
  412.     }
  413.     else if( gad->Flags & GFLG_SELECTED ) {
  414.         State = IDS_SELECTED;
  415.     }
  416.     else State = IDS_NORMAL;
  417.  
  418.     /* Frame rendering goes here. */
  419.     SetAttrs( PD->FrameImage,
  420.             IA_Left,    container.Left,
  421.             IA_Top,     container.Top,
  422.             IA_Width,   container.Width,
  423.             IA_Height,  container.Height,
  424.             TAG_END);
  425.  
  426.     DrawImageState( RP, PD->FrameImage, 0, 0, State, dri);
  427.  
  428.     if( dri ) {
  429.         TextPen = dri->dri_Pens[TEXTPEN];
  430.         ImagePen1 = dri->dri_Pens[SHINEPEN];
  431.         ImagePen2 = dri->dri_Pens[SHADOWPEN];
  432.     }
  433.     else { /* If for some unknown reason Drawinfo is NULL then we
  434.            ** Use these predefined values, which should work atleast
  435.            ** for current OS releases. */
  436.         TextPen = ImagePen2 = 1;
  437.         ImagePen1 = 2;
  438.     }
  439.  
  440.     /* Draw the PopupMenu Image. */
  441.     PUMG_DrawPopupMenuImage( RP, ImagePen1, ImagePen2,
  442.         5LU + container.Left, 2LU + container.Top, -5L + container.Height);
  443.  
  444.     /*******************************/
  445.     /* Text rendering starts here. */
  446.     /*******************************/
  447.  
  448.     /* Do we have a proper font. */
  449.     if( PD->Font == NULL ) {
  450.         /* If not we use the font we have in RastPort. */
  451.         PD->Font = RP->Font;
  452.     }
  453.     else SetFont( RP, PD->Font );
  454.  
  455.     /* Check if we have nothing to print. */
  456.     if( PD->Count > 0 ) {
  457.         ULONG len, i = 0;
  458.         char *label_name;
  459.  
  460.         node = PD->Labels->lh_Head;
  461.         while( node->ln_Succ ) {
  462.             if( i == PD->Active ) {
  463.                 label_name = node->ln_Name;
  464.                 if( label_name ) {
  465.                     len = TextFit( RP, label_name, (ULONG)strlen(label_name),
  466.                         &temp_te, NULL, 1, (ULONG)container.Width - 28,
  467.                         1LU + PD->Font->tf_YSize);
  468.  
  469.                     TextWidth = 1 + temp_te.te_Extent.MaxX - temp_te.te_Extent.MinX;
  470.  
  471.                     SetAPen(RP, TextPen);
  472.                     Move( RP, 10L + container.Left + (container.Width - TextWidth)/2
  473.                         - temp_te.te_Extent.MinX, (LONG)
  474.                         PD->Font->tf_Baseline + (1 + container.Top + rect.MaxY
  475.                         - PD->Font->tf_YSize)/2 );
  476.  
  477.                     Text( RP, label_name, len );
  478.                 }
  479.  
  480.                 /* End the drawing. */
  481.                 break;
  482.             }
  483.             else {
  484.                 i++;
  485.                 node = node->ln_Succ;
  486.             }
  487.         }
  488.     }
  489.  
  490.     /* Disabled pattern rendering is here. */
  491.     if( State == IDS_DISABLED ) {
  492.         BorderHeight = 1;
  493.         BorderWidth = (IntuitionBase->LibNode.lib_Version < 39) ? 1 : 2;
  494.  
  495.         container.Left += BorderWidth;
  496.         container.Top += BorderHeight;
  497.         container.Width = max( 1, container.Width - 2*BorderWidth );
  498.         container.Height = max( 1, container.Height - 2*BorderHeight );
  499.  
  500.         SetDrMd(RP,JAM1);
  501.         SetAfPt(RP, patterndata, 1);
  502.  
  503.         RectFill(RP,  (LONG)container.Left, (LONG)container.Top,
  504.                 -1L + container.Left + container.Width,
  505.                 -1L + container.Top + container.Height );
  506.  
  507.         SetAfPt(RP, NULL, 0 );
  508.     }
  509.  
  510.     /* Copy current Rectangle. */
  511.     PD->rect = rect;
  512.  
  513.     return( retval );
  514. }
  515.  
  516.  
  517. static ULONG PUMG_GOACTIVE( Class *cl,
  518.                             Object *o,
  519.                             struct gpInput *gpi )
  520. {
  521.     ULONG retval = GMR_MEACTIVE, Left, Top;
  522.     struct RastPort *rp;
  523.     PUMGData *PD = INST_DATA( cl, o );
  524.     struct GadgetInfo *gi = gpi->gpi_GInfo;
  525.     struct Gadget *gad = (struct Gadget *)o;
  526.  
  527.     /* Test if we are disabled. */
  528.     if( gad->Flags & GFLG_DISABLED ) return( GMR_NOREUSE );
  529.  
  530.     /* Call first our parent class. */
  531.     (void)DoSuperMethodA(cl, o, gpi);
  532.  
  533.     /* Chech whether we were activated from mouse or keyboard. */
  534.     PD->ActiveFromMouse = (gpi->gpi_IEvent != NULL);
  535.  
  536.     /* Select this gadget. */
  537.     gad->Flags |= GFLG_SELECTED;
  538.  
  539.     /* We make sure that NewLook isn't active if DrawInfo version
  540.     ** isn't high enough. Since V39 DrawInfo version >= 2!! */
  541.     if( PD->NewLook ) {
  542.         if( gi->gi_DrInfo && (gi->gi_DrInfo->dri_Version < 2)) {
  543.             PD->NewLook = FALSE;
  544.         }
  545.     }
  546.  
  547.     rp = ObtainGIRPort( gi );
  548.     if( rp ) {
  549.         /* Render ourselves as selected gadget. */
  550.         DoMethod( o, GM_RENDER, gi, rp, GREDRAW_UPDATE );
  551.         ReleaseGIRPort( rp );
  552.  
  553.         /* Get the domain top/left position. */
  554.         Left = gi->gi_Domain.Left;
  555.         Top = gi->gi_Domain.Top;
  556.  
  557.         /* If this is window, we have to add window Left/Top values too. */
  558.         if( gi->gi_Window ) {
  559.             Left += gi->gi_Window->LeftEdge;
  560.             Top += gi->gi_Window->TopEdge;
  561.         }
  562.  
  563.         /* If NewLook is ON, then size of one item is smaller. */
  564.         PD->ItemHeight = PD->Font->tf_YSize + (PD->NewLook ? 1 : 5);
  565.  
  566.         /* Count how many items fits to menu. */
  567.         PD->FitsItems = (gi->gi_Screen->Height - 4) / PD->ItemHeight;
  568.         if( PD->FitsItems > PD->Count ) {
  569.             PD->FitsItems = PD->Count;
  570.         }
  571.  
  572.         PD->popup_window = OpenWindowTags(NULL,
  573.             WA_Left,            Left + PD->rect.MinX,
  574.             WA_Top,             Top + PD->rect.MaxY,
  575.             WA_Width,           1 + PD->rect.MaxX - PD->rect.MinX,
  576.             WA_Height,          4 + PD->FitsItems*PD->ItemHeight,
  577.             WA_Activate,        FALSE,
  578.             WA_CustomScreen,    gi->gi_Screen,
  579.             WA_SizeGadget,      FALSE,
  580.             WA_DragBar,         FALSE,
  581.             WA_DepthGadget,     FALSE,
  582.             WA_CloseGadget,     FALSE,
  583.             WA_Borderless,      TRUE,
  584.             WA_Flags,           0,
  585.             WA_AutoAdjust,      TRUE,
  586.             WA_RMBTrap,         TRUE,
  587.             WA_SimpleRefresh,   TRUE,
  588.             WA_NoCareRefresh,   TRUE,
  589.             TAG_END );
  590.  
  591.         if( PD->popup_window == NULL ) {
  592.             retval = GMR_NOREUSE;
  593.         }
  594.         else {
  595.             /* We make sure Active item isn't too large to display. */
  596.             if( PD->FitsItems < PD->Active ) PD->Active = PD->FitsItems-1;
  597.  
  598.             /* If activated from keyboard we can set temporary value
  599.             ** to currently activated item. Otherwise we set it
  600.             ** to -1 which means that there is no active item. */
  601.             PD->Temp_Active = PD->ActiveFromMouse ? (ULONG)~0 : PD->Active;
  602.  
  603.             /* Render all items. */
  604.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo, 0, -1);
  605.         }
  606.     }
  607.     else retval = GMR_NOREUSE;
  608.  
  609.     return(retval);
  610. }
  611.  
  612. static ULONG PUMG_HANDLEINPUT( Class *cl,
  613.                                Object *o,
  614.                                struct gpInput *gpi )
  615. {
  616.     ULONG retval = GMR_MEACTIVE;
  617.     struct InputEvent *ie = gpi->gpi_IEvent;
  618.     PUMGData *PD = INST_DATA(cl, o);
  619.     WORD X, Y;
  620.     WORD count, old_active;
  621.     struct GadgetInfo *gi = gpi->gpi_GInfo;
  622.     struct TagItem tags;    /* If our possible child class, doesn't know
  623.                             ** how to handle NULL AttrList ptr, then
  624.                             ** this can save lots of crashes. */
  625.  
  626.     /* If there is anykind of AutoPoint program then our main window
  627.     ** might get inactive and we wouldn't get any more messages.
  628.     ** So we check out that we are active and deactivate ourselves
  629.     ** if our window isn't active anymore. */
  630.     if( gi->gi_Window ) {
  631.         if( (gi->gi_Window->Flags & WFLG_WINDOWACTIVE) == 0 ) {
  632.             return( GMR_NOREUSE );
  633.         }
  634.     }
  635.  
  636.     if( PD->ActiveFromMouse ) {
  637.         X = PD->popup_window->MouseX;
  638.         Y = PD->popup_window->MouseY;
  639.  
  640.         count = ( (Y - 2) >= 0 ) ? (Y - 2) / (PD->ItemHeight) : ~0;
  641.  
  642.         old_active = PD->Temp_Active;
  643.  
  644.         if( (X > 2) && (X < (PD->popup_window->Width - 2))
  645.                 && (count >= 0) && (count < PD->FitsItems) ) {
  646.             PD->Temp_Active = (UWORD)count;
  647.         }
  648.         else PD->Temp_Active = (UWORD)~0;
  649.  
  650.         if( old_active != (WORD)PD->Temp_Active ) {
  651.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,(ULONG)PD->Temp_Active, 1 );
  652.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,(ULONG)old_active, 1 );
  653.         }
  654.  
  655.         while( ie && (retval == GMR_MEACTIVE) ) {
  656.             if( ie->ie_Class == IECLASS_RAWMOUSE ) {
  657.                 if( ie->ie_Code == SELECTUP ) {
  658.                     retval = GMR_NOREUSE;
  659.  
  660.                     if( (PD->Temp_Active != (UWORD)~0) ) {
  661.                         PD->Active = PD->Temp_Active;
  662.                         PUMG_MakeCheckings( PD );
  663.  
  664.                         PUMG_SetTagArg(tags, TAG_END, NULL);
  665.                         (VOID)DoMethod( o, OM_NOTIFY, &tags, gi, 0);
  666.  
  667.                         retval |= GMR_VERIFY;
  668.                         *gpi->gpi_Termination = (ULONG)PD->Active;
  669.                     }
  670.                 }
  671.             }
  672.  
  673.             ie = ie->ie_NextEvent;
  674.         }
  675.     }
  676.     else {
  677.         while( ie && (retval == GMR_MEACTIVE) ) {
  678.             switch( ie->ie_Class )
  679.             {
  680.             case IECLASS_RAWMOUSE:
  681.                 if( ie->ie_Code != IECODE_NOBUTTON ) {
  682.                     retval = GMR_REUSE; /* Reuse the InputEvent. */
  683.                 }
  684.                 break;
  685.             case IECLASS_RAWKEY:
  686.                 old_active = PD->Temp_Active;
  687.                 switch( ie->ie_Code )
  688.                 {
  689.                 case CURSORDOWN:
  690.                     if( ie->ie_Qualifier & (ALTLEFT|ALTRIGHT) ) {
  691.                         PD->Temp_Active = PD->FitsItems-1;  /* Jump to end. */
  692.                     }
  693.                     else if( PD->Temp_Active < (PD->FitsItems-1) ) {
  694.                         PD->Temp_Active += 1;
  695.                     }
  696.                     break;
  697.                 case CURSORUP:
  698.                     if( ie->ie_Qualifier & (ALTLEFT|ALTRIGHT) ) {
  699.                         PD->Temp_Active = 0;    /* Jump to start. */
  700.                     }
  701.                     else if( PD->Temp_Active > 0 ) {
  702.                         PD->Temp_Active -= 1;
  703.                     }
  704.                     break;
  705.                 case 0x45:  /* ESC key. */
  706.                     retval = GMR_NOREUSE;
  707.                     break;
  708.                 case 0x44:  /* RETURN key. */
  709.                     PD->Active = PD->Temp_Active;
  710.                     PUMG_MakeCheckings( PD );
  711.  
  712.                     PUMG_SetTagArg(tags, TAG_END, NULL);
  713.                     (VOID)DoMethod( o, OM_NOTIFY, &tags, gi, 0);
  714.  
  715.                     retval = GMR_NOREUSE|GMR_VERIFY;
  716.                     *gpi->gpi_Termination = (ULONG)PD->Active;
  717.                     break;
  718.                 }
  719.  
  720.                 /* Update the popupwindow items, if changes were made. */
  721.                 if( old_active != PD->Temp_Active ) {
  722.                     PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,
  723.                         (ULONG)PD->Temp_Active, 1 );
  724.                     PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,
  725.                         (ULONG)old_active, 1 );
  726.                 }
  727.             }
  728.  
  729.             ie = ie->ie_NextEvent;
  730.         }
  731.     }
  732.  
  733.     return(retval);
  734. }
  735.  
  736.  
  737. static ULONG PUMG_GOINACTIVE( Class *cl,
  738.                               Object *o,
  739.                               struct gpGoInactive *gpgi )
  740. {
  741.     ULONG retval;
  742.     struct RastPort *rp;
  743.     PUMGData *PD = INST_DATA(cl, o);
  744.  
  745.     retval = DoSuperMethodA(cl, o, gpgi);
  746.  
  747.     ((struct Gadget *)o)->Flags &= ~GFLG_SELECTED;
  748.  
  749.     rp = ObtainGIRPort( gpgi->gpgi_GInfo );
  750.     if( rp ) {
  751.         DoMethod( o, GM_RENDER, gpgi->gpgi_GInfo, rp, GREDRAW_UPDATE );
  752.         ReleaseGIRPort( rp );
  753.     }
  754.  
  755.     if( PD->popup_window ) {
  756.         CloseWindow(PD->popup_window);
  757.         PD->popup_window = NULL;
  758.     }
  759.  
  760.     return(retval);
  761. }
  762.  
  763. /* Static functions for help with real Method functions. */
  764.  
  765. static void PUMG_MakeCheckings( PUMGData *PD )
  766. {
  767.     struct Node *node;
  768.  
  769.     PD->Count = 0;
  770.  
  771.     if( PD->Labels == NULL ) {
  772.         PD->Active = 0;
  773.     }
  774.     else if( PD->Labels != (struct List *)~0 ) {
  775.         node = (struct Node *)PD->Labels->lh_Head;
  776.         while( node->ln_Succ ) {
  777.             PD->Count += 1;
  778.             node = node->ln_Succ;
  779.         }
  780.  
  781.         if( PD->Active >= PD->Count ) {
  782.             PD->Active = PD->Count + (PD->Count == 0) - 1;
  783.         }
  784.     }
  785. }
  786.  
  787. static void PUMG_GetGadgetRect( Object *o,
  788.                                 struct GadgetInfo *gi,
  789.                                 struct Rectangle *rect )
  790. {
  791.     struct Gadget *gad = (struct Gadget *)o;
  792.     LONG W, H;
  793.  
  794.     rect->MinX = gad->LeftEdge;
  795.     rect->MinY = gad->TopEdge;
  796.     W = gad->Width;
  797.     H = gad->Height;
  798.  
  799.     if( gi ) {
  800.         if( gad->Flags & GFLG_RELRIGHT ) rect->MinX += gi->gi_Domain.Width - 1;
  801.         if( gad->Flags & GFLG_RELBOTTOM ) rect->MinY += gi->gi_Domain.Height - 1;
  802.         if( gad->Flags & GFLG_RELWIDTH ) W += gi->gi_Domain.Width;
  803.         if( gad->Flags & GFLG_RELHEIGHT ) H += gi->gi_Domain.Height;
  804.     }
  805.  
  806.     rect->MaxX = rect->MinX + W - (W > 0);
  807.     rect->MaxY = rect->MinY + H - (H > 0);
  808. }
  809.  
  810. static void PUMG_DrawPopupWindow( PUMGData *PD,
  811.                                   struct DrawInfo *dri,
  812.                                   ULONG From, LONG Count)
  813. {
  814.     int i, End;
  815.     struct Node *node;
  816.     struct Window *win = PD->popup_window;
  817.     struct RastPort *RP = win->RPort;
  818.  
  819.     if( PD->Count && dri ) {
  820.         /* If we want to draw all entries then we draw
  821.         ** window borders too. */
  822.         if( Count == -1) {
  823.             Count = PD->FitsItems;
  824.  
  825.             if( PD->NewLook ) {
  826.                 /* Set background to MENU background color. */
  827.                 SetRast( RP, (ULONG)dri->dri_Pens[BARBLOCKPEN] );
  828.  
  829.                 SetAPen( RP, (ULONG)dri->dri_Pens[BARDETAILPEN] );
  830.                 Move( RP, 0, -1L + win->Height );
  831.                 Draw( RP, 0, 0 );
  832.                 Draw( RP, -1L + win->Width, 0 );
  833.                 Draw( RP, -1L + win->Width, -1L + win->Height);
  834.                 Draw( RP, 1, -1L + win->Height);
  835.                 Draw( RP, 1, 1);
  836.                 Move( RP, -2L + win->Width, 1 );
  837.                 Draw( RP, -2L + win->Width, -2L + win->Height);
  838.             }
  839.             else {
  840.                 SetAPen( RP, (ULONG)dri->dri_Pens[SHINEPEN]);
  841.                 Move( RP, 0, -1L + win->Height);
  842.                 Draw( RP, 0, 0 );
  843.                 Draw( RP, -1L + win->Width, 0 );
  844.                 SetAPen( RP, (ULONG)dri->dri_Pens[SHADOWPEN]);
  845.                 Draw( RP, -1L + win->Width, -1L + win->Height);
  846.                 Draw( RP, 1, -1L + win->Height);
  847.             }
  848.         }
  849.  
  850.         SetFont( RP, PD->Font );
  851.         SetDrMd( RP, JAM1);
  852.  
  853.         node = PD->Labels->lh_Head;
  854.  
  855.         for( i = 0, End = From + Count; node->ln_Succ ; i++ ) {
  856.             if( i < PD->FitsItems ) {
  857.                 if( (i >= From) && ( i < End ) ) {
  858.                     PUMG_DrawFrame( PD->popup_window, i, dri, node->ln_Name,
  859.                         PD->Font, (BOOL)(i == PD->Temp_Active),
  860.                         PD->NewLook, (ULONG)PD->ItemHeight );
  861.                 }
  862.                 else if( i >= End ) return;
  863.             }
  864.  
  865.             node = node->ln_Succ;
  866.         }
  867.     }
  868. }
  869.  
  870. static void PUMG_DrawFrame( struct Window *win,
  871.                             int order,
  872.                             struct DrawInfo *dri,
  873.                             UBYTE *name,
  874.                             struct TextFont *tf,
  875.                             BOOL Active,
  876.                             BOOL NewLook,
  877.                             ULONG ItemHeight )
  878. {
  879.     ULONG   Pen1, Pen2, TextPen, BPen;
  880.     ULONG   Top, Width, Bottom, MaxX,
  881.             Len, TextWidth,
  882.             font_height = tf->tf_YSize;
  883.     struct  RastPort *RP = win->RPort;
  884.     struct  TextExtent temp_te;
  885.  
  886.     TextPen = dri->dri_Pens[TEXTPEN];
  887.  
  888.     if( Active ) {
  889.         if( NewLook ) {
  890.             BPen = dri->dri_Pens[BARDETAILPEN];
  891.             TextPen = dri->dri_Pens[BARBLOCKPEN];   /* Override previous value. */
  892.         }
  893.         else {
  894.             Pen2 = dri->dri_Pens[SHINEPEN];
  895.             Pen1 = dri->dri_Pens[SHADOWPEN];
  896.             BPen = dri->dri_Pens[FILLPEN];
  897.         }
  898.     }
  899.     else {
  900.         if( NewLook ) {
  901.             BPen = dri->dri_Pens[BARBLOCKPEN];
  902.             TextPen = dri->dri_Pens[BARDETAILPEN];  /* Override previous value. */
  903.         }
  904.         else {
  905.             Pen2 = dri->dri_Pens[SHADOWPEN];
  906.             Pen1 = dri->dri_Pens[SHINEPEN];
  907.             BPen = dri->dri_Pens[BACKGROUNDPEN];
  908.         }
  909.     }
  910.  
  911.     Top = 2 + order * ItemHeight;
  912.     Bottom = Top + ItemHeight - 1;
  913.     MaxX = win->Width - 4;
  914.  
  915.     SetAPen( RP, BPen );
  916.     RectFill( RP, 4, Top, MaxX - 1, Bottom );
  917.  
  918.     if( NewLook == FALSE ) {    /* Draw Recessed Border. */
  919.         SetAPen( RP, Pen1);
  920.         Move( RP, 3, Bottom);
  921.         Draw( RP, 3, Top );
  922.         Draw( RP, MaxX, Top );
  923.         SetAPen( RP, Pen2);
  924.         Draw( RP, MaxX, Bottom );
  925.         Draw( RP, 4, Bottom );
  926.     }
  927.  
  928.     SetAPen( RP, TextPen);
  929.  
  930.     Width = win->Width - 10;
  931.  
  932.     Len = TextFit( RP, name, (ULONG)strlen(name), &temp_te, NULL, 1,
  933.         Width, 1 + font_height);
  934.  
  935.     TextWidth = temp_te.te_Extent.MaxX - temp_te.te_Extent.MinX;
  936.  
  937.     Move( RP, 5 + (Width - TextWidth)/2 - temp_te.te_Extent.MinX,
  938.         (ItemHeight - font_height)/2 + 1 + Top + tf->tf_Baseline );
  939.     Text( RP, name, Len );
  940. }
  941.  
  942. static void PUMG_DrawPopupMenuImage( struct RastPort *RP,
  943.                                      ULONG Pen1,
  944.                                      ULONG Pen2,
  945.                                      ULONG Left,
  946.                                      ULONG Top,
  947.                                      LONG Height )
  948. {
  949.     ULONG count, i;
  950.  
  951.     if( Height > 0 ) {
  952.         SetAPen( RP, Pen1 );
  953.         Move(RP, Left, Top + Height - 1 );
  954.         Draw(RP, Left, Top );
  955.  
  956.         count = (Height-1) / 4;
  957.         for( i = 0; i <= count; i++ ) {
  958.             Move(RP, Left, Top + i*4);
  959.             Draw(RP, 10 + Left, Top + i*4);
  960.         }
  961.  
  962.         /* Draw other horizontal bar. */
  963.         Move( RP, 16 + Left, Top);
  964.         Draw( RP, 16 + Left, Top + Height);
  965.  
  966.  
  967.         SetAPen( RP, Pen2 );
  968.         Move(RP, 1 + Left, Top + Height - 1);
  969.         Draw(RP, 11 + Left, Top + Height - 1);
  970.         Draw(RP, 11 + Left, Top);
  971.  
  972.         if( Height >= 4 ) {
  973.             count = (Height-4) / 4;
  974.             for( i = 0; i <= count; i++ ) {
  975.                 Move(RP, 1 + Left, 3 + Top + i*4);
  976.                 Draw(RP, 11 + Left, 3 + Top + i*4);
  977.             }
  978.         }
  979.  
  980.         /* Draw other horizontal bar. */
  981.         Move( RP, 15 + Left, Top);
  982.         Draw( RP, 15 + Left, Top + Height );
  983.     }
  984. }
  985.  
  986.