[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>
|