home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / netrexx.zip / NetRexx / Tablet.nrx < prev    next >
Text File  |  1997-06-30  |  18KB  |  408 lines

  1. /* ---------------------------------------------------------- */
  2. /* Tablet -- use the composite tab gif [tablet.gif]           */
  3. /*                                                            */
  4. /* Use as:                                                    */
  5. /*                                                            */
  6. /*   <applet code="Tablet.class" width=100% height=40>        */
  7. /*   <param name=tab1 value="News nrnews.htm">                */
  8. /*   <param name=tab2 value="Documentation nrdocs.htm">       */
  9. /*   <param name=show value="2">                              */
  10. /*   </applet>                                                */
  11. /*                                                            */
  12. /* The 'name' parameters define the data for each tab:        */
  13. /*                                                            */
  14. /*   word 1 -- Text to show on the tab.  Use an underscore    */
  15. /*             ('_') to include a blank.                      */
  16. /*                                                            */
  17. /*   word 2 -- URI to be shown when the tab is clicked.       */
  18. /*             This is relative to the directory in which the */
  19. /*             current document appears.                      */
  20. /*                                                            */
  21. /*   rest   -- (not yet implemented) bubble-help for the tab. */
  22. /*                                                            */
  23. /* The 'show' parameter either gives the number of the tab    */
  24. /* that will be brought to the front (numbered 1-n, from the  */
  25. /* left), or will be 0 (meaning show the lower edge of the    */
  26. /* 'card index').  The default show is 1.                     */
  27. /* For show=0, the height in the applet tag should be 15.     */
  28. /* ---------------------------------------------------------- */
  29. options binary      -- optional; runs a bit faster if set
  30.  
  31. class Tablet extends Applet
  32.  
  33.  properties constant -- private
  34.  
  35.   -- Positions in the 'original' image (proto) of the pieces
  36.   -- that we want; note the two risers must be the same width.
  37.   -- The parts up, down, and downb are extended (repeated) to
  38.   -- form tabs and shoulders.
  39.   -- For each slice we need the start offset (x) and width (w)
  40.   xrise =  0; wrise =14  -- rise   (rise left tab)
  41.   xup   = 14; wup   = 6  -- up     (tab top)
  42.   xdrop = 20; wdrop =14  -- drop   (drop from top to shoulder)
  43.   xdown = 34; wdown = 6  -- down   (flat shoulder)
  44.   xterm = 40; wterm =14  -- term   (right shoulder; termination)
  45.   xstar = 54; wstar =15  -- start  (left shoulder)
  46.   xrism = 69; wrism =14  -- rism   (rise middle)
  47.   xstarb= 83; wstarb=15  -- starb  (start bottom)
  48.   xdownb= 98; wdownb= 6  -- downb  (down bottom)
  49.   xtermb=104; wtermb=14  -- termb  (termination bottom)
  50.   xfin  =128             -- finish offset (= total width)
  51.   -- Heights of the full tabs and 'bottom' pieces
  52.   yproto =40             -- full size pieces
  53.   ybottom=15             -- bottom pieces
  54.  
  55.  properties private
  56.  
  57.   tabfont  ='Helvetica'  -- font face for tab text
  58.   tabweight=Font.BOLD    -- weight
  59.   tabcol   =Color.black  -- color
  60.   tabcolhi =Color.blue   -- color, when highlit
  61.   tabcollo =Color.gray   -- color, when current (fronttab)
  62.   tabpoints=int          -- size for desired text height [points]
  63.   tabheight=16           -- desired text height [pels]
  64.   tabbasey =22           -- text baseline
  65.   tabbasex = 0           -- text baseline x adjustment
  66.  
  67.   shadow=Image           -- the Image to draw (null if no good)
  68.   proto =Image           -- the Image prototype
  69.   wid   =0               -- working tabs width
  70.   hig   =0               -- working tabs height
  71.   xscale=float           -- scale factor in x
  72.   yscale=float           -- scale factor in y
  73.  
  74.   -- data
  75.   tabs     =int          -- number of tabs
  76.   wtab     =int[]        -- tab widths array
  77.   otab     =int[]        -- tab body offsets array
  78.   fronttab =int 0        -- tab focus number [0->tabs-1]
  79.   mousetab =int(-1)      -- tab under mouse  [0->tabs-1, or -1]
  80.   mouselast=int(-1)      -- tab under mouse  [0->tabs-1, or -1]
  81.   tabword  =String[]     -- String for the tab
  82.   taburl   =String[]     -- URL for the tab
  83. --tabhelp  =String[]     -- help String for the tab [nyi]
  84.  
  85.  /* ---------------------------------------------------------- */
  86.  /* init is called once the applet is loaded.  It sets up the  */
  87.  /* master 'tabs' image, using pieces from the prototype.      */
  88.  /* Note that if the prototype image used to form the tabs     */
  89.  /* cannot be loaded for some reason, we'll still get the      */
  90.  /* text segments, and they'll work as expected.               */
  91.  /* ---------------------------------------------------------- */
  92.  method init
  93.   s='Copyright (c) IBM, 1997'
  94.  
  95.   /* Get the parameters */
  96.   -- Determine the most forward, if any
  97.   s=getParameter('show') -- the number of the front tab (default 1)
  98.   if s\=null then do
  99.     i=Integer.parseInt(s, 10)
  100.     if i<0 then signal NumberFormatException
  101.     fronttab=i-1         -- OK, it's valid; -1 will mean bottom-edge
  102.   catch NumberFormatException
  103.     System.out.println('Bad show parameter:' s)
  104.     end
  105.  
  106.   /* Get the prototype image.  It has a transparent background. */
  107.   /* The prototype is of height yproto. */
  108.   proto=getgif('tablet.gif')       -- get the template
  109.   -- [will be null if not loaded, but we still get Text]
  110.  
  111.   /* Take quick path if just drawing bottom edge */
  112.   if fronttab<0 then do
  113.     hig=ybottom                    -- bottom edge is less high than full
  114.     wid=getSize.width              -- fixed image size
  115.     shadow=createImage(wid, hig)   -- working image
  116.     draw=shadow.getGraphics        -- for graphics
  117.     draw.setColor(Color.white)     -- background
  118.     draw.fillRect(0, 0, wid, hig)  -- ..
  119.     if proto\=null then drawunder  -- draw using pieces from prototype
  120.      else /* missing image */ do
  121.       draw.setColor(Color.gray)    -- make a default line
  122.       draw.fillRect(0, 2, wid, 4)  -- ..
  123.       end
  124.     return
  125.     end
  126.  
  127.   /* real tabs */
  128.   -- Count the tabs
  129.   tabs=0
  130.   loop n=1 by 1
  131.     s=getParameter('tab'n)
  132.     if s=null then leave
  133.     tabs=tabs+1
  134.     end
  135.   if tabs=0 then return  -- give up (will display 'no tabs')
  136.   if fronttab>=tabs then fronttab=tabs-1     -- clamp
  137.   -- Parse the tabs
  138.   tabword=String[tabs]
  139.   taburl =String[tabs]
  140.   loop n=1 to tabs
  141.     c=getParameter('tab'n).toCharArray
  142.     /* [This is where we really *need* NetRexx parse, or Rexx.word(x) :-)] */
  143.     -- First word is for tab [we allow multi-word with '_']
  144.     loop i=0 to c.length-1 while c[i] =' '; end   -- i to first non-blank
  145.     loop j=i to c.length-1 while c[j]\=' '        -- j to next blank
  146.       if c[j]=='_' then c[j]=' '                  -- [translate underscore]
  147.       end j
  148.     if j<=i then tabword[n-1]='?'
  149.             else tabword[n-1]=String(c, i, j-i)
  150.     -- Second word is URL
  151.     loop i=j to c.length-1 while c[i] =' '; end   -- i to next non-blank
  152.     loop j=i to c.length-1 while c[j]\=' '; end   -- j to next blank
  153.     if j<=i then taburl[n-1]='?'
  154.             else taburl[n-1]=String(c, i, j-i)
  155.     -- Remainder would be help [currently ignored]
  156.     -- say 'tab'n': "'tabword[n-1]'" "'taburl[n-1]'"'
  157.     end n
  158.  
  159.   /* Prepare the drawing area, and measure the tab text */
  160.   wid=getSize.width                     -- default image size
  161.   hig=yproto                            -- full height
  162.   shadow=createImage(wid, hig)          -- working image
  163.   draw=shadow.getGraphics               -- for graphics
  164.  
  165.   -- measure the height of the font, and choose size to match
  166.   draw.setFont(Font(tabfont, tabweight, 36)) -- measure at this size
  167.   h36=draw.getFontMetrics.getAscent     -- height at size '36'
  168.   tabpoints=int(36*(tabheight/h36)+0.5) -- size for required height (round)
  169.   draw.setFont(Font(tabfont, tabweight, tabpoints)) -- set actual font
  170.   fm=draw.getFontMetrics                -- find out about font
  171.   -- say 'Font' tabfont':' tabheight h36 tabpoints fm.getAscent
  172.  
  173.   -- measure the width of each text segment; save in wtab array
  174.   wtab=int[tabs]                        -- width of each tab body
  175.   otab=int[tabs]                        -- offset of each tab body
  176.   wtotal=0                              -- total of all bodies
  177.   loop t=0 to tabs-1
  178.     otab[t]=wtotal+wrise*(t+1)          -- offset of body start
  179.     wtab[t]=fm.stringWidth(tabword[t])  -- actual width
  180.     wtotal=wtotal+wtab[t]
  181.     -- say 'wtab['t']:' wtab[t]
  182.     end t
  183.   minwid=wtotal+wrise*tabs+wdrop+wterm  -- minimum width needed
  184.   if minwid>wid then do                 -- requested width is wider
  185.     wid=minwid
  186.     shadow=createImage(wid, hig)        -- need new, wider image
  187.     draw=shadow.getGraphics             -- for graphics
  188.     end
  189.  
  190.   xscale=wid/getSize.width              -- record scale factors
  191.   yscale=hig/getSize.height             -- ..
  192.   -- say 'wid scales:' wid xscale yscale
  193.  
  194.   draw.setColor(Color.white)            -- background
  195.   draw.fillRect(0, 0, wid, hig)         -- ..
  196.  
  197.   drawtabs                              -- lay out the tabs
  198.  
  199.  /* ---------------------------------------------------------- */
  200.  /* Update a tab's text [and front shoulder, if needed].       */
  201.  /* ---------------------------------------------------------- */
  202.  method updatetext(t=int)
  203.   drawtext(otab[t], tabword[t], t=mousetab, t\=fronttab)
  204.   --- draw shoulder in front if not front tab
  205.   if t=fronttab then return
  206.   x=otab[t]
  207.   wdowns=wtab[t]                        -- how many downs to fill
  208.   loop while wdowns>0
  209.     if wdowns>wdown then w=wdown
  210.                     else w=wdowns
  211.     drawslice(x, xdown, w)
  212.     wdowns=wdowns-w
  213.     x=x+w
  214.     end
  215.   return
  216.  
  217.  /* ---------------------------------------------------------- */
  218.  /* Draw an under-image (the bottom of an index card).         */
  219.  /* ---------------------------------------------------------- */
  220.  method drawunder
  221.   drawslice(0, xstarb, wstarb)               -- start
  222.   x=wstarb
  223.   wdowns=wid-(wstarb+wtermb)                 -- how many downs to fill
  224.   loop while wdowns>0
  225.     if wdowns>wdownb then w=wdownb
  226.                      else w=wdowns
  227.     drawslice(x, xdownb, w)
  228.     wdowns=wdowns-w
  229.     x=x+w
  230.     end
  231.   drawslice(x, xtermb, wtermb)               -- terminator
  232.   this.repaint                               -- ensure onscreen
  233.  
  234.  /* ---------------------------------------------------------- */
  235.  /* Update all the tab drawings.                               */
  236.  /* ---------------------------------------------------------- */
  237.  method drawtabs
  238.   /* Display all the tabs, from hindmost to front.  Only the front tab
  239.      will have shoulders, added after the tab body. */
  240.   loop t=tabs-1 to 0 by -1
  241.     n=t+fronttab              -- tab to display
  242.     if n>=tabs then n=n-tabs  -- ..
  243.     -- draw riser [note different if tab 0]
  244.     x=otab[n]-wrise
  245.     if n=0 then xr=xrise
  246.            else xr=xrism
  247.     drawslice(x, xr, wrise)
  248.     x=x+wrise
  249.     -- draw uppers
  250.     wups=wtab[n]
  251.     loop while wups>0
  252.       if wups>wup then w=wup
  253.                   else w=wups
  254.       drawslice(x, xup, w)
  255.       wups=wups-w
  256.       x=x+w
  257.       end
  258.     -- draw drop
  259.     drawslice(x, xdrop, wdrop)
  260.     -- draw the text for the tab
  261.     drawtext(otab[n], tabword[n], n=mousetab, n\=fronttab)
  262.     end t
  263.   /* shoulders of the front tab */
  264.   if fronttab>0 then do label leftshoulder
  265.     drawslice(0, xstar, wstar)               -- start
  266.     x=wstar
  267.     wdowns=otab[fronttab]-wrise-x            -- how many downs to fill
  268.     loop while wdowns>0
  269.       if wdowns>wdown then w=wdown
  270.                       else w=wdowns
  271.       drawslice(x, xdown, w)
  272.       wdowns=wdowns-w
  273.       x=x+w
  274.       end
  275.     end leftshoulder
  276.   /* always a right shoulder */
  277.   x=otab[fronttab]+wtab[fronttab]+wdrop      -- right edge of focus tab, +1
  278.   wdowns=wid-wterm-x                         -- how many downs to fill
  279.   loop while wdowns>0
  280.     if wdowns>wdown then w=wdown
  281.                     else w=wdowns
  282.     drawslice(x, xdown, w)
  283.     wdowns=wdowns-w
  284.     x=x+w
  285.     end
  286.   drawslice(x, xterm, wterm)                 -- terminator
  287.   this.repaint                               -- ensure onscreen
  288.  
  289.  /* ---------------------------------------------------------- */
  290.  /* Draw a vertical slice from the prototype into the shadow.  */
  291.  /* Note for 1.0.x we don't have setClip, so we must keep      */
  292.  /* creating Graphics contexts.                                */
  293.  /* ---------------------------------------------------------- */
  294.  method drawslice(x=int, xfrom=int, wfrom=int)
  295.   if proto=null then return                  -- nothing to draw
  296.   draw=shadow.getGraphics                    -- needed to reset clip
  297.   draw.clipRect(x, 0, wfrom, hig)            -- where to draw
  298.   draw.drawImage(proto, x-xfrom, 0, this)    -- trickily shift the source
  299.  
  300.  /* ---------------------------------------------------------- */
  301.  /* Draw the text for a tab.                                   */
  302.  /* ---------------------------------------------------------- */
  303.  method drawtext(x=int, s=String, high=boolean, enabled=boolean)
  304.   draw=shadow.getGraphics                    -- needed to reset clip
  305.   draw.setFont(Font(tabfont, tabweight, tabpoints))
  306.   select
  307.     when \enabled then draw.setColor(tabcollo)
  308.     when high     then draw.setColor(tabcolhi)
  309.     otherwise          draw.setColor(tabcol)
  310.     end
  311.   draw.drawString(s, x+tabbasex, tabbasey)
  312.  
  313.  /* ---------------------------------------------------------- */
  314.  /* The following methods respond to mouse movements and       */
  315.  /* actions.                                                   */
  316.  /* MouseEnter and MouseExit are not called on some platforms, */
  317.  /* but are allowed for in any case.                           */
  318.  /* ---------------------------------------------------------- */
  319.  /* mouseDown takes an action if over a tab */
  320.  method mouseDown(e=Event, x=int, y=int) returns boolean
  321.   t=hit(e, x, y)
  322.   if t<0 then return 0
  323.   if t=fronttab then return 0      -- this one's inactive
  324.   -- don't change the current drawing; browser may be lazy
  325.   do
  326.     this.getAppletContext.showDocument(URL(this.getDocumentBase, taburl[t]))
  327.   catch MalformedURLException
  328.     System.out.println('Bad URL:' taburl[t])
  329.   end
  330.   return 1
  331.  
  332.  /* Actions to take as mouse moves */
  333.  method mouseMove(e=Event, x=int, y=int) returns boolean
  334.   t=hit(e, x, y)
  335.   if mouselast\=t then do     -- change noted
  336.     mousetab=t
  337.     if mouselast>=0 then
  338.       updatetext(mouselast)   -- will revert old tab
  339.     if t>=0 then
  340.       updatetext(t)           -- will highlight new tab
  341.     mouselast=t
  342.     this.repaint
  343.     end
  344.   return 1
  345.  
  346.  /* Drag is treated just like Move (redraws button if leaves button) */
  347.  method mouseDrag(e=Event, x=int, y=int) returns boolean
  348.    return mouseMove(e, x, y)
  349.  /* Enter and exit call our mouseMove to ensure known state */
  350.  method mouseEnter(e=Event, x=int, y=int) returns boolean
  351.    return mouseMove(e, x, y)
  352.  method mouseExit(e=Event, x=int, y=int) returns boolean
  353.    return mouseMove(e, x, y)
  354.  
  355.  /* Returns number of tab/card we are over, or -1 if none */
  356.  method hit(e=Event, x=int, y=int) returns int
  357.    if e=null   then return -1                -- no event
  358.    x=(x*xscale)%1                            -- apply scaling in X
  359.    y=(y*yscale)%1                            -- .. and Y
  360.    if x<5      then return -1
  361.    if x>wid-5  then return -1
  362.    if y<5      then return -1                -- above tabs
  363.    if y>hig-5  then return -1                -- edge detector
  364.    -- next effect isn't very nice; disable
  365.    -- if y>31  then return fronttab          -- below shoulder must be front
  366.    o=0
  367.    loop t=0 to tabs-1
  368.      if t=fronttab then do
  369.        if x>=o then
  370.         if x<o+wrise+wtab[t]+wdrop then return t
  371.        end
  372.       else /* behind tab */ do
  373.        if x>=o+wrise/2 then
  374.         if x<o+wrise+wtab[t]+wdrop/2 then return t
  375.        end
  376.      o=o+wrise+wtab[t]
  377.      end t
  378.    return -1
  379.  
  380.  /* ---------------------------------------------------------- */
  381.  /* Utility routine to load a GIF and await its arrival.       */
  382.  /* The argument is a URI (relative to the document base).     */
  383.  /* ---------------------------------------------------------- */
  384.  method getgif(gif=String) returns Image
  385.   newimage=getImage(getDocumentBase(), gif)  -- get the image
  386.   tracker=MediaTracker(this)                 -- 'this' is the "observer"
  387.   tracker.addImage(newimage, 0)              -- track image arrival, ID=0
  388.   do
  389.     tracker.waitForID(0)                     -- wait for image 0 to complete
  390.   catch InterruptedException                 -- something stopped us
  391.     return null                              -- can do no more
  392.   end
  393.   -- Good images have useful dimensions, bad images have -1 or 0 in X or Y
  394.   if newimage.getWidth(this) <=0 then return null
  395.   if newimage.getHeight(this)<=0 then return null
  396.   return newimage                            -- looks good
  397.  
  398.  /* ---------------------------------------------------------- */
  399.  /* paint(g) draws the whole image to size, if available.      */
  400.  /* ---------------------------------------------------------- */
  401.  method update(g=Graphics)                   -- override Applet's update
  402.   paint(g)                                   -- method to avoid flicker
  403.  method paint(g=Graphics)
  404.   if shadow=null
  405.    then g.drawString('[no tabs]', 2, 12)     -- alternative
  406.    else g.drawImage(shadow, 0, 0, getSize.width, getSize.height, this)
  407.  
  408.