Construção do Editor de Textos

Vamos dar os primeiros passos na construção de um Editor de Textos.
Clique em File e depois em New Application.
Pegu
e um componente RichEdit na palleta Win32 (ou Win95) e a coloque no Form.
Altere a propriedade BorderStyle do Form para bsNone (para tirar a barra de títulos e suas propriedades).
Altere a propriedade Align do RichEdit para alCliente (Assim o RichEdit ocupará toda a área cliente do Form).
Na palleta Standart pegue um componente PopudMenu (para criarmos alguns menus pop-up), e coloque no Form.
Dê um clique com o botão direito do mouse sobre o componente PopupMenu e escolha a opção Menu Designer ...
Abrir-se-á o Menu Designer (Editor de Menus). Na propriedade Caption (no Object Inspector) do Editor digite:Arquivo

Dê um clique na parte selecionada do Menu Designer. Aparecerá a palavra Arquivo selecionada no menu flutuante, e abrir-se-á, logo abaixo, um novo campo para menu.
Dê um clique nesse novo campo (que ficará selecionado) e, na propriedade Caption, digite:
Editar
Muito bem, já sabemos criar o Menu Popup.
Agora, no Menu Designer, onde tem a palavra Arquivo dê um clique para selecionar o menu correspondente. Depois, com o botão direito do mouse, dê um clique sobre o menu Arquivo já selecionado. Aparecerá um menu Pop-up. Escolha a opção Create Submenu.
Na propriedade Caption digite:&Abrir

O Símbolo & cria um atalho (tecla de atalho + a letra sublinhada) para o menu. Abaixo do menu Abrir, Crie o menu Locali&zar; abaixo do menu Localizar crie o menu &Salvar; abaixo do menu Salvar crie o traço separador de menus digitando apenas um traço: - ;abaixo do traço, crie o menu Sai&r.

Nota: quando criamos atalho, tomemos cuidado para não usar as mesmas teclas de atalho para funções diferentes.

 

Construção do Editor de Textos
Segunda parte
Agora dê um duplo clique no menu Sair. Abrir-se-á o Editor Properties que é o Editor de Códigos do programa. Digite:
Close();
O Código deve ficar assim:

void __fastcall TForm1::Sair1Click(TObject *Sender)
{
Close();
}

Para ativar o PopupMenu, no evento OnCreate do Form1 digite:
PopupMenu1->AutoPopup = true;
RichEdit1->PopupMenu = PopupMenu1;
O Código deve ficar assim:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
        PopupMenu1 -> AutoPopup = true;
        RichEdit1 -> PopupMenu = PopupMenu1;
}

Rode o Programa (Tecla F9). Você perceberá que criou um Editor de Textos bem estranho. Digite ou delete letras e palavras.

Para fechar o programa, dê um clique com o botão direito do mouse sobre o Editor e escolha a opção Sair no menu Arquivo.

Se você seguiu os passos corretamente, e o programa fechou sem problemas, na propriedade WindowState do Form1 escolha a opção: wsMaximized, e rode novamente o programa. Ele ocupará toda a tela do Windows. Siga o mesmo procedimento para encerrar o programa. Uma outra forma de encerrar o aplicativo é pressionando simultaneamente as teclas alt + F4.

 

Dê um duplo clique na propriedade Lines de RichEdit1. No editor que se abrir, delete a palavra RichEdit1 e dê um OK, o que fará que essa string não apareça mais na execução do programa.
Na palette Dialogs, pegue um componente OpenDialog e coloque no projeto. Na propriedade InitialDir de OpenDialog podemos digitar o caminho do diretório onde queremos que a Caixa de Diálogo Abrir inicialize (por exemplo: C:\Meus documentos) e, feito isso, podemos completar o caminho e escolher um arquivo inicial na propriedade FileName (por exemplo: C:\Meus documentos\Tutorial.txt). Na propriedade Filter, nós podemos escolher os arquivos que o Editor pode abrir, por meio do Filter Editor.
No Filter Editor digite:

Filter Name

Filter

arquivos richtext *.rtf

*.rtf

arquivos de texto *.txt

*.txt

todos os arquivo *.*

*.*

e dê um OK.
Abra o MenuDesigner e dê um duplo clique no submenu Abrir para abrir o Editor Properties.
Entre as chaves { e } digite:

if (OpenDialog1 -> Execute())
RichEdit1 -> Lines -> LoadFromFile(OpenDialog1 -> FileName);
else
MessageBeep(0);
O código deverá ficar assim:

 

void __fastcall TForm1::Abrir1Click(TObject *Sender)
{
        if (OpenDialog1 -> Execute())
          RichEdit1 -> Lines -> LoadFromFile(OpenDialog1 -> FileName);
          else
        MessageBeep(0);
}

 

Nosso Editor já está em condições de abrir documentos. Rode o programa e abra alguns documentos de texto (*.txt) ou richtext (*.rtf).

 

Passemos ao submenu Localizar.
Coloque um componente FindDialog no projeto. Em seguida abra o Menu Designer e dê um duplo clique em Localizar. No Editor Properties, entre as chaves { e } que se abrirem, digite:

 

   FindDialog1->Execute();
   int FoundAt, StartPos, ToEnd; // declara três variáveis int

////////////////////////////////////////////////////////////////////////
// INICIA A BUSCA A PARTIR DA SELEÇÃO ATUAL
// OU DE OUTRA FORMA
// INICIA A PARTIR DO INÍCIO DO TEXTO

   /*SelLength devolve o número de caracteres selecionados*/
    if (RichEdit1->SelLength) /* se há caracteres selecionados em RichEdit1*/
   /*SelStart devolve a posição do primeiro caracter selecionado no texto*/
   /*o código abaixo devolve a posição do último caracter selecionado*/
    StartPos = RichEdit1->SelStart + RichEdit1->SelLength;
  else
    StartPos = 0;
///////////////////////////////////////////////////////////////////////////

    /*Length() retorna o número de caracteres de AnsiString*/
    /*Ou ToEnd será igual a Text.Lengt() ou igual ao número dos
    caracteres a serem contados após o último caracter selecionado*/
    ToEnd = RichEdit1->Text.Length() - StartPos;
    /*FindText busca o texto num controle rich edit para a string
     especificada no parametro SearchStr (no exemplo: FindDialog1->FindText)*/
    FoundAt = RichEdit1->FindText(FindDialog1->FindText,
                                            StartPos, ToEnd, TSearchTypes());
  if (FoundAt != -1) // se a variável for difetente de -1
    RichEdit1->SetFocus(); // coloca o foco em RichEdit1
    // coloca o cursor na primeira ocorrência
    RichEdit1->SelStart = FoundAt;
    // seleciona a string procurada no texto do RichEdit1
    RichEdit1->SelLength = FindDialog1->FindText.Length();

 

No Object Inspector, no evento (Events) OnFind de FindDialog1 escolha a opção Localizar1Click. Em Properties, dê um duplo clique em Options. Altere de false para true as seguintes opções:

 

frHideMatchCase

true

frHideWholeWord

true

frHideUpDown

true

Agora seu editor já se encontra apto para efetuar algumas buscas de textos ou caracteres.

 

Coloque um componente SaveDialog no projeto, abra o Menu Designer e dê um duplo clique em Salvar. No Editor Properties, entre as chaves { e } que se abrirem, digite:

 

    if (SaveDialog1 -> Execute())
        RichEdit1 -> Lines -> SaveToFile(SaveDialog1 -> FileName);
    else
        MessageBeep(0);

 

Em SaveDialog, temos funcionando de forma semelhante ao funcionamento em OpenDialog, as propriedades InitialDir, FileName e Filter, com a diferença de que, no presente caso, estamos cuidando de salvar arquivos, e não abri-los.

 

Como experiência, no Filter Editor digite:

 

Filter Name

Filter

arquivos richtext *.rtf

*.rtf

arquivos de texto *.txt

*.txt

todos os arquivo *.*

*.*

 

e escolha um diretório inicial para salvar os arquivos.

 

Pois bem, todos os submenus do menu Arquivo já estão funcionando. Agora vamos criar mais alguns menus e submenus.
Selecione o menu Editar no Menu Designer e crie os seguintes submenus: Cortar, Copiar, Colar, Deletar e Selecionar Tudo.
Vamos aos códigos:

submenu Cortar:

void __fastcall TForm1::Cortar1Click(TObject *Sender)
{
  TComponent *pComponent = PopupMenu1->PopupComponent;
      if (pComponent)
         {
           if (pComponent->ClassNameIs("TRichEdit"))
              ((TRichEdit *)pComponent)->CutToClipboard();
           else
               MessageBeep(0);
         }
}

submenu Copiar

void __fastcall TForm1::Copiar1Click(TObject *Sender)
{
  TComponent *pComponent = PopupMenu1->PopupComponent;
      if (pComponent)
         {
           if (pComponent->ClassNameIs("TRichEdit"))
              ((TRichEdit *)pComponent)->CopyToClipboard();
           else
               MessageBeep(0);
         }
}

submenu Colar

void __fastcall TForm1::Colar1Click(TObject *Sender)
{
  TComponent *pComponent = PopupMenu1->PopupComponent;
      if (pComponent)
         {
           if (pComponent->ClassNameIs("TRichEdit"))
              ((TRichEdit *)pComponent)->PasteFromClipboard();
           else
               MessageBeep(0);
         }
}

Agora vamos trabalhar com um código mais simples nas funções Deletar e Selecionar Tudo.

submenu Deletar

void __fastcall TForm1::Deletar1Click(TObject *Sender)
{
        RichEdit1 -> ClearSelection();
}

submenu Selecionar Tudo

void __fastcall TForm1::SelecionarTudo1Click(TObject *Sender)
{
        RichEdit1 -> SelectAll();
}

 

Exercício: Pesquise TComponent no Help do C++Builder e procure entender seu papel nas funções que usaram-no. Depois insira comentários documentando detalhadamente os códigos acima.

 

Abaixo do menu Editar, crie o menu Formatar com dois submenus: Fontes e Cores.
submenu Fontes

Coloque um componente FontDialog no projeto.

Eis o código:

 

void __fastcall TForm1::Fontes1Click(TObject *Sender)
{       /*coloca os atributos do texto selecionado no RichEdit1
        em FontDialog1*/
        FontDialog1->Font->Assign(RichEdit1->SelAttributes);
        /*Sendo chamada a caixa de diálogo de fontes*/
        if(FontDialog1->Execute())
        /*as fontes selecionadas de RichEdit1 receberão os atributos
        da caixa de diálogos de fontes*/
                RichEdit1->SelAttributes->Assign(FontDialog1->Font);
}

 

Nota: TtextAttributes::Assign

Coloca as propriedades de um objeto TTextAttributes em conformidade com as propriedades especificadas em outro objeto TTextAttributes ou num objeto TFont.

Use Assign para mudar todos os atributos de texto simultaneamente. Assign pode colocar as características das fonts selecionadas num texto em conformidade com as características default da font ou vice-versa. Quando o início é um objeto TTextAttributes, Assign adapta somente Color, Name, Style, e propriedades Pitch. Quando o início é um objeto TFont, Assign também adapta o Size.

Nota: Assign somente substitui em tempo de execução quando ´início é um objeto TTextAttributes, um objeto TFont, ou um objeto que tenha implementado um método AssignTo que trate o objeto TTextAttributes. Outras origens produzem exceções de erro.

Coloque um componente ColorDialog no projeto.

Eis o código:

void __fastcall TForm1::Cores1Click(TObject *Sender)
{
   if (ColorDialog1 -> Execute())
   /*a cor de RichEdit1 será a cor escolhida em ColorDialog1*/
      RichEdit1 -> Color = ColorDialog1 -> Color;
}

O código de Cores1Click não é subsistente. Ou seja, se você alterar a cor do editor de textos e fechar o aplicativo, na próxima vez que abrir o editor de textos, ele abrirá na cor padrão, estabelecida inicialmente no Object Inspector. Futuramente nós veremos um modo de conservar a cor atrávés de arquivos .INI.

Vamos alterar algumas propriedades:
Para colocar uma barra de rolagem vertical, altere a propriedade ScrollBarss de RichEdit1 para ssVertical.
Normalmente os componentes Memo e RichEdit têm uma limitação na quantidade de dados que podem receber, que é uma quantia padrão. Podemos aumentá-la ou diminui-la bastante. Na verdade, tudo depende da quantidade de dados que trabalharemos. Para alterar essa quantia de dados devemos mudar a propriedade MaxLength (no Object Inspector ou em tempo de execução). Por exemplo, se colocarmos a propriedade MaxLength para 2147483645 (que é um tamanho praticamente incomensurável) tais componentes poderão receber dois bilhões, cento e quarenta e sete milhões, quatrocentos e oitenta e três mil, seiscentos e quarenta e cinco caracteres.
No momento, nosso projeto deve estar com esta aparência:

Exercícios:

1) O submenu Localizar deve ser retirado do menu Arquivo e colocado no menu Editar;

2) Falta uma função que ative a impressora para imprimir textos. Providencie a colocação do componente e do código correspondente.

3) Coloque atalhos nos menus. Use as propriedades ShortCut correspondentes.

Feito isso, continuemos. Vamos dar ao nosso Editor a possibilidade de alterar seu tamanho.
Abra o MenuDesigner e, abaixo do menu Formatar, crie o menu Janela que deverá conter os seguintes submenus: Maximizar, Normal, Minimizar.
Vamos aos códigos:

void __fastcall TForm1::Maximizar1Click(TObject *Sender)
{
BorderStyle = bsNone;
WindowState = wsMaximized;
}

//------------------------------------------------------
void __fastcall TForm1::Normal1Click(TObject *Sender)
{
BorderStyle = bsNone;
WindowState = wsNormal;
}

//------------------------------------------------------
void __fastcall TForm1::Minimizar1Click(TObject *Sender)
{
BorderStyle = bsSizeable;
WindowState = wsMinimized;
}

Execute o programa e experimente todos os menus. Observe se todos os comandos estão obedecendo perfeitamente. Caso haja algum problema, dê uma olhada se não digitou algum código errado.

Agora vamos deixá-lo mais parecido com aqueles editores que estamos acostumados a trabalhar. Em primeiro lugar, altere o nome do submenu Salvar para Salvar Como. Feito isso, dê um clique com o botão direito do mouse sobre o submenu Salvar Como e, em seguida, um clique em insert. Abrir-se-á um espaço para um novo submenu. Dê-lhe o nome de Salvar e, como exercício, digite para ele o código que o usuário salva o texto automaticamente sem chamar a caixa de diálogo Salvar Como (caso o arquivo já esteja gravado em disco). Caso você não consiga, não se preocupe. Em lições futuras estudaremos esse evento.

Abra novamente o Menu Designer e crie o menu Visualizar, com os seguintes submenus: Barra de títulos, Barra de Ferramentas e Régua. Vamos ao primeiro código:

void __fastcall TForm1::BarradeTtulos1Click(TObject *Sender)
{
if(BorderStyle == bsSizeable)
BorderStyle = bsNone;
else
BorderStyle = bsSizeable;
}

Vamos colocar uma Barra de Ferramentas e uma Régua em nosso Editor. Primeiramente coloque um componente TPanel (Panel), da página Standard no Form. Desse componente, no Object Inspector, delete qualquer caracter da propriedade Caption, altere a propriedade Align para alTop e a Propriedade Visible para false. Feito isso, eis o código do menu correspondente:

void __fastcall TForm1::BarradeFerramentas1Click(TObject *Sender)
{
if(Panel1->Visible == true)
Panel1->Visible = false;
else
Panel1->Visible = true;
}

Após terminar de digitar o código, coloque um segundo TPanel no Form. As Propriedades são rigorosamente as mesmas: Caption = “”; Align = alTop e Visible = false. Você perceberá que esse segundo Panel se instalará abaixo do primeiro (de Panel1). Eis o código:

void __fastcall TForm1::Rgua1Click(TObject *Sender)
{
if(Panel2->Visible == true)
Panel2->Visible = false;
else
Panel2->Visible = true;
}

O primeiro Panel será usado para construírmos a Barra de Ferramentas e o segundo, a régua.
Rode a aplicação e experimente o resultado parcial.

O C++Builder possui componentes Buttons que podem carregar imagens. Trata-se dos SpeedButtons e BitBtns. Para colocar imagens nesses botões, basta editar sua propriedade Glyph no Object Inspector. As imagens a seguir, tiradas dos próprios exemplos do C++Builder, sendo que algumas delas podem ser editadas para ser carregadas no botão adequado do nosso editor de textos.



Caso você ainda não tenha providenciado, vamos agora estudar como podemos colocar um botão (ou menu) Salvar em nosso Editor. Antes, porém, certifique-se de ter providenciado a alteração solicitada no final da seção: Construção do Editor de Textos - Nona parte.

Dificuldade: Quando o botão for pressionado (ou o menu respectivo receber o evento correspondente), o programa deverá fazer uma verificação para saber se o arquivo já se encontra gravado em disco; em caso afirmativo, deverá conhecer o caminho completo para salvar o arquivo; em caso negativo, deverá chamar a caixa de diálogos Salvar Como. Comecemos trabalhando com os menus. Vá para o Editor de Códigos e lhe dê um clique com o botão direito do mouse. No menu pop-up que se abrir, escolha a opção: Open Source/Header File, ou simplesmente pressione as teclas Ctrl + F6 simultaneamente. Abrir-se-á o arquivo de cabeçalho Unit1.h. Na parte de baixo do arquivo, Depois da linha que inicia as declarações privadas da classe, declare duas AnsiString que servirão para guardar os caminhos dos arquivos que forem abertos ou salvos, respectivamente. Eis as declarações.

private:        // User declarations
        AnsiString Caminho_Abrir;
        AnsiString Caminho_Salvar;
public:         // User declarations
Nota: Futuramente entenderemos melhor a finalidade deste arquivo.

Agora vamos implementar algumas modificações nos códigos dos eventos Abrir e Salvar Como do nosso Editor de Textos:

void __fastcall TForm1::Abrir1Click(TObject *Sender)
{
        if (OpenDialog1 -> Execute())
        {
                RichEdit1 -> Lines -> LoadFromFile(OpenDialog1 -> FileName);

                //Caminho_Abrir atribuída com caminho do último arquivo aberto
                Caminho_Abrir = OpenDialog1->FileName;
                //atribui Caminho_Abrir para Caminho_Salvar (para ficarem iguais)
                Caminho_Salvar = Caminho_Abrir;
        }
        else
                MessageBeep(0);
}
//---------------------------------------------------------------------------

void __fastcall Tform1::Salvar1Click/*atual Salvar Como*/(TObject *Sender)
{
   if (SaveDialog1 -> Execute())
   {
        RichEdit1 -> Lines -> SaveToFile(SaveDialog1 -> FileName);

        // Caminho_Salvar atribuída com caminho do último arquivo salvo
        Caminho_Salvar = SaveDialog1->FileName;
        // atribui Caminho_Salvar para Caminho_Abrir (para ficarem iguais)
        Caminho_Abrir = Caminho_Salvar;
   }
   else
        MessageBeep(0);
}
//---------------------------------------------------------------------------
Agora basta digitarmos o código para o menu salvar:
void __fastcall TForm1::Salvar2Click/*Salvar*/(TObject *Sender)
{
     // se o arquivo aberto já foi salvo ...
     if((Caminho_Abrir == Caminho_Salvar) && (Caminho_Salvar != ""))
     // o arquivo será salvo sem chamar a caixa Salvar Como
             RichEdit1->Lines->SaveToFile(Caminho_Abrir);
     else // senão
             // a caixa de diálogos Salvar Como será Chamada 
             Salvar1Click(Sender);
}
Vamos agora suspender, por algum tempo, a construção do Editor de Textos, a fim de abordarmos alguns pontos muito importantes. Brevemente retornaremos às lições.

 

Dando continuidade à construção de nosso Editor de Textos, vamos providenciar alguns botões (SpeedButtons) que realizarão as mesmas tarefas realizadas pelos menus correspondentes. Esses botões devem ser agrupados. Por exemplo, um primeiro grupo de botões realizará as mesmas tarefas executadas pelos principais menus normalmente inseridos no menu Arquivo (Novo, Abrir, Salvar e Salvar Como). Depois disso, deixamos um pequeno espaço entre os botões para iniciar o segundo grupo (correspondente ao menu Editar) e assim sucessivamente você colocará aquelas tarefas que você entende como principais e, portanto, obrigatoriamente disponíveis em menus e em botões. Veja abaixo uma ilustração de como poderiam se dar tais agrupamentos:

A partir daqui (referente à barra de ferramentas), deixaremos você um pouco mais à vontade com relação ao layout do Editor. Ou seja, Você decidirá quais botões serão criados (tarefas desejadas). Mesmo porque estaremos trabalhando de uma forma um tanto repetitiva. Forneceremos código para tarefas que você ainda não conhece, bem como do botão Abrir. Os demais, seguirão o mesmo caminho, baseando-se nos códigos prontos dos menus equivalentes. As imagens para os botões você poderá escolher livremente, tomando o cuidado de escolher aquelas que se encaixam no perfil da tarefa a ser executada e no tamanho do botão.

Se você quiser dar uma aparência tridimensional nos botões (SpeedButtons) altere a propriedade Flat para True.

Nota: Toolbars oferecem um modo fácil de organizar e administrar controles visuais. Podemos criar uma barra de botões com um componente panel e alguns speed buttons, ou simplesmente usar o componente ToolBar. Para maiores esclarecimentos, clique aqui

Eis o código para a opção Novo (no caso um menu) que pode ser colocado no grupo Arquivo:

void __fastcall TForm1::Novo1Click(TObject *Sender)
{
ShellExecute(0, "open", Application->ExeName.c_str(), 0, 0, SW_SHOW);
}

Vamos colocar o código do botão Abrir, o qual alteramos a propriedade Name para SpButtonAbrir:

void __fastcall TForm1::SpButtonAbrirClick(TObject *Sender)
{
 Abrir1Click(Sender);
}

Exercício: Baseando-se no código acima, coloque o código dos outros botões.

Também podemos colocar um comando (menu e/ou botão) que desfaz a última ação do usuário (o famoso Ctrl + z). Eis o código:

void __fastcall TForm1::Desfazer1Click(TObject *Sender)
{
  SendMessage(RichEdit1->Handle, WM_UNDO, 0, 0);
}

Caso você não tenha conseguido inserir um código para imprimir, veja um exemplo:

/*Envia o Text do RichEdit para a impressora imprimeir*/
if ( PrintDialog1->Execute() ) RichEdit1->Print(RichEdit1->Text);

Assumimos que você já se encontra apto a completar o códigos da maioria dos botões da Barra de Ferramentas (os estudados nos menus).

Nenhum Editor seria completo sem as opções de alinhamento de texto. Vamos possibilitar, ao nosso Editor, o alinhamento à esquerda, ao centro e à direita. Para tanto, devemos colocar três SpeedButton na Barra de Ferramentas. Esses botões devem trabalhar em conjunto, pois quando o evento de um estiver produzindo efeitos, os dos outros dois deverão permanecer inativados. Assinale a Propriedade GroupIndex de todos eles para 1. Isso fará que eles pertençam ao mesmo grupo. Assinale, também, a propriedade Down do Botão Responsável pelo alinhamento à esquerda para True. Dessa forma, esse alinhamento à esquerda será padrão para o Editor.

Eis os códigos para os SpeedButton já com os novos nomes (Name):

void __fastcall TForm1::SpdBtAlinhEsqClick(TObject *Sender)
{
RichEdit1->Paragraph->Alignment = taLeftJustify;
}

//------------------------------------------------------
void __fastcall TForm1::SpdBtAlinhCentClick(TObject *Sender)
{
RichEdit1->Paragraph->Alignment = taCenter;
}

//------------------------------------------------------
void __fastcall TForm1::SpdBtAlinhDirClick(TObject *Sender)
{
RichEdit1->Paragraph->Alignment = taRightJustify;
}

Agora precisamos forçar a mudança espontânea do estado (Down) dos botões. O código abaixo verifica a necessidade de alterar o estado dos botões através do evento OnKeyUp.

void __fastcall TForm1::RichEdit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
if(RichEdit1->Paragraph->Alignment == taLeftJustify)
  SpdBtAlinhEsq->Down = true;
else if(RichEdit1->Paragraph->Alignment == taCenter)
  SpdBtAlinhCent->Down = true;
else if(RichEdit1->Paragraph->Alignment == taRightJustify)
  SpdBtAlinhDir->Down = true;
}

Que tal acrescentarmos alguns botões para alterar atributos de texto (negrito, itálico e sublinhado).

Nota: SelAttributes descreve as características de um texto selecionado num controle RichEdit
Use SelAttributes para revelar ou adaptar as características de font do texto selecionado. SelAttributes é um objeto TTextAttributes, que especifica características como font face, color, size, style, e pitch (número de caracteres). Para mudar um único atributo de um texto selecionado, interprete SelAttributes, e adapte uma de suas propriedades. Para mudar todos os atributos de um texto selecionado, coloque SelAttributes para um objeto TTextAttributes que represente a configuração desejada dos atributos. Se não há texto selecionado, SelAttributes representa a posição do cursor.
Quando inserimos um novo texto, as características da font são compatíveis com SelAttributes.
SelAttributes está disponível apenas em tempo de execução.

Introduziremos juntos o código responsável pelo sublinhamento do texto. Os códigos necessários ao negrito e itálico ficarão por sua conta.
Não é novidade para você SpeedButtons trabalhando em grupo (GroupIndex). Já imaginou um grupo de apenas um botão? Pois bem, é o que faremos agora. Cada um dos botões deverá ter um número único na propriedade GroupIndex, ou seja, apenas um botão com tal número.
Coloque três SpeedButtons no Form. Do botão responsável pelo sublinhamento do texto, altere a propriedade Name para SpdBtUnderline. Altere a propriedade AllowAllUp desse botão para true; e altere o número do GroupIndex do mesmo para 2 (lembre-se de que nenhum outro botão poderá ter esse mesmo número para essa propriedade). Com esses dois simples procedimentos nós damos um primeiro passo para forçar o botão a trabalhar down ou up corretamente. Vejamos o código responsável pelas ações de sublinhamento:

void __fastcall TForm1::SpdBtUnderlineClick(TObject *Sender)
{
// Se o texto selecionado em RichEdit1 não está sublinhado
if(RichEdit1->SelAttributes->Style == RichEdit1->SelAttributes->Style >> fsUnderline)
// ele será sublinhado
RichEdit1->SelAttributes->Style = RichEdit1->SelAttributes->Style << fsUnderline;
else if // ou então
// se o texto estiver sublinhado
(RichEdit1->SelAttributes->Style == RichEdit1->SelAttributes->Style << fsUnderline)
// ele será desmarcado
RichEdit1->SelAttributes->Style = RichEdit1->SelAttributes->Style >> fsUnderline;
}

Agora nós vamos acrescentar uma linha de código no evento OnKeyUp de RichEdit1:

void __fastcall TForm1::RichEdit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
//atualiza o estado (Down) do botão de sublinhado
SpdBtUnderline->Down = RichEdit1->SelAttributes->Style.Contains(fsUnderline);

//atualiza o estado (Down) dos botões - alinhamento de texto
if(RichEdit1->Paragraph->Alignment == taLeftJustify)
 SpdBtAlinhEsq->Down = true;
else if(RichEdit1->Paragraph->Alignment == taCenter)
  SpdBtAlinhCent->Down = true;
else if(RichEdit1->Paragraph->Alignment == taRightJustify)
  SpdBtAlinhDir->Down = true;
}

Com isso encerramos esta seção do Editor, cabendo lembrar que para negrito usamos fsBold, enquanto para itálico usamos fsItalic.

Vamos agora colocar comando para alterar o tipo (Name) e o tamanho (Size) das fontes de nosso Editor. Para manejar o tipo, usaremos um ComboBox e, para o tamanho, um Edit conjugado com um UpDown. Desta feita, como exercício, forneceremos apenas os códigos necessários ao funcionamento destas ações, com um comentário básico. Caberá a você quebrar um pouquinho a cabeça para entendê-los. Se você vem acompanhando este curso desde o seu início, a compreensão será moleza!
Notas: 1º. Observe que alguns códigos são novos; outros, não (visto que já estavam no Editor); 2º Impeça que o usuário digite no ComboBox e altere o seu texto inicial, de acordo com a fonte padrão em seu sitema.

UpDown: Associate = Edit1; Max = 72; Min = 1.

//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
 // ComboBox com os nomes das fontes instaladas no sistema
  ComboBox1->Items->Assign(Screen->Fonts);

        PopupMenu1 -> AutoPopup = true;
        RichEdit1 -> PopupMenu = PopupMenu1;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::RichEdit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
//atualiza o tipo (Name) de font no ComboBox
ComboBox1->Text = RichEdit1->SelAttributes->Name;

//Atualiza o tamanho (Size) da font em Edit1
Edit1->Text = RichEdit1->SelAttributes->Size;

//atualiza o estado (Down) do botão de sublinhado
SpeedButton10->Down = RichEdit1->SelAttributes->Style.Contains(fsUnderline);

//atualiza o estado (Down) dos botões - alinhamento de texto
if(RichEdit1->Paragraph->Alignment == taLeftJustify)
  SpdBtAlinhEsq->Down = true;
else if(RichEdit1->Paragraph->Alignment == taCenter)
  SpdBtAlinhCent->Down = true;
else if(RichEdit1->Paragraph->Alignment == taRightJustify)
  SpdBtAlinhDir->Down = true;

}
//---------------------------------------------------------------------------

void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
  // O tipo de font de RichEdit1 será igual os especificado em ComboBox1
  RichEdit1->SelAttributes->Name = ComboBox1->Items->Strings[ComboBox1->ItemIndex];
  // Coloca o foco em RichEdit1
  RichEdit1->SetFocus();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit1Change(TObject *Sender)
{
  // O size da font de RichEdit1 será
  // igual ao especificado em Edit1
  RichEdit1->SelAttributes->Size = atoi(Edit1->Text.c_str());
}
//---------------------------------------------------------------------------

 

void __fastcall TForm1::Edit1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  // impede que o usuário digite no Edit
  RichEdit1->SetFocus();
}
//---------------------------------------------------------------------------

A seguir, entraremos na parte final da construção do nosso editor, aprendendo a construir uma régua através da qual poderemos estabelecer limites para os textos com os quais estivermos trabalhando.

Passemos agora à construção da régua naquele segundo componente Panel já colocado no Editor. Visualize previamente a disposição dos componentes da régua:

Eis as propriedades de Panel2:

Propriedades

Align = alTop
Alignment = taLeftJustify
Bevellnner = bvLowered
BevelWidth = 1 //profundidade da borda
BorderWidth = 1 //largura da borda

Coloque um Bevel (paleta Additional) dentro do Panel e procure deixá-lo parecido com uma linha que divida o comprimento do Panel em duas partes iguais (conforme ilustração). Eis as propriedades:

Propriedades de Bevel1

Align = alNone
Height = 2
Left = 4
Shape = bsTopLine
Stile = bsLowered

Coloque um Label (Label1) acima do Bevel e outro abaixo (Label2)

 

Propriedades de Label1

AutoSize = true

Caption = ê

DragCursor = crArrow

Font = Charset = DEFAULT_CHARSET
Color = clRed
Height = -11
Name = Wingdings
Pitch = fpDefault
Size = 8
Stile = []

Height = 11

Left = 2

No Label2 as propriedades são as mesmas, exceto Caption = é

Coloque outro Label no Panel. O Label3 deverá ser colocado próximo à extremidade direita inferior do componente Panel1, com as mesmas propriedades supramencionadas para Label1 e Label2, excetuando sua propriedades Caption que será = ñ, Font Color = clBlue e Left = 770. Uma vez adotadas essas providências, a régua de nosso projeto já deverá estar parecida com a ilustração acima.

Vá para o Editor de Códigos e, na paleta onde está escrito o nome do arquivo (se não foi alterado quando salvo, deve chamar-se: Unit1.cpp) dê um clique com o botão direito do mouse e escolha: Open Sourse/Hearder File ou simplesmente aperte as teclas Ctrl + F6. Será aberto outro arquivo, o Unit1.h.
Em class TForm1 : public TForm, __published: declare o protótipo da função MudarParag() e, na parte privada da Classe, declare os dados membros: int Arrastar e bool Arrastando e a função void __fastcall Regua(void). A parte privada fica entre a declaração private e a declaração public. Observe abaixo como deverá ficar o código:

        (...)
class TForm1 : public TForm
{
__published:
        (...)
        void __fastcall MudarParag(TObject *Sender);

private:        // User declarations
        (...)
        int Arrastar;
        bool Arrastando;
        void __fastcall Regua(void);

public:         // User declarations

        __fastcall TForm1(TComponent* Owner);
};
        (...)

Nota: os trechos (...) não constituem parte integrante do código, mas indicam que trechos do mesmo foram suprimidos porque, no momento, não nos interessa vê-los.
Não se esqueça de introduzir comentários indicando tratar-se de linhas de código para régua!
Volte para o arquivo Unit1.cpp e faça o seguinte:
Declare e inicialize duas constantes globais:

//-------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//distância que o cursor fica da seta conforme a movimentamos
const float Reg = 4.0 / 3.0;
//Distância dos dois Labels da margem esquerda
const int Semp = 6;
//-------------------------------------------------------------------
#pragma package(smart_init)

Passemos à definição da função void __fastcall TForm1::MudarParag(TObject * Sender):

/************digite todo o código abaixo, inclusive o cabeçalho da
             função, pois essa função não foi criada automaticamente
             pelo compilador, mas por nós.
             observe que os comandos if() estabelecem posições
             limítrofres para os componentes Label***************/
void __fastcall TForm1::MudarParag(TObject * Sender)
{
   Label1->Left = int(RichEdit1->Paragraph->FirstIndent*Reg)-4+Semp;
      if (Label1->Left >= 772)
         Label1->Left = 770;

           Label2->Left  = int((RichEdit1->Paragraph->LeftIndent+
           RichEdit1->Paragraph->FirstIndent)*Reg)-4+Semp;
           if (Label2->Left >= 770)
                    Label2->Left = 748;

           Label3->Left = Panel1->ClientWidth-6-int(
                 (RichEdit1->Paragraph->RightIndent+Semp)*Reg);
           if (Label3->Left >= 772)
           Label3->Left = 770;
                 if (Label3->Left <= 12)
                    Label3->Left = 13;
}

 

TparaAttributes::FirstIndent - FirstIndent especifica a distância, em pixels, da primeira linha de um parágrafo relativa à margem esquerda.
TparaAttributes::LeftIndent - LeftIndent especifica a distância, em pixels, do parágrafo relativa à margem esquerda.
TparaAttributes::RightIndent - RightIndent especifica a distância, em pixels, do parágrafo relativa à margem direita.
TcustomRichEdit.Paragraph - Paragraph especifica a informação de formatação do atual parágrafo. Interprete Paragraph para colocar objeto TParaAttributes usado por controle rich edit para especificar a informação da formatação do parágrafo. Use o objeto TParaAttributes para ler ou escrever a informação da formatação do parágrafo para o atual parágrafo. Estas informações incluem alignment, indentation, numbering e tabs. Paragraph é uma propriedade read-only, porque um objeto TCustomRichEdit possui somente um objeto TParaAttributes, que não pode ser mudado. Os atributos do corrente parágrafo, entretanto, pode ser mudado pela colocação marcação da propriedade do objeto TParaAttributes. Estes pode ser setados um por um, ou todos podem ser setados para um valor de um objeto TParaAttributes existente pelo uso Paragraph->Assign.
O parágrafo atual são os parágrafos que contém o texto selecionado. Se não há texto selecionado, o atual parágrafo é aquele que contém o cursor.
Nota: Paragraph está disponível somente em tempo de execução.

Na paleta Events de RichEdit1, em OnSelectionChange selecione a função MudarParag, o que fará que as setas Lable(s) sejam atualizadas conforme o movimento do cursor no texto.
Nota: para efetuar a associação acima, dê um clique no lado direito do evento mencionado no Object Inspector. Dê um clique na seta que aparecerá e escolha a função mencionada no combobox que se abrirá.
Passaremos agora à definição da função void __fastcall TForm1::Regua(void). Não se esqueça de que esta função não foi criada automaticamente pelo C++Builder:

void __fastcall TForm1::Regua(void)
{
    int Distanc = 0; // variável para o contador
      while (Distanc < 15) // números da régua
      {
        Distanc++; //incremento
        // imprime os números de 1 a 15 na régua
        Panel1->Caption = Panel1->Caption  + '\t' + Distanc;
      }
}

Nota: Seria interessante colocar, manualmente, aquelas linhas de comentários usadas pelo compilador C++Builder (//----------) para separar essas instruções.
Precisamos fazer o compilador chamar a função acima na criação do formulário (evento OnCreate do Form):

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    // chama a função régua na criação do formulário
    Regua();
    (...)
}

Ajuste a propriedade Height de Bevel1 para 1.
A régua ainda não está funcional, mas já pode ser visualizada em tempo de execução.

Inicialmente vamos nos lembrar das funcionalidades básicas dos eventos OnMouseDown, OnMouseMove e OnMouseUP:

Tcontrol::OnMouseDown - OnMouseDown ocorre quando, estando o ponteiro do mouse sobre um controle, o usuário pressiona um botão do mouse. O evento OnMouseDown pode responder ao pressionamento sobre os botões esquerdo, direito e central do mouse, bem como sobre combinações entre botões do mouse com as teclas Shift, Ctrl, e Alt. X e Y são as coordenadas (pixel) do ponteiro do mouse na client area do Sender.
Tcontrol::OnMouseMove - OnMouseMove ocorre quando o usuário move o ponteiros do mouse, enquanto o ponteiro do mouso está sobre algum controle. Podemos usar o parâmetro Shift do evento OnMouseDown para determinar o estado das teclas (ou combinações) mencionadas no tópico anterior ( OnMouseDown ). X e Y são as coordenadas (pixel) da nova localização do ponteiro do mouse client area do Sender .
Tcontrol::OnMouseUpOnMouseUp ocorre no momento em que o usuário está liberando o botão do mouse que estava pressionando um componente com o ponteiro do mouse. O evento OnMouseUp pode responder a ações sobre os botões esquerdo, direito e central do mouse, bem como sobre combinações entre botões do mouse com as teclas Shift, Ctrl, e Alt. X e Y são as coordenadas (pixel) do ponteiro do mouse na client area do Sender.

Passemos ao tratamento desses eventos nos componentes envolvidos nas ações respectivas. Inicialmente vamos escrever uma função responsável pelo evento OnMouseDown, associando-a aos componentes respectivos. Em Unit1.h, na parte __published, declare o protótipo da função AreguaMouseDown():

void __fastcall AReguaMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);

Agora, em Unit1.cpp:


void __fastcall TForm1::AReguaMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
     TLabel * oTmpLabel = (TLabel *)Sender;
     Arrastar = oTmpLabel->Width / 2;
     oTmpLabel->Left = oTmpLabel->Left+X-Arrastar;
     Arrastando = True;
}

Vá ao Object Inspector e, na guia Events, associe os eventos OnMouseDown de Label1, Label2 e Label3 à função AreguaMouseDown (do mesmo modo que procedemos para associar RichEdit1 a OnSelectionChange).

Agora vamos tratar o evento OnMouseMove. O procedimento é bem parecido com o anterior. Em __published de Unit1.h:

void __fastcall AReguaMouseMove(TObject *Sender, TShiftState Shift,
 int X, int Y);

Em Unit1.cpp:

void __fastcall TForm1::AReguaMouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
   if (Arrastando) {
        TLabel * oTmpLabel = (TLabel *)Sender;
        oTmpLabel->Left = oTmpLabel->Left+X-Arrastar;
     }
}

Vá ao Object Inspector e, na guia Events, associe os eventos OnMouseMove de Label1, Label2 e Label3 à função AReguaMouseMove.

No que se refere a OnMouseUP, Label1, Label2 e Label3 não compartilham o mesmo evento. Devemos, então, proceder ao tratamento individual para cada componente. Iniciemos por Label1. Na guia Events, dê um clique em OnMouseUp, para chamar o método correspondente. Eis o código:

void __fastcall TForm1::Label1MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
     Arrastando = False;
     RichEdit1 -> Paragraph -> FirstIndent =
     int((Label1->Left+Arrastar-Semp) / Reg);
     Label2MouseUp(Sender, Button, Shift, X, Y);
}

Eis o código para o evento OnMouseUp de Label2:

void __fastcall TForm1::Label2MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
     Arrastando = False;
     RichEdit1->Paragraph->LeftIndent =
     int((Label2->Left+Arrastar-Semp) / Reg)-RichEdit1 ->Paragraph ->FirstIndent;
     MudarParag(Sender);
}

Aqui está o código para o mesmo evento de Label3:

void __fastcall TForm1::Label3MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    Arrastando = False;
     RichEdit1->Paragraph->RightIndent =
         int((Panel2->ClientWidth-Label3->Left+Arrastar-2) /
         Reg)-2*Semp;
     MudarParag(Sender);
}

Ajuste a Propriedade TabStop dos controles RichEdi1, ComboBox1 e Edit1 para false (para impedir que o foco saia de RichEdit1 quando for pressionada a tecla de tabulação); Ajuste a Propriedade WantTabs de RichEdit1 para true (ativará a marcação de textos com tabulações nesse componente).

Assim, conforme prometido no início deste curso, completamos o nosso Editor de Textos. Na verdade, fomos além daquilo que propusemos, uma vez que esse editor não está limitado a edição de textos não formatados como o Bloco de Notas do Windows. Para criar um Editor como o NotePad, use um componente Memo no lugar do RichEdit, mas com consciência de que as formatações possibilitadas por este Editor estarão ausentes naquele!

Por fim, informamos que o nosso Editor pode ser melhorado. Agora cabe a você procurar as falhas e corrigi-las, uma vez que o tratamento de erros ou melhora na lógica dos comandos é uma obrigação fundamental de todos os programadores.

A seguir, dando continuidade no curso, estaremos trabalhando na finalização do projeto Paint, visto que já possuímos praticamente todos os conceitos necessários à sua conclusão.

VMS Desenvolvimentos

Diversas Dicas, Apostilas, Arquivos Fontes, Tutoriais, Vídeo Aula, Download de Arquivos Relacionado a Programação em C++ Builder.

Voltar ao Site