CRUD em AdvPL – Parte 08

Introdução

Prontos para mais um capítulo da novela do CRUD? Neste post vamos fazer algumas alterações no layout dos componentes, e acrescentar as funcionalidades de busca indexada por ID e NOME e mudança de ordem de consulta. O fonte completo está disponível no GITHUB, link disponível no final do post.

Alteração da disposição dos componentes

Os botões de navegação “Primeiro”, “Anterior”, “Próximo” e “Último”, que antes eram dispostos horizontalmente na parte de baixo do formulário de campos da Agenda, serão remanejados para a direita do formulário, verticalmente, e com o espaço que ganhamos, vamos acrescentar mais alguns botões.

Crud V2 Consulta

Para isso ser feito de forma simples, primeiro criamos um painel a mais na caixa de diálogo, e colocamos seu alinhamento à direita.

@ 0,0 MSPANEL oPanelNav OF oDlg SIZE 70,600 COLOR CLR_WHITE,CLR_GRAY
oPanelNav:ALIGN := CONTROL_ALIGN_RIGHT

Agora, todos os botões de navegação, inclusive os novos botões de consulta e ordem, são acrescentados neste painel. Praticamente aproveitamos todas as coordenadas dos botões do menu de opções do painel esquerdo, afinal as coordenadas dos componentes são sempre relativas à coordenada 0,0 (canto superior esquerdo) do seu container — no caso um objeto tPanel.

// Cria os Botões de Navegação Livre
@ 05,05 BUTTON oBtnFirst PROMPT "Primeiro" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,7,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnFirst) // [7] Primeiro

@ 020,05 BUTTON oBtnPrev PROMPT "Anterior" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,8,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnPrev) // [8] Anterior

@ 35,05 BUTTON oBtnNext PROMPT "Próximo" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,9,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnNext) // [9] Proximo

@ 50,05 BUTTON oBtnLast PROMPT "Último" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,10,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnLast) // [10] Último

@ 65,05 BUTTON oBtnPesq PROMPT "Pesquisa" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,11,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnPesq) // [11] Pesquisa

@ 80,05 BUTTON oBtnOrd PROMPT "Ordem" SIZE 60,15 ;
  ACTION ManAgenda(oDlg,aBtns,aGets,12,@nMode) OF oPanelNav PIXEL
aadd(aBtns,oBtnOrd) // [12] Ordem

A função ManAgenda() agora passa a receber as ações 11 (Pesquisa) e 12 (Ordem). Logo, teremos que implementar estas ações. Mas antes tem uma parte do código que podemos simplificar.

Escondendo os botões de navegação

Na versão anterior do programa Agenda, os botões de navegação eram criados a partir do painel de visualização e edição de registros da agenda (oPanelCrud), e para esconder ou mostrar os botões de navegação, era necessário endereçar cada botão individualmente. Dessa forma, o fonte de ligar e desligar os botões de navegação ficaria assim:

// -------------------------------------------------
// Habilita ou desabilita os botões de navegação
// -------------------------------------------------
STATIC Function SetNavBtn(aBtns,lEnable)
IF lEnable
  aBtns[7]:Show() // Primeiro
  aBtns[8]:Show() // Anterior
  aBtns[9]:Show() // Proximo
  aBtns[10]:Show() // Ultimo
  aBtns[11]:Show() // Pesquisa
  aBtns[12]:Show() // Ordem
Else
  aBtns[7]:Hide() // Primeiro
  aBtns[8]:Hide() // Anterior
  aBtns[9]:Hide() // Proximo
  aBtns[10]:Hide() // Ultimo
  aBtns[11]:Hide() // Pesquisa
  aBtns[12]:Hide() // Ordem
Endif
Return

Agora, que os botões de navegação estão dentro de um painel, podemos simplesmente esconder ou mostrar o painel de navegação inteiro, apenas com uma instrução. E nós não precisamos sequer passar o objeto do Painel como parâmetro, veja a nova função abaixo:

STATIC Function SetNavBtn(aBtns,lEnable)
Local oPanel := aBtns[7]:oParent 
If lEnable
  oPanel:Show()
Else
  oPanel:Hide()
Endif
Return

Desta forma, pegamos o objeto apenas do sétimo botão — botão “Primeiro” — e através dele pegamos o objeto do componente onde ele foi criado usando a propriedade oParent. Assim, conseguimos esconder e mostrar o painel inteiro — e automaticamente todos os componentes criados dentro dele.

Podemos também, ao invés de esconder e mostrar o painel, podemos desabilitar o painel, de modo que o painel e seus componentes fiquem visíveis, mas não possam ser acionados. Neste caso, o fonte ficaria assim:

STATIC Function SetNavBtn(aBtns,lEnable)
Local oPanel := aBtns[7]:oParent
oPanel:SetEnable(lEnable)
Return

Definindo a ordem de consulta

Quando entramos na opção de consulta, a aplicação troca a ordem de navegação da tabela — usando a função DbSetOrder() — para ordem alfabética pelo nome do contato da agenda, usando o índice criado a partir do campo NOME. Quando confirmamos a inclusão de um novo registro, a ordem pode ser alterada para ID, caso seja necessário determinar o último número registrado na Agenda para incrementar e gerar o próximo ID.

Neste momento, vamos criar um recurso para permitir mudar por nossa conta a ordem de navegação da tabela em modo de consulta, mas antes precisamos mostrar em algum lugar qual a ordem que está sendo utilizada atualmente.

Painéis e alinhamentos

Vamos criar um painel superior, dentro da área usada para os campos do formulário, e dentro dele vamos colocar a informação da ordem em uso. Vamos aproveitar a possibilidade de criar um painel dentro de outro, para criar um painel central, onde hoje é criado o painel para CRUD, e dentro desse painel central, colocamos um painel com alinhamento superior, para colocar a informação sobre a ordenação do arquivo, e na área que sobrou, colocamos o painel do CRUD. A implementação ficaria dessa forma:

@ 0,0 MSPANEL oPanelMenu OF oDlg SIZE 70,600 COLOR CLR_WHITE,CLR_GRAY
oPanelMenu:ALIGN := CONTROL_ALIGN_LEFT

@ 0,0 MSPANEL oPanelNav OF oDlg SIZE 70,600 COLOR CLR_WHITE,CLR_GRAY
oPanelNav:ALIGN := CONTROL_ALIGN_RIGHT

@ 0,0 MSPANEL oPanelCenter OF oDlg SIZE 700,600 COLOR CLR_WHITE,CLR_LIGHTGRAY
oPanelCenter:ALIGN := CONTROL_ALIGN_ALLCLIENT

@ 0,0 MSPANEL oPanelOrd OF oPanelCenter SIZE 100,20 COLOR CLR_WHITE,CLR_BLUE
oPanelOrd:ALIGN := CONTROL_ALIGN_TOP

@ 0,0 MSPANEL oPanelCrud OF oPanelCenter SIZE 700,600 COLOR CLR_WHITE,CLR_LIGHTGRAY
oPanelCrud:ALIGN := CONTROL_ALIGN_ALLCLIENT

Reparem que os painéis oPanelCrud e oPanelOrd são criados a partir do oPanelCenter, que pega todo o espaço da DIALOG que sobrou depois de alinhar o painel oPanelMenu à esquerda e o oPanelNav a direita.

Se você pensar bem, criar um painel superior para mostrar a ordem de índice … não é um tiro de canhão para matar uma mosca ? Não necessariamente, afinal para eu criar este objeto TSAY na parte superior, dentro do painel do CRUD, seria necessário realinhar todos os componentes mais pra baixo, para abrir espaço para este TSAY. Da forma que foi feita, eu não precisei alterar nenhuma coordenada, apenas criei a sequência de painéis, e se amanhã eu achar que a ordem fica mais legal em baixo, basta eu mudar o alinhamento do painel, simples assim.

Mostrando a ordem de consulta atual

Dentro do painel oPanelOrd, vamos colocar um componente visual para informar a ordenação atual de consulta do arquivo. Neste caso, vamos usar um objeto tSay:

// Mostra Ordenação atual do arquivo de agenda
@ 5,5 SAY oSayOrd PROMPT " " SIZE 100,12 COLOR CLR_WHITE,CLR_BLUE OF oPanelOrd PIXEL
oSayOrd:SetText("Ordem .... "+ AGENDA->(IndexKey()))

Na abertura do programa, a tabela AGENDA é aberta com o ALIAS “AGENDA”, então para simplificar a implementação, eu apenas seto o texto do objeto tSay para colocar a chave de índice da ordem atual da tabela, obtida com a função IndexKey(). Logo, a nova tela de abertura da Agenda deve ficar assim:

Crud V2 Entrada

As trocas de ordem do arquivo são feitas dentro da função ManAgenda(), mas o componente de interface que mostra a ordem é um Objeto TSay, que não é passado como parâmetro. Logo, vamos passar ele como um novo parâmetro para a função ManAgenda(), e atualizá-lo quando necessário, usando a mesma fórmula anteriormente usada, mas sendo executada para pegar o estado atual.

Mudando a Ordem

Pensando novamente em solução SIMPLES, a ação do botão “Ordem” deve apenas trocar a ordem para ID ou NOME. Existem inúmeras formas de se fazer isso, porém como o objetivo é ser simples, e são apenas 2 ordens para escolher, eu optei pela implementação mais simples:

(...)
ElseIf nAction == 12 // Troca de Ordem
   IF ChangeOrd(oDlg)
      // Se a ordem foi trocada 
      // Atualiza texto com a chave do indice em uso 
      oSayOrd:SetText("Ordem .... "+ AGENDA->(IndexKey()))
   Endif
Else 
(...)

E, para permitir a escolha, a nova função ChangeOrd()

Static Function ChangeOrd(oDlg)
Local nOrdAtu := AGENDA->(IndexOrd())
Local nNewOrd := 0
If nOrdAtu == 1 
  If MsgYesNo("Deseja alterar para ordem de NOME ?")
    nNewOrd := 2
  Endif
Else
  If MsgYesNo("Deseja alterar para ordem de ID ?")
    nNewOrd := 1
  Endif
Endif
if ( nNewOrd > 0 ) 
  AGENDA->(DBSETORDER(nNewOrd))
  Return .T.
Endif
return .F.

Implementando a Busca sobre o índice

E, para finalizar, vamos fazer a busca rápida sobre o índice, na ação 11. Para isso, vamos perguntar ao operador do programa, o que ele procura. A ordem de busca usada será a ordem atual. Primeiro, dentro da função ManAgenda(), vamos inserir a execução da ação 11 — Pesquisa.

(...)
ElseIf nAction == 11 // Pesquisa Indexada

   // Realiza a busca pelo índice atual 
   PesqIndeX(oDlg)

   // Atualiza na tela o conteúdo do registro atual 
   ReadRecord(aGets)

Else
(...)

Agora, vamos implementar a funcionalidade de busca, acrescentando as duas funções abaixo:

STATIC Function PesqIndeX(oDlgParent)
Local oDlgPesq 
Local cTitle
Local cStrBusca
Local nTamanho
Local nRecSave 
Local lFound := .F.
Local cIndexFld := AGENDA->(Indexkey())
Local oGet1 , oBtn1

// Monta titulo da janela de pesquisa
cTitle := 'Pesquisa por '+ cIndexFld

// Guarda numero do registro atual 
nRecSave := AGENDA->(Recno())

If indexord() == 1 // Campo ID
  nTamanho := 6
  cStrBusca := space(nTamanho)
  cPicture := "@9"
ElseIf indexord() == 2 // Campo NOME
  nTamanho := 50
  cStrBusca := space(nTamanho)
  cPicture := "@!"
Endif

DEFINE DIALOG oDlgPesq TITLE (cTitle) ;
   FROM 0,0 TO 120,415 PIXEL;
   OF oDlgParent ; 
   COLOR CLR_BLACK, CLR_LIGHTGRAY

@ 05,05 GET oGet1 VAR cStrBusca PICTURE (cPicture) SIZE CALCSIZEGET(nTamanho) ,12 OF oDlgPesq PIXEL

@ 25,05 BUTTON oBtn1 PROMPT "Buscar" SIZE 60,15 ;
   ACTION IIF( SeekAgenda(cIndexFld,cStrBusca) , (lFound := .T. , oDlgPesq:End()) , oGet1:SetFocus() ) OF oDlgPesq PIXEL

ACTIVATE DIALOG oDlgPesq CENTER

If !lFound
   // Nao achou, volta ao registro antes da busca 
   AGENDA->(dbgoto(nRecSave))
Endif

Return

// Ajusta o valor informado na tela de acordo com o campo / indice 
// para fazer a busca corretamente
STATIC Function SeekAgenda(cIndexFld,cStrBusca)
IF cIndexFld == 'ID'
   cStrBusca := strzero(val(cStrBusca),6)
ElseIF cIndexFld == 'NOME'
   cStrBusca := alltrim(cStrBusca)
Endif
If !DbSeek(cStrBusca)
   MsgStop("Informação não encontrada.","Busca por ["+cStrBusca+"]")
   Return .F.
Endif
return .T.

Funcionamento e considerações

A função de busca utilizada  — DBSeek() — vai posicionar no primeiro registro que satisfazer a chave informada. No caso no NOME, pode ser informado apenas uma ou mais primeiras letras do nome, e se houver um nome que comece com estas letras, ele será o registro que vai ser trazido na tela.

Caso a informação não seja encontrada, será exibida uma mensagem, e o programa retorna para a tela de entrada de valor de busca, para você alterar o valor informado ou digitar um valor novo. Caso o registro seja encontrado, a janela fecha sozinha, e o registro encontrado é trazido na tela.

Conclusão

Daqui a pouco esse CRUD vira um produto … e ainda têm muito mais para ser explorado. Quer a versão atualizada desse código ? Acesse o GITHUB na URL https://github.com/siga0984/Blog e faça download do arquivo AGENDA.PRW — devidamente atualizado. Basta compilar, e chamar a função U_AGENDA diretamente no SmartClient.

Referências

 

Um comentário sobre “CRUD em AdvPL – Parte 08

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s