{***********************************************************************}
{									}
{ __BuildMnu  Create all menu structures to construct a menu		}
{									}
{ function  __BuildMnu(X1,Y1,X2,Y2 : byte;				}
{			     Title : string;				}
{			     MDefs : _MenuDefs; 			}
{			   TextPtr : pointer;				}
{			 TextItems : byte) : _MenuPtr;			}
{									}
{	      X1,Y1,	  Column and row of upper left and lower	}
{	      X2,Y2	  right corners of the menu window.		}
{	      Title	  The menu title which is to be displayed on	}
{			  the border, or null for none. 		}
{	      MDefs	  Record structure which defines many of the	}
{			  menu characteristics. 			}
{	      TextPtr	  Pointer to an array of strings which are the	}
{			  menu items text strings.			}
{	      TextItems   Number of menu text items to display. 	}
{									}
{ Description This function provides a simplified method of creating	}
{	      menus, replacing calls to __MakeWin, __MakeMnu, __ItemMnu }
{	      and __KeyMnu with a single call.				}
{									}
{	      The function returns a pointer to the created _Menu	}
{	      structure, or NIL if an error occurs.  _MenuError is	}
{	      also be set on error conditions.				}
{									}
{	      The variable TextPtr must be the address of an array of	}
{	      _MenuStrings whose elements become the menu items.  Items }
{	      are truncated to fit within the window.  If TextItems	}
{	      specifies more items than will fit within the window, the }
{	      additional items are ignored.  In either case, _MenuError }
{	      is set to _InvalidItem as a warning.  To leave a blank	}
{	      line in a vertical menu, put a null string at the approp- }
{	      riate position in the array.				}
{									}
{	      The menu may						}
{									}
{		- have horizontal or vertical array of items		}
{		- have a border with specified attributes		}
{		- have a title in one of six positions			}
{		- have a highlighted character for each menu item	}
{		- be transmitted with Alt/highlight character		}
{									}
{	      These features are controlled by	MDefs.	You may 	}
{	      define your own variable of type _MenuDefs, or use	}
{	      the predefined variable _MenuDefaults provided in 	}
{	      the  Unit_Mnu interface.	One easy way to construct	}
{	      a menu is to modify fields in _MenuDefaults as		}
{	      necessary for a particular menu, then call		}
{	      __BuildMnu to create the menu.  This process may be	}
{	      repeated for each menu you create.  Menus created 	}
{	      with __BuildMnu may be further customized with		}
{	      calls to __KeyMnu and __ItemMnu, so a reasonable		}
{	      strategy is to define a menu with __BuildMnu, then	}
{	      modify a single item (for  example, make it		}
{	      protected temporarily) or add keystrokes. 		}
{									}
{	      MDefs defines the following menu characteristics: 	}
{									}
{		 _DBorType     Border type (see __BoxScn).		}
{		 _DBorFore,						}
{		 _DBorBack,						}
{		 _DWinFore,						}
{		 _DWinBack,						}
{		 _DBarFore,						}
{		 _DBarBack,						}
{		 _DProtFore,						}
{		 _DProtBack,						}
{		 _DHiFore,						}
{		 _DHiBack      The video attributes of the menu's	}
{			       border, items, highlight bar,		}
{			       protected items, and highlighted 	}
{			       character.				}
{		 _DTitlePos    Position of title (if  any).   The	}
{			       type  _WinTitlePos  is described in	}
{			       the WINDOW MANAGEMENT chapter.		}
{		 _DInitBarPos  Initial item highlighted.		}
{		 _DAltXmit     If TRUE, pressing Alt and an item's	}
{			       highlighted character  will  choose	}
{			       an item and transmit the menu.		}
{			       However, if a menu contains		}
{			       duplicate highlighted characters,	}
{			       only the last will be selected.		}
{		 _DHighlight   If TRUE, highlight the first		}
{			       non-blank character of each item.	}
{		 _DHorizOfs    If 0, a vertical style menu is		}
{			       constructed. Otherwise a horiz-		}
{			       ontal style menu is constucted		}
{			       with  _MHorizOfs blanks placed		}
{			       between the items.			}
{									}
{	      On error _MenuError may be set to:  _InvalidWindow	}
{						  _InvalidMenu		}
{						  _InvalidKey		}
{						  _InvalidItem		}
{									}
{ Version     4.00 (C)Copyright Blaise Computing Inc. 1987		}
{_______________________________________________________________________}

function  __BuildMnu(X1,Y1,X2,Y2 : byte;
			   Title : string;
			   MDefs : _MenuDefs;
			 TextPtr : pointer;
		       TextItems : byte) : _MenuPtr;


  {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  { HighLightPos    Return the position of the highlight character in a }
  { menu-item string. The function returns 0 if no character qualifies. }
  { To qualify, the character must be the first non-blank character and }
  { must be in the set of the upper and lower case letters along with	}
  { the characters '0' - '9'.						}
  {_____________________________________________________________________}

  function  HighLightPos(TextStr : _MenuString) : byte;

    const
      BLANK = #32;		      { Blank (ASCII 32) character     }
      TAB   = #9;		      { Horizontal Tab (ASCII 9)       }

    var
      I,Len :  byte;

    begin {HighLightPos}

      Len	   := Length(TextStr);
      I 	   := 1;
      HighLightPos := 0;

      { Find the position of the first character not a blank or tab.	}

      while ((I <= Len) and
			((TextStr[I] = BLANK) or (TextStr[I] = TAB))) do
	Inc(I);

      if (I <= Len) then
	 if ( ((TextStr[I] >= '0') and (TextStr[I] <= '9')) or
	      ((TextStr[I] >= 'a') and (TextStr[I] <= 'z')) or
	      ((TextStr[I] >= 'A') and (TextStr[I] <= 'Z')) ) then
	    HighLightPos := I

    end;  {HighLightPos}

  var
    WinPtr    : Unit_Win._WindowPtr;
    MPtr      : _MenuPtr;
    IPtr      : _ItemPtr;
    KPtr      : _KeyPtr;
    HorzMenu  : boolean;
    MaxCol    : byte;
    MaxRows   : byte;
    Row,Col   : byte;
    Items     : word;
    TempStr   : _MenuString;
    TempPtr   : pointer;
    HiChPos   : byte;
    ChIndex   : word;
    Check     : boolean;
    CTop,CBot : byte;

  begin {__BuildMnu}

    __BuildMnu := NIL;

    with MDefs do
      begin

	{ First build the specified window.  The cursor size is deter-	}
	{ mined by the current video device.				}

	if (_CurDevice = _MONO) then
	   begin
	     CTop := 12;
	     CBot := 13
	   end
	else if (_CurDevice = _COLOR) then
	   if (__IsEmScn or (_CurDevice = _ColorAdapter)) then
	      begin
		CTop := 6;
		CBot := 7
	      end
	   else
	      begin
		CTop := 11;
		CBot := 12
	      end;

	WinPtr := __MakeWin(X1,Y1,X2,Y2,CTop,CBot,
					_DWinFore,_DWinBack,
					_DBorType,_DBorFore,_DBorBack);
	if (Length(Title) > 0) then
	   Check := __TitleWin(WinPtr,Title,_DBorFore,_DBorBack,
							    _DTitlePos);

	{ Next construct the menu descriptor record.			}

	MPtr := __MakeMnu(WinPtr,_DBarFore,_DBarBack,0,0,
						  _DProtFore,_DProtBack);
	if (MPtr = NIL) then
	   Exit;		{ __MakeMnu sets the error condition.	}

	{ Next determine allowable item length and max number of items	}
	{ that will fit in the menu window.				}

	HorzMenu := (_DHorizOfs > 0);

	with WinPtr^._Dim do
	  begin
	    MaxCol   := _XLower - _XUpper + 1;
	    MaxRows  := _YLower - _YUpper + 1
	  end;

	{ For vertical menus, we compute the maximum number of items as }
	{ the lesser of the number that will fit in the window or the	}
	{ number that is specified in TextItems.  The horizontal case	}
	{ is handled as the string array is scanned in the loop below.	}

	if (not HorzMenu) then
	   begin
	     Row := 0;
	     if (MaxRows >= TextItems) then	{ Item which don't fit	}
		MaxRows := TextItems		{ are discarded.	}
	     else
		SetMenuError(_InvalidItem)
	   end
	else
	   Row := 1;

	{ Now we scan through the array of strings, making each string	}
	{ into a menu item and optionally assigning keystrokes and	}
	{ highlighted characters based on the first nonblank character	}
	{ in the string.						}

	TempPtr := TextPtr;
	Col	:= 1;
	Items	:= 0;

	repeat

	  TempStr := _MenuString(TempPtr^);
	  TempPtr := Ptr(_VectorAddr(TempPtr)._Seg,
			      _VectorAddr(TempPtr)._Ofs + _DMStrLen + 1);

	  if (Length(TempStr) > MaxCol) then
	     begin
	       TempStr := Copy(TempStr,1,MaxCol);	{ Truncate and	}
	       SetMenuError(_InvalidItem)		{ continue.	}
	     end;

	  if (not HorzMenu) then
	     Inc(Row);

	  { If the item is not null (to designate a space on a vertical }
	  { menu), create the item and key records.			}

	  if (Length(TempStr) > 0) then
	     begin

	       { Determine the character to highlight, if any.		}

	       if (_DHighLight) then
		  HiChPos := HighLightPos(TempStr)
	       else
		  HiChPos := 0;

	       if ((HorzMenu) and (Col + Length(TempStr) > MaxCol)) then
		  SetMenuError(_InvalidItem)
	       else
		  Iptr := __ItemMnu(MPtr,Col,Row,HiChPos,
					_DHiFore,_DHiBack,FALSE,TempStr);
	       if (Iptr = NIL) then
		  Exit;

	       { Now add keystroke for highlighted character, if any.	}
	       { Both shifted and unshifted keystrokes are added for	}
	       { the letters.						}

	       if ((_DHighLight) and (HiChPos <> 0)) then
		  begin
		    ChIndex := Ord(UpCase(TempStr[HiChPos]));
		    KPtr    := __KeyMnu(MPtr,Iptr,_Keys(ChIndex),
						 _KNOACTION_MNU,_NextCh);

		    { If the first character of the string is one of	}
		    { the letters, add lower case:			}

		    if (ChIndex > 64) then
		       begin
			 Inc(ChIndex,32);
			 KPtr := __KeyMnu(MPtr,Iptr,_Keys(ChIndex),
						  _KNOACTION_MNU,_NextCh)
		       end;

		    { If transmit on the Alt-key combination is re-	}
		    { quested, index into the Alt-key section of the	}
		    { _Keys type (add offset of 151 for letters, 190	}
		    { for numbers).  Note that Alt plus the character	}
		    { will select the LAST menu item with a particular	}
		    { character highlighted.				}

		    if (_DAltXmit) then
		       begin
			 if (ChIndex > 64) then
			    Inc(ChIndex,151)
			 else
			    Inc(ChIndex,190);
			 KPtr := __KeyMnu(MPtr,IPtr,_Keys(ChIndex),
						    _KTRANSMIT_MNU,_Goto)
		       end
		  end
	     end;

	  { Determine position of next item on a horizontal menu.	}

	  if (HorzMenu) then
	     Inc(Col,Length(TempStr) + _DHorizOfs);

	  Inc(Items);

	until ((Items = TextItems) or (Row > MaxRows) or (Col > MaxCol));

      end; {with MDefs}

    if (Items <> TextItems) then
       SetMenuError(_InvalidItem);	 { Some items not included	 }

    __BuildMnu := MPtr

  end;	{__BuildMnu}
