home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gui / gauge.lha / gauge.c next >
Encoding:
C/C++ Source or Header  |  1997-01-26  |  35.5 KB  |  1,351 lines

  1. /*
  2. **    $VER: Gauge.c 1.1 (26.1.97)
  3. **
  4. **    Gauge.c -- An implementation of a progress requester as per the
  5. **               Amiga User Interface Style Guide.
  6. **
  7. **    Copyright © 1997 by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
  8. **        Freely Distributable
  9. */
  10.  
  11. #include <intuition/intuitionbase.h>
  12. #include <intuition/gadgetclass.h>
  13. #include <intuition/imageclass.h>
  14. #include <intuition/classusr.h>
  15.  
  16. #include <graphics/videocontrol.h>
  17. #include <graphics/gfxmacros.h>
  18.  
  19. #include <exec/memory.h>
  20.  
  21. #include <clib/macros.h>
  22.  
  23. #include <clib/intuition_protos.h>
  24. #include <clib/graphics_protos.h>
  25. #include <clib/utility_protos.h>
  26. #include <clib/exec_protos.h>
  27. #include <clib/alib_protos.h>
  28.  
  29. #include <string.h>
  30. #include <stdarg.h>
  31.  
  32. #include "gauge.h"
  33.  
  34. /***************************************************************************/
  35.  
  36. void kprintf(const char *,...);
  37.  
  38. /***************************************************************************/
  39.  
  40.     /* For quick casting. */
  41.  
  42. #define G(o) ((struct Gadget *)o)
  43. #define I(o) ((struct Image *)o)
  44.  
  45. /***************************************************************************/
  46.  
  47. extern struct Library *IntuitionBase;
  48. extern struct Library *GfxBase;
  49. extern struct Library *UtilityBase;
  50. extern struct Library *SysBase;
  51.  
  52. /***************************************************************************/
  53.  
  54. STATIC STRPTR Percent = "  0%100%";
  55.  
  56. /***************************************************************************/
  57.  
  58.     /* This is what NewGaugeA() will return. */
  59.  
  60. struct Gauge
  61. {
  62.     struct Window    *Window;
  63.     struct Window    *Parent;
  64.     struct Screen    *Screen;
  65.     struct Screen    *PubScreen;
  66.     LONG         LeftPercentWidth;
  67.     LONG         RightPercentWidth;
  68.     Object        *ButtonGadget;
  69.     Object        *ButtonImage;
  70.     Object        *BackgroundImage;
  71.     Object        *BackgroundFrameImage;
  72.     struct DrawInfo    *DrawInfo;
  73.     struct Gadget     BackgroundGadget;
  74.     Class        *GaugeClass;
  75.     Object        *GaugeObject;
  76.     Object        *GaugeFrameImage;
  77.     LONG         FillSize;
  78.     LONG         FillLeft;
  79.     LONG         Space;
  80.     struct MsgPort    *UserPort;
  81.     BOOL         UnlockPubScreen;
  82. };
  83.  
  84.     /* The special info attached to the private gauge fill class. */
  85.  
  86. struct GaugeInfo
  87. {
  88.     struct Gauge    *Gauge;
  89.     WORD         Fill;
  90.     WORD         Flags;
  91. };
  92.  
  93.     /* Control tags for the gauge fill class. */
  94.  
  95. #define GAUGE_Data    (TAG_USER+0x777)
  96. /*#define GAUGE_Fill    (TAG_USER+0x778)*/
  97.  
  98.     /* Bit numbers for the update procedure. */
  99.  
  100. enum
  101. {
  102.     GAUGEB_Fill,
  103.     GAUGEB_Frame
  104. };
  105.  
  106.     /* Bit field definitions made from the above. */
  107.  
  108. #define GAUGEF_Fill    (1<<GAUGEB_Fill)
  109. #define GAUGEF_Frame    (1<<GAUGEB_Frame)
  110.  
  111. /***************************************************************************/
  112.  
  113.     /* This works like TextLength() but takes negative
  114.      * kerning into account.
  115.      */
  116.  
  117. STATIC LONG
  118. TextWidth(struct RastPort *RPort,STRPTR String,LONG Len)
  119. {
  120.     struct TextExtent Extent;
  121.  
  122.     TextFit(RPort,String,(Len < 0) ? strlen(String) : Len,&Extent,NULL,1,32767,32767);
  123.  
  124.     return(Extent.te_Width - Extent.te_Extent.MinX);
  125. }
  126.  
  127. STATIC VOID
  128. FillBox(struct RastPort *RPort,LONG Left,LONG Top,LONG Width,LONG Height)
  129. {
  130.     if(Width > 0 && Height > 0)
  131.         RectFill(RPort,Left,Top,Left + Width - 1,Top + Height - 1);
  132. }
  133.  
  134.     /* This implements the GM_RENDER method. */
  135.  
  136. STATIC ULONG
  137. RenderMethod(Class *class,Object *object,struct gpRender *msg)
  138. {
  139.     struct GaugeInfo *GaugeInfo = INST_DATA(class,object);
  140.     LONG Left,Top,Width,Height;
  141.     struct RastPort *RPort;
  142.     UWORD *Pens;
  143.     LONG Flags;
  144.  
  145.         /* If this is a complete update, redraw everything. */
  146.  
  147.     if(msg->gpr_Redraw == GREDRAW_REDRAW)
  148.         Flags = (GAUGEF_Fill|GAUGEF_Frame);
  149.     else
  150.         Flags = GaugeInfo->Flags;
  151.  
  152.         /* Reset these for good. */
  153.  
  154.     GaugeInfo->Flags = 0;
  155.  
  156.         /* Get the object's position and size. */
  157.  
  158.     Left    = G(object)->LeftEdge;
  159.     Top    = G(object)->TopEdge;
  160.     Width    = G(object)->Width;
  161.     Height    = G(object)->Height;
  162.  
  163.         /* Handy shortcuts. */
  164.  
  165.     RPort    = msg->gpr_RPort;
  166.     Pens    = msg->gpr_GInfo->gi_DrInfo->dri_Pens;
  167.  
  168.         /* Draw the frame and text around the fill gauge? */
  169.  
  170.     if(Flags & GAUGEF_Frame)
  171.     {
  172.             /* This draws the frame. */
  173.  
  174.         DrawImage(RPort,(struct Image *)GaugeInfo->Gauge->GaugeFrameImage,Left + GaugeInfo->Gauge->FillLeft,Top);
  175.  
  176.             /* And all this below draws the 0% and 100% labels. */
  177.  
  178.         SetAPen(RPort,Pens[TEXTPEN]);
  179.         SetDrMd(RPort,JAM1);
  180.  
  181.         Move(RPort,Left + GaugeInfo->Gauge->FillLeft - (GaugeInfo->Gauge->Space + GaugeInfo->Gauge->LeftPercentWidth),Top + RPort->TxBaseline);
  182.         Text(RPort,Percent,4);
  183.  
  184.         Move(RPort,Left + GaugeInfo->Gauge->FillLeft + GaugeInfo->Gauge->FillSize + GaugeInfo->Gauge->Space,Top + RPort->TxBaseline);
  185.         Text(RPort,&Percent[4],4);
  186.     }
  187.  
  188.         /* Draw the fill gauge? */
  189.  
  190.     if(Flags & GAUGEF_Fill)
  191.     {
  192.         LONG RightFill,LeftFill;
  193.         LONG Fill;
  194.  
  195.         SetDrMd(RPort,JAM2);
  196.  
  197.         Left += GaugeInfo->Gauge->FillLeft;
  198.  
  199.         Fill = GaugeInfo->Fill;
  200.  
  201.         LeftFill    = (GaugeInfo->Gauge->FillSize * Fill) / 100;
  202.         RightFill    = GaugeInfo->Gauge->FillSize - LeftFill;
  203.  
  204.         if(LeftFill > 0)
  205.         {
  206.             SetAPen(RPort,Pens[FILLPEN]);
  207.             FillBox(RPort,Left,Top,LeftFill,Height);
  208.         }
  209.  
  210.         if(RightFill > 0)
  211.         {
  212.             SetAPen(RPort,Pens[BACKGROUNDPEN]);
  213.             FillBox(RPort,Left + GaugeInfo->Gauge->FillSize - RightFill,Top,RightFill,Height);
  214.         }
  215.     }
  216.  
  217.     return(1);
  218. }
  219.  
  220.     /* This implements the OM_NEW method. */
  221.  
  222. STATIC ULONG
  223. NewMethod(Class *class,Object *object,struct opSet *msg)
  224. {
  225.         /* Let the superclass do its work. */
  226.  
  227.     if(object = (Object *)DoSuperMethodA(class,object,(Msg)msg))
  228.     {
  229.             /* Set any attributes passed in via the OM_NEW method. */
  230.  
  231.         DoMethod(object,OM_SET,msg->ops_AttrList,NULL);
  232.     }
  233.  
  234.     return((ULONG)object);
  235. }
  236.  
  237.     /* This implements the OM_SET method. */
  238.  
  239. STATIC VOID
  240. SetMethod(Class *class,Object *object,struct opSet *msg)
  241. {
  242.     struct GaugeInfo *GaugeInfo = INST_DATA(class,object);
  243.     struct TagItem *List,*Item;
  244.     LONG Flags;
  245.  
  246.         /* This is where we will record the bits to change
  247.          * and redraw.
  248.          */
  249.  
  250.     Flags = 0;
  251.  
  252.         /* Scan the attribute list. */
  253.  
  254.     List = msg->ops_AttrList;
  255.  
  256.     while(Item = NextTagItem(&List))
  257.     {
  258.         switch(Item->ti_Tag)
  259.         {
  260.                 /* The gauge data link. This is from the initial
  261.                  * data passed during OM_NEW.
  262.                  */
  263.  
  264.             case GAUGE_Data:
  265.  
  266.                 GaugeInfo->Gauge = (struct Gauge *)Item->ti_Data;
  267.                 break;
  268.  
  269.                 /* Gauge fill value (between 0 and 100). */
  270.  
  271.             case GAUGE_Fill:
  272.  
  273.                 GaugeInfo->Fill = (LONG)Item->ti_Data;
  274.                 Flags |= GAUGEF_Fill;
  275.  
  276.                 if(GaugeInfo->Fill > 100)
  277.                     GaugeInfo->Fill = 100;
  278.                 else if(GaugeInfo->Fill < 0)
  279.                     GaugeInfo->Fill = 0;
  280.  
  281.                 break;
  282.         }
  283.     }
  284.  
  285.         /* If there is something to update, do just that. */
  286.  
  287.     if(Flags && msg->ops_GInfo)
  288.     {
  289.         struct RastPort *RPort;
  290.  
  291.         if(RPort = ObtainGIRPort(msg->ops_GInfo))
  292.         {
  293.                 /* So the render method knows what to update. */
  294.  
  295.             GaugeInfo->Flags = Flags;
  296.  
  297.                 /* Update its imagery. */
  298.  
  299.             DoMethod(object,GM_RENDER,msg->ops_GInfo,RPort,GREDRAW_UPDATE);
  300.  
  301.             ReleaseGIRPort(RPort);
  302.         }
  303.     }
  304. }
  305.  
  306.     /* This is the fill gauge class dispatcher. */
  307.  
  308. STATIC ULONG
  309. GaugeClassDispatch(Class *class,Object *object,Msg msg)
  310. {
  311.     extern void geta4(void);
  312.  
  313.     geta4();
  314.  
  315.     switch(msg->MethodID)
  316.     {
  317.         case GM_RENDER:
  318.  
  319.             return(RenderMethod(class,object,(struct gpRender *)msg));
  320.  
  321.         case OM_NEW:
  322.  
  323.             return(NewMethod(class,object,(struct opSet *)msg));
  324.  
  325.         case OM_SET:
  326.         case OM_UPDATE:
  327.  
  328.             SetMethod(class,object,(struct opSet *)msg);
  329.             break;
  330.     }
  331.  
  332.     return(DoSuperMethodA(class,object,msg));
  333. }
  334.  
  335. /***************************************************************************/
  336.  
  337.     /* This is to find out how large the visible portion of the screen
  338.      * is and where this portion's top left corner is.
  339.      */
  340.  
  341. STATIC VOID
  342. GetScreenInfo(struct Screen *Screen,LONG *Left,LONG *Top,LONG *Width,LONG *Height)
  343. {
  344.     struct TagItem Tags[2] = { VTAG_VIEWPORTEXTRA_GET, NULL, TAG_DONE };
  345.     struct ViewPortExtra *Extra;
  346.  
  347.     if(!VideoControl(Screen->ViewPort.ColorMap,Tags))
  348.         Extra = (struct ViewPortExtra *)Tags[0].ti_Data;
  349.     else
  350.         Extra = NULL;
  351.  
  352.     if(Extra)
  353.     {
  354.         struct Rectangle Clip;
  355.  
  356.         QueryOverscan(GetVPModeID(&Screen->ViewPort),&Clip,OSCAN_TEXT);
  357.  
  358.         *Width    = Extra->DisplayClip.MaxX - Extra->DisplayClip.MinX + 1;
  359.         *Height    = Extra->DisplayClip.MaxY - Extra->DisplayClip.MinY + 1;
  360.  
  361.         if(*Width < Clip.MaxX - Clip.MinX + 1)
  362.             *Width = Clip.MaxX - Clip.MinX + 1;
  363.  
  364.         if(*Height < Clip.MaxY - Clip.MinY + 1)
  365.             *Height = Clip.MaxY - Clip.MinY + 1;
  366.     }
  367.     else
  368.     {
  369.         *Width    = Screen->Width;
  370.         *Height    = Screen->Height;
  371.     }
  372.  
  373.     *Left    = ABS(Screen->LeftEdge);
  374.     *Top    = ABS(Screen->TopEdge);
  375.  
  376.     if(*Left > Screen->Width || *Left < 0)
  377.         *Left = 0;
  378.  
  379.     if(*Top > Screen->Height || *Top < 0)
  380.         *Top = 0;
  381. }
  382.  
  383.     /* This centres the window within the visible bounds of a screen,
  384.      * but actually it just fills in the window coordinates.
  385.      */
  386.  
  387. STATIC VOID
  388. CentreWindow(struct Screen *Screen,LONG Width,LONG Height,LONG *Left,LONG *Top)
  389. {
  390.     LONG ScreenWidth,ScreenHeight,ScreenLeft,ScreenTop;
  391.  
  392.     GetScreenInfo(Screen,&ScreenLeft,&ScreenTop,&ScreenWidth,&ScreenHeight);
  393.  
  394.     *Left    = ScreenLeft + (ScreenWidth - Width) / 2;
  395.     *Top    = ScreenTop + (ScreenHeight - Height) / 2;
  396. }
  397.  
  398. /***************************************************************************/
  399.  
  400. /****** gauge/GetGaugeA ******************************************
  401. *
  402. *   NAME
  403. *    GetGaugeA -- Obtain information about the gauge display.
  404. *
  405. *   SYNOPSIS
  406. *    Attributes = GetGaugeA(Gauge,TagList)
  407. *
  408. *    LONG GetGaugeA(struct Gauge *,struct TagItem *);
  409. *
  410. *    Attributes = GetGauge(Gauge,Tags);
  411. *
  412. *    LONG GetGauge(struct Gauge *,...);
  413. *
  414. *   FUNCTION
  415. *    The Gauge structure is meant to be opaque, you should not peek
  416. *    inside to find the information you need, such as the pointer
  417. *    to the Window the display is built on top of. Rather, use this
  418. *    routine to find out about it. This routine also provides means
  419. *    to find out if a user has hit the "Stop" button.
  420. *
  421. *   INPUTS
  422. *    Gauge - Pointer to a Gauge structure as created by NewGaugeA().
  423. *        NULL is a valid parameter.
  424. *
  425. *    TagList - Pointer to a table of TagItem structures.
  426. *        NULL is a valid parameter.
  427. *
  428. *    If you provide a tag list, the following item types are valid:
  429. *
  430. *    GAUGE_MsgPort (struct MsgPort *) - Pointer to the port IntuiMessages
  431. *        arrive at the gauge Window.
  432. *
  433. *    GAUGE_Window (struct Window *) - Pointer to the Window the progress
  434. *        requester is built on top of. If the gauge display is using a shared
  435. *        message arrival port, you can use the Window pointer to find out if
  436. *        an input event was meant for the gauge display rather than a different
  437. *        Window.
  438. *
  439. *    GAUGE_Hit (LONG) - If the user hit the "Stop" button, this routine will
  440. *        return TRUE, FALSE otherwise.
  441. *
  442. *    GAUGE_SigBit (LONG) - Provides the signal bit of the message arrival
  443. *        port the gauge Window uses.
  444. *
  445. *   RESULT
  446. *    Attributes - Number of attributes this function could provide
  447. *        information on.
  448. *
  449. *   EXAMPLE
  450. *    \* Find out whether the user has hit the "Stop" button. *\
  451. *
  452. *    LONG stop;
  453. *
  454. *    if(GetGauge(gauge,
  455. *        GAUGE_Hit,&stop,
  456. *    TAG_END) == 1)
  457. *    {
  458. *        if(stop)
  459. *            printf("user wants to stop the work in progress.\n");
  460. *        else
  461. *            printf("the work in progress can continue.\n");
  462. *    }
  463. *    else
  464. *    {
  465. *        printf("something is wrong.\n");
  466. *    }
  467. *
  468. *   NOTES
  469. *    If this function returns a number smaller than the actual number
  470. *    of attributes you inquired about, some of the variables you have
  471. *    provided will not be initialized.
  472. *
  473. *   SEE ALSO
  474. *    NewGaugeA
  475. *    gadtools.library/GT_GetGadgetAttrs
  476. *
  477. *****************************************************************************
  478. *
  479. */
  480.  
  481. LONG
  482. GetGaugeA(struct Gauge *Gauge,struct TagItem *TagList)
  483. {
  484.     LONG Total;
  485.  
  486.     Total = 0;
  487.  
  488.     if(Gauge)
  489.     {
  490.         struct IntuiMessage *Message;
  491.         struct TagItem *Item;
  492.  
  493.         while(Item = NextTagItem(&TagList))
  494.         {
  495.             switch(Item->ti_Tag)
  496.             {
  497.                 case GAUGE_MsgPort:
  498.  
  499.                     *(struct MsgPort **)Item->ti_Data = Gauge->Window->UserPort;
  500.                     Total++;
  501.                     break;
  502.  
  503.                 case GAUGE_Window:
  504.  
  505.                     *(struct Window **)Item->ti_Data = Gauge->Window;
  506.                     Total++;
  507.                     break;
  508.  
  509.                 case GAUGE_Hit:
  510.  
  511.                     if(Gauge->UserPort)
  512.                     {
  513.                         Forbid();
  514.  
  515.                         *(LONG *)Item->ti_Data = FALSE;
  516.  
  517.                         for(Message = (struct IntuiMessage *)Gauge->UserPort->mp_MsgList.lh_Head ; Message->ExecMessage.mn_Node.ln_Succ  ; Message = (struct IntuiMessage *)Message->ExecMessage.mn_Node.ln_Succ)
  518.                         {
  519.                             if(Message->IDCMPWindow == Gauge->Window && Message->Class == IDCMP_GADGETUP && Message->IAddress == G(Gauge->ButtonGadget))
  520.                             {
  521.                                 *(LONG *)Item->ti_Data = TRUE;
  522.                                 break;
  523.                             }
  524.                         }
  525.  
  526.                         Permit();
  527.                     }
  528.                     else
  529.                     {
  530.                         if(SetSignal(0,0) & (1UL<<Gauge->Window->UserPort->mp_SigBit))
  531.                             *(LONG *)Item->ti_Data = TRUE;
  532.                         else
  533.                             *(LONG *)Item->ti_Data = FALSE;
  534.                     }
  535.  
  536.                     Total++;
  537.  
  538.                     break;
  539.  
  540.                 case GAUGE_SigBit:
  541.  
  542.                     *(LONG *)Item->ti_Data = Gauge->Window->UserPort->mp_SigBit;
  543.                     Total++;
  544.                     break;
  545.             }
  546.         }
  547.     }
  548.  
  549.     return(Total);
  550. }
  551.  
  552. /****** gauge/SetGaugeA ******************************************
  553. *
  554. *   NAME
  555. *    SetGaugeA -- Change the title or the position of the
  556. *        progress indicator of the gauge display.
  557. *
  558. *   SYNOPSIS
  559. *    SetGaugeA(Gauge,TagList)
  560. *
  561. *    VOID SetGaugeA(struct Gauge *Gauge,struct TagItem *);
  562. *
  563. *    SetGauge(Gauge,Tags);
  564. *
  565. *    VOID SetGauge(struct Gauge *,...);
  566. *
  567. *   FUNCTION
  568. *    After you have opened the progress requester display, you
  569. *    will want to change the indicator that shows how far the
  570. *    work the requester represents has progressed. Occasionally,
  571. *    the requester title may need changing, too, as work progresses
  572. *    into a different phase.
  573. *
  574. *   INPUTS
  575. *    Gauge - Pointer to a Gauge structure as created by NewGaugeA().
  576. *        NULL is a valid parameter.
  577. *
  578. *    TagList - Pointer to a table of TagItem structures.
  579. *        NULL is a valid parameter.
  580. *
  581. *    If you provide a tag list, the following item types are valid:
  582. *
  583. *    GAUGE_Title (STRPTR) - The gauge display bears a title which tells
  584. *        about the action in progress the display indicates. This title
  585. *        will be eventually be the title of the progress Window. Steps
  586. *        will be taken to make the progress gauge Window large enough to
  587. *        show the complete title.
  588. *
  589. *    GAUGE_Fill (LONG) - Progress is shown with a bar that grows from
  590. *        left to right. You specify how far the bar should grow with this
  591. *        tags in percentage, i.e. a value of 0 will show no bar, 50 will
  592. *        show a bar that covers half its maximum size and 100 will produce
  593. *        the complete bar. Any values outside this range will be truncated
  594. *        to the range.
  595. *
  596. *   EXAMPLE
  597. *    \* Set the fill indicator to 70% *\
  598. *
  599. *    SetGauge(gauge,
  600. *        GAUGE_Fill, 70,
  601. *    TAG_END);
  602. *
  603. *   SEE ALSO
  604. *    NewGaugeA
  605. *    intuition.library/SetAttrsA
  606. *
  607. *****************************************************************************
  608. *
  609. */
  610.  
  611. VOID
  612. SetGaugeA(struct Gauge *Gauge,struct TagItem *TagList)
  613. {
  614.     if(Gauge)
  615.     {
  616.         struct TagItem *Item;
  617.  
  618.         while(Item = NextTagItem(&TagList))
  619.         {
  620.             switch(Item->ti_Tag)
  621.             {
  622.                 case GAUGE_Title:
  623.  
  624.                     SetWindowTitles(Gauge->Window,(STRPTR)Item->ti_Data,(STRPTR)~0);
  625.                     break;
  626.  
  627.                 case GAUGE_Fill:
  628.  
  629.                     SetGadgetAttrs(G(Gauge->GaugeObject),Gauge->Window,NULL,
  630.                         GAUGE_Fill,Item->ti_Data,
  631.                     TAG_DONE);
  632.                     break;
  633.             }
  634.         }
  635.     }
  636. }
  637.  
  638. /****** gauge/DisposeGauge ******************************************
  639. *
  640. *   NAME
  641. *    DisposeGauge -- Close a progress requester Window.
  642. *
  643. *   SYNOPSIS
  644. *    DisposeGauge(Gauge)
  645. *
  646. *    VOID DisposeGauge(struct Gauge *);
  647. *
  648. *   FUNCTION
  649. *    When you are finished with the work the progress gauge represents,
  650. *    you should close the gauge Window. This routine will close the
  651. *    display opened by NewGaugeA().
  652. *
  653. *   INPUTS
  654. *    Gauge - Pointer to a Gauge structure as created by NewGaugeA().
  655. *        NULL is a valid parameter.
  656. *
  657. *   EXAMPLE
  658. *    \* Close the gauge display *\
  659. *
  660. *    DisposeGauge(gauge);
  661. *
  662. *   SEE ALSO
  663. *    NewGaugeA
  664. *
  665. *****************************************************************************
  666. *
  667. */
  668.  
  669. VOID
  670. DisposeGauge(struct Gauge *Gauge)
  671. {
  672.     if(Gauge)
  673.     {
  674.         if(Gauge->Window)
  675.         {
  676.             if(Gauge->UserPort && Gauge->Window->UserPort)
  677.             {
  678.                 struct IntuiMessage *IntuiMsg;
  679.                 struct Node *Next;
  680.  
  681.                 Forbid();
  682.  
  683.                 for(IntuiMsg = (struct IntuiMessage *)Gauge->Window->UserPort->mp_MsgList.lh_Head ; Next = IntuiMsg->ExecMessage.mn_Node.ln_Succ ; IntuiMsg = (struct IntuiMessage *)Next)
  684.                 {
  685.                     if(IntuiMsg->IDCMPWindow == Gauge->Window)
  686.                     {
  687.                         Remove((struct Node *)IntuiMsg);
  688.  
  689.                         ReplyMsg((struct Message *)IntuiMsg);
  690.                     }
  691.                 }
  692.  
  693.                 Gauge->Window->UserPort = NULL;
  694.  
  695.                 ModifyIDCMP(Gauge->Window,NULL);
  696.  
  697.                 Permit();
  698.             }
  699.  
  700.             CloseWindow(Gauge->Window);
  701.         }
  702.  
  703.         DisposeObject(Gauge->ButtonImage);
  704.         DisposeObject(Gauge->BackgroundImage);
  705.         DisposeObject(Gauge->BackgroundFrameImage);
  706.         DisposeObject(Gauge->ButtonGadget);
  707.         DisposeObject(Gauge->GaugeObject);
  708.         DisposeObject(Gauge->GaugeFrameImage);
  709.  
  710.         FreeScreenDrawInfo(Gauge->Screen,Gauge->DrawInfo);
  711.  
  712.         if(Gauge->GaugeClass)
  713.             FreeClass(Gauge->GaugeClass);
  714.  
  715.         if(Gauge->UnlockPubScreen)
  716.             UnlockPubScreen(NULL,Gauge->PubScreen);
  717.  
  718.         FreeVec(Gauge);
  719.     }
  720. }
  721.  
  722. /****** gauge/NewGaugeA ******************************************
  723. *
  724. *   NAME
  725. *    NewGaugeA -- Create a progress gauge display requester
  726. *
  727. *   SYNOPSIS
  728. *    Gauge = NewGaugeA(TagList)
  729. *
  730. *    struct Gauge *NewGaugeA(struct TagItem *);
  731. *
  732. *    Gauge = NewGauge(Tags);
  733. *
  734. *    struct Gauge *NewGauge(Tag firstTag,...);
  735. *
  736. *   FUNCTION
  737. *    The "Amiga User Interface Style Guide" gives an example of how a
  738. *    progress requester should look like (page 29). This example
  739. *    lacks "teeth" as it just represents a visual cue, not something
  740. *    you can work with--until you implement it. NewGaugeA() will create
  741. *    such a progress requester.
  742. *
  743. *   INPUTS
  744. *    You control the initial attributes of the gauge display with
  745. *    tagitems:
  746. *
  747. *    GAUGE_Window (struct Window *) - The parent Window to "attach"
  748. *        the gauge to. The gauge display will appear on the same Screen
  749. *        as the parent Window, centred within the bounds of the Window.
  750. *        These are the only relations to the "parent" Window.
  751. *
  752. *    GAUGE_Screen (struct Screen *) - The Screen to open the gauge on.
  753. *        The gauge will appear centred within the visible part of
  754. *        this Screen.
  755. *
  756. *    GAUGE_PubScreen (struct Screen *) - Pointer to a Public Screen the
  757. *        gauge display should be opened on as a Visitor Window. The gauge
  758. *        will appear centred within the visible part of this Screen.
  759. *
  760. *    GAUGE_PubScreenName (STRPTR) - Name of a Public Screen the gauge
  761. *        display should be opened on as a Visitor Window. If the named
  762. *        Screen cannot be found, the display will default to open on the
  763. *        Default Public Screen. You can control the fall-back behaviour
  764. *        with the GAUGE_PubScreenFallback tag.
  765. *
  766. *    GAUGE_PubScreenFallback (BOOL) - If the named Public Screen the
  767. *        display is to open on cannot be found, the gauge display will
  768. *        default to open on the Default Public Screen. In most cases,
  769. *        this will be the Workbench Screen. This tag controls whether
  770. *        the fall-back will take place or whether the display will fail
  771. *        to open.
  772. *        (Default: TRUE)
  773. *
  774. *    GAUGE_UserPort (struct MsgPort *) - You can have multiple Windows
  775. *        sharing the IntuiMessage arrival MsgPort. If you with to share
  776. *        some other Window's arrival port with the gauge display, you
  777. *        can specify the port address with this tag.
  778. *
  779. *    GAUGE_ButtonLabel (STRPTR) - There is one single button in the
  780. *        gauge display, which when hit by the user indicates that
  781. *        whatever action in progress the gauge represents should be
  782. *        brought to a stop. By default, this button bears the label
  783. *        "Stop". You can provide a replacement label, such as for
  784. *        localization, using this tag.
  785. *        (Default: "Stop")
  786. *
  787. *    GAUGE_Title (STRPTR) - The gauge display bears a title which tells
  788. *        about the action in progress the display indicates. This title
  789. *        will be eventually be the title of the progress Window. Steps
  790. *        will be taken to make the progress gauge Window large enough to
  791. *        show the complete title.
  792. *        (Default: "")
  793. *
  794. *    GAUGE_Fill (LONG) - Progress is shown with a bar that grows from
  795. *        left to right. You specify how far the bar should grow with this
  796. *        tags in percentage, i.e. a value of 0 will show no bar, 50 will
  797. *        show a bar that covers half its maximum size and 100 will produce
  798. *        the complete bar. Any values outside this range will be truncated
  799. *        to the range.
  800. *        (Default: 0)
  801. *
  802. *   RESULT
  803. *    Gauge - Pointer to a Gauge structure that represents the progress
  804. *        requester Window and its contents. You should not peek the data
  805. *        this structure holds, but use the GetGaugeA() function instead.
  806. *        If NewGaugeA() cannot create the progress display, it will return
  807. *        NULL.
  808. *
  809. *   EXAMPLE
  810. *    \* Create the progress report Window and wait for the user to
  811. *     * hit the "Stop" button. Make it look like the example in
  812. *     * the "Amiga User Interface Style Guide".
  813. *     *\
  814. *
  815. *    struct Gauge *gauge;
  816. *    struct MsgPort *port;
  817. *
  818. *    gauge = NewGauge(
  819. *        GAUGE_Title, "Rendering \"Boing.Ball\"",
  820. *        GAUGE_Fill,  40,
  821. *    TAG_END);
  822. *
  823. *    if(gauge != NULL)
  824. *    {
  825. *        GetGauge(&gauge,
  826. *            GAUGE_MsgPort,&port,
  827. *        TAG_END);
  828. *
  829. *        WaitPort(port);
  830. *
  831. *        DisposeGauge(gauge);
  832. *    }
  833. *
  834. *   NOTES
  835. *    Unless you provide a MsgPort to be used as the IntuiMessage
  836. *    arrival port, this function will create one for you. To find
  837. *    out about this port, use GetGaugeA().
  838. *
  839. *    If this function is not told about the Screen it should open
  840. *    the progress requester Window on, it will default to opening
  841. *    the Window on the Default Public Screen. Usually, this will
  842. *    be the Workbench Screen.
  843. *
  844. *    The gauge Window contains one single button the user can
  845. *    hit if the action the requester represents should be aborted.
  846. *    In this event, an IDCMP_GADGETUP message will be generated.
  847. *    This is the *only* type of event the gauge Window can generate.
  848. *    Thus, when you receive any kind of input from this Window you
  849. *    can be certain that the user wants to stop the work in progress.
  850. *
  851. *   SEE ALSO
  852. *    intuition.library/OpenWindow
  853. *    intuition.library/OpenWindowTagList
  854. *
  855. *****************************************************************************
  856. *
  857. */
  858.  
  859. struct Gauge *
  860. NewGaugeA(struct TagItem *TagList)
  861. {
  862.     struct Gauge *Gauge;
  863.  
  864.         /* Start by allocating memory for the data structures. */
  865.  
  866.     if(Gauge = (struct Gauge *)AllocVec(sizeof(struct Gauge),MEMF_ANY|MEMF_CLEAR))
  867.     {
  868.         STRPTR ButtonLabel,Title;
  869.         LONG Fill;
  870.  
  871.         ButtonLabel    = (STRPTR)GetTagData(GAUGE_ButtonLabel,(ULONG)"Stop",TagList);
  872.         Title        = (STRPTR)GetTagData(GAUGE_Title,(ULONG)"",TagList);
  873.         Fill        = (LONG)GetTagData(GAUGE_Fill,0,TagList);
  874.         Gauge->Parent    = (struct Window *)GetTagData(GAUGE_Window,NULL,TagList);
  875.         Gauge->Screen    = (struct Screen *)GetTagData(GAUGE_Screen,NULL,TagList);
  876.         Gauge->UserPort    = (struct MsgPort *)GetTagData(GAUGE_UserPort,NULL,TagList);
  877.  
  878.             /* If no parent window and screen are provided,
  879.              * use the default public screen instead.
  880.              */
  881.  
  882.         if(!Gauge->Screen && !Gauge->Parent)
  883.         {
  884.             Gauge->PubScreen = (struct Screen *)GetTagData(GAUGE_PubScreen,NULL,TagList);
  885.  
  886.             if(!Gauge->PubScreen)
  887.             {
  888.                 STRPTR Name;
  889.  
  890.                 Name = (STRPTR)GetTagData(GAUGE_PubScreenName,NULL,TagList);
  891.  
  892.                 if(Name)
  893.                 {
  894.                     if(Gauge->PubScreen = LockPubScreen(Name))
  895.                         Gauge->UnlockPubScreen = TRUE;
  896.                     else
  897.                     {
  898.                         if(!GetTagData(GAUGE_PubScreenFallback,TRUE,TagList))
  899.                             return(NULL);
  900.                     }
  901.                 }
  902.             }
  903.  
  904.             if(!Gauge->PubScreen)
  905.             {
  906.                 if(!(Gauge->PubScreen = LockPubScreen(NULL)))
  907.                     return(NULL);
  908.                 else
  909.                 {
  910.                     Gauge->Screen = Gauge->PubScreen;
  911.                     Gauge->UnlockPubScreen = TRUE;
  912.                 }
  913.             }
  914.         }
  915.  
  916.             /* Now check which screen to use. */
  917.  
  918.         if(!Gauge->Screen)
  919.             Gauge->Screen = Gauge->Parent->WScreen;
  920.  
  921.             /* Get the screen drawing info and create the private
  922.              * gauge fill class.
  923.              */
  924.  
  925.         Gauge->DrawInfo        = GetScreenDrawInfo(Gauge->Screen);
  926.         Gauge->GaugeClass    = MakeClass(NULL,GADGETCLASS,NULL,sizeof(struct GaugeInfo),0);
  927.  
  928.             /* Did we get what we wanted? */
  929.  
  930.         if(Gauge->DrawInfo && Gauge->GaugeClass)
  931.         {
  932.             STATIC UWORD Crosshatch[] = { 0x5555, 0xAAAA };
  933.             extern ULONG HookEntry( struct Hook *, Object *, APTR );
  934.  
  935.                 /* Fill in the class dispatcher. */
  936.  
  937.             Gauge->GaugeClass->cl_Dispatcher.h_Entry = (HOOKFUNC)HookEntry;
  938.             Gauge->GaugeClass->cl_Dispatcher.h_SubEntry = (HOOKFUNC)GaugeClassDispatch;
  939.  
  940.                 /* Create the cross-hatch window background. */
  941.  
  942.             Gauge->BackgroundImage = NewObject(NULL,FILLRECTCLASS,
  943.                 IA_APattern,    Crosshatch,
  944.                 IA_APatSize,    1,
  945.                 IA_Mode,    JAM2,
  946.                 IA_FGPen,    Gauge->DrawInfo->dri_Pens[SHINEPEN],
  947.                 IA_BGPen,    Gauge->DrawInfo->dri_Pens[BACKGROUNDPEN],
  948.             TAG_DONE);
  949.  
  950.                 /* Create the frame within the background. */
  951.  
  952.             Gauge->BackgroundFrameImage = NewObject(NULL,FRAMEICLASS,
  953.                 IA_Recessed,    TRUE,
  954.             TAG_DONE);
  955.  
  956.                 /* Create the frame to surround the gauge fill bar. */
  957.  
  958.             Gauge->GaugeFrameImage = NewObject(NULL,FRAMEICLASS,
  959.                 IA_Recessed,    TRUE,
  960.                 IA_EdgesOnly,    TRUE,
  961.             TAG_DONE);
  962.  
  963.                 /* Create the frame to be placed around the gauge
  964.                  * button and if this succeeds, create the
  965.                  * button itself.
  966.                  */
  967.  
  968.             if(Gauge->ButtonImage = NewObjectA(NULL,FRAMEICLASS,NULL))
  969.             {
  970.                 Gauge->ButtonGadget = NewObject(NULL,FRBUTTONCLASS,
  971.                     GA_Text,    ButtonLabel,
  972.                     GA_Image,    Gauge->ButtonImage,
  973.                     GA_RelVerify,    TRUE,
  974.                     GA_DrawInfo,    Gauge->DrawInfo,
  975.                 TAG_DONE);
  976.             }
  977.  
  978.                 /* Did we get everything we wanted? */
  979.  
  980.             if(Gauge->ButtonImage &&
  981.                Gauge->BackgroundImage &&
  982.                Gauge->BackgroundFrameImage &&
  983.                Gauge->ButtonGadget &&
  984.                Gauge->GaugeFrameImage)
  985.             {
  986.                 LONG Size,Max,InteriorWidth,InteriorHeight;
  987.                 struct TextExtent Extent;
  988.                 struct RastPort RastPort;
  989.                 LONG DistX,DistY;
  990.                 ULONG TitleWidth,DepthWidth;
  991.                 Object *DepthImage;
  992.                 struct IBox FrameBox,ContentsBox;
  993.  
  994.                     /* Check how wide the window depth arrangement
  995.                      * gadget will become.
  996.                      */
  997.  
  998.                 if(DepthImage = NewObject(NULL,SYSICLASS,
  999.                     SYSIA_Size,    (Gauge->Screen->Flags & SCREENHIRES) ? SYSISIZE_MEDRES : SYSISIZE_LOWRES,
  1000.                     SYSIA_Which,    DEPTHIMAGE,
  1001.                     SYSIA_DrawInfo,    Gauge->DrawInfo,
  1002.                 TAG_DONE))
  1003.                 {
  1004.                     GetAttr(IA_Width,DepthImage,&DepthWidth);
  1005.  
  1006.                     DisposeObject(DepthImage);
  1007.                 }
  1008.                 else
  1009.                     DepthWidth = (Gauge->Screen->Flags & SCREENHIRES) ? 23 : 17;
  1010.  
  1011.                     /* We need a RastPort to measure font sizes, so we
  1012.                      * set up one here.
  1013.                      */
  1014.  
  1015.                 InitRastPort(&RastPort);
  1016.                 SetFont(&RastPort,Gauge->DrawInfo->dri_Font);
  1017.  
  1018.                     /* This is how wide the window title will become. */
  1019.  
  1020.                 TitleWidth = TextWidth(&RastPort,Title,strlen(Title)) + 2 + DepthWidth;
  1021.  
  1022.                     /* Find out how large the gauge button will become.
  1023.                      * We'll use this information in a minute.
  1024.                      */
  1025.  
  1026.                 TextFit(&RastPort,ButtonLabel,strlen(ButtonLabel),&Extent,NULL,1,32767,32767);
  1027.  
  1028.                     /* This determines the layout distances. The screen
  1029.                      * aspect ratio is taken into account.
  1030.                      */
  1031.  
  1032.                 DistY = 2;
  1033.                 DistX = (DistY * Gauge->DrawInfo->dri_Resolution.Y) / Gauge->DrawInfo->dri_Resolution.X;
  1034.  
  1035.                     /* Now set up the button size. We must do this after
  1036.                      * creating the button. If we set the size in the
  1037.                      * NewObject() call the button will ignore it.
  1038.                      */
  1039.  
  1040.                 SetGadgetAttrs(G(Gauge->ButtonGadget),NULL,NULL,
  1041.                     GA_DrawInfo,    Gauge->DrawInfo,
  1042.                     GA_Width,    4*DistX + Extent.te_Width - Extent.te_Extent.MinX,
  1043.                     GA_Height,    4*DistY + Extent.te_Height,
  1044.                 TAG_DONE);
  1045.  
  1046.                     /* We need to fit the frame around the fill
  1047.                      * gauge. To find out how large this frame
  1048.                      * would become, we tell the frame image to
  1049.                      * determine its layout size.
  1050.                      */
  1051.  
  1052.                 FrameBox.Left    = 0;
  1053.                 FrameBox.Top    = 0;
  1054.                 FrameBox.Width    = 10;
  1055.                 FrameBox.Height    = 10;
  1056.  
  1057.                 DoMethod(Gauge->GaugeFrameImage,IM_FRAMEBOX,&FrameBox,&ContentsBox,Gauge->DrawInfo,0);
  1058.  
  1059.                     /* Determine how much room the 0% and 100% text will occupy. */
  1060.  
  1061.                 Gauge->LeftPercentWidth        = TextWidth(&RastPort,Percent,4);
  1062.                 Gauge->RightPercentWidth    = TextWidth(&RastPort,&Percent[4],4);
  1063.                 Gauge->Space            = (4 * DistX) + (FrameBox.Width - 10);
  1064.  
  1065.                 Max = 2 * Gauge->Space + Gauge->LeftPercentWidth + 100 + Gauge->RightPercentWidth;
  1066.  
  1067.                     /* Make sure that the gauge display will be wide enough. */
  1068.  
  1069.                 if(3 * G(Gauge->ButtonGadget)->Width > Max)
  1070.                     Max = 3 * G(Gauge->ButtonGadget)->Width;
  1071.  
  1072.                     /* We want the entire title to be visible. This is why
  1073.                      * we inquired about the widths of the window depth
  1074.                      * arrangement gadget and the title text.
  1075.                      */
  1076.  
  1077.                 if(TitleWidth > Max)
  1078.                     Max = TitleWidth;
  1079.  
  1080.                     /* Now we can set up the size of the interior
  1081.                      * window area.
  1082.                      */
  1083.  
  1084.                 InteriorWidth    = DistX * 4 + Max + DistX * 4;
  1085.                 InteriorHeight    = DistY * 4 + Extent.te_Height + DistY * 4;
  1086.  
  1087.                     /* Guess I'm the first guy who uses the golden section
  1088.                      * for the gadget layout.
  1089.                      */
  1090.  
  1091.                 Size = (1618 * InteriorHeight) / 1000;
  1092.  
  1093.                 if(Size > InteriorWidth)
  1094.                     InteriorWidth = Size;
  1095.  
  1096.                     /* All the image data will be attached to one
  1097.                      * dummy gadget that goes into the window
  1098.                      * background.
  1099.                      */
  1100.  
  1101.                 Gauge->BackgroundGadget.LeftEdge    = Gauge->Screen->WBorLeft;
  1102.                 Gauge->BackgroundGadget.TopEdge        = Gauge->Screen->WBorTop + Gauge->Screen->Font->ta_YSize + 1;
  1103.                 Gauge->BackgroundGadget.Flags        = GFLG_GADGIMAGE|GFLG_GADGHNONE;
  1104.                 Gauge->BackgroundGadget.GadgetRender    = Gauge->BackgroundImage;
  1105.                 Gauge->BackgroundGadget.Width        = DistX * 4 + InteriorWidth + DistX * 4;
  1106.                 Gauge->BackgroundGadget.Height        = DistY * 4 + InteriorHeight + DistY * 2 + G(Gauge->ButtonGadget)->Height + DistY * 2;
  1107.  
  1108.                     /* Link the two images together. I wish there were
  1109.                      * an imageclass attribute to do this.
  1110.                      */
  1111.  
  1112.                 I(Gauge->BackgroundImage)->NextImage = I(Gauge->BackgroundFrameImage);
  1113.  
  1114.                     /* Now find out how wide the fill gauge will be
  1115.                      * and where it should be placed.
  1116.                      */
  1117.  
  1118.                 Gauge->FillSize    = InteriorWidth - (2 * Gauge->Space + Gauge->LeftPercentWidth + Gauge->RightPercentWidth + 2 * DistX * 4);
  1119.                 Gauge->FillLeft = Gauge->LeftPercentWidth + Gauge->Space;
  1120.  
  1121.                     /* Now comes the magic part; we need to fit
  1122.                      * the frame around the fill gauge. This is
  1123.                      * done by telling the frame image how large
  1124.                      * the fill gauge will become and requesting
  1125.                      * that it should find the proper placement
  1126.                      * and size for this.
  1127.                      */
  1128.  
  1129.                 FrameBox.Left    = 0;
  1130.                 FrameBox.Top    = 0;
  1131.                 FrameBox.Width    = Gauge->FillSize;
  1132.                 FrameBox.Height    = Extent.te_Height;
  1133.  
  1134.                 DoMethod(Gauge->GaugeFrameImage,IM_FRAMEBOX,&FrameBox,&ContentsBox,Gauge->DrawInfo,0);
  1135.  
  1136.                     /* That's all, now make the coordinates and size permanent. */
  1137.  
  1138.                 SetAttrs(Gauge->GaugeFrameImage,
  1139.                     IA_Left,    ContentsBox.Left,
  1140.                     IA_Top,        ContentsBox.Top,
  1141.                     IA_Width,    ContentsBox.Width,
  1142.                     IA_Height,    ContentsBox.Height,
  1143.                 TAG_DONE);
  1144.  
  1145.                     /* Make the cross-hatch window background as large
  1146.                      * as the dummy gadget.
  1147.                      */
  1148.  
  1149.                 SetAttrs(Gauge->BackgroundImage,
  1150.                     IA_Left,    0,
  1151.                     IA_Top,        0,
  1152.                     IA_Width,    Gauge->BackgroundGadget.Width,
  1153.                     IA_Height,    Gauge->BackgroundGadget.Height,
  1154.                 TAG_DONE);
  1155.  
  1156.                     /* Put the frame into the top left corner of
  1157.                      * the background.
  1158.                      */
  1159.  
  1160.                 SetAttrs(Gauge->BackgroundFrameImage,
  1161.                     IA_Left,    DistX * 4,
  1162.                     IA_Top,        DistY * 4,
  1163.                     IA_Width,    InteriorWidth,
  1164.                     IA_Height,    InteriorHeight,
  1165.                 TAG_DONE);
  1166.  
  1167.                     /* Centre the button within the window. */
  1168.  
  1169.                 SetAttrs(Gauge->ButtonGadget,
  1170.                     GA_Left,    Gauge->BackgroundGadget.LeftEdge + (Gauge->BackgroundGadget.Width - G(Gauge->ButtonGadget)->Width) / 2,
  1171.                     GA_Top,        Gauge->BackgroundGadget.TopEdge    + DistY * 4 + DistY * 4 + Extent.te_Height + DistY * 4 + DistY * 2,
  1172.                 TAG_DONE);
  1173.  
  1174.                     /* Now create the gauge display object. */
  1175.  
  1176.                 if(Fill < 0)
  1177.                     Fill = 0;
  1178.                 else if (Fill > 100)
  1179.                     Fill = 100;
  1180.  
  1181.                 Gauge->GaugeObject = NewObject(Gauge->GaugeClass,NULL,
  1182.                     GA_Left,    Gauge->BackgroundGadget.LeftEdge + DistX * 4 + DistX * 4,
  1183.                     GA_Top,        Gauge->BackgroundGadget.TopEdge    + DistY * 4 + DistY * 4,
  1184.                     GA_Width,    InteriorWidth - (DistX * 4 + DistX * 4),
  1185.                     GA_Height,    InteriorHeight - (DistY * 4 + DistY * 4),
  1186.                     GA_Previous,    Gauge->ButtonGadget,
  1187.                     GA_Next,    &Gauge->BackgroundGadget,
  1188.                     GAUGE_Data,    Gauge,
  1189.                     GAUGE_Fill,    Fill,
  1190.                 TAG_DONE);
  1191.  
  1192.                     /* Did we get what we wanted? */
  1193.  
  1194.                 if(Gauge->GaugeObject)
  1195.                 {
  1196.                     LONG Left,Top,Width,Height;
  1197.                     ULONG TagValue;
  1198.  
  1199.                         /* Now determine the size of the window to open
  1200.                          * and its placement.
  1201.                          */
  1202.  
  1203.                     Width    = Gauge->Screen->WBorLeft + Gauge->BackgroundGadget.Width + Gauge->Screen->WBorRight;
  1204.                     Height    = Gauge->Screen->WBorTop + Gauge->Screen->Font->ta_YSize + 1 + Gauge->BackgroundGadget.Height + Gauge->Screen->WBorBottom;
  1205.  
  1206.                     if(Gauge->Parent)
  1207.                     {
  1208.                         Left    = Gauge->Parent->LeftEdge + (Gauge->Parent->Width - Width) / 2;
  1209.                         Top    = Gauge->Parent->TopEdge + (Gauge->Parent->Height - Height) / 2;
  1210.                     }
  1211.                     else
  1212.                     {
  1213.                         Left    = 0;
  1214.                         Top    = 0;
  1215.  
  1216.                         CentreWindow(Gauge->Screen,Width,Height,&Left,&Top);
  1217.                     }
  1218.  
  1219.                         /* Special treatment for public screens. */
  1220.  
  1221.                     if(Gauge->PubScreen)
  1222.                         TagValue = WA_PubScreen;
  1223.                     else
  1224.                         TagValue = WA_CustomScreen;
  1225.  
  1226.                         /* Now open the window, putting it all together. */
  1227.  
  1228.                     if(Gauge->Window = OpenWindowTags(NULL,
  1229.                         WA_Left,    Left,
  1230.                         WA_Top,        Top,
  1231.                         WA_Width,    Width,
  1232.                         WA_Height,    Height,
  1233.                         WA_Title,    Title,
  1234.                         WA_Flags,    WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_RMBTRAP | WFLG_NOCAREREFRESH | WFLG_SIMPLE_REFRESH | WFLG_ACTIVATE,
  1235.                         WA_Gadgets,    Gauge->ButtonGadget,
  1236.                         WA_IDCMP,    Gauge->UserPort ? NULL : IDCMP_GADGETUP,
  1237.                         TagValue,    Gauge->Screen,
  1238.                     TAG_DONE))
  1239.                     {
  1240.                         if(Gauge->UserPort)
  1241.                         {
  1242.                             Gauge->Window->UserPort = Gauge->UserPort;
  1243.  
  1244.                             if(ModifyIDCMP(Gauge->Window,IDCMP_GADGETUP))
  1245.                                 return(Gauge);
  1246.                             else
  1247.                                 Gauge->Window->UserPort = NULL;
  1248.                         }
  1249.                         else
  1250.                             return(Gauge);
  1251.                     }
  1252.                 }
  1253.             }
  1254.         }
  1255.  
  1256.         DisposeGauge(Gauge);
  1257.     }
  1258.  
  1259.     return(NULL);
  1260. }
  1261.  
  1262. /***************************************************************************/
  1263.  
  1264. LONG
  1265. GetGauge(struct Gauge *Gauge,...)
  1266. {
  1267.     va_list VarArgs;
  1268.     LONG Result;
  1269.  
  1270.     va_start(VarArgs,Gauge);
  1271.     Result = GetGaugeA(Gauge,(struct TagItem *)VarArgs);
  1272.     va_end(VarArgs);
  1273.  
  1274.     return(Result);
  1275. }
  1276.  
  1277. VOID
  1278. SetGauge(struct Gauge *Gauge,...)
  1279. {
  1280.     va_list VarArgs;
  1281.  
  1282.     va_start(VarArgs,Gauge);
  1283.     SetGaugeA(Gauge,(struct TagItem *)VarArgs);
  1284.     va_end(VarArgs);
  1285. }
  1286.  
  1287. struct Gauge *
  1288. NewGauge(Tag tag1,...)
  1289. {
  1290.     return(NewGaugeA((struct TagItem *)&tag1));
  1291. }
  1292.  
  1293. /***************************************************************************/
  1294.  
  1295. /****** gauge/--background-- ******************************************
  1296. *
  1297. *   PURPOSE
  1298. *    The "Amiga User Interface Style Guide" contains an example of how
  1299. *    a progress requester should look like (page 29), but this example
  1300. *    has no "teeth". It is just a visual cue, you still have to implement
  1301. *    the requester yourself. Around Christmas Day, the issue was brought
  1302. *    up in comp.sys.amiga.programmer how to program these progress
  1303. *    requesters. This is my attempt at solving the problem.
  1304. *
  1305. *   GOALS
  1306. *    - The progress requester should be entirely self-contained and
  1307. *      reentrant.
  1308. *    - It should be simple to find out if the user has hit the
  1309. *      "Stop" button.
  1310. *    - Creation and management of the requester should follow a
  1311. *      familiar model.
  1312. *    - The display should be font sensitive.
  1313. *
  1314. *   IMPLEMENTATION
  1315. *    The implementation consists of a set of BOOPSI objects, which are
  1316. *    linked together, plus a display element for the gauge. This is
  1317. *    similar to how Intuition builds system requesters and has the
  1318. *    advantage of delegating the work load of refreshing and maintaining
  1319. *    the display to Intuition. The application to create the display
  1320. *    just has to listen for the user to hit the "Stop" button, it need
  1321. *    not worry about housekeeping work.
  1322. *
  1323. *    The gauge creation code is reentrant, it was written with SAS/C
  1324. *    in mind. The only compiler dependant part is the custom class
  1325. *    dispatcher. You must initialize IntuitionBase, GfxBase,
  1326. *    UtilityBase and SysBase for the gauge code to work.
  1327. *
  1328. *   COPYRIGHT AND USAGE RESTRICTIONS
  1329. *    This implementation is Copyright © 1997 by Olaf `Olsen' Barthel.
  1330. *    It is freely distributable. I place no restrictions on its usage.
  1331. *    You may use this code in commercial software or shareware programs
  1332. *    without having to pay royalties to me. An acknowledgement would
  1333. *    still be nice, though :^) If you find bugs in the code or make
  1334. *    enhancements, please notify me. I would like to keep this code
  1335. *    updated, so everyone will be able to take advantage of it.
  1336. *
  1337. *    My electronic mail address:
  1338. *
  1339. *        olsen@sourcery.han.de
  1340. *
  1341. *    My postal address:
  1342. *
  1343. *        Olaf Barthel
  1344. *        Brabeckstrasse 35
  1345. *        D-30559 Hannover
  1346. *        Federal Republic of Germany
  1347. *
  1348. *****************************************************************************
  1349. *
  1350. */
  1351.