home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / books / progem / rsrctree.5 < prev    next >
Text File  |  1986-11-01  |  25KB  |  484 lines

  1.     Permission to reprint or excerpt is granted only if the following line
  2.     appears at the top of the article:
  3.  
  4.       ANTIC PUBLISHING INC., COPYRIGHT 1985.  REPRINTED BY PERMISSION.
  5.  
  6.  
  7.  
  8.     Professional GEM  by Tim Oren
  9.     Column #5 - Resource Tree Structures
  10.  
  11.  
  12.        This is the fifth issue of ST PROFESSIONAL GEM, concluding our trek
  13.     through  GEM  dialogs  and  resources  with  a  look  at  the internal
  14.     structure of object trees.  Also, I'll answer a number of questions of
  15.     general  interest  which  have  been  received  via  the  ANTIC ONLINE
  16.     FEEDBACK.   As  always,  there is a download file associated with this
  17.     column:  GEMCL5.C,  which you will find in DL3 of the new Atari 16-bit
  18.     SIG (type GO PCS-58 or GO ATARI16).
  19.  
  20.        Even if you have no immediate use for this issue's code, be sure to
  21.     take  the  download anyway; some of the routines will be used in later
  22.     articles.
  23.  
  24.        In  the  last  installment, we established that resources trees are
  25.     pointed  to  by  the tree index, and that they are composed of objects
  26.     which contain pointers onward to other structures.  However, we passed
  27.     over  the issue of linkage among the objects within a tree.  It is now
  28.     time to go back and cure this omission.
  29.  
  30.        The  technical  term  for the linkage scheme of an object tree is a
  31.     "right-threaded  binary  tree".  If you already know what this is, you
  32.     can  skim  over the next few paragraphs.  If you happen to have access
  33.     to  a  copy of the book "FUNDAMENTAL ALGORITHMS", which is part of the
  34.     series  THE  ART OF COMPUTER PROGRAMMING by Donald E. Knuth, you might
  35.     want  to  read  his  excellent discussion of binary trees beginning on
  36.     page 332.
  37.  
  38.        For  the  following  discussion, you should have a listing of the C
  39.     image  of  a resource tree in front of you.  For those who do not have
  40.     the  listing  from  the last column, I have included a fragment at the
  41.     beginning  of the download.  Before we begin, I should warn you of one
  42.     peculiarity of "computer trees": They grow upside-down!  That is, when
  43.     they  are  diagrammed  or described, their root is at the top, and the
  44.     "leaves" grow downward.  You will see this both in the listing, and in
  45.     the way the following discussion talks about moving through trees.
  46.  
  47.        Each  GEM  object  tree  begins  at its ROOT object, numbered zero,
  48.     which  is  the  object  pointed at by the tree index.  There are three
  49.     link fields at the beginning of each object.  They are called OB_NEXT,
  50.     OB_HEAD, and OB_TAIL, which is the order in which they appear.
  51.  
  52.        Each  of the links is shown as an index relative to the root of the
  53.     current tree.  This means that the link '0' would refer to the root of
  54.     the  tree, while '2' would indicate the object two lines below it. The
  55.     special  link -1 is called NIL, and means that there is no link in the
  56.     given direction.
  57.  
  58.        Each object, or node, in a tree may have "offspring" or nodes which
  59.     are  nested  below it.  If it does, then its OB_HEAD will point to its
  60.     first  (or  "leftmost")  "child",  while the OB_TAIL will point to the
  61.     last  ("rightmost")  of  its offspring.  The OB_NEXT pointer links the
  62.     children  together,  with  the  OB_NEXT  of  the first pointing to the
  63.     second,  and  so on, until the OB_NEXT of the last finally points back
  64.     to its parent, the object at which we started.
  65.  
  66.        Remember  that each of these children may in turn have offspring of
  67.     their  own, so that the original "parent" may have a large and complex
  68.     collection of "descendents".
  69.  
  70.        Let's  look  at the first tree in the download to see an example of
  71.     this  structure.   The  very  first object is the ROOT.  Note that its
  72.     OB_NEXT  is  NIL,  meaning that there are no more objects in the tree:
  73.     the ROOT is both the beginning and the end of the tree.  In this case,
  74.     the OB_HEAD is 1 and the OB_TAIL is 3, showing that there are at least
  75.     two different children.
  76.  
  77.        Following  OB_HEAD  down to the next line, we can trace through the
  78.     OB_NEXT links (2, 3, 0) as they lead through a total of three children
  79.     and  back  to  the  ROOT.  You will notice that the first two children
  80.     have  NIL  for  the OB_HEAD and OB_TAILs, indicating that they have no
  81.     further offspring.
  82.  
  83.        However,  node  three,  the  last  child of the ROOT, does have the
  84.     value 4 for both its OB_HEAD and OB_TAIL.  By this we can tell that it
  85.     has  one,  and only one, offspring.  Sure enough, when we look at node
  86.     four,  we  see  that its OB_NEXT leads immediately back to node three.
  87.     Additionally,  it  has  no  further  offspring because its OB_HEAD and
  88.     OB_TAIL are NIL.
  89.  
  90.        You  will  find  that  object  trees  are always written out by the
  91.     Resource  Construction  Set  in "pre-order".  (Again, see Knuth if you
  92.     have  a copy.)  This means that the ROOT is always written first, then
  93.     its  offspring  left to right.  This rule is applied recursively, that
  94.     is,  we  go  down to the next level and write out each of these nodes,
  95.     then THEIR children left to right, and so on.
  96.  
  97.        For  a  further  example, look at the next tree in rs_object in the
  98.     download.   You  will  see  that  the  ROOT has an OB_HEAD of 1 and an
  99.     OB_TAIL  of 6, but that it actually has only three offspring (nodes 1,
  100.     2  and  6).   We see that node 2 itself had children, and applying the
  101.     rule  given  above,  they  were written out before continuing with the
  102.     next child of the ROOT.
  103.  
  104.        Why  was  this  seemingly  complex  structure  chosen for GEM?  The
  105.     reason  has  do  with  the  tasks  of  drawing objects in their proper
  106.     locations on the screen, and determining which object was "hit" when a
  107.     mouse click is detected.
  108.  
  109.        To  find out how this works, we must look at four more fields found
  110.     in each object: OB_X, OB_Y, OB_WIDTH, and OB_HEIGHT.  These fields are
  111.     the last four on each line in the sample trees.
  112.  
  113.        Each  object  in  a  tree  "owns" a rectangle on the screen.  These
  114.     fields define that rectangle.  When a resource is stored "outside" the
  115.     program  the  fields  are  in  character units, so that an object with
  116.     OB_WIDTH of 10 and OB_HEIGHT of 2 (for instance) would define a screen
  117.     area 10 characters wide and 2 high.
  118.  
  119.        When  the  resource is read into memory with an rsrc_load call, GEM
  120.     multiplies  the appropriate character dimension in pixels into each of
  121.     these  fields.  In this way portability is achieved: the same resource
  122.     file  works  for  any  of  the  ST's  three  resolutions.  Knowing how
  123.     rsrc_load  works,  your  code  should  treat  these  fields  as  pixel
  124.     coordinates.
  125.  
  126.        (I  have  committed  one oversimplification above.  If an object is
  127.     not  created  on  a  character  boundary in the RCS, then the external
  128.     storage  method described will not work.  In this case, the lower byte
  129.     of  each  rectangle  field  is  used  to  store  the nearest character
  130.     position,  while the upper byte stores the pixel remainder to be added
  131.     after the character size is multiplied in.)
  132.  
  133.        Non-character-boundary  objects  may  only be created in the "FREE"
  134.     tree mode of the Resource Construction Set (also called "PANEL" in RCS
  135.     2.0).  You should use them only in programs which will run in a single
  136.     ST  screen  mode,  because  pixel coordinates are not portable between
  137.     resolutions.)
  138.  
  139.        The  first  real  secret of object rectangles is that each OB_X and
  140.     OB_Y  is  specified  RELATIVE  to the X and Y coordinate of its parent
  141.     object  within the tree.  This is the first property we have seen that
  142.     is actually "inherited" from level to level within the tree.
  143.  
  144.        The  second secret is more subtle: Every object's rectangle must be
  145.     entirely contained within the rectangle of its parent.  This principle
  146.     goes  by the names "bounding rectangles" or "visual hierarchy".  We'll
  147.     see  in  a  moment  how  useful  it  is  when  detecting  mouse/object
  148.     collisions.
  149.  
  150.  
  151.     HOW GEM DOES IT
  152.  
  153.        Knowing  these  secrets, and the linkage structure of object trees,
  154.     we  can  deduce  how  a  number  of the GEM operations must work.  For
  155.     instance,  consider objc_offset, which returns the actual screen X and
  156.     Y  of an object.  We can see now that simply loading the OB_X and OB_Y
  157.     fields  of  the  object  does  not  suffice: they only give the offset
  158.     relative  to the parent object.  So, objc_offset must BEGIN with these
  159.     values,  and then work its way back up to the ROOT of the tree, adding
  160.     in the offsets found at each level.
  161.  
  162.        This  can  be  done  by following the OB_NEXT links from the chosen
  163.     object.   Whenever  OB_NEXT  points  to an object whose OB_TAIL points
  164.     right  back  to the same location, then the new node is another level,
  165.     or  "parent"  in the tree, and objc_offset adds its OB_X and OB_Y into
  166.     the  running totals.  When OB_NEXT becomes NIL, then the ROOT has been
  167.     reached  and  the  totals  are  the  values  to  return.  (By the way,
  168.     remember  that  the  OB_X  and  OB_Y  of  the ROOT are undefined until
  169.     form_center has been called for the tree.  They are shown as zeroes in
  170.     the sample trees.)
  171.  
  172.        We  can also figure out objc_draw.  It works its way DOWN the tree,
  173.     drawing each object as it comes to it.  It, too, must keep a running X
  174.     and  Y  variable,  adding in object offsets as it descends tree levels
  175.     (using  OB_HEAD),  and  subtracting them again as it returns from each
  176.     level.   Since  the larger objects are nearer the ROOT, we can now see
  177.     why  they are drawn first, with smaller objects drawn later or "on top
  178.     of" them.
  179.  
  180.        (If  you  write  an  application  which needs to move portions of a
  181.     dialog or screen with respect to each other, you can take advantage of
  182.     inheritance  of  screen position in objc_draw.  Simply by changing the
  183.     OB_X and/or OB_Y of an object, you can move it and its entire sub-tree
  184.     to  a  new  location  in  the  dialog.   For  instance,  changing  the
  185.     coordinates of the parent box of a set of radio buttons will cause all
  186.     of the buttons to move along with it.)
  187.  
  188.        Objc_draw also gives us an example of the uses of visual hierarchy.
  189.     Recall  that a clipping rectangle is specified when calling objc_draw.
  190.     At each level of the tree we know that all objects below are contained
  191.     in  the  screen  rectangle  of  the  current  object.   If the current
  192.     rectangle  falls  completely outside the specified clipping rectangle,
  193.     we  know  immediately  that we need not draw the object, or any of its
  194.     descendents!  This  ability  to  ignore  an  entire  subtree is called
  195.     "trivial rejection".
  196.  
  197.        Now  it's  rather  easy  to figure out objc_find.  It starts out by
  198.     setting  its  "object  found"  variable  to  NIL.   It begins a "walk"
  199.     through  the  entire object tree, following OB_HEAD and OB_NEXT links,
  200.     and keeping a current X and Y, just like objc_draw.
  201.  
  202.        At  each  node  visited, it simply checks to see if the "mouse" X,Y
  203.     specified  in  the call are inside the current object's rectangle.  If
  204.     they  are,  that  object  becomes  the found object, and the tree walk
  205.     continues  with the object's offspring, and then siblings.  Notice how
  206.     this  checking  of  offspring  makes sure that a smaller object nested
  207.     within, i.e., below, a larger object is found correctly.
  208.  
  209.        If  the  mouse X,Y position is not within the object being checked,
  210.     then  by  visual  hierarchy  it cannot be within any of its offspring,
  211.     either.   Trivial  rejection  wins  again,  and the entire sub-tree is
  212.     skipped!  Objc_find moves on to the OB_NEXT of the rejected object.
  213.  
  214.  
  215.     THOUGHT EXPERIMENTS
  216.  
  217.        Thinking  about  the  objc_find  algorithm reveals some information
  218.     about  its  performance,  and a few tricks we may use in improving the
  219.     appearance of dialogs and other object trees.
  220.  
  221.        First consider the problem of a dialog which contains many objects.
  222.     If we lay them all out "side-by-side", then they will all be immediate
  223.     offspring  of  the  ROOT  object.   In  this  situation,  the  trivial
  224.     rejection  method  will  gain  nothing.   The  time objc_find takes to
  225.     complete will vary linearly with the total number of objects.  This is
  226.     called an "Order N" process.
  227.  
  228.        Suppose  that  instead  we  broke up the dialog into two areas with
  229.     invisible  boxes, then broke up each of these areas in a like fashion,
  230.     and  so  on until we got down to the size of the individual selectable
  231.     objects.  The number of bottom level objects in this scheme is a power
  232.     of  two  equal  to the depth of the tree. Trivial rejection is used to
  233.     its  fullest in this case.  It is called an "Order Log N" process, and
  234.     is much more efficient for large numbers of objects.
  235.  
  236.        In  practice,  the  speed  of  the ST will allow you to ignore this
  237.     distinction  for  most dialogs and other trees.  But if you get into a
  238.     situation  when  speed is critical in searching a large tree, remember
  239.     that nesting objects can improve performance dramatically.
  240.  
  241.        If  you  have  been  following closely, you may have also noticed a
  242.     hole  in  the  visual  hierarchy  rule.   It says that all of a node's
  243.     children must lie within its rectangle, but it does NOT guarantee that
  244.     the  children's  rectangles will be disjoint, that is, not overlap one
  245.     another.  This peculiarity is the basis of several useful tricks.
  246.  
  247.        First,  remember  that  objc_find  always  tries to scan the entire
  248.     tree.   That is, it doesn't quit when it finds the first object on the
  249.     given  coordinates.  As mentioned above, this normally guarantees that
  250.     nested  objects  will  be found.  Consider, however, what happens when
  251.     the  mouse coordinates are on a point where two or more objects AT THE
  252.     SAME  LEVEL  overlap:  they  will  replace  one  another as the "found
  253.     object" until objc_find returns with the one which is "last", that is,
  254.     rightmost in the tree.
  255.  
  256.        This  quirk can be used to advantage in a number of cases.  Suppose
  257.     that  you  have in a dialog an image and a string which you would like
  258.     to  be  selected  together  when  either is clicked.  Nesting within a
  259.     common  parent  achieves  nothing in this case.  Instead, knowing that
  260.     form_do must use objc_find, you could use our trick.
  261.  
  262.        You  have  to know that the Resource Construction Set normally adds
  263.     objects  in  a  tree left to right, in the order in which you inserted
  264.     them.   You proceed to build the dialog in the following order: insert
  265.     the  image first, the string next, then carefully add an invisible box
  266.     which  is  not  nested within either, and size it to cover them both.
  267.     Set  the SELECTABLE attribute for the box, and the dialog manager will
  268.     find it, and invert the whole area, when either the image or string is
  269.     clicked.
  270.  
  271.        By  the  way,  remember that the SORT option in the RCS will change
  272.     the  order  of  an  object's  offspring.  If you are going to try this
  273.     trick, don't use SORT!  It will undo all of your careful work.
  274.  
  275.  
  276.     A TREEWALKER OF OUR OWN
  277.  
  278.        Since  the  GEM  system  gets so much mileage out of walking object
  279.     trees,  it  seems  reasonable that the same method should be useful in
  280.     application  programs.   In  the download you will find map_tree(). As
  281.     many  LISP veterans might guess from the name, this code will traverse
  282.     all  or  part of an object tree, applying a function to each node.  It
  283.     also  allows  the  function  to  return  a true/false value specifying
  284.     whether the sub-tree below a particular node should be ignored.  Let's
  285.     examine  map_tree()  in  more  detail as a final review of object tree
  286.     structure.
  287.  
  288.        First,  look  at the parameters.  "tree" is the long address of the
  289.     object  tree  of  interest, as retrieved by rsrc_gaddr.  "this" is the
  290.     node  at  which to begin the traverse, and "last" is the node at which
  291.     to terminate.
  292.  
  293.        In most cases, the beginning node will be ROOT, and the final value
  294.     will be NIL.  This will result in the entire tree being traversed. You
  295.     may  use  other  values,  but  be sure that you CAN get to "last" from
  296.     "this" by following tree links!  Although map_tree() includes a safety
  297.     check  to  prevent  "running  off"  the  tree, you could get some very
  298.     strange results from incorrect parameters.
  299.  
  300.        The  declaration for the final parameter, "routine", makes use of C
  301.     construct  which  may be new to some.  It is a pointer to a subroutine
  302.     which returns a WORD as a result.
  303.  
  304.        Map_tree() begins by initializing a temporary variable, tmp1, which
  305.     is  used  to store the number of the last node visited.  Since no node
  306.     will follow itself, setting tmp1 to the starting node is safe.
  307.  
  308.        The  main  loop  of  the routine simply repeats visiting a new node
  309.     until  the  last value is reached, or the safety check for end of tree
  310.     is satisfied.
  311.  
  312.        At  any node visited, we can be in one of two conditions. Either we
  313.     are at a node which is "new", that is, not previously visited, or else
  314.     we are returning to a parent node which has already been processed. We
  315.     can  detect  the  latter  condition by comparing the last node visited
  316.     (tmp1)  with  the OB_TAIL pointer of the current node.  If the node is
  317.     "old",  it  is  not processed a second time, we simply update tmp1 and
  318.     continue.
  319.  
  320.        If  the  node  is new, we call "routine" to process it, sending the
  321.     tree address and object number as parameters.  If a FALSE is returned,
  322.     we will ignore any subtree below this node.  On a TRUE return, we load
  323.     up  the  OB_HEAD  pointer  and follow it if a subtree exists.  (If you
  324.     don't  care about rejecting subtrees, simply remove the if condition.)
  325.     Finally, if the new node had no subtree, or was rejected by "routine",
  326.     we follow along its OB_NEXT link to the next node.
  327.  
  328.        A  simple  application  of  our  new  tool shows its power.  From a
  329.     previous  column you may recall the tedium of deselecting every button
  330.     inside  a  dialog  after  it was completed.  Using map_tree(), you can
  331.     deselect  EVERY  OBJECT in the tree by using map_tree(tree, ROOT, NIL,
  332.     desel_obj);  You  must  use a slightly modified version of desel_obj()
  333.     (included  in  the  download)  which  always returns TRUE.  Be sure to
  334.     define  or declare desel_obj() in your code BEFORE making the map_tree
  335.     call!
  336.  
  337.        In  future columns, I will return to map_tree() and show how it can
  338.     be  used  for  advanced  techniques  such as animated dialogs.  In the
  339.     meantime, experiment and enjoy!
  340.  
  341.  
  342.  
  343.  
  344. >>>>>>>>>>>>>>>>>>>>>>>>>>> Sample object trees <<<<<<<<<<<<<<<<<<<<<<<<
  345.  
  346. OBJECT rs_object[] = {
  347. -1, 1, 3, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 18,12,     /* Tree # 1 */
  348. 2, -1, -1, G_STRING, NONE, NORMAL, 0x0L, 3,1, 12,1,
  349. 3, -1, -1, G_BUTTON, 0x7, NORMAL, 0x1L, 5,9, 8,1,
  350. 0, 4, 4, G_BOX, NONE, NORMAL, 0xFF1172L, 3,3, 12,5,
  351. 3, -1, -1, G_IMAGE, LASTOB, NORMAL, 0x0L, 3,1, 6,3,
  352. -1, 1, 6, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 23,12,     /* Tree # 2 */
  353. 2, -1, -1, G_TEXT, NONE, NORMAL, 0x0L, 0,1, 23,1,
  354. 6, 3, 5, G_IBOX, NONE, NORMAL, 0x1100L, 6,3, 11,5,
  355. 4, -1, -1, G_BUTTON, 0x11, NORMAL, 0x5L, 0,0, 11,1,
  356. 5, -1, -1, G_BUTTON, 0x11, NORMAL, 0x6L, 0,2, 11,1,
  357. 2, -1, -1, G_BOXCHAR, 0x11, NORMAL, 0x43FF1400L, 0,4, 11,1,
  358. 0, -1, -1, G_BOXTEXT, 0x27, NORMAL, 0x1L, 5,9, 13,1,
  359. -1, 1, 3, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 32,11,     /* Tree # 3 */
  360. 2, -1, -1, G_ICON, NONE, NORMAL, 0x0L, 4,1, 6,4,
  361. 3, -1, -1, G_FTEXT, EDITABLE, NORMAL, 0x2L, 12,2, 14,1,
  362. 0, 4, 4, G_FBOXTEXT, 0xE, NORMAL, 0x3L, 3,5, 25,4,
  363. 3, -1, -1, G_ICON, LASTOB, NORMAL, 0x1L, 1,0, 6,4};
  364.  
  365.  
  366. >>>>>>>>>>>>>>>>>>>>>>>>>> Object tree walk utility <<<<<<<<<<<<<<<<<<<<<<
  367.  
  368.         VOID
  369. map_tree(tree, this, last, routine)
  370.         LONG            tree;
  371.         WORD            this, last;
  372.         WORD            (*routine)();
  373.         {
  374.         WORD            tmp1;
  375.  
  376.         tmp1 = this;            /* Initialize to impossible value: */
  377.                                 /* TAIL won't point to self!       */
  378.                                 /* Look until final node, or off   */
  379.                                 /* the end of tree                 */
  380.         while (this != last && this != NIL)
  381.                                 /* Did we 'pop' into this node     */
  382.                                 /* for the second time?            */
  383.                 if (LWGET(OB_TAIL(this)) != tmp1)
  384.                         {
  385.                         tmp1 = this;    /* This is a new node       */
  386.                         this = NIL;
  387.                                         /* Apply operation, testing  */
  388.                                         /* for rejection of sub-tree */
  389.                         if ((*routine)(tree, tmp1))
  390.                                 this = LWGET(OB_HEAD(tmp1));
  391.                                         /* Subtree path not taken,   */
  392.                                         /* so traverse right         */
  393.                         if (this == NIL)
  394.                                 this = LWGET(OB_NEXT(tmp1));
  395.                         }
  396.                 else                    /* Revisiting parent:        */
  397.                                         /* No operation, move right  */
  398.                         {
  399.                         tmp1 = this;
  400.                         this = LWGET(OB_NEXT(tmp1));
  401.                         }
  402.         }
  403.  
  404.  
  405. >>>>>>>>>>>>>>>>>> Sample routine to use with map_tree() <<<<<<<<<<<<<<<
  406.  
  407.      VOID
  408. undo_obj(tree, which, bit)     /* clear specified bit in object state     */
  409.      LONG     tree;
  410.      WORD     which, bit;
  411.      {
  412.      WORD     state;
  413.  
  414.      state = LWGET(OB_STATE(which));
  415.      LWSET(OB_STATE(which), state & ~bit);
  416.      }
  417.  
  418.      VOID
  419. desel_obj(tree, which)          /* turn off selected bit of spcfd object*/
  420.      LONG     tree;
  421.      WORD     which;
  422.      {
  423.      undo_obj(tree, which, SELECTED);
  424.      return (TRUE);
  425.      }
  426.  
  427.  
  428. >>>>>>>>>>>>>>>>>>>>>>>>>> Sample .ICN Files <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  429.  
  430. >>>>>>>>>> Save everything between >>><<< lines as CLOCK.ICN <<<<<<<<<<<<<<
  431. /* GEM Icon Definition: */
  432. #define ICON_W 0x0030
  433. #define ICON_H 0x0018
  434. #define DATASIZE 0x0048
  435. UWORD clock[DATASIZE] =
  436. { 0x0000, 0x0000, 0x0000, 0x0000,
  437.   0x3FFC, 0x0000, 0x000F, 0xC003,
  438.   0xF000, 0x0078, 0x0180, 0x1E00,
  439.   0x0180, 0x0180, 0x0180, 0x0603,
  440.   0x0180, 0xC060, 0x1C00, 0x0006,
  441.   0x0038, 0x3000, 0x018C, 0x000C,
  442.   0x60C0, 0x0198, 0x0306, 0x6000,
  443.   0x01B0, 0x0006, 0x4000, 0x01E0,
  444.   0x0002, 0xC000, 0x01C0, 0x0003,
  445.   0xCFC0, 0x0180, 0x03F3, 0xC000,
  446.   0x0000, 0x0003, 0x4000, 0x0000,
  447.   0x0002, 0x6000, 0x0000, 0x0006,
  448.   0x60C0, 0x0000, 0x0306, 0x3000,
  449.   0x0000, 0x000C, 0x1C00, 0x0000,
  450.   0x0038, 0x0603, 0x0180, 0xC060,
  451.   0x0180, 0x0180, 0x0180, 0x0078,
  452.   0x0180, 0x1E00, 0x000F, 0xC003,
  453.   0xF000, 0x0000, 0x3FFC, 0x0000
  454. };
  455. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> End of CLOCK.ICN <<<<<<<<<<<<<<<<<<<<<<<<<<
  456.  
  457.  
  458. >>>>>>>>> Save everything between >>>><<<<< lines as CLOCKM.ICN <<<<<<<<<<
  459. /* GEM Icon Definition: */
  460. #define ICON_W 0x0030
  461. #define ICON_H 0x0018
  462. #define DATASIZE 0x0048
  463. UWORD clockm[DATASIZE] =
  464. { 0x0000, 0x0000, 0x0000, 0x0000,
  465.   0x7FFE, 0x0000, 0x001F, 0xFFFF,
  466.   0xFC00, 0x00FF, 0xFFFF, 0xFF00,
  467.   0x03FF, 0xFFFF, 0xFFC0, 0x0FFF,
  468.   0xFFFF, 0xFFF0, 0x3FFF, 0xFFFF,
  469.   0xFFFC, 0x7FFF, 0xFFFF, 0xFFFE,
  470.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  471.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  472.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  473.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  474.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  475.   0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
  476.   0xFFFF, 0xFFFF, 0xFFFF, 0x7FFF,
  477.   0xFFFF, 0xFFFE, 0x3FFF, 0xFFFF,
  478.   0xFFFC, 0x0FFF, 0xFFFF, 0xFFF0,
  479.   0x03FF, 0xFFFF, 0xFFC0, 0x00FF,
  480.   0xFFFF, 0xFF00, 0x001F, 0xFFFF,
  481.   0xF800, 0x0000, 0x7FFE, 0x0000
  482. };
  483. >>>>>>>>>>>>>>>>>>>>>>>>> End of CLOCKM.ICN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  484.