[comp.sys.mac.programmer] A Custom WDEF

ephraim@think.COM (ephraim vishniac) (02/22/88)

Since mentioning in a comp.sys.mac article that I had seen a circular
window on the Mac, several people have asked for source code or a copy
of the program.  Following is the help text from the program called
"Windows Demo."  The program features a variety of unusual window
types, and the help text explains how they are produced.

I will send the actual program (and this text) to info-mac, so that
internet users can retrieve it from sumex.  Even though it was
compiled long ago with Megamax C, the program now runs on a Mac II - I
just patched all those nasty 02B6's to 0A78's and there's no problem
any more.

----------------------  Start of Help Text  -------------------------

Program by Richard Koch
           Department of Mathematics
           University of Oregon
           Eugene, Oregon 97403


   The Macintosh toolbox contains routines which move windows, resize
them, and so forth. These routines are quite complicated; when a
window is moved, the system must erase the old version of the window,
draw the window in its new position, draw newly exposed portions of
other windows, and recalculate the visible portions of all windows. It
is natural to suppose that the toolbox routines which do these things
assume that windows are rectangular, but in fact the routines have
been written to handle windows of any shape whatever.

   Most Macintosh programmers use one of the six predefined windows at
the left; these windows are basically rectangular (the final window
has curved corners). But it is easy to define other window shapes.
Windows can have holes in the middle, curved borders, fancy frames,
and other unusual features.

   Some Macintosh users believe that the computer keeps a complete
copy of each window in memory even when the window is not visible on
the screen. But that is not correct. The Macintosh knows how to draw
window frames and redraws portions of these frames when they become
visible. But the contents of windows are the responsibility of
programs; the Macintosh issues a warning when a previously hidden
portion of a window becomes visible, and the program does whatever it
wishes. The program you are running is lazy and erases windows
entirely when new portions become visible; by turning this feature
off, you can see that the toolbox erases each newly visible portion of
a window before reporting that it should be redrawn.

   Since round windows require extensive calculations, they slow the
system down enough to let us see the window moving algorithms in
operation. If a window is moved from one portion of the screen to
another, the toolbox directly moves screen memory and no redrawing is
necessary. It is easier for the toolbox to make this pixel move when
the window moves up than when the window moves down. If a window is
moved part way off the screen and later moved back on, the toolbox
copies those portions still on the screen by a direct memory move, but
remaining portions of the window frame and contents must be redrawn.

   To produce an unusual window, it is only necessary to write one
function of the form

   long MyWindow(varCode, theWindow, message, param)
   int varCode, message; WindowPtr theWindow; long param;

This function is called by the toolbox when the toolbox needs to know
the shape of the entire window or its content region, when it needs to
know where the mouse went down in the window, or when it needs to draw
the window frame. All other Macintosh window operations are handled
automatically without help from the programmer. The parameter
"message" explains what operation to perform, and the parameter
"param" contains additional information for some requests. It is
possible to create several related windows with only minor variations
in appearance; the parameter varCode explains which variant to use.
This parameter must be between 0 and 15.

   The code for "MyWindow" can be put in the main program code area,
or it can be placed in a resource of type WDEF. In the program you are
running, code for the rectangular and square windows is included with
other program code, but code for the round windows is in a WDEF
resource with ID #128.

   The Macintosh system contains two WDEF resources with ID's 0 and 1 which define all of the standard windows on the left. The first five windows are defined in WDEF #0 using varCodes 0 through 5. The final window is defined in WDEF #1 using varCode 0.

   When the Macintosh is running a program and needs a resource, it
first searches the program file for the resource and only searches the
system file if the resource cannot be found in the program file.
Consequently, it is possible to trick programs so they will produce
round windows rather than rectangular windows. Using ReEdit, cut the
entire WDEF resource from the program you are running and paste it
into MacWrite. Then change its resource ID # from 128 to 0. When you
run the resulting MacWrite, it will use round windows for all
operations. (Warning: Text written into MacWrite will disappear under
the frame of the round window, and the "OK" box for some dialogs will
be hidden beyond the window frame. Often a dialog will accept "RETURN"
as the equivalent of clicking on "OK," but otherwise you will be stuck
and have to reset the computer.)

   The round window WDEF can be pasted into the FINDER itself,
yielding a round directory.

   It is easy to use the round window from this program in your own
programs. Simply copy WDEF #128 into your resources and change its ID
number as appropriate. Your resource code should also contain a window
definition somewhat like

   TYPE WIND
    ,128                ;  window resource ID
   First Round Window   ;  window title
   40 80 120 300        ;  top left bottom right
   Visible NoGoAway     ;  window status
   2048                 ;  WindProcID (see below)
   0                    ;  RefCon

   The actual program code will create this window in the usual way
using a line like

   wp = GetNewWindow(128, wStorage, -1L);

   Of course this last line will be different if you are using another
language, but everything else will be the same.

   The only strange feature here is the rule for assigning the
WindProcID number. This number should equal sixteen times the WDEF
resource number of the appropriate window definition code plus the
varCode of the window variant. For example, the first five windows
pictured at the left are produced by WDEF #0 in the system file with
varCodes 0 through 5, so their WindProcID's are 0 through 5. The sixth
window to the left is produced by WDEF #1 in the system file using
varCode 0, so its WindProcID is 16.

   The WDEF for round windows allows sixteen possible variants. The
rightmost bit in the variant number is 1 if the window has a title and
0 otherwise. The next bit left is 1 if the window has no grow box and
0 otherwise. The next bit is 1 if the window has a fancy woven frame
and 0 otherwise. The final left bit is 1 if the title appears on the
top and 0 if it appears on the bottom. So the variant number for a
window with a title, grow box, fancy frame, and title at the top is 1
+ 2*0 + 4*1 + 8*1 = 13.

   If you want to change the appearance of the round windows from this
program, you will have to rewrite the code for "MyWindow." Below is
this code, written using Megamax C.

#include <qd.h>
#include <qdvars.h>
#include <win.h>
#include <menu.h>
#include <event.h>
#include <dialog.h>

pascal long MyWindow(varCode,theWindow,message,param)
int varCode, message; WindowPeek theWindow; long param;

{ typedef Rect *RPtr;
  Rect r, rclose[4], rgrow, rtitle;
  int i,j,k;
  Point p;
  GrafPtr temp;
  PenState pnState;
  RgnHandle hstructure, hcontent, frame, htitle;
  Pattern pat;
  char title[256];
  GetPort(&temp); SetPort((GrafPtr)theWindow);
    r = (*((GrafPtr)theWindow)).portRect;
    i = r.a.right - r.a.left; j = r.a.bottom - r.a.top;
    if (i > j) i = j;
    r.a.left = 0; r.a.top = 0;  

    LocalToGlobal(&(r.a));
    SetPort(temp);
    r.a.bottom = r.a.top + i; r.a.right = r.a.left + i;
    SetRect(&rclose[0],r.a.left + 3,r.a.top + i/2 - 6,
                       r.a.left + 14,r.a.top + i/2 + 5);
    SetRect(&rclose[1],r.a.right - 14,r.a.top + i/2 -6,
                       r.a.right -3,r.a.top + i/2 + 5);
    SetRect(&rclose[2],r.a.left + i/2 - 6,r.a.top + 3,
                       r.a.left + i/2 + 5,r.a.top + 14);
    SetRect(&rclose[3],r.a.left + i/2 - 6,r.a.bottom -14,
                       r.a.left + i/2 + 5,r.a.bottom -3);
    rgrow.a.right = r.a.left + i/2 + ((i/2)*10)/14;
    rgrow.a.bottom =  r.a.top  + i/2 + ((i/2)*10)/14;
    rgrow.a.left = rgrow.a.right - (9*141)/100;
    rgrow.a.top = rgrow.a.bottom - (9*141)/100;
    for (k = 0; k < 256; k++) title[k] =
     *(*((*theWindow).titleHandle) + k);ptocstr(&title);
    j = StringWidth(&title) / 2 + 15;
    rtitle.a.left = r.a.left + i/2 - j;
    rtitle.a.right = rtitle.a.left + j + j;
    if (varCode & 0x0008) {
                    rtitle.a.top = r.a.top;
                    rtitle.a.bottom = r.a.top + 16;}

            else   {rtitle.a.bottom = r.a.bottom;
                    rtitle.a.top = r.a.bottom - 16;}
  p.a.v = HiWord(param); p.a.h = LoWord(param);
  hstructure = (*theWindow).strucRgn;
  hcontent = (*theWindow).contRgn;

  switch(message)
   {case 0: if ((*theWindow).visible) {
            if (param == 0L)  {
              GetPenState(&pnState);
              PenSize(18,18);PenMode(patBic);
              FrameOval(&r);
              SetPenState(&pnState);
                   if (((*theWindow).hilited) &&
               (varCode & 0x0004)) {
                 pat[0] = 0xF8; pat[1] = 0x74;
                 pat[2] = 0x22; pat[3] = 0x47;
                 pat[4] = 0x8F; pat[5] = 0x17;
                 pat[6] = 0x22; pat[7] = 0x71;
                 FrameOval(&r);InsetRect(&r,1,1);
                 FrameOval(&r);InsetRect(&r,1,1);
                 frame = NewRgn();OpenRgn();
                 FrameOval(&r);InsetRect(&r,13,13);

                 FrameOval(&r);CloseRgn(frame);
                 FillRgn(frame,&pat);DisposeRgn(frame);
                 FrameOval(&r);InsetRect(&r,1,1);
                 FrameOval(&r);
                 }
              else {
                 FrameOval(&r);InsetRect(&r,3,3);
                 if ((*theWindow).hilited)
                   for (i = 0; i < 6; i++) {
                    FrameOval(&r);InsetRect(&r,2,2);}
                 else InsetRect(&r,12,12);
                 InsetRect(&r,1,1);FrameOval(&r);
                 }
              if (((*theWindow).hilited) &&
                 ((*theWindow).goAwayFlag)) {
                 for (i = 0; i < 4; i++) {
                   EraseRect(&rclose[i]);
                   FrameRect(&rclose[i]);}
              }
              if (((*theWindow).hilited) &&
                 ((varCode & 0x0002) == 0)){
                  EraseRect(&rgrow); FrameRect(&rgrow);}
              if (varCode & 0x0001) {
                  EraseRect(&rtitle);FrameRect(&rtitle);

                  if ((*theWindow).hilited) {
                     InsetRect(&rtitle,1,1);
                     FrameRect(&rtitle);
                     InsetRect(&rtitle,-1,-1);
                     MoveTo(rtitle.a.left + 15,
                            rtitle.a.top + 13);
                     DrawString(&title);
                     }
              }
            }
            else if (param == 4L) {
               GetPenState(&pnState);
               PenMode(patXor);
               for (i = 0; i < 4; i++)
                 if ((i == 2) && (varCode & 1) &&
                    (varCode & 8));
                 else if ((i == 3) && (varCode & 1) &&
                         !(varCode & 8));
                 else {
                   MoveTo(rclose[i].a.left+2,
                          rclose[i].a.top+2);
                   Line(6,6);
                   MoveTo(rclose[i].a.left+2,
                          rclose[i].a.bottom-3);

                   Line(6,-6);
                   MoveTo(rclose[i].a.left+1,
                          rclose[i].a.top + 5);
                   Line(8,0);
                   MoveTo(rclose[i].a.left+5,
                          rclose[i].a.top+1);
                   Line(0,8);
                   InsetRect(&rclose[i],4,4);
                   EraseRect(&rclose[i]);
                   }
                 SetPenState(&pnState);
                 }
            }
            return(0L); break;
    case 1: if (PtInRgn(&p,hcontent)) return(1L);
            else if ((PtInRect(&p,&rclose[0])) &&
                    ((*theWindow).goAwayFlag)  &&
                    ((*theWindow).hilited))
                 return(4L);
            else if ((PtInRect(&p,&rclose[1])) &&
                    ((*theWindow).goAwayFlag)  &&
                    ((*theWindow).hilited))
                 return(4L);

            else if ((PtInRect(&p,&rclose[2])) &&
                    ((*theWindow).goAwayFlag)  &&
                    ((*theWindow).hilited)     &&
                    (!((varCode & 1)           &&
                    (varCode & 8))))
                 return(4L);
            else if ((PtInRect(&p,&rclose[3])) &&
                    ((*theWindow).goAwayFlag)  &&
                    (!((varCode & 1)           &&
                    !(varCode & 8)))           &&
                    ((*theWindow).hilited))
                 return(4L);
            else if ((PtInRect(&p,&rgrow))     &&
                    ((varCode & 0x0002) == 0)  &&
                    ((*theWindow).hilited))
                 return(3L);
            else if (PtInRgn(&p, hstructure))
                 return(2L);
            else return(0L); break;   

    case 2: SetEmptyRgn(hstructure);
              OpenRgn();
              FrameOval(&r);
              CloseRgn(hstructure);
              if (varCode & 0x0001) {
                    htitle = NewRgn();
                    RectRgn(htitle, &rtitle);
                    UnionRgn(hstructure, htitle,
                             hstructure);
                    DisposeRgn(htitle);
                    }
            InsetRect(&r,18,18);
            SetEmptyRgn(hcontent);
              OpenRgn();
              FrameOval(&r);
              CloseRgn(hcontent);
            break;
    case 3: return(0L); break;
    case 4: return(0L); break; 

    case 5: r = *(RPtr)param;
            i = r.a.right - r.a.left;
            j = r.a.bottom - r.a.top;
            if (i > j) i = j;
            r.a.right = r.a.left + i;
            r.a.bottom = r.a.top + i;
            FrameOval(&r); return(0L); break;
    case 6: return(0L); break;
    }
}

----------------------  End of Help Text  ---------------------------------

Ephraim Vishniac					  ephraim@think.com
Thinking Machines Corporation / 245 First Street / Cambridge, MA 02142-1214

lee@hhb.UUCP (lee daniels) (02/24/88)

in article <16995@think.UUCP>, ephraim@think.COM (ephraim vishniac) says:
> 
> Since mentioning in a comp.sys.mac article that I had seen a circular
> window on the Mac, several people have asked for source code or a copy
> of the program.
> 
> I will send the actual program (and this text) to info-mac, so that
> internet users can retrieve it from sumex.

I keep seeing great things posted to internet sites.  Is there any access
to these internet sites via anonymous uucp so that the rest of us can get
the good stuff.  If this is not possible, can someone tell me how one gets
on the internet.

Thanks in advance.

Lee Daniels

HHB Systems
1000 Wyckoff Ave
Mahwah, N.J. 07430

net address:  uucp!philabs!lee@hhb  or  uucp!princeton!lee@hhb

>