[Notes2-team] Infos e mais uma parte da viagem nos sources do Notes
Brought to you by:
andersonrb
From: Anderson R. B. <and...@po...> - 2004-08-27 04:31:45
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>ManipulandoOsEditores</title> </head> <body> <big><big><span style="font-weight: bold;">Uma viagem aos sources do Notes - parte III</span><br style="font-weight: bold;"> <span style="font-weight: bold;">Implementando novos comandos no editor</span></big></big><br> <br> Na terceira parte da nossa viagem aos sources do Notes, finalmente vamos explorar o editor! Melhor ainda, o texto inteiro estará "baseado em fatos reais" - enquanto escrevo isto, estamos há poucos dias do lançamento do alpha 3 do Notes 2004 e eu estarei implementando no Notes o que estarei ensinando você a fazer aqui.<br> <br> A feature que vamos implementar será uma bem conheciada dos editores de código: permitir ao usuário converter tabs para espaços e vice-versa. Criaremos duas novas ações de formatação do código que permitem ao usuário fazer isto. Na parte II você aprendeu a criar ações e colocá-las no menu, então não preciso explicar novamente como se faz isto. Eu criei duas ações ("actEditFormatSpacesToTabs" e "actEditFormatTabsToSpaces") e coloquei elas em Editar >> Formatar, junto com outras ações que estão ligadas a formatação do código.<br> <br> Após criadas as ações e os items dos menus, lembrei-me que tínhamos que desabilitar estas ações quando nada estivesse selecionado ou quando o texto fosse readonly. Ainda bem que o Notes, já tem como fazer isto de maneira bem fácil, bastou adicionar o código a seguir no "FormCreate" do formulário principal e estava pronto:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;"> <pre>// Habilita as ações apenas quando houver seleção e o texto não for readonly<br>ActionMan.ControlAction(actEditFormatTabToSpaces, [ecHasSelection, ecNotReadonly]);<br>ActionMan.ControlAction(actEditFormatSpacesToTabs, [ecHasSelection, ecNotReadonly]); </pre> </td> </tr> </tbody> </table> <br> Olhando o código da unit frm_Princ (que é onde está o form principal) vemos que as ações do menu editar não estão implementadas nela e sim em um objeto chamado "tabs". Este é um objeto da classe "TNotesTabsManager", o gerenciador de tabs do Notes. O objeto tabs permite criar novas tabs/editores, fechar tabs, etc. Há algum tempo o tamanho da Unit "frm_Princ.pas" era gigantesco e por isso refactorings foram feitos distribuindo todo aquele código para novas Units, como por exemlo a "NotesTabsManager.pas". Bom, olhando está unit descobrimos mais uma coisinha: na verdade também não é nela que os métodos estão implementados! Na verdade tudo que ela faz é verificar se um editor está ativo e repassar para o editor ativo a ação pedida lá no form principal. <br> <br> <span style="font-style: italic;">- Oh God! Para que esse passa-repassa de tarefas de uma unit para outra e para outra? Parece até fila do SUS! Isso vai deixar tudo mais lento!</span><br> <br> Em teoria, vai ficar mais lento. Na prática, o usuário nunca, nunca conseguirá perceber a diferença. Chamadas de métodos com até dois parâmetros são chamados sem precisar acessar a memória, pois os parâmetros são movidos diretamente para os registradores do processador, o que é absurdamente rápido. Se você não confia em mim, pergunte para qualquer guru de Delphi. Isto tudo tem uma vantagem, apesar de dar um poquinho mais de trabalho para nós: esse passa e repassa de tarefas cria layers entre um pedaço e outro do programa o que evita que você tenha que reescrever muito código para substituir um dos componentes (por exemplo, se fóssemos trocar de controle de edição, provavelmente não teríamos que reescrever muita coisa).<br> <br> Bom, então vamos para a Unit "NotesEditorTab.pas", pois é lá que toda a diversão acontece! Logo você vai perceber que opções semelhantes a que estamos implementando estão na classe TNotesEditorTab... não precisa ser um gênio pra adivinhar que é nela que deveremos colocar nossos novos métodos, não?! Colocamos o código abaixo na parte pública da classe:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;">{ Converte tabs para espaços }<br> procedure TabsToSpaces;<br> { Converte espaços para tabs }<br> procedure SpacesToTabs;<br> </td> </tr> </tbody> </table> <br> Pronto. Se você nunca escreveu classes/componentes no Delphi, provavelmente não sabe, mas o próximo passo pode ser dado pelo editor do Delphi. Use a tecla de atalho Ctrl+Shift+C para que o delphi crie o código inicial dos nossos métodos:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;">procedure TNotesEditorTab.SpacesToTabs;<br> begin<br> <br> end;<br> <br> procedure TNotesEditorTab.TabsToSpaces;<br> begin<br> <br> end;<br> </td> </tr> </tbody> </table> <br> A implementação dos métodos é bem simples:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;">procedure TNotesEditorTab.SpacesToTabs;<br> var<br> S: string;<br> begin<br> if fEditor.SelAvail then<br> begin<br> S:= StringOfChar(#32, fEditor.TabWidth);<br> fEditor.SelText:= StringReplace(fEditor.SelText, S, #9, [rfReplaceAll]);<br> end;<br> end;<br> <br> procedure TNotesEditorTab.TabsToSpaces;<br> var<br> S: string;<br> begin<br> if fEditor.SelAvail then<br> begin<br> S:= StringOfChar(#32, fEditor.TabWidth);<br> fEditor.SelText:= StringReplace(fEditor.SelText, #9, S, [rfReplaceAll]);<br> end;<br> end;<br> </td> </tr> </tbody> </table> <br> Para acessar o editor mesmo, usamos o campo fEditor (variável). O Notes usa o SynEdit como componente de edição. O TNotesEditor serve apenas como uma classe que agrupa uma Tab e um SynEditor (Editor + Tab = NotesEditorTab). Para aprender a usar o SynEdit não há outro modo senão dar uma olhada no código dele - infelizmente ele não vem com documentação alguma. (Porém alguns métodos e propriedades são bem semelhantes ao do TMemo e do TRichEdit.) A propriedade SelText que você vê acima permite manipular o texto selecionado. O que fazemos é criar uma string com espaços do tamanho de um caracter tab e trocar esta string pelo caracter tab ou vice-versa.<br> <br> Implementado isto, temos que implementar métodos na classe TNotesTabsManager que chamem nossos novos métodos. Eles ficariam assim:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;">{ Converte tabs para espaços }<br> procedure EditorTabsToSpaces(Sender: TObject);<br> { Converte espaços para tabs }<br> procedure EditorSpacesToTabs(Sender: TObject);<br> </td> </tr> </tbody> </table> <br> Note que temos uma novidade - o parâmetro Sender. Ele é necessário por que assinalaremos nossos métodos ao evento OnExecute das ações que criamos (lembra delas?). Use o truque do Ctrl+Shift+C e vejamos a implementação:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;">procedure TNotesTabsManager.EditorSpacesToTabs(Sender: TObject);<br> begin<br> if ActiveEditorTab <> nil then<br> ActiveEditorTab.SpacesToTabs;<br> end;<br> <br> procedure TNotesTabsManager.EditorTabsToSpaces(Sender: TObject);<br> begin<br> if ActiveEditorTab <> nil then<br> ActiveEditorTab.TabsToSpaces;<br> end;<br> </td> </tr> </tbody> </table> <br> Nós chamamos a função "ActiveEditorTab" para descobrirmos se há um editor ativo (se não houver, ela retornará nil). Depois usamos ela para acessar o TNotesEditorTab que está ativo e assim chamar os métodos recém-criados nesta classe. Esta função ActiveEditorTab pode ser acessada em qualquer unit do Notes que tenha no uses a unit "NotesGlobals".<br> <br> Estamos quase lá! Agora só precisamos associar estes novos méotodos ao evento OnExecute dentro do método Initialize (que é chamado quando o Notes está carregando mas já está visível ao usuário) do form principal:<br> <br> <table style="width: 100%; text-align: left; background-color: rgb(243, 243, 243);" border="0" cellpadding="2" cellspacing="2"> <tbody> <tr> <td style="vertical-align: top;"> actEditFormatTabToSpaces.OnExecute:= tabs.EditorTabsToSpaces;<br> actEditFormatSpacesToTabs.OnExecute:= tabs.EditorSpacesToTabs;<br> </td> </tr> </tbody> </table> <br> E por hoje era só pessoal! Para complementar o que você aprendeu hoje, dê uma olhada em todas estas units por quais passamos, principalmente nas partes comentadas :)<br> Anderson<br> <br> </body> </html> |