CodeBlock com Macro Substituição

Projeto MiniGui - Biblioteca visual para Harbour/xHarbour

Moderador: Moderadores

Avatar do usuário
carlaoonline
Usuário Nível 3
Usuário Nível 3
Mensagens: 190
Registrado em: 24 Ago 2014 22:38
Localização: Porto Alegre-RS

CodeBlock com Macro Substituição

Mensagem por carlaoonline »

Boa tarde!


Estou com dificuldades em colocar um CodeBlock no evento ON CHANGE de um CheckBox.

O detalhe é que esse CodeBlock está em um elemento de uma matriz.

Queria fazer exatamente como fazemos com a macro substituição, mas no caso do codeblock atribuído a uma variável, está ficando sempre o conteúdo da última variável (pois trata-se de um codeblock)

Ex.:
No lugar disso

Código: Selecionar todos

   on change {||ManipulaCheckAcessos(6,24,This.Value)}
Quero usar isso:

Código: Selecionar todos

aCaixas:="{      {  10 ,  "teste"   ,  {||ManipulaCheckAcessos(6,24,This.Value)}     }   , ;
                      {   20    "teste2"  ,                   ""                                                  }

 bBloco:= aCaixas[1][3]        
  
  // e ao criar o controle, que o evento seja
   on change bBloco

Já tentei de várias maneiras mas aind não achei a forma correta.

Segue um código de exemplo que fiz, se eu colocar um IF lá na criação do controle, até resolvo meu problema, mas para o futuro gostaria de saber como se faz.


Código: Selecionar todos

#include "minigui.ch"

//-----------------------------------------------------------
Function Main()
//-----------------------------------------------------------
local nCaixa, nLinha, nColuna, bBloco, cNomeControle, nValorAcesso, aCaixas

       
       
define window form_acesso;
   at 000,000;    
   width 760;    
   height 560;    
   title 'Teste';    
   main ;    
   nosize    
   
   define tab tab_acesso;    
      of form_acesso;        
      at 003,010;        
      width form_acesso.width-020;        
      height form_acesso.height-090;        
      font 'verdana';        
      size 010;        
      bold;        
      value 001;        
      flat        
      
      
      
      page 'Clientes'         
      

      
           //   Coluna01         Coluna02                              Coluna03          

 // aCaixas:={ {   005   ,  "Menu Clientes"                  ,  "{||ManipulaCheckAcessos(6,24,This.Value)}" } , ;  // Enable ou Disable os chkbox_acesso_006 a 024        
    aCaixas:={ {   005   ,  "Menu Clientes"                  ,    "ManipulaCheckAcessos(6,24,This.Value)"   } , ;  // Enable ou Disable os chkbox_acesso_006 a 024                               
               {   007   ,  "Menu Clientes->Incluir"         ,                 ""                           } , ;        
               {   008   ,  "Menu Clientes->Ver"             ,                 ""                           } , ;        
               {   010   ,  "Menu Clientes->Transferir"      ,                 ""                           } , ;        
               {   011   ,  "Menu Clientes->Pesquisar"       ,                 ""                           } , ;        
               {   013   ,  "Tela do cliente->Botao Editar"  ,                 ""                           }  }        
      
      
            nLinha=20        
            nColuna=120        
      

            FOR nCaixa = 1 to LEN(aCaixas)        
                nLinha = nLinha + 30        
      
                // msgbox("teste    "+STR(nCaixa)+"     " +HB_VALTOSTR(Empty(aCaixas[nCaixa][03]) ) )        
      
                IF Empty(aCaixas[nCaixa][03])  // Se não houver codeblock na coluna 3        
      
                   bBloco:="Nothing()"           
                   // bBloco:="{||Nothing()}"        
      
                 ELSE        
      
      
        // AQUI, NAO SEI COMO COLOCAR O CODEBLOCK DENTRO DA VARIAVEL        
        // DE MODO QUE CADA "ON CHANGE" DE CADA CONTROLE CRIADO ABAIXO        
        // RECEBA O CODEBLOCK DA COLUNA 3 DA MATRIZ aCaixas, CASO POSSUA.        
      
                   // bBloco := aCaixas[nCaixa][03]        
                   // bBloco := &( "{ || " + aCaixas[nCaixa][03] + " }" )        
                   // bBloco := & ( {||ManipulaCheckAcessos(6,24,This.Value)}  )        
                   //  bBloco := "ManipulaCheckAcessos(6,24,This.Value)"        
                   // bBloco:=aCaixas[nCaixa][03]             
                   bBloco := "{||ManipulaCheckAcessos(6,24,This.Value)}"        
      
                ENDIF        
      
                cNomeControle="chkbox_acesso_"+RIGHT( "00"+ALLTRIM(     STR(  aCaixas[nCaixa][01]   )    ) ,3 )        
                define checkbox &cNomeControle        
                       caption aCaixas[nCaixa][02]        
                       col nColuna        
                       row nLinha        
                       width 350        
                       value .T.        
                       fontname 'verdana'        
                       fontsize 10        
                       fontbold .T.        
                       transparent .F.        
                       on change &(bBloco)          
                       // on change {||ManipulaCheckAcessos(6,24,This.Value)}        
                       // on change & (  {||ManipulaCheckAcessos(6,24,This.Value)}  )        
                       // on change &bBloco        
                       // on change bBloco        
      
      
                end checkbox        
            NEXT        
      
      end page        
                       
   end tab    
   
   on key escape action thiswindow.release    
end window
       

form_acesso.center
form_acesso.activate

Return 
//-------------------------------------------------



//-------------------------------------------------
Function ManipulaCheckAcessos(nInicio,nFinal,lMostra)
// Caso, por exemplo, se nInicio=6 e nFinal=24, entao, confomre lMostra
// habilita ou desabilita os controles checkBox_006 ate checkbox_Acesso_024
// caso existam.
//-------------------------------------------------
Local nControleCount, cNomeControle
FOR nControleCount=nInicio TO nFinal
    cNomeControle="chkbox_acesso_"+RIGHT("00"+ALLTRIM(STR(nControleCount)) ,3)
    IF _IsControlDefined(cNomeControle,"Form_Acesso")
       SETPROPERTY("Form_Acesso",cNomeControle,"ENABLED",lMostra)
    ENDIF
NEXT

// MSGBOX("Funcionou-> "+Str(nInicio)+ "   " + Str(nFinal) + "     "+Hb_ValToStr(lMostra) )
Return
//-------------------------------------------------



//-----------------------------------------------------------
Function Nothing()  // Retorna NIL
//-----------------------------------------------------------
MSGBOX("NADA")
Return Nil
//-----------------------------------------------------------


Grato por enquanto.
alxsts
Colaborador
Colaborador
Mensagens: 3092
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil

CodeBlock com Macro Substituição

Mensagem por alxsts »

Olá!

Esqueça macro substituição...

A linha abaixo, da forma como você escreveu, cria uma string...

Código: Selecionar todos

bBloco := "{||ManipulaCheckAcessos(6,24,This.Value)}"
Se quer um code block, retire as aspas:

Código: Selecionar todos

bBloco := { |l, c, v| ManipulaCheckAcessos( l, c, v ) }
No evento, avalie o code block com a função própria (Eval():

Código: Selecionar todos

on change Eval( bBloco, 6, 24, This.Value )   
Pode até não funcionar pois não conheço MiniGUI mas, o caminho é este...
[]´s
Alexandre Santos (AlxSts)
Avatar do usuário
carlaoonline
Usuário Nível 3
Usuário Nível 3
Mensagens: 190
Registrado em: 24 Ago 2014 22:38
Localização: Porto Alegre-RS

CodeBlock com Macro Substituição

Mensagem por carlaoonline »

Opa!

Valeu pela ajuda.
carlaoonline escreveu:No evento, avalie o code block com a função própria (Eval():
Expandir visualizacaoVer codigo
1 on change Eval( bBloco, 6, 24, This.Value )   


Pode até não funcionar pois não conheço MiniGUI mas, o caminho é este...
O exemplo vai me servir bastante para outras partes, nesse caso aqui funciona da mesma forma que colocando o codeblock direto,
o problema é que na criação dos controles em um loop, ficam todo os eventos ON CHANGE Iguais (diferenciados apenas pelos parâmetros)

O que eu preciso é colocar no evento ON CHANGE de cada checkbox (que são criados vários em um único loop)
, um codeblock que talvez sejam totalmente diferentes, ou seja, são os codeblocks que estão na matriz "aCaixas" que serão passada como referência.


on change (QUERO COLOCAR AQUI O CODEBLOCK CORRESPONDENTE QUE ESTÁ NA MATRIZ aCaixas[coluna3] A CADA LOOP)


Reparem no loop que os controles CheckBox são criados na quantidade da LEN(aCaixas)
e em cada CheckBox eu queria colocar o codeblock da matriz.

Código: Selecionar todos

#include "minigui.ch"

//-----------------------------------------------------------
Function Main()
//-----------------------------------------------------------
local nCaixa, nLinha, nColuna, bBloco, cNomeControle, nValorAcesso
Local cStringCodeBlock, aCaixas

define window form_acesso;
   at 000,000;    
   width 760;    
   height 560;    
   title 'Teste';    
   main ;    
   nosize    
   
   define tab tab_acesso;    
      of form_acesso;        
      at 003,010;        
      width form_acesso.width-020;        
      height form_acesso.height-090;        
      font 'verdana';        
      size 010;        
      bold;        
      value 001;        
      flat        
   
           //   Coluna01         Coluna02                              Coluna03          

    aCaixas:={ {   005   ,  "Menu Clientes"                  ,  {||ManipulaCheckAcessos(6,24,This.Value)} } , ;  // Enable ou Disable os chkbox_acesso_006 a 024        
               {   007   ,  "Menu Clientes->Incluir"         ,  {||Nothing()}                           } , ;        
               {   008   ,  "Menu Clientes->Ver"             ,  {||MsgBox("Teste"), Muda_fnt(This.name) }                           } , ;        
               {   010   ,  "Menu Clientes->Transferir"      ,  {||Nothing()}                           } , ;        
               {   011   ,  "Menu Clientes->Pesquisar"       ,  {||MsgBox("Novo"), Muda_Cor(This.Name)  }                       } , ;        
               {   013   ,  "Tela do cliente->Botao Editar"  ,  {||Nothing()}                           }  }        
      
      
            nLinha=20        
            nColuna=120        
      
      page 'Clientes'         
            FOR nCaixa = 1 to LEN(aCaixas)        
                nLinha = nLinha + 30        
                cNomeControle = "chkbox_acesso_"+RIGHT( "00"+ALLTRIM(     STR(  aCaixas[nCaixa][01]   )    ) ,3 )        
				cStringCodeBlock = aCaixas[nCaixa][03]
                define checkbox &cNomeControle        
                       caption aCaixas[nCaixa][02]  
                       col nColuna        
                       row nLinha        
					   
					   
                       on change (QUERO COLOCAR AQUI O CODEBLOCK CORRESPONDENTE   aCaixas[nCaixa][3]   A CADA LOOP)
					   
					   
					   
                end checkbox        
            NEXT        
      end page        
                       
   end tab    
   
   on key escape action thiswindow.release    
end window
       

form_acesso.center
form_acesso.activate

Return 
//-------------------------------------------------



//-------------------------------------------------
Function Cria_Bloco(cValor)
//-------------------------------------------------
return &("{||"+cValor+"}")
//-------------------------------------------------


//-------------------------------------------------
Function Muda_Fnt(cNomeControle)
//-------------------------------------------------
SETPROPERTY("Form_Acesso",cNomeControle,"FontBold", GETPROPERTY("Form_Acesso",cNomeControle,"value") )
return 
//-------------------------------------------------


//-------------------------------------------------
Function Muda_Cor(cNomeControle)
//-------------------------------------------------
IF GETPROPERTY("Form_Acesso",cNomeControle,"value")
   SETPROPERTY("Form_Acesso",cNomeControle,"fontname","Arial"  )
 ELSE  
   SETPROPERTY("Form_Acesso",cNomeControle,"fontname","Times New Roman"  )
ENDIF   
return 
//-------------------------------------------------




//-------------------------------------------------
Function ManipulaCheckAcessos(nInicio,nFinal,lMostra)
// Caso, por exemplo, se nInicio=6 e nFinal=24, entao, confomre lMostra
// habilita ou desabilita os controles checkBox_006 ate checkbox_Acesso_024
// caso existam.
//-------------------------------------------------
Local nControleCount, cNomeControle
FOR nControleCount=nInicio TO nFinal
    cNomeControle="chkbox_acesso_"+RIGHT("00"+ALLTRIM(STR(nControleCount)) ,3)
    IF _IsControlDefined(cNomeControle,"Form_Acesso")
       SETPROPERTY("Form_Acesso",cNomeControle,"ENABLED",lMostra)
    ENDIF
NEXT

// MSGBOX("Funcionou-> "+Str(nInicio)+ "   " + Str(nFinal) + "     "+Hb_ValToStr(lMostra) )
Return
//-------------------------------------------------

//-----------------------------------------------------------
Function Nothing()  // Retorna NIL
//-----------------------------------------------------------
MSGBOX("NADA")
Return Nil
//-----------------------------------------------------------


Esse exemplo é apenas didático, obviamente será usado em outra finalidade.

Ainda não consegui.

Grato.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

CodeBlock com Macro Substituição

Mensagem por JoséQuintas »

As vezes com variáveis, melhor usar uma função.

Código: Selecionar todos

bCode := ToBlock( array[3] )
...
FUNCTION ToBlock( x )
   RETURN &( x )
Deste jeito, não importa se array[3] vai mudar de valor depois.
Apenas exemplo, e com macro, porque citou macro.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
carlaoonline
Usuário Nível 3
Usuário Nível 3
Mensagens: 190
Registrado em: 24 Ago 2014 22:38
Localização: Porto Alegre-RS

CodeBlock com Macro Substituição

Mensagem por carlaoonline »

Usando com funções realmente funciona, porém se quiser usar os recursos de um codeblock não tem como, e tb
o empecilho é que não pode usar "this.value" ou qualquer outra referência 'This',
tem que usar GetProperty('Form_Acesso','chkbox_acesso_005','value') mencionando o 'nome do controle'
e daí já sai um pouco do automático dependendo a utilização...


Resumindo, tentei de inúmeras maneiras e cheguei a conclusão que:

No ON CHANGE do checkbox (e talvez de outas propriedades e tb de outros controles...)
NÃO TEM COMO usar um CodeBlock que está em uma matriz,

tem que digitar ele na linha ON CHANGE.


Assim é tranquilo...

Código: Selecionar todos

ON CHANGE {||MsgBox(This.Name) , ManipulaCheckAcessos(6,24,this.value)} 


Assim não teve jeito...

Código: Selecionar todos

     // o bloco direto na matriz...
aCaixas:= {   005 ,  "Menu 1" ,  {||MsgBox(This.Name) , ManipulaCheckAcessos(6,24,this.value)}   }
bCode= aCaixas[3]

     //  ou  o bloco com aspas (dentro de uma String) ...
aCaixas:= {   005 ,  "Menu 1" ,  " {||MsgBox(This.Name) , ManipulaCheckAcessos(6,24,this.value)}"  }
bCode= aCaixas[3]

    // ou o bloco ainda sem formato de bloco, a ser convertido depois em uma função   
aCaixas:= {   005 ,  "Menu 1" ,  "MsgBox(This.Name) , ManipulaCheckAcessos(6,24,this.value) "  }
bBloco=Cria_Bloco( aCaixas[3])


...
ON CHANGE     ??  bBloco  ??  &bBloco  ??  EVAL...   ??   // não teve jeito com codeblock, somente com função




Assim FUNCIONOU,
usando macro substituição da string que contém as funções que está na matriz,
funciona se não usar a referência "This"

Código: Selecionar todos

#include "minigui.ch"

//-----------------------------------------------------------
Function Main()
//-----------------------------------------------------------
local nCaixa, nLinha, nColuna, bBloco, cNomeControle, nValorAcesso, cString
Local cStringCodeBlock, aCaixas

define window form_acesso;
   at 000,000;    
   width 760;    
   height 560;    
   title 'Teste';    
   main ;    
   nosize    
   
   define tab tab_acesso;    
      of form_acesso;        
      at 003,010;        
      width form_acesso.width-020;        
      height form_acesso.height-090;        
      font 'verdana';        
      size 010;        
      bold;        
      value 001;        
      flat        

   

/*
           //   Coluna01         Coluna02                              Coluna03          
    aCaixas:={ {   005   ,  "Menu Clientes"                  ,  {||ManipulaCheckAcessos(6,24,this.value)}                } , ; // GetProperty('Form_Acesso','chkbox_acesso_005','value')
               {   007   ,  "Menu Clientes->Incluir"         ,  {||Nothing(), msgbox('Valor='+HB_ValToStr(This.Value))}  } , ;        
               {   008   ,  "Menu Clientes->Ver"             ,  {||MsgBox('Teste'), Muda_fnt(This.name)}                 } , ;        
               {   010   ,  "Menu Clientes->Transferir"      ,  {||Nothing(), msgbox('Linha='+HB_ValToStr(This.Row))}    } , ;        
               {   011   ,  "Menu Clientes->Pesquisar"       ,  {||MsgBox('Novo'), Muda_Cor(This.Name)}                  } , ;        
               {   013   ,  "Tela do cliente->Botao Editar"  ,  {||Nothing(), msgbox('Testando')}                        }   }      
*/

           //   Coluna01         Coluna02                              Coluna03          	  
    aCaixas:={ {   005   ,  "Menu Clientes"                  ,  "Nothing(), ManipulaCheckAcessos(6,24,GetProperty('Form_Acesso','chkbox_acesso_005','value'))"     } , ; // GetProperty('Form_Acesso','chkbox_acesso_005','value')
               {   007   ,  "Menu Clientes->Incluir"         ,  "Nothing(), msgbox('Valor='+HB_ValToStr(GetProperty('Form_Acesso','chkbox_acesso_007','value')))"  } , ;        
               {   008   ,  "Menu Clientes->Ver"             ,  "MsgBox('Teste'), Muda_fnt(GetProperty('Form_Acesso','chkbox_acesso_008','name'))"                 } , ;        
               {   010   ,  "Menu Clientes->Transferir"      ,  "Nothing(), msgbox('Linha='+HB_ValToStr(GetProperty('Form_Acesso','chkbox_acesso_010','row')))"    } , ;        
               {   011   ,  "Menu Clientes->Pesquisar"       ,  "MsgBox('Novo'), Muda_Cor(GetProperty('Form_Acesso','chkbox_acesso_011','name'))"                  } , ;        
               {   013   ,  "Tela do cliente->Botao Editar"  ,  "Nothing(), msgbox('Testando')"                        }   }      	  
	  
	  
            nLinha=20        
            nColuna=120        
      
      page 'Clientes'         
            FOR nCaixa = 1 to LEN(aCaixas)        
                nLinha = nLinha + 30        
                cNomeControle = "chkbox_acesso_"+RIGHT( "00"+ALLTRIM(     STR(  aCaixas[nCaixa][01]   )    ) ,3 )        
				
				cString=aCaixas[nCaixa][03]
				MsgBox("nCaixa="+Alltrim(Str(nCaixa))+"    cString="+HB_ValToStr(cString))				
				
                define checkbox &cNomeControle        
                       caption  aCaixas[nCaixa][02]  
                       col      nColuna        
                       row      nLinha        
                       value    .T.
	   
	   
                       on change &cString

				  
                end checkbox      
   
				
            NEXT        
      end page        
                       
   end tab    
   
   on key escape action thiswindow.release    
end window
       

form_acesso.center
form_acesso.activate

Return 
//-------------------------------------------------



//-------------------------------------------------
Function ToBlock( x )
//-------------------------------------------------
RETURN &( x )
//-------------------------------------------------


//-------------------------------------------------
Function Cria_Bloco(cValor)
//-------------------------------------------------
return &("{||"+cValor+"}")
//-------------------------------------------------


//-------------------------------------------------
Function Muda_Fnt(cNomeControle)
//-------------------------------------------------
msgbox("Controle->"+cNomeControle)
SETPROPERTY("Form_Acesso",cNomeControle,"FontBold", GETPROPERTY("Form_Acesso",cNomeControle,"value") )
return 
//-------------------------------------------------

//-------------------------------------------------
Function Muda_Cor(cNomeControle)
//-------------------------------------------------
IF GETPROPERTY("Form_Acesso",cNomeControle,"value")
   SETPROPERTY("Form_Acesso",cNomeControle,"fontname","Arial"  )
 ELSE  
   SETPROPERTY("Form_Acesso",cNomeControle,"fontname","Times New Roman"  )
ENDIF   
return 
//-------------------------------------------------

//-------------------------------------------------
Function ManipulaCheckAcessos(nInicio,nFinal,lMostra)
// Caso, por exemplo, se nInicio=6 e nFinal=24, entao, confomre lMostra
// habilita ou desabilita os controles checkBox_006 ate checkbox_Acesso_024
// caso existam.
//-------------------------------------------------
Local nControleCount, cNomeControle
FOR nControleCount=nInicio TO nFinal
    cNomeControle="chkbox_acesso_"+RIGHT("00"+ALLTRIM(STR(nControleCount)) ,3)
    IF _IsControlDefined(cNomeControle,"Form_Acesso")
       SETPROPERTY("Form_Acesso",cNomeControle,"ENABLED",lMostra)
    ENDIF
NEXT

// MSGBOX("Funcionou-> "+Str(nInicio)+ "   " + Str(nFinal) + "     "+Hb_ValToStr(lMostra) )
Return
//-------------------------------------------------

//-----------------------------------------------------------
Function Nothing()  // Retorna NIL
//-----------------------------------------------------------
MSGBOX("NADA")
Return Nil
//-----------------------------------------------------------

Lá no fundo, ainda existe a esperança, quem sabe está na declaração da variável:
Local bBloco AS codeblock ...


Grato pelo apoio.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

CodeBlock com Macro Substituição

Mensagem por JoséQuintas »

quote="carlaoonline"]Resumindo, tentei de inúmeras maneiras e cheguei a conclusão que:

No ON CHANGE do checkbox (e talvez de outas propriedades e tb de outros controles...)
NÃO TEM COMO usar um CodeBlock que está em uma matriz,
tem que digitar ele na linha ON CHANGE.[/quote]

Pequena correção

NÃO existe OOP em minigui, isso é criado apenas pra fonte DO USUÁRIO, que passa pelo pré-processador e processa os #include.
Quando coloca o mesmo fonte em codeblock, ele não existe, porque não tem pré-processador.
Não é que não funciona em array, é que o código fonte NÃO existe pra fazer o macro, ele NÃO é aceito sem pré-processador.

Funciona direto no ON CHANGE porque aí ele é traduzido na compilação pelo pré-processador.
Mas como texto.... o texto está errado, em formato inválido pra run-time.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

CodeBlock com Macro Substituição

Mensagem por JoséQuintas »

Simples:

Compile com /p e olhe o arquivo PPO.
Se colocar igual no codeblock funciona.

Vai ter que fazer sempre isso?
Vai sim, fazer o que....
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder