No artigo de hoje, vamos demonstrar como utilizar a classe FWCalendar para montar um calendário simples em AdvPL.
Se você já precisou montar um calendário em AdvPL, a classe FWCalendar é ideal para você, esse classe apesar de já ter sido descontinuada (faz parte do software legado segundo o TDN), ela ainda funciona bem para algumas necessidades.
Nesse exemplo que montei, ele é simples, mas é possível que você adapte as suas necessidades e até a gravação de registros em tabelas.
Abaixo um print da tela inicial assim quando a tela é aberta:
Se você clicar com o botão direito em um dia, ele dá a opção de criar um novo agendamento.
Será aberto uma tela simples, para você informar o cliente, a loja e uma descrição (não será armazenado nada no banco de dados).
Após a confirmação, será exibido o agendamento no dia, e se você quiser, pode clicar com o botão direito em cima dele (como não é salvo no banco de dados, só fica em tempo de execução, e não é gravado a descrição).
Partindo agora para o código fonte, abaixo alguns pontos importantes antes de vermos o código fonte:
- O calendário, é instanciando em um objeto chamado oCalend
- A função acionada pelo clique do botão direito é a fCliqueDir
- A função fCaledFont, converte a fonte padrão de MS Sans Serif para Tahoma na abertura da tela
- A função fMesAno, serve para definir o título de navegação (por exemplo, Maio / 2021) e também para acionar a função de busca das informações
- A função fBuscaInfo, aciona a busca de informações (nesse ponto se você quiser chamar uma query SQL para popular os dados), tanto que nessa parte, os registros são manipulados na parte do SetInfo
- Por último, a função fPopOpcao, mostra o ParamBox com os dados do registro e também salva no calendário (aqui você pode adaptar sua rotina para gravar com RecLock em uma tabela customizada)
Abaixo o código fonte desenvolvido:
//Bibliotecas
#Include "TOTVS.ch"
//Posições do array dos agendamentos do calendário
#Define ID 1 // Id do Celula
#Define OBJETO 2 // Objeto de Tela
#Define DATADIA 3 // Data Completa da Celula
#Define DIA 4 // Dia Ref. Data da Celula
#Define MES 5 // Mes Ref. Data da Celula
#Define ANO 6 // Ano Ref. Data da Celula
#Define NSEMANO 7 // Semana do Ano Ref. Data da Celula
#Define NSEMMES 8 // Semana do Mes Ref. Data da Celula
#Define ATIVO 9 // É celula referente a um dia ativo
#Define FOOTER 10 // É celula referente ao rodape
#Define HEADER 11 // É celula referente ao Header
#Define SEMANA 12 // É celula referente a semana
#Define BGDefault 13 // Cor de BackGround da Celula
/*/{Protheus.doc} User Function zCalend
Tela de agendamentos do Telemarketing
@type Function
@author Atilio
@since 27/01/2021
@version 1.0
@obs Calendário baseado no fonte TECA190D
/*/
User Function zCalend()
Local aArea := GetArea()
Private aSize := MsAdvSize(.F.)
fMontaTela()
RestArea(aArea)
Return
Static Function fMontaTela()
Local nCorFundo := 16777215
Local nLargBtn := 50
//Data
Private dDtIni := Date()
Private cMes := StrZero(Month(dDtIni), 2)
Private cAno := StrZero(Year(dDtIni), 4)
//Objetos e componentes
Private oDlgTmk
Private oFwLayer
Private oPanTitulo
Private oPanCalend
Private oPanPreMon
Private oPanNexMon
Private oPanSair
Private oMesAtual
Private cMesAno
Private cTitHtml
//Cabeçalho
Private oSayModulo, cSayModulo := 'FAT'
Private oSayTitulo, cSayTitulo := 'Calendário de Agendamentos'
Private oSaySubTit, cSaySubTit := 'Clique com o botão direito para registrar agendamentos'
//Tamanho da janela
Private nJanLarg := aSize[5]
Private nJanAltu := aSize[6]
//Fontes
Private cFontUti := "Tahoma"
Private oFontMod := TFont():New(cFontUti, , -38)
Private oFontSub := TFont():New(cFontUti, , -20)
Private oFontSubN := TFont():New(cFontUti, , -20, , .T.)
Private oFontBtn := TFont():New(cFontUti, , -14)
Private oFontSay := TFont():New(cFontUti, , -12)
//Variáveis usadas para atualização das informações
Private aInfoDia
Private nSelecao
Private cTextoSel
Private nPosCell
//Cria a janela
DEFINE MSDIALOG oDlgTmk TITLE "Agendamentos Telemarketing" FROM 0, 0 TO nJanAltu, nJanLarg PIXEL
//Criando a camada
oFwLayer := FwLayer():New()
oFwLayer:init(oDlgTmk,.F.)
//Adicionando 3 linhas, a de título, a superior e a do calendário
oFWLayer:addLine("TIT", 10, .F.)
oFWLayer:addLine("SUP", 05, .F.)
oFWLayer:addLine("CAL", 85, .F.)
//Adicionando as colunas das linhas
oFWLayer:addCollumn("HEADERTEXT", 050, .T., "TIT")
oFWLayer:addCollumn("BLANKBTN", 040, .T., "TIT")
oFWLayer:addCollumn("BTNSAIR", 010, .T., "TIT")
oFWLayer:addCollumn("BLANKSUP1", 015, .T., "SUP")
oFWLayer:addCollumn("BTNPREVMONTH", 020, .T., "SUP")
oFWLayer:addCollumn("TITLE", 030, .T., "SUP")
oFWLayer:addCollumn("BTNNEXTMONTH", 020, .T., "SUP")
oFWLayer:addCollumn("COLCAL", 100, .T., "CAL")
//Criando os paineis
oPanTitulo := oFWLayer:GetColPanel("TITLE", "SUP")
oPanCalend := oFWLayer:GetColPanel("COLCAL", "CAL")
oPanPreMon := oFWLayer:GetColPanel("BTNPREVMONTH", "SUP")
oPanNexMon := oFWLayer:GetColPanel("BTNNEXTMONTH", "SUP")
oPanSair := oFWLayer:GetColPanel("BTNSAIR", "TIT")
oPanHeader := oFWLayer:GetColPanel("HEADERTEXT", "TIT")
//Títulos e SubTítulos
oSayModulo := TSay():New(004, 003, {|| cSayModulo}, oPanHeader, "", oFontMod, , , , .T., RGB(149, 179, 215), , 200, 30, , , , , , .F., , )
oSayTitulo := TSay():New(004, 045, {|| cSayTitulo}, oPanHeader, "", oFontSub, , , , .T., RGB(031, 073, 125), , 200, 30, , , , , , .F., , )
oSaySubTit := TSay():New(014, 045, {|| cSaySubTit}, oPanHeader, "", oFontSubN, , , , .T., RGB(031, 073, 125), , 300, 30, , , , , , .F., , )
//Criando os botões
oBtnEnd := TButton():New(006, 001, "Fechar", oPanSair, {|| oDlgTmk:End()}, nLargBtn, 018, , oFontBtn, , .T., , , , , , )
//Cria o calendário
oCalend := FWCalendar():New( VAL(cMes), VAL(cAno) )
oCalend:aNomeCol := { 'Domingo' , 'Segunda' , 'Terça' , 'Quarta' , 'Quinta' , 'Sexta' , 'Sábado' , 'Semana'} //'Domingo' # 'Segunda' # 'Terça' # 'Quarta' # 'Quinta' # 'Sexta' # 'Sábado' # 'Semana'
oCalend:lWeekColumn := .F.
oCalend:lFooterLine := .F.
oCalend:bLClicked := {|| }
oCalend:bLDblClick := {|| }
oCalend:bRClicked := {|aInfo, oObj, nRow, nCol| fCliqueDir(aInfo, oObj, nRow, nCol) }
fCalendFont()
oCalend:Activate( oPanCalend )
//Criando o Say com o mês Atual
oMesAtual := TSay():New(0, 0, {|| }, oPanTitulo, , , , , , .T., 20, 20, , , , , , , , .T.)
oMesAtual:Align := CONTROL_ALIGN_ALLCLIENT
oMesAtual:nClrPane := nCorFundo
fMesAno(Val(cMes), Val(cAno))
//Criando o botão do Mês Anterior
@ 0, 0 BTNBMP oPrevMonth Resource "PMSSETAESQ" Size 80, 90 Of oPanPreMon Pixel
oPrevMontht:cToolTip := "Mes Anterior" //"Mes Anterior"
oPrevMonth:bAction := { || FwMsgRun(Nil, {|| fMudaMes(oPanCalend, oCalend, 2 )}, Nil, "Montando calendário...") } //"Montando calendário..."
oPrevMonth:Align := CONTROL_ALIGN_RIGHT
//Criando o botão do Próximo Mês
@ 0, 0 BTNBMP oNextMonth Resource "PMSSETADIR" Size 90, 90 Of oPanNexMon Pixel
oNextMonth:cToolTip := "Proximo Mes" //"Proximo Mes"
oNextMonth:bAction := { || FwMsgRun(Nil, {|| fMudaMes(oPanCalend, oCalend, 1 )}, Nil, "Montando calendário...") } //"Montando calendário..."
oNextMonth:Align := CONTROL_ALIGN_LEFT
Activate MsDialog oDlgTmk Centered
Return
/*
Função que muda de mês
*/
Static Function fMudaMes(oPan, oCalend, nOp)
Local nMonth := oCalend:nMes
Local nYear := oCalend:nAno
Default nOp := 1
//Se for a seta ->, incrementa um mês
If nOp == 1
If nMonth == 12
nMonth := 01
nYear += 1
Else
nMonth := nMonth += 1
EndIf
//Se for a seta <-, diminui um mês
ElseIf nOp == 2
If nMonth == 01
nMonth := 12
nYear -= 1
Else
nMonth := nMonth -= 1
EndIf
EndIf
//Define o calendário e seta o título
oCalend:SetCalendar( oPan, cValToChar(nMonth), cValToChar(nYear) )
fMesAno(nMonth, nYear)
Return
/*
Função que define o texto do título em cima do calendário
*/
Static Function fMesAno(nMonth, nYear)
cMesAno := Capital(MesExtenso(nMonth)) + " / " + cValToChar(nYear)
cTitHtml := fTitHTML(cMesAno)
oMesAtual:SetText( cTitHtml )
//Chama a busca de informações para definir as informações no calendário
fBuscaInfo()
Return Nil
/*
Função que transforma o título no formato html
*/
Static Function fTitHTML(cMesAno)
Local cRet := ''
cRet += '<p align="center">'
cRet += '<font face="' + cFontUti + '" color="#000000" style="font-size:14px"><strong>' + cMesAno + '</strong></font>'
cRet += '</p>'
Return cRet
/*
Função que define o primeiro calendário com a fonte Tahom
*/
Static Function fCalendFont()
oCalend:aFontDay[1] := cFontUti
oCalend:aFontDayHead[1] := cFontUti
oCalend:aFontDayText[1] := cFontUti
oCalend:aFontFooter[1] := cFontUti
oCalend:aFontFsFer[1] := cFontUti
oCalend:aFontHeader[1] := cFontUti
oCalend:aFontOff[1] := cFontUti
oCalend:aFontToday[1] := cFontUti
oCalend:aFontWeek[1] := cFontUti
oCalend:cHtmlDay := StrTran(oCalend:cHtmlDay, "MS Sans Serif", cFontUti)
oCalend:cHtmlDayOff := StrTran(oCalend:cHtmlDayOff, "MS Sans Serif", cFontUti)
oCalend:cHtmlFooter := StrTran(oCalend:cHtmlFooter, "MS Sans Serif", cFontUti)
oCalend:cHtmlHeader := StrTran(oCalend:cHtmlHeader, "MS Sans Serif", cFontUti)
oCalend:cHtmlToday := StrTran(oCalend:cHtmlToday, "MS Sans Serif", cFontUti)
oCalend:cHtmlWeek := StrTran(oCalend:cHtmlWeek, "MS Sans Serif", cFontUti)
oCalend:cHtmlWeekend := StrTran(oCalend:cHtmlWeekend, "MS Sans Serif", cFontUti)
Return
/*
Função que busca as informações e atualiza a agenda
*/
Static Function fBuscaInfo()
Local nCell
Local nDia
For nCell := 1 To (Len(oCalend:aCell) - 10)
nDia := oCalend:aCell[nCell][DIA] // Dia
//nMes := oCalend:aCell[nCell][MES] // Mês
//nAno := oCalend:aCell[nCell][ANO] // Ano
//Se for um dia útil
If oCalend:aCell[nCell][ATIVO] .And. nDia == 10
aItens := {}
aAdd(aItens, "000001 - Cliente A")
aAdd(aItens, "000002 - Cliente B")
aAdd(aItens, "000003 - Cliente C")
//Define as informações da célula
oCalend:SetInfo(oCalend:aCell[nCell][ID], aClone(aItens))
EndIf
Next
Return
/*
Função que mostra PopUp, ao clicar com o botão direito
*/
Static Function fCliqueDir(aInfo, oObj, nRow, nCol)
Local cClassName := Upper(Alltrim(oObj:ClassName()))
Local oMenu
Local oMenuItem := {}
Local aOpcoes := {}
Local nOpcao := 0
Local dData := aInfo[DATADIA]
aInfoDia := aInfo
nSelecao := aInfo[OBJETO]:nSelectedIndex
cTextoSel := ""
nPosCell := aScan(oCalend:aCell, {|x| AllTrim(Upper(x[1])) == aInfo[1]})
//Somente se estiver dentro do ListBox
If cClassName == "TLISTBOX"
aAdd(aOpcoes, {"Novo Agendamento", {|| fPopOpcao(3, dData)}})
//Se houver linhas, terá outras opções
If nSelecao != 0
aAdd(aOpcoes, {"Visualizar Agendamento", {|| fPopOpcao(2, dData)}})
aAdd(aOpcoes, {"Alterar Agendamento", {|| fPopOpcao(4, dData)}})
aAdd(aOpcoes, {"Excluir Agendamento", {|| fPopOpcao(5, dData)}})
cTextoSel := aInfo[OBJETO]:oListBoxContent:aItems[nSelecao]
Endif
EndIf
//Criando o menu e os itens
MENU oMenu POPUP
For nOpcao := 1 To Len(aOpcoes)
aAdd( oMenuItem, MenuAddItem(aOpcoes[nOpcao][1], , , .T., , , , oMenu, aOpcoes[nOpcao][2], , , , , {|| .T.}) )
Next
ENDMENU
oMenu:Activate(nRow, nCol, oObj)
Return
Static Function fPopOpcao(nOpcao, dData)
Local aPergs := {}
Local cTexto := ""
Local cEditCli := ".F."
Local cCliente := Space(TamSX3('A1_COD')[1])
Local cLoja := Space(TamSX3('A1_LOJA')[1])
Local cObserv := ""
Default nOpcao := 3
Default dData := Date()
//Define o texto
If nOpcao == 3
cEditCli := ".T."
cTexto := "Inclusão de Agendamento"
Else
cEditCli := ".F."
If nOpcao == 2
cTexto := "Visualização de Agendamento"
ElseIf nOpcao == 4
cTexto := "Alteração de Agendamento"
ElseIf nOpcao == 5
cTexto := "Exclusão de Agendamento"
EndIf
cCliente := SubStr(cTextoSel, 1, 6)
cLoja := "01"
cTexto += " (" + SubStr(cTextoSel, 10, Len(cTextoSel)) + ")"
EndIf
//Adiciona os parâmetros
aAdd(aPergs, {09, cTexto, 200, 40, .T.})
aAdd(aPergs, {01, "Data", dData, "", ".T.", "", ".F.", 80, .T.})
aAdd(aPergs, {01, "Cliente", cCliente, "", ".T.", "SA1", cEditCli, 80, .T.})
aAdd(aPergs, {01, "Loja", cLoja, "", ".T.", "", cEditCli, 80, .T.})
aAdd(aPergs, {11, "Histórico", cObserv, ".T.", ".T.", .T.})
//Se a pergunta for confirmada
If ParamBox(aPergs, "Informe os parâmetros", , , , , , , , , .F., .F.)
cCliente := MV_PAR03
cLoja := MV_PAR04
cObserv := MV_PAR05
cNomeCli := Posicione('SA1', 1, FWxFilial('SA1') + cCliente + cLoja, "A1_NOME")
//Se for inclusão, adiciona no calendário
If nOpcao == 3
aItens := aClone(aInfoDia[OBJETO]:oListBoxContent:aItems)
aAdd(aItens, cCliente + " - " + SubStr(cNomeCli, 1, 15))
oCalend:SetInfo(oCalend:aCell[nPosCell][ID], aClone(aItens))
//Se for exclusãoRetira o elemento do array e depois define no calendário
ElseIf nOpcao == 5
aItens := aClone(aInfoDia[OBJETO]:oListBoxContent:aItems)
aDel(aItens, nSelecao)
aSize(aItens, Len(aItens) - 1)
oCalend:SetInfo(oCalend:aCell[nPosCell][ID], aClone(aItens))
EndIf
EndIf
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.




Boa tarde, tudo bem? vc teria algum exemplo usando dados de uma tabela? agradeço muito pelos conteúdos, nos ajuda bastante.
Bom dia Alessandra, tudo joia graças a Deus e você?
Primeiramente, obrigado pelo feedback, é bondade e generosidade sua.
Então, nós já fizemos com banco de dados em alguns clientes, mas não temos ainda um exemplo didático para publicar.
Vamos adicionar aqui nas sugestões de pautas futuras.
Tenha uma ótima e abençoada quarta feira.
Um forte abraço.