Takes Menu Items specified in a form
{ { "First Menu item", "First Message" }, { "Second Menu item", "Second Message" }, { "Third Menu Item", "Third Message" }, etc. }
, and calculates rows and columns to generate Horizontal, Vertical, or Random menus.
Compile:
call ..\..\batch\compile.bat MultMenu /C /LE c:\minigui\Harbour\lib\hbnf /C /LE c:\minigui\Harbour\lib\SUPER %2 %3 %4 %5 %6 %7 %8 %9
Code: Select all
#Include "MiniGui.ch"
#Include "hbinkey.ch"
#Include "inkey.ch"
Function MultMenu
Local MainMenu_a := { ;
{'Add', "Add Something", },;
{'Open', "Open Something" },;
{'Edit', "Change Something" },;
{'Delete', "Delete Something"},;
{'Reset', "Reset Everything"},;
{'Quit', "Get the heck out" }}
Local CompleteMenu_a := { ;
{1,1 , "Add", 2, 1, "Add something"},;
{1,5 , "Open", 2, 1, "Open something"},;
{1,10 , "Delete", 2, 1, "Delete something"},;
{1,17 , "Change Date", 2, 1, "Change the current Date"},;
{1,29 , "List", 2, 1, "Display List"},;
{1,34 , "Purge", 2, 1, "Purge something"},;
{1,40 , "Purge 2", 2, 1, "Purge something else"},;
{1,48 , "Purge 3", 2, 1, "Purge something one more time"},;
{1,57 , 'Quit', 2, 1, 'Get da heck out'}}
SETCOLOR( "W+/B,B/W,,W+/B")
cls(23, chr(177))
ComputedArray_a := Fill_in_Coordinates_for_Menu_Array( MainMenu_a, "HORIZONTALBAR" )
nSelected := Execute_MenuArray(ComputedArray_a,1,.t.)
cls()
ComputedArray_a := Fill_in_Coordinates_for_Menu_Array( MainMenu_a, "VERTICALBAR" )
nSelected := Execute_MenuArray(ComputedArray_a,1,.t.)
cls()
ComputedArray_a := Fill_in_Coordinates_for_Menu_Array( MainMenu_a, "RANDOMMENU" )
nSelected := Execute_MenuArray(ComputedArray_a,1,.t.)
cls()
nSelected := Execute_MenuArray(CompleteMenu_a,1,.t.)
cls()
Return Nil
// From Super.lib
FUNCTION cls(ncColorAtt,Fill_Character_s)
local Color_s
Color_s := iif(valtype(ncColorAtt)=="N",at2char(ncColorAtt),ncColorAtt)
Fill_Character_s := repl( iif(Fill_Character_s#nil,Fill_Character_s," "),9 )
dispbox(0,0,maxrow(),maxcol(),Fill_Character_s,Color_s)
RETURN ''
Function At2char(nColor)
local ForeGround_a := {"N","B","G","BG","R","RB","GR","W",;
"N+","B+","G+","BG+","R+","RB+","GR+","W+"}
local BackGround_a := {"N","B","G","BG","R","RB","GR","W",;
"N*","B*","G*","BG*","R*","RB*","GR*","W*"}
local nFore := nColor%16
local nBack := INT(nColor/16)
local cForeground := ForeGround_a[nFore+1]
local cBackGround := BackGround_a[nBack+1]
return ( cForeground+'/'+cBackGround )
// Originally Rat_Menu2() From SuperLib
FUNCTION Execute_MenuArray(Menus_a,nStart,Menu_First_Letter_Executes_b)
/*
Menus_a should be a nested array of menu arrays Menu_a.
Each menu array should be of the form
{ MenuRow, MenuColumn, MenuItem, MessageRow, MessageColumn, Message }
nStart - The Nth Prompt to start with. Defaults to the first prompt
Menu_First_Letter_Executes_b -
If Menu_First_Letter_Executes_b is True, then a single first letter and ENTER will return.
If Menu_First_Letter_Executes_b is False, then two first letters are required.
*/
Local Normal_Color_s := ""
Local Enhanced_Color_s := ""
local NthOption_n := 0
Local NthOption_a := {}
Local Message_Row_n := 0
local Selected_Item_n := 0
local Previous_Selected_Item_n := 0
local LastKeyPressed_n
Local nFound
Local nMrow
Local nMcol
Local nMouseFound
local FirstLettersOfMenus_s := ""
local LastKeyPressed_c
Local Key_cb // SETKEY Code Block
local Cursor_n := setcursor(0)
Normal_Color_s := Token(Setcolor(),",",1)
Enhanced_Color_s := Token(Setcolor(),",",2)
NthOption_n := 0
NthOption_a := {}
// Return the current selected item, the starting item or the last item, whichever is less.
Selected_Item_n := min( iif(nStart#nil,max(nStart,1),1), len(Menus_a) )
Previous_Selected_Item_n := Selected_Item_n
Menu_First_Letter_Executes_b := iif(Menu_First_Letter_Executes_b#nil,Menu_First_Letter_Executes_b,.t.)
// Initial Display of all the menu items
AEVal( Menus_a, { | NthOption_a, NthOption_n | DisplayCurrentMenuItem( NthOption_a, NthOption_n, Normal_Color_s, Enhanced_Color_s ) } )
// Concatenate all first letters of the menus into a single string
FirstLettersOfMenus_s := ConcatenateAllFirstLetters_of_Menus("ConcatenateFirstLetters",Menus_a,"")
while .t.
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,1], Menus_a[Previous_Selected_Item_n,2], Menus_a[Previous_Selected_Item_n,3], Normal_Color_s )
Previous_Selected_Item_n := Selected_Item_n
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,1], Menus_a[Previous_Selected_Item_n,2], Menus_a[Previous_Selected_Item_n,3], Enhanced_Color_s )
// TODO: Customize background of message depending on type of menu
// Specifically, change the background fill characters for the left of the message.
// Clear Message
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,4], Menus_a[Previous_Selected_Item_n,5], Repl( chr(177), MaxCol() ), Normal_Color_s )
// Display Message
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,4], Menus_a[Previous_Selected_Item_n,5], Menus_a[Previous_Selected_Item_n,6], Normal_Color_s )
LastKeyPressed_n := rat_event(0)
LastKeyPressed_c := upper(chr(LastKeyPressed_n))
do case
case LastKeyPressed_n == K_LEFT .or. LastKeyPressed_n==K_UP
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,4], Menus_a[Previous_Selected_Item_n,5], Repl( chr(177), MaxCol() ), Normal_Color_s )
Selected_Item_n := IIF(Selected_Item_n=1,len(Menus_a),Selected_Item_n-1)
case LastKeyPressed_n == K_RIGHT .or. LastKeyPressed_n == K_DOWN
hb_DispOutAtBox( Menus_a[Previous_Selected_Item_n,4], Menus_a[Previous_Selected_Item_n,5], Repl( chr(177), MaxCol() ), Normal_Color_s )
Selected_Item_n := IIF(Selected_Item_n=len(Menus_a),1,Selected_Item_n+1)
Case IsThisCharacterContainedInThatString( LastKeyPressed_c, FirstLettersOfMenus_s )
if (nFound := AT(LastKeyPressed_c,subst(FirstLettersOfMenus_s,Selected_Item_n)) ) > 0
Selected_Item_n := nFound+Selected_Item_n-1
else
Selected_Item_n := at(LastKeyPressed_c,FirstLettersOfMenus_s)
endif
if Menu_First_Letter_Executes_b .or. Selected_Item_n==Previous_Selected_Item_n
keyboard chr(13)
endif
case LastKeyPressed_n = K_ENTER .or. LastKeyPressed_n==K_PGUP .or. LastKeyPressed_n== K_PGDN
exit
case LastKeyPressed_n = K_ESC
Selected_Item_n := 0
exit
case ( (Key_cb := SetKey(LastKeyPressed_n)) <> NIL )
Eval(Key_cb,"",0,"")
endcase
end
setcursor(Cursor_n)
return Selected_Item_n
Procedure DisplayCurrentMenuItem( NthOption_a, NthOption_n, Normal_Color_s, Enhanced_Color_s )
hb_DispOutAtBox( NthOption_a[1], NthOption_a[2], NthOption_a[3], Normal_Color_s )
Return
Function ConcatenateFirstLetters(FirstLettersOfMenus_s,NthOption_a)
FirstLettersOfMenus_s += upper(left(NthOption_a[3],1 ))
Return FirstLettersOfMenus_s
/*
Map functions were written by a generous fellow Harbour traveler in one of the google groups.
For an explanation of each Map function, see
https://www.freecodecamp.org/news/javascript-map-reduce-and-filter-explained-with-examples/
*/
// Returns an array, whose elements are the value of xFunc applied to each element in aArg1
// aArg2, aArg3, aArg4, ... aArgnN have not been tested yet.
// AMap( <xFunc, [,aArg1] [, aArgN])
function AMap(...)
local aOut := {}
local aTemp
local aArgs := hb_aParams()
local nArgCount := len(aArgs) // func is the first arg so skip it.
local xFunc := iif(len(aArgs)>0,aArgs[1],nil)
local nArgInd
local lObject := hb_isObject(xFunc)
local nArgStart := iif(lObject,3,2)
local nResCount := iif(nArgCount>1,len(aArgs[2]),0)
local nResInd
local oObj := iif(lObject,xFunc,nil)
if lObject
xFunc := aArgs[2]
endif
for nResInd:=1 to nResCount
aTemp := {}
for nArgInd:=nArgStart to nArgCount
aAdd(aTemp,aArgs[nArgInd][nResInd])
next
if lObject
aAdd(aOut, HB_ExecFromArray(oObj,xFunc,aTemp))
else
aAdd(aOut, HB_ExecFromArray(xFunc,aTemp))
endif
next
return aOut
/*
Applies function Reduction_Function_x to FirstValueToBeApplied_x and first element of Parameters_a,
then applies Reduction_Function_x to the result of the first application and the second element of Parameters_a,
then applies Reduction_Function_x to the result of the previous application and the third element of Parameters_a, etc,
until it arrives at a single result which is the accumulation of the functiona applied to all consecutive elements of the array.
AReduce( "Add", { 1, 2, 3 } )
=> 6 ( 0 + 1 + 2 + 3 = 6 )
AReduce( "ConcatenateAllFirstLetters_of_Menus", { "What", "the", "heck" } )
=> "Wth" ( Concatenate "" with "W" with "t" with "h" )
Returns same type as FirstValueToBeApplied_x accumulated from each element
*/
function AReduce(Reduction_Function_x,Parameters_a,FirstValueToBeApplied_x)
local StartingElement_n := 1
local nLength := len(Parameters_a)
local nInd
if hb_isNil(FirstValueToBeApplied_x) .and. nLength>0
StartingElement_n := 2
FirstValueToBeApplied_x := Parameters_a[1]
endif
hb_ForNext( StartingElement_n, nLength, { |nInd| FirstValueToBeApplied_x := HB_ExecFromArray( Reduction_Function_x, {FirstValueToBeApplied_x,Parameters_a[nInd]}) } )
return FirstValueToBeApplied_x
/*
Applies constraint function xFunc to each parameter of Parameters_a,
, returning an array with elements that satisfy the constraint.
See
https://www.phptutorial.net/php-tutorial/php-array-filter/
and
https://en.wikipedia.org/wiki/Filter_(higher-order_function)
for illustrative examples and explanations
*/
Function aFilter(xFunc,Parameters_a)
local aOut := {}
local nLength := len(Parameters_a)
local nInd
for nInd := 1 to nLength
if HB_ExecFromArray(xFunc,{Parameters_a[nInd]})
aAdd(aOut,Parameters_a[nInd])
endif
next
return aOut
Function IsThisCharacterContainedInThatString( ThisCharacter_s, ThatString_s )
Local ThisCharacterContainedInThatString_b := .F.
ThisCharacterContainedInThatString_b := ThisCharacter_s $ ThatString_s
Return ThisCharacterContainedInThatString_b
Function Fill_in_Coordinates_for_Menu_Array( MenuArray_a, Menu_Type_s )
/*
Convert a menu array without coordinates like
{ MenuItem, Message }
to a menu array with Row and Column coordinates like
{ MenuRow, MenuColumn, MenuItem, MessageRow, MessageColumn, Message }
*/
Local CompleteMenuArray_a := {}
Do Case
Case Menu_Type_s == "HORIZONTALBAR"
CompleteMenuArray_a := AMap( "ConvertMenu_to_HorizontalBar", MenuArray_a )
Case Menu_Type_s == "VERTICALBAR"
CompleteMenuArray_a := AMap( "ConvertMenu_to_VerticalBar", MenuArray_a )
Case Menu_Type_s == "RANDOMMENU"
CompleteMenuArray_a := AMap( "ConvertMenu_to_RandomMenu", MenuArray_a )
End Case
Return CompleteMenuArray_a
Function ConvertMenu_to_HorizontalBar( MenuItem_a )
/*
MenuItem_a has the structure
{ MenuItem, Message }
*/
Local HorizontalMenuItem_a := {}
Local Row_n := 1
Local MenuItem_s := ""
Local Message_s := ""
STATIC Column_n := 0
MenuItem_s := Trim( MenuItem_a[ 1 ] )
Message_s := Trim( MenuItem_a[ 2 ] )
// Menu item
AADD( HorizontalMenuItem_a, Row_n )
AADD( HorizontalMenuItem_a, Column_n )
AADD( HorizontalMenuItem_a, MenuItem_s )
// Messages associated with each Menu item
AADD( HorizontalMenuItem_a, Row_n + 1 )
AADD( HorizontalMenuItem_a, 0 )
AADD( HorizontalMenuItem_a, Message_s )
// Calculate Next Column, which will be the previous column plus the length of the current menu item plus the length of one space.
Column_n := Column_n + Len( Trim( MenuItem_s ) ) + 1
Return HorizontalMenuItem_a
Function ConvertMenu_to_VerticalBar( MenuItem_a )
/*
MenuItem_a has the structure
{ MenuItem, Message }
*/
Local VerticalMenuItem_a := {}
Local Column_n := 1
Local MenuItem_s := ""
Local Message_s := ""
STATIC Row_n := 0
MenuItem_s := Trim( MenuItem_a[ 1 ] )
Message_s := Trim( MenuItem_a[ 2 ] )
// Menu item
AADD( VerticalMenuItem_a, Row_n )
AADD( VerticalMenuItem_a, Column_n )
AADD( VerticalMenuItem_a, MenuItem_s )
// Messages associated with each Menu item
AADD( VerticalMenuItem_a, Row_n )
AADD( VerticalMenuItem_a, Column_n + Len( MenuItem_s ) + 2 )
AADD( VerticalMenuItem_a, Message_s )
// Calculate Next Row, which will be the previous Row plus 1.
Row_n := Row_n + 1
Return VerticalMenuItem_a
/*
Please do not ask me, why I built something as insane and useless
as a menu picklist located at random points on the screen.
It just sounded like it would be fun to see.
*/
Function ConvertMenu_to_RandomMenu( MenuItem_a )
/*
MenuItem_a has the structure
{ MenuItem, Message }
*/
#DEFINE TEMPORARYRANDOMROWLIMIT 15 // Maximum number of row coordinates
#DEFINE TEMPORARYRANDOMCOLUMNLIMIT 72 // Maximum number of column coordinates
/* TODO - The maximum number of rows and columns used to extract the random rows and columns,
should be a factor of the total number of menu items.
*/
Local RandomMenuItem_a := {}
Local RandomRow_n := 0
Local RandomColumn_n := 0
Local Column_n := 1
Local MenuItem_s := ""
Local Message_s := ""
RandomRow_n := hb_RandomInt( TEMPORARYRANDOMROWLIMIT )
RandomColumn_n := hb_RandomInt( TEMPORARYRANDOMCOLUMNLIMIT )
MenuItem_s := Trim( MenuItem_a[ 1 ] )
Message_s := Trim( MenuItem_a[ 2 ] )
// Menu item
AADD( RandomMenuItem_a, RandomRow_n )
AADD( RandomMenuItem_a, RandomColumn_n )
AADD( RandomMenuItem_a, MenuItem_s )
// Messages associated with each Menu item
AADD( RandomMenuItem_a, RandomRow_n )
AADD( RandomMenuItem_a, RandomColumn_n + Len( MenuItem_s ) + 2 )
AADD( RandomMenuItem_a, Message_s )
Return RandomMenuItem_a
Function ConcatenateAllFirstLetters_of_Menus( ConcatenateFirstLetters_s, Menus_a, FirstValue_s )
Local FirstLettersOfMenus_s := ""
FirstLettersOfMenus_s := AReduce("ConcatenateFirstLetters",Menus_a, FirstValue_s )
Return FirstLettersOfMenus_s
[attachment=0]MultiMenu.jpg[/attachment]