// WhoRYou.cmm - CEnvi demonstration for designing a dialog window.
//               This demo prompts the user for information about
//               themselves.  You can see from this sample code
//               that dedigning your own windows and windows behavior
//               is very flexible, but also complicated.

#include <Window.lib>
#include <WinUtil.lib>
#include <Message.lib>

main()
{
   do {
      if ( GetUserID(FirstName,LastName,Sex) )
         TryAgain = ShowOKmessage(FirstName,LastName,Sex);
      else
         TryAgain = ShowCancelMessage();
   } while( TryAgain );
}


GetUserID(FirstName,LastName,Sex) // dialog box for user info
{
   // Initialize some size parameters
   AveCharWidth, AveCharHeight;
   GetCharacterSizes(AveCharWidth,AveCharHeight);
   AveRowGap = AveCharHeight / 2;
   AveColGap = AveCharWidth

   // Initially make the main window (which will be resized later).
   // For now draw it off the screen so no one sees
   MainWindow = MakeWindow(NULL,NULL,"GetUserIDFunc","Scientific Survey",
                           WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE,
                           -10,-10,9,9,NULL,uid);

   // Make a couple of lines of descriptive text
   Description = "This scientifically-designed survery will determine if "
                 "CEnvi is right for you.  Use a number 2 pencil.  "
                 "Good luck! (no cheating)";
   #define  MAX_TEXTLEN 40
   DescRowCount = (strlen(Description) + 15/*wrap room*/) / MAX_TEXTLEN ;
   MakeWindow(MainWindow,"static","GetUserIDChildFunc",Description,WS_CHILD | WS_VISIBLE,
              AveColGap,AveRowGap,MAX_TEXTLEN * AveCharWidth,DescRowCount * AveCharHeight,NULL,uid);
   BottomRow = AveRowGap + DescRowCount * AveCharHeight;

   // Request the user's first name, which is three fields
   #define EDIT_MARGIN  (AveCharHeight / 4)  // extra space around edit field
   BottomRow += AveRowGap;
   prompt = "First Name";
   EditLength = 22;  // default size of window for data input
   MakeWindow(MainWindow,"static",NULL,prompt,WS_CHILD | WS_VISIBLE,
              AveColGap,BottomRow + EDIT_MARGIN,
              width = AveCharWidth * (strlen(prompt)+1),AveCharHeight,NULL);
   MakeWindow(MainWindow,"static",NULL,NULL,WS_CHILD | WS_VISIBLE | SS_BLACKFRAME,
              col = AveColGap * 2 + width,BottomRow,
              (1+EditLength)*AveCharWidth,AveCharHeight + 2*EDIT_MARGIN,NULL);
   uid.FirstNameHwnd = MakeWindow(MainWindow,"edit","GetUserIDChildFunc",NULL,
         WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT,
         col + AveCharWidth / 2,BottomRow + EDIT_MARGIN,
         EditLength*AveCharWidth,AveCharHeight,NULL,uid);
   BottomRow += AveCharHeight + 2*EDIT_MARGIN;

   // Request the user's last name, which is much like the first
   BottomRow += AveRowGap;
   prompt = "Last Name";
   EditLength = 30;  // default size of window for data input
   MakeWindow(MainWindow,"static",NULL,prompt,WS_CHILD | WS_VISIBLE,
              AveColGap,BottomRow + EDIT_MARGIN,
              width = AveCharWidth * (strlen(prompt)+1),AveCharHeight,NULL);
   MakeWindow(MainWindow,"static",NULL,NULL,WS_CHILD | WS_VISIBLE | SS_BLACKFRAME,
              col = AveColGap * 2 + width,BottomRow,
              (1+EditLength)*AveCharWidth,AveCharHeight + 2*EDIT_MARGIN,NULL);
   uid.LastNameHwnd = MakeWindow(MainWindow,"edit","GetUserIDChildFunc",NULL,
         WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT,
         col + AveCharWidth / 2,BottomRow + EDIT_MARGIN,
         EditLength*AveCharWidth,AveCharHeight,NULL,uid);
   BottomRow += AveCharHeight + 2*EDIT_MARGIN;

   // Add radio buttons to select sex
   BottomRow += AveRowGap;
   #define PUSHBUTT_HEIGHT AveCharHeight
   width = AveCharWidth * (4 + strlen("Female"));
   uid.FemaleHwnd = MakeWindow(MainWindow,"button","GetUserIDChildFunc","Female",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
              AveColGap * 3,BottomRow,width,PUSHBUTT_HEIGHT,NULL,uid);
   uid.MaleHwnd = MakeWindow(MainWindow,"button","GetUserIDChildFunc","Male",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
              AveColGap * 4 + width,BottomRow,AveCharWidth * (4 + strlen("Male")),PUSHBUTT_HEIGHT,NULL,uid);
   BottomRow += PUSHBUTT_HEIGHT;

   // Finally, add the OK and CANCEL buttons
   BottomRow += AveRowGap * 2;
   #define BUTTON_WIDTH  10 * AveCharWidth
   #define BUTTON_HEIGHT AveCharHeight * 3 / 2
   uid.OKhwnd = MakeWindow(MainWindow,"button","GetUserIDChildFunc","OK",
              WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_DISABLED,
              AveColGap * 3,BottomRow,BUTTON_WIDTH,BUTTON_HEIGHT,NULL,uid);
   uid.CancelHwnd = MakeWindow(MainWindow,"button","GetUserIDChildFunc","CANCEL",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              AveColGap * 6 + BUTTON_WIDTH,BottomRow,BUTTON_WIDTH,BUTTON_HEIGHT,NULL,uid);


   SizeAndCenterDisplay(MainWindow,AveColGap,AveRowGap);

   // Initialize with FirstName as the active field
   SetFocus(uid.FirstNameHwnd);
   uid.OKselected = False;

   while ( DoWindows()  &&  !uid.OKselected ) ;

   if ( uid.OKselected ) {
      GetEditText(uid.FirstNameHwnd,FirstName);
      FirstName[0] = toupper(FirstName[0]);
      GetEditText(uid.LastNameHwnd,LastName);
      LastName[0] = toupper(LastName[0]);
      if ( SendMessage(uid.FemaleHwnd,BM_GETCHECK,0,0) )    strcpy(Sex,"woman");
      else if ( SendMessage(uid.MaleHwnd,BM_GETCHECK,0,0) ) strcpy(Sex,"man");
      else                                                  strcpy(Sex,"person");
      BreakWindow(MainWindow);
      return(TRUE);
   }

   return(FALSE);
}


/**** Windows functions called from GetUserInfo ****/

GetUserIDFunc(hwnd,msg,parm1,parm2,uid)
{
   if ( msg == WM_COMMAND ) {
      childHwnd = parm2 & 0xFFFF;
      switch ( (parm2 >> 16) & 0xFFFF ) {
         case BN_CLICKED:
            switch( childHwnd ) {
               case uid.OKhwnd:
                  uid.OKselected = TRUE;
                  break;
               case uid.CancelHwnd:
                  BreakWindow(hwnd);
                  break;
               default:
                  ShouldOKbeEnabled(uid);
                  break;
            }
            break;
         case EN_CHANGE:
            ShouldOKbeEnabled(uid);
            break;
      }
   }
}

GetUserIDChildFunc(hwnd,msg,parm1,parm2,uid)
{
   #define VK_SHIFT  0x10

   if ( WM_CHAR == msg ) {
      switch ( parm1 ) {
         case '\t':
            // Tab to next field or back tab to previous field.  Will allow moving
            // to any field that is not "static" type and not disabled
            Backward = (0x80 & DynamicLink("USER","GETKEYSTATE",SWORD16,PASCAL,VK_SHIFT));
            Sibling = hwnd;
            BLObSize(_className,40);
            do {
               // if this is the end of the list then go to the other end
               if ( Sibling == GetWindow(Sibling,Backward ? GW_HWNDFIRST : GW_HWNDLAST ) )
                  Sibling = GetWindow(Sibling,Backward ? GW_HWNDLAST : GW_HWNDFIRST );
               else
                  Sibling = GetWindow(Sibling,Backward ? GW_HWNDPREV : GW_HWNDNEXT);
               _len = GetClassName(Sibling,_className,39);
            } while( (_len == 6  &&  !memicmp(_className,"static",6))
                  || !IsWindowEnabled(Sibling) );
            SetFocus(Sibling);
            return 0;
         case ' ':
            // don't let last name or first name accept spaces
            if ( hwnd == uid.LastNameHwnd  ||  hwnd == uid.FirstNameHwnd )
               return 0;
            break;
         case '\r';
            // Enter selects the OK button, and so post a ' ' to the
            // OK button window
            if ( IsWindowEnabled(uid.OKhwnd) ) {
               PostMessage(uid.OKhwnd,WM_KEYDOWN,' ',parm2);
               PostMessage(uid.OKhwnd,WM_KEYUP,' ',parm2);
            }
            return(0);
      }
   }
}


ShouldOKbeEnabled(uid)  // adjust the OK button to be enabled or NOT
{
   WantOKstate = ( 0 != GetWindowTextLength(uid.FirstNameHwnd)
                && 0 != GetWindowTextLength(uid.LastNameHwnd)
                && ( SendMessage(uid.FemaleHwnd,BM_GETCHECK,0,0)
                  || SendMessage(uid.MaleHwnd,BM_GETCHECK,0,0) ) );
   EnableWindow(uid.OKhwnd,WantOKstate);
}

/*** Message windows after user id form is complete ***/
#include <MsgBox.lib>

ShowOKmessage(FirstName,LastName,Sex)
{
   sprintf(message,"%s %s:\rScientific analysis of your survey indicates that you "
           "are a %s who knows a good shareware product when you see it.  "
           "Numerological analyses show that you like to have complete "
           "product manuals and you don't like registration-reminder "
           "screens."
           "\rRecommendation: You should register CEnvi right away."
           "\r\rDo you want to try the survey again?",
           FirstName,LastName,Sex);
   return( IDYES == MessageBox(message,"SURVEY RESULTS",MB_YESNO) );
}

ShowCancelMessage()
{
   return( IDYES == MessageBox(
               "You did not complete the survey.  You are obviously in a hurry "
               "to send in your CEnvi registration form."
               "\r\rDo you want to try the survey again?",
               "SURVEY CANCELED",MB_YESNO) );
}

/*** UTILITIES CALLED BY THE ABOVE CODE ***/

SizeAndCenterDisplay(hwnd,RightMargin,BottomMargin)
{
   // Make the total size of this window fit around its children, with
   // RightMargin and BottomMargin extra.  Then center this in the
   // screen and display it.

   // Find maximum row and col of all children
   ChildList = WindowList(hwnd);
   assert( NULL != ChildList );
   GetWindowRect(hwnd,ParentRect);
   MaxChildCol = MaxChildRow = 0;
   for ( i = GetArraySpan(ChildList); 0 <= i; i-- ) {
      GetWindowRect(ChildList[i],ChildRect);
      MaxChildCol = max(MaxChildCol,ChildRect.right - ParentRect.left);
      MaxChildRow = max(MaxChildRow,ChildRect.bottom - ParentRect.top);
   }

   // The window width and height must just be big enough for these
   // maximums plus the margins and the window border
   width = MaxChildCol + 1 + RightMargin + GetSystemMetrics(SM_CXBORDER);
   height = MaxChildRow + 1 + BottomMargin + GetSystemMetrics(SM_CXBORDER);

   // Figure where to center this window in the screen
   scrWidth = GetSystemMetrics(SM_CXSCREEN);
   scrHeight = GetSystemMetrics(SM_CYSCREEN);
   col = (ScrWidth - width) / 2;
   row = (ScrHeight - height) / 2;

   MoveWindow(hwnd,col,row,width,height,TRUE);
}

GetCharacterSizes(width,height)
{
   hdc = GetDC(ScreenHandle());
   SelectObject(hdc,GetStockObject(SYSTEM_FONT));
   GetTextMetrics(hdc,tm);
   width = tm.AveCharWidth;
   height = tm.Height + tm.ExternalLeading;
   ReleaseDC(ScreenHandle(),hdc);
}

GetWindowRect(hwnd,Rectangle)
{
   // set up blob to retrieve four integers
   BLObSize(_rect,4 * 2/*integer size*/);
   DynamicLink("USER","GETWINDOWRECT",SWORD16,PASCAL,hwnd,_rect);
   Rectangle.left = BLObGet(_rect,0,SWORD16);
   Rectangle.top = BLObGet(_rect,2,SWORD16);
   Rectangle.right = BLObGet(_rect,4,SWORD16);
   Rectangle.bottom = BLObGet(_rect,6,SWORD16);
}

MoveWindow(hwnd,col,row,width,height,redraw)
{
   DynamicLink("USER","MOVEWINDOW",SWORD16,PASCAL,
               hwnd,col,row,width,height,TRUE);
}

GetClassName(hwnd,ClassName,MaxCount)
{
   return DynamicLink("USER","GETCLASSNAME",SWORD16,PASCAL,
                      hwnd,ClassName,MaxCount);
}

SetFocus(hwnd)
{
   return DynamicLink("USER","SETFOCUS",SWORD16,PASCAL,hwnd);
}

GetWindowTextLength(hwnd)
{
   return DynamicLink("USER","GETWINDOWTEXTLENGTH",SWORD16,PASCAL,hwnd);
}

GetEditText(hwnd,buf)   // make buf big enough
{
   _len = GetWindowTextLength(hwnd);
   BLObSize(buf,_len+1);
   if ( _len )
      DynamicLink("USER","GETWINDOWTEXT",SWORD16,PASCAL,hwnd,buf,_len+1);
   buf[_len] = '\0';
}

IsWindowEnabled(WinHandle)
{
   return DynamicLink("USER","ISWINDOWENABLED",SWORD16,PASCAL,WinHandle);
}

EnableWindow(WinHandle,EnableState)
{
   return DynamicLink("USER","ENABLEWINDOW",SWORD16,PASCAL,WinHandle,EnableState);
}