Um Treeview com opção de expandir cada opção do menu.
Realmente ficaria interessante.
Atualizei de leve, e acrescentei pra ver os grupos de um usuário.
Só a título de curiosidade ou talvez de exemplo.
Nessa imagem, tem o primeiro conjunto de opções com nome do usuário.
Depois tem mais três menus por cima.
Esses três vém daquele array do menu.
Reparem que a rotina é "feita a mão", usando FOR/NEXT pra montar o menu, e InKey(0) pro usuário teclar algo.
A rotina completa, sem limite de níveis, é esta, que só precisa do array com o menu:
BoxAcesso( 4, 15, axMenu, 1, "ACESSOS DE " + cUsuario + " (PRÓPRIO/GRUPO)", .F. )
...
FUNCTION BoxAcesso( mLini, mColi, mMenuOpt, mOpc, mTitulo, mSaiSetas, lLiberado4, lLiberado5 )
LOCAL mCont, mLinf, mColf, mTecla
mLinf := mLini + Len( mMenuOpt ) + 2
mColf := mColi + 44
WOpen( mLini, mColi, mLinf, mColf, mTitulo )
Mensagem( "Selecione e tecle ENTER, <S> (Tem Acesso), <N> (Sem Acesso), <ESC> sai" )
DO WHILE .T.
FOR mCont = 1 TO Len( mMenuOpt )
@ mLini + 1 + mCont, mColi + 1 SAY " " + Pad( mMenuOpt[ mCont, 1 ], 31 ) + iif( Len( mMenuOpt[ mCont, 2 ] ) > 0, Chr(16), " " ) + " " + ;
iif( mMenuOpt[ mCont, 4 ], "SIM", "---" ) + " " + iif( mMenuOpt[ mCont, 5 ], "SIM", "---" ) + " " COLOR iif( mCont == mOpc, SetColorFocus(), SetColorBox() )
NEXT
SetColor( SetColorNormal() )
mTecla := Inkey(0)
DO CASE
CASE mTecla == K_ESC
EXIT
CASE mSaiSetas .AND. ( mTecla == K_RIGHT .OR. mTecla == Asc( "6" ) .OR. mTecla == K_LEFT .OR. mTecla == Asc( "4" ) ) // setas
EXIT
CASE mTecla == K_LBUTTONDOWN
IF MROW() > mLini + 1 .AND. MROW() < mLini + 2 + Len( mMenuOpt ) .AND. MCOL() > mColi .AND. MCOL() < mColi + 38
mOpc := MROW() - mLini - 1
Keyboard Chr( K_ENTER )
ENDIF
CASE mTecla == K_RBUTTONDOWN ; Keyboard Chr( K_ESC )
CASE mTecla == K_DOWN .OR. mTecla == Asc( "2" ) ; mOpc := iif( mOpc == Len( mMenuOpt ), 1, mOpc + 1 )
CASE mTecla == K_UP .OR. mTecla == Asc( "8" ) ; mOpc := iif( mOpc == 1, Len( mMenuOpt ), mOpc - 1 )
CASE mTecla == K_HOME .OR. mTecla == Asc( "7" ) ; mOpc := 1
CASE mTecla == K_END .OR. mTecla == Asc( "1" ) ; mOpc := Len( mMenuOpt )
CASE mTecla == Asc( "S" ) .OR. mTecla == Asc( "s" ) ; // MudaAcess( mMenuOpt, mOpc, .T. )
CASE mTecla == Asc( "N" ) .OR. mTecla == Asc( "n" ) ; // MudaAcess( mMenuOpt, mOpc, .F. )
CASE mTecla == K_ENTER
IF Len( mMenuOpt[ mOpc, 2 ] ) > 0
BoxAcesso( mLini + 2, mColi + 10, mMenuOpt[ mOpc, 2 ], 1, mMenuOpt[ mOpc, 1 ], @mMenuOpt[ mOpc, 4 ], @mMenuOpt[ mOpc, 5 ] )
ELSE
// SelecionaUsuarios( mColi + 20, mMenuOpt[ mOpc, 3 ] )
ENDIF
ENDCASE
ENDDO
WClose()
FOR mCont = 1 TO Len( mMenuOpt )
IF mMenuOpt[ mCont, 4 ]
lLiberado4 := .T.
ENDIF
IF mMenuOpt[ mCont, 5 ]
lLiberado5 := .T.
ENDIF
NEXT
RETURN NIL
Rotinas externas:
- wOpen() e wClose() - apenas pra montar o box (contorno) da opção e salvar/restaurar tela.
- Mensagem() e SetColorNormal() - o nome diz tudo
- MudaAcess() e SelecionaUsuarios() já fazem parte de configuração, e não deste menu
Toda navegação é essa rotina, incluindo uso de mouse.
E ao teclar numa opção com subopções:
CASE mTecla == K_ENTER
IF Len( mMenuOpt[ mOpc, 2 ] ) > 0
BoxAcesso( mLini + 2, mColi + 10, mMenuOpt[ mOpc, 2 ], 1, mMenuOpt[ mOpc, 1 ], @mMenuOpt[ mOpc, 4 ], @mMenuOpt[ mOpc, 5 ] )
ELSE
// SelecionaUsuarios( mColi + 20, mMenuOpt[ mOpc, 3 ] )
ENDIF
Ela chama ela mesma, passando o "sub-array".
Conforme a configuração vai sendo feita, o próprio array armazena o resultado.
Acho que ficou visível o que precisei fazer pra acrescentar a parte de grupos.
Só acrescentei um elemento ao array.
Rotina sem classes, sem nada sofisticado, apenas comandos/funções simples.
O uso de recursividade é que fez a diferença.
Um ótimo exemplo pra mostrar a vantagem de variáveis LOCAL.
Se usasse variáveis PRIVATE ou PUBLIC, sem chance.